1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-25 12:15:18 +01:00

[PM-13723] track history in generator components (#11673)

* add history support to generator components
* increase generator history length
This commit is contained in:
✨ Audrey ✨ 2024-10-23 15:38:26 -04:00 committed by GitHub
parent a2a15d42d5
commit d5643f42b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 63 additions and 10 deletions

View File

@ -37,6 +37,7 @@ import {
isUsernameAlgorithm, isUsernameAlgorithm,
toCredentialGeneratorConfiguration, toCredentialGeneratorConfiguration,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history";
// constants used to identify navigation selections that are not // constants used to identify navigation selections that are not
// generator algorithms // generator algorithms
@ -51,6 +52,7 @@ const NONE_SELECTED = "none";
export class CredentialGeneratorComponent implements OnInit, OnDestroy { export class CredentialGeneratorComponent implements OnInit, OnDestroy {
constructor( constructor(
private generatorService: CredentialGeneratorService, private generatorService: CredentialGeneratorService,
private generatorHistoryService: GeneratorHistoryService,
private toastService: ToastService, private toastService: ToastService,
private logService: LogService, private logService: LogService,
private i18nService: I18nService, private i18nService: I18nService,
@ -182,9 +184,16 @@ export class CredentialGeneratorComponent implements OnInit, OnDestroy {
// continue with origin stream // continue with origin stream
return generator; return generator;
}), }),
withLatestFrom(this.userId$),
takeUntil(this.destroyed), takeUntil(this.destroyed),
) )
.subscribe((generated) => { .subscribe(([generated, userId]) => {
this.generatorHistoryService
.track(userId, generated.credential, generated.category, generated.generationDate)
.catch((e: unknown) => {
this.logService.error(e);
});
// update subjects within the angular zone so that the // update subjects within the angular zone so that the
// template bindings refresh immediately // template bindings refresh immediately
this.zone.run(() => { this.zone.run(() => {

View File

@ -2,6 +2,7 @@ import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from "@angular/core"; import { Component, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output } from "@angular/core";
import { import {
BehaviorSubject, BehaviorSubject,
catchError,
distinctUntilChanged, distinctUntilChanged,
filter, filter,
map, map,
@ -14,7 +15,9 @@ import {
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
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 { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { ToastService } from "@bitwarden/components";
import { Option } from "@bitwarden/components/src/select/option"; import { Option } from "@bitwarden/components/src/select/option";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
@ -25,6 +28,7 @@ import {
isPasswordAlgorithm, isPasswordAlgorithm,
AlgorithmInfo, AlgorithmInfo,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history";
/** Options group for passwords */ /** Options group for passwords */
@Component({ @Component({
@ -34,6 +38,9 @@ import {
export class PasswordGeneratorComponent implements OnInit, OnDestroy { export class PasswordGeneratorComponent implements OnInit, OnDestroy {
constructor( constructor(
private generatorService: CredentialGeneratorService, private generatorService: CredentialGeneratorService,
private generatorHistoryService: GeneratorHistoryService,
private toastService: ToastService,
private logService: LogService,
private i18nService: I18nService, private i18nService: I18nService,
private accountService: AccountService, private accountService: AccountService,
private zone: NgZone, private zone: NgZone,
@ -109,10 +116,32 @@ export class PasswordGeneratorComponent implements OnInit, OnDestroy {
// wire up the generator // wire up the generator
this.algorithm$ this.algorithm$
.pipe( .pipe(
filter((algorithm) => !!algorithm),
switchMap((algorithm) => this.typeToGenerator$(algorithm.id)), switchMap((algorithm) => this.typeToGenerator$(algorithm.id)),
catchError((error: unknown, generator) => {
if (typeof error === "string") {
this.toastService.showToast({
message: error,
variant: "error",
title: "",
});
} else {
this.logService.error(error);
}
// continue with origin stream
return generator;
}),
withLatestFrom(this.userId$),
takeUntil(this.destroyed), takeUntil(this.destroyed),
) )
.subscribe((generated) => { .subscribe(([generated, userId]) => {
this.generatorHistoryService
.track(userId, generated.credential, generated.category, generated.generationDate)
.catch((e: unknown) => {
this.logService.error(e);
});
// update subjects within the angular zone so that the // update subjects within the angular zone so that the
// template bindings refresh immediately // template bindings refresh immediately
this.zone.run(() => { this.zone.run(() => {

View File

@ -36,6 +36,7 @@ import {
isUsernameAlgorithm, isUsernameAlgorithm,
toCredentialGeneratorConfiguration, toCredentialGeneratorConfiguration,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history";
// constants used to identify navigation selections that are not // constants used to identify navigation selections that are not
// generator algorithms // generator algorithms
@ -57,6 +58,7 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy {
*/ */
constructor( constructor(
private generatorService: CredentialGeneratorService, private generatorService: CredentialGeneratorService,
private generatorHistoryService: GeneratorHistoryService,
private toastService: ToastService, private toastService: ToastService,
private logService: LogService, private logService: LogService,
private i18nService: I18nService, private i18nService: I18nService,
@ -153,9 +155,16 @@ export class UsernameGeneratorComponent implements OnInit, OnDestroy {
// continue with origin stream // continue with origin stream
return generator; return generator;
}), }),
withLatestFrom(this.userId$),
takeUntil(this.destroyed), takeUntil(this.destroyed),
) )
.subscribe((generated) => { .subscribe(([generated, userId]) => {
this.generatorHistoryService
.track(userId, generated.credential, generated.category, generated.generationDate)
.catch((e: unknown) => {
this.logService.error(e);
});
// update subjects within the angular zone so that the // update subjects within the angular zone so that the
// template bindings refresh immediately // template bindings refresh immediately
this.zone.run(() => { this.zone.run(() => {

View File

@ -1,6 +1,6 @@
import { Jsonify } from "type-fest"; import { Jsonify } from "type-fest";
import { GeneratorCategory } from "./options"; import { CredentialAlgorithm } from "@bitwarden/generator-core";
/** A credential generation result */ /** A credential generation result */
export class GeneratedCredential { export class GeneratedCredential {
@ -14,7 +14,7 @@ export class GeneratedCredential {
*/ */
constructor( constructor(
readonly credential: string, readonly credential: string,
readonly category: GeneratorCategory, readonly category: CredentialAlgorithm,
generationDate: Date | number, generationDate: Date | number,
) { ) {
if (typeof generationDate === "number") { if (typeof generationDate === "number") {

View File

@ -1,9 +1,9 @@
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { CredentialAlgorithm } from "@bitwarden/generator-core";
import { GeneratedCredential } from "./generated-credential"; import { GeneratedCredential } from "./generated-credential";
import { GeneratorCategory } from "./options";
/** Tracks the history of password generations. /** Tracks the history of password generations.
* Each user gets their own store. * Each user gets their own store.
@ -27,7 +27,7 @@ export abstract class GeneratorHistoryService {
track: ( track: (
userId: UserId, userId: UserId,
credential: string, credential: string,
category: GeneratorCategory, category: CredentialAlgorithm,
date?: Date, date?: Date,
) => Promise<GeneratedCredential | null>; ) => Promise<GeneratedCredential | null>;

View File

@ -8,12 +8,13 @@ import { PaddedDataPacker } from "@bitwarden/common/tools/state/padded-data-pack
import { SecretState } from "@bitwarden/common/tools/state/secret-state"; import { SecretState } from "@bitwarden/common/tools/state/secret-state";
import { UserKeyEncryptor } from "@bitwarden/common/tools/state/user-key-encryptor"; import { UserKeyEncryptor } from "@bitwarden/common/tools/state/user-key-encryptor";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { CredentialAlgorithm } from "@bitwarden/generator-core";
import { GeneratedCredential } from "./generated-credential"; import { GeneratedCredential } from "./generated-credential";
import { GeneratorHistoryService } from "./generator-history.abstraction"; import { GeneratorHistoryService } from "./generator-history.abstraction";
import { GENERATOR_HISTORY, GENERATOR_HISTORY_BUFFER } from "./key-definitions"; import { GENERATOR_HISTORY, GENERATOR_HISTORY_BUFFER } from "./key-definitions";
import { LegacyPasswordHistoryDecryptor } from "./legacy-password-history-decryptor"; import { LegacyPasswordHistoryDecryptor } from "./legacy-password-history-decryptor";
import { GeneratorCategory, HistoryServiceOptions } from "./options"; import { HistoryServiceOptions } from "./options";
const OPTIONS_FRAME_SIZE = 2048; const OPTIONS_FRAME_SIZE = 2048;
@ -25,7 +26,7 @@ export class LocalGeneratorHistoryService extends GeneratorHistoryService {
private readonly encryptService: EncryptService, private readonly encryptService: EncryptService,
private readonly keyService: CryptoService, private readonly keyService: CryptoService,
private readonly stateProvider: StateProvider, private readonly stateProvider: StateProvider,
private readonly options: HistoryServiceOptions = { maxTotal: 100 }, private readonly options: HistoryServiceOptions = { maxTotal: 200 },
) { ) {
super(); super();
} }
@ -33,7 +34,12 @@ export class LocalGeneratorHistoryService extends GeneratorHistoryService {
private _credentialStates = new Map<UserId, SingleUserState<GeneratedCredential[]>>(); private _credentialStates = new Map<UserId, SingleUserState<GeneratedCredential[]>>();
/** {@link GeneratorHistoryService.track} */ /** {@link GeneratorHistoryService.track} */
track = async (userId: UserId, credential: string, category: GeneratorCategory, date?: Date) => { track = async (
userId: UserId,
credential: string,
category: CredentialAlgorithm,
date?: Date,
) => {
const state = this.getCredentialState(userId); const state = this.getCredentialState(userId);
let result: GeneratedCredential = null; let result: GeneratedCredential = null;