mirror of
https://github.com/bitwarden/browser
synced 2025-12-21 10:43:35 +00:00
[PM-14419] At-risk passwords change password service (#13279)
* [PM-14419] Introduce the change-login-password service and its default implementation * [PM-14419] Use the change login password service on the at-risk passwords page * [PM-14419] Add unit tests * [PM-14419] Use existing fixed test environment * [PM-14419] Add mock implementation for ChangeLoginPasswordService in at-risk passwords tests * [PM-14419] Linter
This commit is contained in:
@@ -59,11 +59,20 @@
|
||||
type="button"
|
||||
bitBadge
|
||||
variant="primary"
|
||||
appStopProp
|
||||
(click)="launchChangePassword(cipher)"
|
||||
[title]="'changeButtonTitle' | i18n: cipher.name"
|
||||
[attr.aria-label]="'changeButtonTitle' | i18n: cipher.name"
|
||||
[disabled]="launchingCipher() == cipher"
|
||||
>
|
||||
{{ "change" | i18n }}
|
||||
<ng-container *ngIf="launchingCipher() != cipher">
|
||||
{{ "change" | i18n }}
|
||||
</ng-container>
|
||||
<i
|
||||
*ngIf="launchingCipher() == cipher"
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</bit-item-action>
|
||||
</bit-item>
|
||||
|
||||
@@ -18,6 +18,8 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
import {
|
||||
ChangeLoginPasswordService,
|
||||
DefaultChangeLoginPasswordService,
|
||||
PasswordRepromptService,
|
||||
SecurityTask,
|
||||
SecurityTaskType,
|
||||
@@ -70,6 +72,7 @@ describe("AtRiskPasswordsComponent", () => {
|
||||
const setInlineMenuVisibility = jest.fn();
|
||||
const mockToastService = mock<ToastService>();
|
||||
const mockAtRiskPasswordPageService = mock<AtRiskPasswordPageService>();
|
||||
const mockChangeLoginPasswordService = mock<ChangeLoginPasswordService>();
|
||||
|
||||
beforeEach(async () => {
|
||||
mockTasks$ = new BehaviorSubject<SecurityTask[]>([
|
||||
@@ -156,12 +159,16 @@ describe("AtRiskPasswordsComponent", () => {
|
||||
.overrideComponent(AtRiskPasswordsComponent, {
|
||||
remove: {
|
||||
imports: [PopupHeaderComponent, PopupPageComponent],
|
||||
providers: [AtRiskPasswordPageService],
|
||||
providers: [
|
||||
AtRiskPasswordPageService,
|
||||
{ provide: ChangeLoginPasswordService, useClass: DefaultChangeLoginPasswordService },
|
||||
],
|
||||
},
|
||||
add: {
|
||||
imports: [MockPopupHeaderComponent, MockPopupPageComponent],
|
||||
providers: [
|
||||
{ provide: AtRiskPasswordPageService, useValue: mockAtRiskPasswordPageService },
|
||||
{ provide: ChangeLoginPasswordService, useValue: mockChangeLoginPasswordService },
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, inject } from "@angular/core";
|
||||
import { Component, inject, signal } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { combineLatest, firstValueFrom, map, of, shareReplay, startWith, switchMap } from "rxjs";
|
||||
|
||||
@@ -16,7 +16,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import {
|
||||
BadgeComponent,
|
||||
BadgeModule,
|
||||
ButtonModule,
|
||||
CalloutModule,
|
||||
ItemModule,
|
||||
@@ -24,6 +24,8 @@ import {
|
||||
TypographyModule,
|
||||
} from "@bitwarden/components";
|
||||
import {
|
||||
ChangeLoginPasswordService,
|
||||
DefaultChangeLoginPasswordService,
|
||||
filterOutNullish,
|
||||
PasswordRepromptService,
|
||||
SecurityTaskType,
|
||||
@@ -37,8 +39,6 @@ import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page
|
||||
import { AtRiskPasswordPageService } from "./at-risk-password-page.service";
|
||||
|
||||
@Component({
|
||||
selector: "vault-at-risk-passwords",
|
||||
standalone: true,
|
||||
imports: [
|
||||
PopupPageComponent,
|
||||
PopupHeaderComponent,
|
||||
@@ -46,12 +46,17 @@ import { AtRiskPasswordPageService } from "./at-risk-password-page.service";
|
||||
ItemModule,
|
||||
CommonModule,
|
||||
JslibModule,
|
||||
BadgeComponent,
|
||||
TypographyModule,
|
||||
CalloutModule,
|
||||
ButtonModule,
|
||||
BadgeModule,
|
||||
],
|
||||
providers: [AtRiskPasswordPageService],
|
||||
providers: [
|
||||
AtRiskPasswordPageService,
|
||||
{ provide: ChangeLoginPasswordService, useClass: DefaultChangeLoginPasswordService },
|
||||
],
|
||||
selector: "vault-at-risk-passwords",
|
||||
standalone: true,
|
||||
templateUrl: "./at-risk-passwords.component.html",
|
||||
})
|
||||
export class AtRiskPasswordsComponent {
|
||||
@@ -60,12 +65,20 @@ export class AtRiskPasswordsComponent {
|
||||
private cipherService = inject(CipherService);
|
||||
private i18nService = inject(I18nService);
|
||||
private accountService = inject(AccountService);
|
||||
private platformUtilsService = inject(PlatformUtilsService);
|
||||
private passwordRepromptService = inject(PasswordRepromptService);
|
||||
private router = inject(Router);
|
||||
private autofillSettingsService = inject(AutofillSettingsServiceAbstraction);
|
||||
private toastService = inject(ToastService);
|
||||
private atRiskPasswordPageService = inject(AtRiskPasswordPageService);
|
||||
private changeLoginPasswordService = inject(ChangeLoginPasswordService);
|
||||
private platformUtilsService = inject(PlatformUtilsService);
|
||||
|
||||
/**
|
||||
* The cipher that is currently being launched. Used to show a loading spinner on the badge button.
|
||||
* The UI utilize a bitBadge which does not support async actions (like bitButton does).
|
||||
* @protected
|
||||
*/
|
||||
protected launchingCipher = signal<CipherView | null>(null);
|
||||
|
||||
private activeUserData$ = this.accountService.activeAccount$.pipe(
|
||||
filterOutNullish(),
|
||||
@@ -138,12 +151,6 @@ export class AtRiskPasswordsComponent {
|
||||
});
|
||||
}
|
||||
|
||||
async launchChangePassword(cipher: CipherView) {
|
||||
if (cipher.login?.uri) {
|
||||
this.platformUtilsService.launchUri(cipher.login.uri);
|
||||
}
|
||||
}
|
||||
|
||||
async activateInlineAutofillMenuVisibility() {
|
||||
await this.autofillSettingsService.setInlineMenuVisibility(
|
||||
AutofillOverlayVisibility.OnButtonClick,
|
||||
@@ -159,4 +166,19 @@ export class AtRiskPasswordsComponent {
|
||||
const { userId } = await firstValueFrom(this.activeUserData$);
|
||||
await this.atRiskPasswordPageService.dismissCallout(userId);
|
||||
}
|
||||
|
||||
launchChangePassword = async (cipher: CipherView) => {
|
||||
try {
|
||||
this.launchingCipher.set(cipher);
|
||||
const url = await this.changeLoginPasswordService.getChangePasswordUrl(cipher);
|
||||
|
||||
if (url == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.platformUtilsService.launchUri(url);
|
||||
} finally {
|
||||
this.launchingCipher.set(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user