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:
parent
a2a15d42d5
commit
d5643f42b3
@ -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(() => {
|
||||||
|
@ -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(() => {
|
||||||
|
@ -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(() => {
|
||||||
|
@ -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") {
|
||||||
|
@ -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>;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user