mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-20505] Weak-passwords-report: refresh rows after edit (#14401)
This commit is contained in:
@@ -67,7 +67,7 @@ export class CipherReportComponent implements OnDestroy {
|
||||
protected i18nService: I18nService,
|
||||
private syncService: SyncService,
|
||||
private cipherFormConfigService: CipherFormConfigService,
|
||||
private adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService,
|
||||
protected adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService,
|
||||
) {
|
||||
this.organizations$ = this.accountService.activeAccount$.pipe(
|
||||
getUserId,
|
||||
@@ -207,7 +207,7 @@ export class CipherReportComponent implements OnDestroy {
|
||||
|
||||
// If the dialog was closed by deleting the cipher, refresh the report.
|
||||
if (result === VaultItemDialogResult.Deleted || result === VaultItemDialogResult.Saved) {
|
||||
await this.load();
|
||||
await this.refresh(result, cipher);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,6 +215,10 @@ export class CipherReportComponent implements OnDestroy {
|
||||
this.allCiphers = [];
|
||||
}
|
||||
|
||||
protected async refresh(result: VaultItemDialogResult, cipher: CipherView) {
|
||||
await this.load();
|
||||
}
|
||||
|
||||
protected async repromptCipher(c: CipherView) {
|
||||
return (
|
||||
c.reprompt === CipherRepromptType.None ||
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { CipherId } from "@bitwarden/common/types/guid";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { BadgeVariant, DialogService } from "@bitwarden/components";
|
||||
import { CipherFormConfigService, PasswordRepromptService } from "@bitwarden/vault";
|
||||
import { VaultItemDialogResult } from "@bitwarden/web-vault/app/vault/components/vault-item-dialog/vault-item-dialog.component";
|
||||
|
||||
import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service";
|
||||
|
||||
@@ -40,7 +44,7 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen
|
||||
i18nService: I18nService,
|
||||
syncService: SyncService,
|
||||
cipherFormConfigService: CipherFormConfigService,
|
||||
adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService,
|
||||
protected adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService,
|
||||
) {
|
||||
super(
|
||||
cipherService,
|
||||
@@ -66,62 +70,112 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen
|
||||
this.findWeakPasswords(allCiphers);
|
||||
}
|
||||
|
||||
protected findWeakPasswords(ciphers: CipherView[]): void {
|
||||
ciphers.forEach((ciph) => {
|
||||
const { type, login, isDeleted, edit, viewPassword } = ciph;
|
||||
if (
|
||||
type !== CipherType.Login ||
|
||||
login.password == null ||
|
||||
login.password === "" ||
|
||||
isDeleted ||
|
||||
(!this.organization && !edit) ||
|
||||
!viewPassword
|
||||
) {
|
||||
protected async refresh(result: VaultItemDialogResult, cipher: CipherView) {
|
||||
if (result === VaultItemDialogResult.Deleted) {
|
||||
// remove the cipher from the list
|
||||
this.weakPasswordCiphers = this.weakPasswordCiphers.filter((c) => c.id !== cipher.id);
|
||||
this.filterCiphersByOrg(this.weakPasswordCiphers);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == VaultItemDialogResult.Saved) {
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
let updatedCipher = await this.cipherService.get(cipher.id, activeUserId);
|
||||
|
||||
if (this.isAdminConsoleActive) {
|
||||
updatedCipher = await this.adminConsoleCipherFormConfigService.getCipher(
|
||||
cipher.id as CipherId,
|
||||
this.organization,
|
||||
);
|
||||
}
|
||||
|
||||
const updatedCipherView = await updatedCipher.decrypt(
|
||||
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId),
|
||||
);
|
||||
// update the cipher views
|
||||
const updatedReportResult = this.determineWeakPasswordScore(updatedCipherView);
|
||||
const index = this.weakPasswordCiphers.findIndex((c) => c.id === updatedCipherView.id);
|
||||
|
||||
if (updatedReportResult == null) {
|
||||
// the password is no longer weak
|
||||
// remove the cipher from the list
|
||||
this.weakPasswordCiphers.splice(index, 1);
|
||||
this.filterCiphersByOrg(this.weakPasswordCiphers);
|
||||
return;
|
||||
}
|
||||
|
||||
const hasUserName = this.isUserNameNotEmpty(ciph);
|
||||
let userInput: string[] = [];
|
||||
if (hasUserName) {
|
||||
const atPosition = login.username.indexOf("@");
|
||||
if (atPosition > -1) {
|
||||
userInput = userInput
|
||||
.concat(
|
||||
login.username
|
||||
.substr(0, atPosition)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/[^A-Za-z0-9]/),
|
||||
)
|
||||
.filter((i) => i.length >= 3);
|
||||
} else {
|
||||
userInput = login.username
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/[^A-Za-z0-9]/)
|
||||
.filter((i) => i.length >= 3);
|
||||
}
|
||||
if (index > -1) {
|
||||
// update the existing cipher
|
||||
this.weakPasswordCiphers[index] = updatedReportResult;
|
||||
this.filterCiphersByOrg(this.weakPasswordCiphers);
|
||||
}
|
||||
const result = this.passwordStrengthService.getPasswordStrength(
|
||||
login.password,
|
||||
null,
|
||||
userInput.length > 0 ? userInput : null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.score != null && result.score <= 2) {
|
||||
const scoreValue = this.scoreKey(result.score);
|
||||
const row = {
|
||||
...ciph,
|
||||
score: result.score,
|
||||
reportValue: scoreValue,
|
||||
scoreKey: scoreValue.sortOrder,
|
||||
} as ReportResult;
|
||||
protected findWeakPasswords(ciphers: CipherView[]): void {
|
||||
ciphers.forEach((ciph) => {
|
||||
const row = this.determineWeakPasswordScore(ciph);
|
||||
if (row != null) {
|
||||
this.weakPasswordCiphers.push(row);
|
||||
}
|
||||
});
|
||||
this.filterCiphersByOrg(this.weakPasswordCiphers);
|
||||
}
|
||||
|
||||
protected determineWeakPasswordScore(ciph: CipherView): ReportResult | null {
|
||||
const { type, login, isDeleted, edit, viewPassword } = ciph;
|
||||
if (
|
||||
type !== CipherType.Login ||
|
||||
login.password == null ||
|
||||
login.password === "" ||
|
||||
isDeleted ||
|
||||
(!this.organization && !edit) ||
|
||||
!viewPassword
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hasUserName = this.isUserNameNotEmpty(ciph);
|
||||
let userInput: string[] = [];
|
||||
if (hasUserName) {
|
||||
const atPosition = login.username.indexOf("@");
|
||||
if (atPosition > -1) {
|
||||
userInput = userInput
|
||||
.concat(
|
||||
login.username
|
||||
.substr(0, atPosition)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/[^A-Za-z0-9]/),
|
||||
)
|
||||
.filter((i) => i.length >= 3);
|
||||
} else {
|
||||
userInput = login.username
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.split(/[^A-Za-z0-9]/)
|
||||
.filter((i) => i.length >= 3);
|
||||
}
|
||||
}
|
||||
const result = this.passwordStrengthService.getPasswordStrength(
|
||||
login.password,
|
||||
null,
|
||||
userInput.length > 0 ? userInput : null,
|
||||
);
|
||||
|
||||
if (result.score != null && result.score <= 2) {
|
||||
const scoreValue = this.scoreKey(result.score);
|
||||
return {
|
||||
...ciph,
|
||||
score: result.score,
|
||||
reportValue: scoreValue,
|
||||
scoreKey: scoreValue.sortOrder,
|
||||
} as ReportResult;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected canManageCipher(c: CipherView): boolean {
|
||||
// this will only ever be false from the org view;
|
||||
return true;
|
||||
|
||||
@@ -100,7 +100,7 @@ export class AdminConsoleCipherFormConfigService implements CipherFormConfigServ
|
||||
};
|
||||
}
|
||||
|
||||
private async getCipher(id: CipherId | null, organization: Organization): Promise<Cipher | null> {
|
||||
async getCipher(id: CipherId | null, organization: Organization): Promise<Cipher | null> {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user