1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 22:33:35 +00:00

[PM-14423] item view security task (#13485)

* show pending change password tasks for ciphers in extension
This commit is contained in:
Jason Ng
2025-03-04 12:18:40 -05:00
committed by GitHub
parent bfbad99fb7
commit f7642aa0c6
15 changed files with 177 additions and 20 deletions

View File

@@ -5163,5 +5163,8 @@
}, },
"updateDesktopAppOrDisableFingerprintDialogMessage": { "updateDesktopAppOrDisableFingerprintDialogMessage": {
"message": "To use biometric unlock, please update your desktop application, or disable fingerprint unlock in the desktop settings." "message": "To use biometric unlock, please update your desktop application, or disable fingerprint unlock in the desktop settings."
},
"changeAtRiskPassword": {
"message": "Change at-risk password"
} }
} }

View File

@@ -37,8 +37,16 @@ import {
IconButtonModule, IconButtonModule,
SearchModule, SearchModule,
ToastService, ToastService,
CalloutModule,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { CipherViewComponent, CopyCipherFieldService } from "@bitwarden/vault"; import {
ChangeLoginPasswordService,
CipherViewComponent,
CopyCipherFieldService,
DefaultChangeLoginPasswordService,
DefaultTaskService,
TaskService,
} from "@bitwarden/vault";
import { BrowserApi } from "../../../../../platform/browser/browser-api"; import { BrowserApi } from "../../../../../platform/browser/browser-api";
import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils"; import BrowserPopupUtils from "../../../../../platform/popup/browser-popup-utils";
@@ -82,10 +90,13 @@ type LoadAction =
CipherViewComponent, CipherViewComponent,
AsyncActionsModule, AsyncActionsModule,
PopOutComponent, PopOutComponent,
CalloutModule,
], ],
providers: [ providers: [
{ provide: ViewPasswordHistoryService, useClass: BrowserViewPasswordHistoryService }, { provide: ViewPasswordHistoryService, useClass: BrowserViewPasswordHistoryService },
{ provide: PremiumUpgradePromptService, useClass: BrowserPremiumUpgradePromptService }, { provide: PremiumUpgradePromptService, useClass: BrowserPremiumUpgradePromptService },
{ provide: TaskService, useClass: DefaultTaskService },
{ provide: ChangeLoginPasswordService, useClass: DefaultChangeLoginPasswordService },
], ],
}) })
export class ViewV2Component { export class ViewV2Component {

View File

@@ -3604,5 +3604,8 @@
}, },
"updateBrowserOrDisableFingerprintDialogMessage": { "updateBrowserOrDisableFingerprintDialogMessage": {
"message": "The browser extension you are using is out of date. Please update it or disable browser integration fingerprint validation in the desktop app settings." "message": "The browser extension you are using is out of date. Please update it or disable browser integration fingerprint validation in the desktop app settings."
},
"changeAtRiskPassword": {
"message": "Change at-risk password"
} }
} }

View File

@@ -7,7 +7,9 @@ import { mock } from "jest-mock-extended";
import { CollectionService } from "@bitwarden/admin-console/common"; import { CollectionService } from "@bitwarden/admin-console/common";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
@@ -15,6 +17,7 @@ import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folde
import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { DialogService } from "@bitwarden/components"; import { DialogService } from "@bitwarden/components";
import { ChangeLoginPasswordService, TaskService } from "@bitwarden/vault";
import { EmergencyViewDialogComponent } from "./emergency-view-dialog.component"; import { EmergencyViewDialogComponent } from "./emergency-view-dialog.component";
@@ -52,7 +55,34 @@ describe("EmergencyViewDialogComponent", () => {
{ provide: DIALOG_DATA, useValue: { cipher: mockCipher } }, { provide: DIALOG_DATA, useValue: { cipher: mockCipher } },
{ provide: AccountService, useValue: accountService }, { provide: AccountService, useValue: accountService },
], ],
}).compileComponents(); })
.overrideComponent(EmergencyViewDialogComponent, {
remove: {
providers: [
{ provide: PlatformUtilsService, useValue: PlatformUtilsService },
{
provide: ChangeLoginPasswordService,
useValue: ChangeLoginPasswordService,
},
{ provide: ConfigService, useValue: ConfigService },
],
},
add: {
providers: [
{
provide: TaskService,
useValue: mock<TaskService>(),
},
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
{
provide: ChangeLoginPasswordService,
useValue: mock<ChangeLoginPasswordService>(),
},
{ provide: ConfigService, useValue: mock<ConfigService>() },
],
},
})
.compileComponents();
fixture = TestBed.createComponent(EmergencyViewDialogComponent); fixture = TestBed.createComponent(EmergencyViewDialogComponent);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@@ -9,7 +9,7 @@ import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions
import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components";
import { CipherViewComponent } from "@bitwarden/vault"; import { CipherViewComponent, DefaultTaskService, TaskService } from "@bitwarden/vault";
import { WebViewPasswordHistoryService } from "../../../../vault/services/web-view-password-history.service"; import { WebViewPasswordHistoryService } from "../../../../vault/services/web-view-password-history.service";
@@ -33,6 +33,7 @@ class PremiumUpgradePromptNoop implements PremiumUpgradePromptService {
providers: [ providers: [
{ provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService }, { provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService },
{ provide: PremiumUpgradePromptService, useClass: PremiumUpgradePromptNoop }, { provide: PremiumUpgradePromptService, useClass: PremiumUpgradePromptNoop },
{ provide: TaskService, useClass: DefaultTaskService },
], ],
}) })
export class EmergencyViewDialogComponent { export class EmergencyViewDialogComponent {

View File

@@ -36,6 +36,7 @@ import {
ToastService, ToastService,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { import {
ChangeLoginPasswordService,
CipherAttachmentsComponent, CipherAttachmentsComponent,
CipherFormComponent, CipherFormComponent,
CipherFormConfig, CipherFormConfig,
@@ -43,6 +44,9 @@ import {
CipherFormModule, CipherFormModule,
CipherViewComponent, CipherViewComponent,
DecryptionFailureDialogComponent, DecryptionFailureDialogComponent,
DefaultChangeLoginPasswordService,
DefaultTaskService,
TaskService,
} from "@bitwarden/vault"; } from "@bitwarden/vault";
import { SharedModule } from "../../../shared/shared.module"; import { SharedModule } from "../../../shared/shared.module";
@@ -136,6 +140,8 @@ export enum VaultItemDialogResult {
{ provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService }, { provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService },
{ provide: CipherFormGenerationService, useClass: WebCipherFormGenerationService }, { provide: CipherFormGenerationService, useClass: WebCipherFormGenerationService },
RoutedVaultFilterService, RoutedVaultFilterService,
{ provide: TaskService, useClass: DefaultTaskService },
{ provide: ChangeLoginPasswordService, useClass: DefaultChangeLoginPasswordService },
], ],
}) })
export class VaultItemDialogComponent implements OnInit, OnDestroy { export class VaultItemDialogComponent implements OnInit, OnDestroy {

View File

@@ -12,6 +12,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
@@ -21,6 +22,7 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
import { DialogService, ToastService } from "@bitwarden/components"; import { DialogService, ToastService } from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management"; import { KeyService } from "@bitwarden/key-management";
import { ChangeLoginPasswordService, DefaultTaskService, TaskService } from "@bitwarden/vault";
import { ViewCipherDialogParams, ViewCipherDialogResult, ViewComponent } from "./view.component"; import { ViewCipherDialogParams, ViewCipherDialogResult, ViewComponent } from "./view.component";
@@ -82,7 +84,33 @@ describe("ViewComponent", () => {
}, },
}, },
], ],
}).compileComponents(); })
.overrideComponent(ViewComponent, {
remove: {
providers: [
{ provide: TaskService, useClass: DefaultTaskService },
{ provide: PlatformUtilsService, useValue: PlatformUtilsService },
{
provide: ChangeLoginPasswordService,
useValue: ChangeLoginPasswordService,
},
],
},
add: {
providers: [
{
provide: TaskService,
useValue: mock<TaskService>(),
},
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
{
provide: ChangeLoginPasswordService,
useValue: mock<ChangeLoginPasswordService>(),
},
],
},
})
.compileComponents();
fixture = TestBed.createComponent(ViewComponent); fixture = TestBed.createComponent(ViewComponent);
component = fixture.componentInstance; component = fixture.componentInstance;

View File

@@ -26,7 +26,7 @@ import {
DialogService, DialogService,
ToastService, ToastService,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { CipherViewComponent } from "@bitwarden/vault"; import { CipherViewComponent, DefaultTaskService, TaskService } from "@bitwarden/vault";
import { SharedModule } from "../../shared/shared.module"; import { SharedModule } from "../../shared/shared.module";
import { WebVaultPremiumUpgradePromptService } from "../services/web-premium-upgrade-prompt.service"; import { WebVaultPremiumUpgradePromptService } from "../services/web-premium-upgrade-prompt.service";
@@ -74,6 +74,7 @@ export interface ViewCipherDialogCloseResult {
providers: [ providers: [
{ provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService }, { provide: ViewPasswordHistoryService, useClass: WebViewPasswordHistoryService },
{ provide: PremiumUpgradePromptService, useClass: WebVaultPremiumUpgradePromptService }, { provide: PremiumUpgradePromptService, useClass: WebVaultPremiumUpgradePromptService },
{ provide: TaskService, useClass: DefaultTaskService },
], ],
}) })
export class ViewComponent implements OnInit { export class ViewComponent implements OnInit {

View File

@@ -10503,6 +10503,9 @@
"assignedExceedsAvailable": { "assignedExceedsAvailable": {
"message": "Assigned seats exceed available seats." "message": "Assigned seats exceed available seats."
}, },
"changeAtRiskPassword": {
"message": "Change at-risk password"
},
"removeUnlockWithPinPolicyTitle": { "removeUnlockWithPinPolicyTitle": {
"message": "Remove Unlock with PIN" "message": "Remove Unlock with PIN"
}, },

View File

@@ -3,6 +3,19 @@
{{ "cardExpiredMessage" | i18n }} {{ "cardExpiredMessage" | i18n }}
</bit-callout> </bit-callout>
<ng-container *ngIf="isSecurityTasksEnabled$ | async">
<bit-callout
*ngIf="cipher?.login.uris.length > 0 && hadPendingChangePasswordTask"
type="warning"
[title]="''"
>
<i class="bwi bwi-exclamation-triangle tw-text-warning" aria-hidden="true"></i>
<a bitLink (click)="launchChangePassword()">
{{ "changeAtRiskPassword" | i18n }}
<i class="bwi bwi-popout tw-ml-1" aria-hidden="true"></i>
</a>
</bit-callout>
</ng-container>
<!-- HELPER TEXT --> <!-- HELPER TEXT -->
<p <p
class="tw-text-sm tw-text-muted" class="tw-text-sm tw-text-muted"
@@ -23,7 +36,15 @@
</app-item-details-v2> </app-item-details-v2>
<!-- LOGIN CREDENTIALS --> <!-- LOGIN CREDENTIALS -->
<app-login-credentials-view *ngIf="hasLogin" [cipher]="cipher"></app-login-credentials-view> <app-login-credentials-view
*ngIf="hasLogin"
[cipher]="cipher"
[activeUserId]="activeUserId$ | async"
[hadPendingChangePasswordTask]="
hadPendingChangePasswordTask && (isSecurityTasksEnabled$ | async)
"
(handleChangePassword)="launchChangePassword()"
></app-login-credentials-view>
<!-- AUTOFILL OPTIONS --> <!-- AUTOFILL OPTIONS -->
<app-autofill-options-view <app-autofill-options-view

View File

@@ -1,6 +1,6 @@
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { Component, Input, OnChanges, OnDestroy } from "@angular/core"; import { Component, Input, OnChanges, OnDestroy } from "@angular/core";
import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs"; import { firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
@@ -12,11 +12,17 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { isCardExpired } from "@bitwarden/common/autofill/utils"; import { isCardExpired } from "@bitwarden/common/autofill/utils";
import { CollectionId } from "@bitwarden/common/types/guid"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CollectionId, UserId } from "@bitwarden/common/types/guid";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import { CalloutModule, SearchModule } from "@bitwarden/components"; import { AnchorLinkDirective, CalloutModule, SearchModule } from "@bitwarden/components";
import { ChangeLoginPasswordService } from "../abstractions/change-login-password.service";
import { TaskService, SecurityTaskType } from "../tasks";
import { AdditionalOptionsComponent } from "./additional-options/additional-options.component"; import { AdditionalOptionsComponent } from "./additional-options/additional-options.component";
import { AttachmentsV2ViewComponent } from "./attachments/attachments-v2-view.component"; import { AttachmentsV2ViewComponent } from "./attachments/attachments-v2-view.component";
@@ -48,12 +54,13 @@ import { ViewIdentitySectionsComponent } from "./view-identity-sections/view-ide
ViewIdentitySectionsComponent, ViewIdentitySectionsComponent,
LoginCredentialsViewComponent, LoginCredentialsViewComponent,
AutofillOptionsViewComponent, AutofillOptionsViewComponent,
AnchorLinkDirective,
], ],
}) })
export class CipherViewComponent implements OnChanges, OnDestroy { export class CipherViewComponent implements OnChanges, OnDestroy {
@Input({ required: true }) cipher: CipherView | null = null; @Input({ required: true }) cipher: CipherView | null = null;
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); activeUserId$ = getUserId(this.accountService.activeAccount$);
/** /**
* Optional list of collections the cipher is assigned to. If none are provided, they will be fetched using the * Optional list of collections the cipher is assigned to. If none are provided, they will be fetched using the
@@ -68,12 +75,18 @@ export class CipherViewComponent implements OnChanges, OnDestroy {
folder$: Observable<FolderView | undefined> | undefined; folder$: Observable<FolderView | undefined> | undefined;
private destroyed$: Subject<void> = new Subject(); private destroyed$: Subject<void> = new Subject();
cardIsExpired: boolean = false; cardIsExpired: boolean = false;
hadPendingChangePasswordTask: boolean = false;
isSecurityTasksEnabled$ = this.configService.getFeatureFlag$(FeatureFlag.SecurityTasks);
constructor( constructor(
private organizationService: OrganizationService, private organizationService: OrganizationService,
private collectionService: CollectionService, private collectionService: CollectionService,
private folderService: FolderService, private folderService: FolderService,
private accountService: AccountService, private accountService: AccountService,
private defaultTaskService: TaskService,
private platformUtilsService: PlatformUtilsService,
private changeLoginPasswordService: ChangeLoginPasswordService,
private configService: ConfigService,
) {} ) {}
async ngOnChanges() { async ngOnChanges() {
@@ -137,7 +150,11 @@ export class CipherViewComponent implements OnChanges, OnDestroy {
); );
} }
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); const userId = await firstValueFrom(this.activeUserId$);
if (this.cipher.edit && this.cipher.viewPassword) {
await this.checkPendingChangePasswordTasks(userId);
}
if (this.cipher.organizationId && userId) { if (this.cipher.organizationId && userId) {
this.organization$ = this.organizationService this.organization$ = this.organizationService
@@ -147,15 +164,29 @@ export class CipherViewComponent implements OnChanges, OnDestroy {
} }
if (this.cipher.folderId) { if (this.cipher.folderId) {
const activeUserId = await firstValueFrom(this.activeUserId$);
if (!activeUserId) {
return;
}
this.folder$ = this.folderService this.folder$ = this.folderService
.getDecrypted$(this.cipher.folderId, activeUserId) .getDecrypted$(this.cipher.folderId, userId)
.pipe(takeUntil(this.destroyed$)); .pipe(takeUntil(this.destroyed$));
} }
} }
async checkPendingChangePasswordTasks(userId: UserId): Promise<void> {
const tasks = await firstValueFrom(this.defaultTaskService.pendingTasks$(userId));
this.hadPendingChangePasswordTask = tasks?.some((task) => {
return (
task.cipherId === this.cipher?.id && task.type === SecurityTaskType.UpdateAtRiskCredential
);
});
}
launchChangePassword = async () => {
if (this.cipher != null) {
const url = await this.changeLoginPasswordService.getChangePasswordUrl(this.cipher);
if (url == null) {
return;
}
this.platformUtilsService.launchUri(url);
}
};
} }

View File

@@ -89,6 +89,12 @@
(click)="logCopyEvent()" (click)="logCopyEvent()"
></button> ></button>
</bit-form-field> </bit-form-field>
<bit-hint *ngIf="hadPendingChangePasswordTask">
<a bitLink (click)="launchChangePasswordEvent()">
{{ "changeAtRiskPassword" | i18n }}
<i class="bwi bwi-popout tw-ml-1" aria-hidden="true"></i>
</a>
</bit-hint>
<div <div
*ngIf="showPasswordCount && passwordRevealed" *ngIf="showPasswordCount && passwordRevealed"
[ngClass]="{ 'tw-mt-3': !cipher.login.totp, 'tw-mb-2': true }" [ngClass]="{ 'tw-mt-3': !cipher.login.totp, 'tw-mb-2': true }"

View File

@@ -8,6 +8,7 @@ import { EventCollectionService } from "@bitwarden/common/abstractions/event/eve
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
import { EventType } from "@bitwarden/common/enums"; import { EventType } from "@bitwarden/common/enums";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
@@ -74,6 +75,7 @@ describe("LoginCredentialsViewComponent", () => {
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() }, { provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
{ provide: ToastService, useValue: mock<ToastService>() }, { provide: ToastService, useValue: mock<ToastService>() },
{ provide: I18nService, useValue: { t: (...keys: string[]) => keys.join(" ") } }, { provide: I18nService, useValue: { t: (...keys: string[]) => keys.join(" ") } },
{ provide: ConfigService, useValue: mock<ConfigService>() },
], ],
}).compileComponents(); }).compileComponents();

View File

@@ -1,7 +1,7 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { CommonModule, DatePipe } from "@angular/common"; import { CommonModule, DatePipe } from "@angular/common";
import { Component, inject, Input } from "@angular/core"; import { Component, EventEmitter, inject, Input, Output } from "@angular/core";
import { Observable, switchMap } from "rxjs"; import { Observable, switchMap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
@@ -10,6 +10,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
import { EventType } from "@bitwarden/common/enums"; import { EventType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { UserId } from "@bitwarden/common/types/guid";
import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { import {
@@ -17,6 +18,7 @@ import {
SectionComponent, SectionComponent,
SectionHeaderComponent, SectionHeaderComponent,
TypographyModule, TypographyModule,
LinkModule,
IconButtonModule, IconButtonModule,
BadgeModule, BadgeModule,
ColorPasswordModule, ColorPasswordModule,
@@ -46,10 +48,14 @@ type TotpCodeValues = {
ColorPasswordModule, ColorPasswordModule,
BitTotpCountdownComponent, BitTotpCountdownComponent,
ReadOnlyCipherCardComponent, ReadOnlyCipherCardComponent,
LinkModule,
], ],
}) })
export class LoginCredentialsViewComponent { export class LoginCredentialsViewComponent {
@Input() cipher: CipherView; @Input() cipher: CipherView;
@Input() activeUserId: UserId;
@Input() hadPendingChangePasswordTask: boolean;
@Output() handleChangePassword = new EventEmitter<void>();
isPremium$: Observable<boolean> = this.accountService.activeAccount$.pipe( isPremium$: Observable<boolean> = this.accountService.activeAccount$.pipe(
switchMap((account) => switchMap((account) =>
@@ -59,6 +65,7 @@ export class LoginCredentialsViewComponent {
showPasswordCount: boolean = false; showPasswordCount: boolean = false;
passwordRevealed: boolean = false; passwordRevealed: boolean = false;
totpCodeCopyObj: TotpCodeValues; totpCodeCopyObj: TotpCodeValues;
private datePipe = inject(DatePipe); private datePipe = inject(DatePipe);
constructor( constructor(
@@ -111,4 +118,8 @@ export class LoginCredentialsViewComponent {
this.cipher.organizationId, this.cipher.organizationId,
); );
} }
launchChangePasswordEvent(): void {
this.handleChangePassword.emit();
}
} }

View File

@@ -35,7 +35,7 @@ export class DefaultChangeLoginPasswordService implements ChangeLoginPasswordSer
]); ]);
if (!reliable || wellKnownChangeUrl == null) { if (!reliable || wellKnownChangeUrl == null) {
return cipher.login.uri; return url.origin;
} }
return wellKnownChangeUrl; return wellKnownChangeUrl;