[PM-3609] [Tech-Debt] Add types to password and username generator (#6090)

* Create and use GeneratorOptions

Selection between `password`and `username`

* Use PasswordGeneratorOptions

* Declare and use UsernameGeneratorOptions
This commit is contained in:
Daniel James Smith 2023-09-05 21:48:34 +02:00 committed by GitHub
parent b78d17aa62
commit 255a7381b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 93 additions and 38 deletions

View File

@ -8,8 +8,15 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; import { GeneratorOptions } from "@bitwarden/common/tools/generator/generator-options";
import { UsernameGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/username"; import {
PasswordGenerationServiceAbstraction,
PasswordGeneratorOptions,
} from "@bitwarden/common/tools/generator/password";
import {
UsernameGenerationServiceAbstraction,
UsernameGeneratorOptions,
} from "@bitwarden/common/tools/generator/username";
@Directive() @Directive()
export class GeneratorComponent implements OnInit { export class GeneratorComponent implements OnInit {
@ -24,8 +31,8 @@ export class GeneratorComponent implements OnInit {
subaddressOptions: any[]; subaddressOptions: any[];
catchallOptions: any[]; catchallOptions: any[];
forwardOptions: EmailForwarderOptions[]; forwardOptions: EmailForwarderOptions[];
usernameOptions: any = {}; usernameOptions: UsernameGeneratorOptions = {};
passwordOptions: any = {}; passwordOptions: PasswordGeneratorOptions = {};
username = "-"; username = "-";
password = "-"; password = "-";
showOptions = false; showOptions = false;
@ -118,7 +125,7 @@ export class GeneratorComponent implements OnInit {
} }
async typeChanged() { async typeChanged() {
await this.stateService.setGeneratorOptions({ type: this.type }); await this.stateService.setGeneratorOptions({ type: this.type } as GeneratorOptions);
if (this.regenerateWithoutButtonPress()) { if (this.regenerateWithoutButtonPress()) {
await this.regenerate(); await this.regenerate();
} }

View File

@ -13,7 +13,9 @@ import { BiometricKey } from "../../auth/types/biometric-key";
import { KdfType, ThemeType, UriMatchType } from "../../enums"; import { KdfType, ThemeType, UriMatchType } from "../../enums";
import { EventData } from "../../models/data/event.data"; import { EventData } from "../../models/data/event.data";
import { WindowState } from "../../models/domain/window-state"; import { WindowState } from "../../models/domain/window-state";
import { GeneratedPasswordHistory } from "../../tools/generator/password"; import { GeneratorOptions } from "../../tools/generator/generator-options";
import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password";
import { UsernameGeneratorOptions } from "../../tools/generator/username";
import { SendData } from "../../tools/send/models/data/send.data"; import { SendData } from "../../tools/send/models/data/send.data";
import { SendView } from "../../tools/send/models/view/send.view"; import { SendView } from "../../tools/send/models/view/send.view";
import { CipherData } from "../../vault/models/data/cipher.data"; import { CipherData } from "../../vault/models/data/cipher.data";
@ -439,12 +441,18 @@ export abstract class StateService<T extends Account = Account> {
value: { [id: string]: OrganizationData }, value: { [id: string]: OrganizationData },
options?: StorageOptions options?: StorageOptions
) => Promise<void>; ) => Promise<void>;
getPasswordGenerationOptions: (options?: StorageOptions) => Promise<any>; getPasswordGenerationOptions: (options?: StorageOptions) => Promise<PasswordGeneratorOptions>;
setPasswordGenerationOptions: (value: any, options?: StorageOptions) => Promise<void>; setPasswordGenerationOptions: (
getUsernameGenerationOptions: (options?: StorageOptions) => Promise<any>; value: PasswordGeneratorOptions,
setUsernameGenerationOptions: (value: any, options?: StorageOptions) => Promise<void>; options?: StorageOptions
getGeneratorOptions: (options?: StorageOptions) => Promise<any>; ) => Promise<void>;
setGeneratorOptions: (value: any, options?: StorageOptions) => Promise<void>; getUsernameGenerationOptions: (options?: StorageOptions) => Promise<UsernameGeneratorOptions>;
setUsernameGenerationOptions: (
value: UsernameGeneratorOptions,
options?: StorageOptions
) => Promise<void>;
getGeneratorOptions: (options?: StorageOptions) => Promise<GeneratorOptions>;
setGeneratorOptions: (value: GeneratorOptions, options?: StorageOptions) => Promise<void>;
/** /**
* Gets the user's Pin, encrypted by the user key * Gets the user's Pin, encrypted by the user key
*/ */

View File

@ -14,7 +14,12 @@ import { TrustedDeviceUserDecryptionOption } from "../../../auth/models/domain/u
import { IdentityTokenResponse } from "../../../auth/models/response/identity-token.response"; import { IdentityTokenResponse } from "../../../auth/models/response/identity-token.response";
import { KdfType, UriMatchType } from "../../../enums"; import { KdfType, UriMatchType } from "../../../enums";
import { EventData } from "../../../models/data/event.data"; import { EventData } from "../../../models/data/event.data";
import { GeneratedPasswordHistory } from "../../../tools/generator/password"; import { GeneratorOptions } from "../../../tools/generator/generator-options";
import {
GeneratedPasswordHistory,
PasswordGeneratorOptions,
} from "../../../tools/generator/password";
import { UsernameGeneratorOptions } from "../../../tools/generator/username/username-generation-options";
import { SendData } from "../../../tools/send/models/data/send.data"; import { SendData } from "../../../tools/send/models/data/send.data";
import { SendView } from "../../../tools/send/models/view/send.view"; import { SendView } from "../../../tools/send/models/view/send.view";
import { DeepJsonify } from "../../../types/deep-jsonify"; import { DeepJsonify } from "../../../types/deep-jsonify";
@ -235,9 +240,9 @@ export class AccountSettings {
equivalentDomains?: any; equivalentDomains?: any;
minimizeOnCopyToClipboard?: boolean; minimizeOnCopyToClipboard?: boolean;
neverDomains?: { [id: string]: any }; neverDomains?: { [id: string]: any };
passwordGenerationOptions?: any; passwordGenerationOptions?: PasswordGeneratorOptions;
usernameGenerationOptions?: any; usernameGenerationOptions?: UsernameGeneratorOptions;
generatorOptions?: any; generatorOptions?: GeneratorOptions;
pinKeyEncryptedUserKey?: EncryptedString; pinKeyEncryptedUserKey?: EncryptedString;
pinKeyEncryptedUserKeyEphemeral?: EncryptedString; pinKeyEncryptedUserKeyEphemeral?: EncryptedString;
protectedPin?: string; protectedPin?: string;

View File

@ -22,7 +22,9 @@ import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum";
import { EventData } from "../../models/data/event.data"; import { EventData } from "../../models/data/event.data";
import { WindowState } from "../../models/domain/window-state"; import { WindowState } from "../../models/domain/window-state";
import { migrate } from "../../state-migrations"; import { migrate } from "../../state-migrations";
import { GeneratedPasswordHistory } from "../../tools/generator/password"; import { GeneratorOptions } from "../../tools/generator/generator-options";
import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password";
import { UsernameGeneratorOptions } from "../../tools/generator/username";
import { SendData } from "../../tools/send/models/data/send.data"; import { SendData } from "../../tools/send/models/data/send.data";
import { SendView } from "../../tools/send/models/view/send.view"; import { SendView } from "../../tools/send/models/view/send.view";
import { CipherData } from "../../vault/models/data/cipher.data"; import { CipherData } from "../../vault/models/data/cipher.data";
@ -2367,13 +2369,16 @@ export class StateService<
); );
} }
async getPasswordGenerationOptions(options?: StorageOptions): Promise<any> { async getPasswordGenerationOptions(options?: StorageOptions): Promise<PasswordGeneratorOptions> {
return ( return (
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
)?.settings?.passwordGenerationOptions; )?.settings?.passwordGenerationOptions;
} }
async setPasswordGenerationOptions(value: any, options?: StorageOptions): Promise<void> { async setPasswordGenerationOptions(
value: PasswordGeneratorOptions,
options?: StorageOptions
): Promise<void> {
const account = await this.getAccount( const account = await this.getAccount(
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
); );
@ -2384,13 +2389,16 @@ export class StateService<
); );
} }
async getUsernameGenerationOptions(options?: StorageOptions): Promise<any> { async getUsernameGenerationOptions(options?: StorageOptions): Promise<UsernameGeneratorOptions> {
return ( return (
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
)?.settings?.usernameGenerationOptions; )?.settings?.usernameGenerationOptions;
} }
async setUsernameGenerationOptions(value: any, options?: StorageOptions): Promise<void> { async setUsernameGenerationOptions(
value: UsernameGeneratorOptions,
options?: StorageOptions
): Promise<void> {
const account = await this.getAccount( const account = await this.getAccount(
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
); );
@ -2401,13 +2409,13 @@ export class StateService<
); );
} }
async getGeneratorOptions(options?: StorageOptions): Promise<any> { async getGeneratorOptions(options?: StorageOptions): Promise<GeneratorOptions> {
return ( return (
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
)?.settings?.generatorOptions; )?.settings?.generatorOptions;
} }
async setGeneratorOptions(value: any, options?: StorageOptions): Promise<void> { async setGeneratorOptions(value: GeneratorOptions, options?: StorageOptions): Promise<void> {
const account = await this.getAccount( const account = await this.getAccount(
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
); );

View File

@ -0,0 +1,3 @@
export type GeneratorOptions = {
type?: "password" | "username";
};

View File

@ -1,3 +1,4 @@
export { PasswordGeneratorOptions } from "./password-generator-options";
export { PasswordGenerationServiceAbstraction } from "./password-generation.service.abstraction"; export { PasswordGenerationServiceAbstraction } from "./password-generation.service.abstraction";
export { PasswordGenerationService } from "./password-generation.service"; export { PasswordGenerationService } from "./password-generation.service";
export { GeneratedPasswordHistory } from "./generated-password-history"; export { GeneratedPasswordHistory } from "./generated-password-history";

View File

@ -1,2 +1,3 @@
export { UsernameGeneratorOptions } from "./username-generation-options";
export { UsernameGenerationServiceAbstraction } from "./username-generation.service.abstraction"; export { UsernameGenerationServiceAbstraction } from "./username-generation.service.abstraction";
export { UsernameGenerationService } from "./username-generation.service"; export { UsernameGenerationService } from "./username-generation.service";

View File

@ -0,0 +1,19 @@
export type UsernameGeneratorOptions = {
type?: "word" | "subaddress" | "catchall" | "forwarded";
wordCapitalize?: boolean;
wordIncludeNumber?: boolean;
subaddressType?: "random" | "website-name";
subaddressEmail?: string;
catchallType?: "random" | "website-name";
catchallDomain?: string;
website?: string;
forwardedService?: string;
forwardedAnonAddyApiToken?: string;
forwardedAnonAddyDomain?: string;
forwardedDuckDuckGoToken?: string;
forwardedFirefoxApiToken?: string;
forwardedFastmailApiToken?: string;
forwardedForwardEmailApiToken?: string;
forwardedForwardEmailDomain?: string;
forwardedSimpleLoginApiKey?: string;
};

View File

@ -1,9 +1,11 @@
import { UsernameGeneratorOptions } from "./username-generation-options";
export abstract class UsernameGenerationServiceAbstraction { export abstract class UsernameGenerationServiceAbstraction {
generateUsername: (options: any) => Promise<string>; generateUsername: (options: UsernameGeneratorOptions) => Promise<string>;
generateWord: (options: any) => Promise<string>; generateWord: (options: UsernameGeneratorOptions) => Promise<string>;
generateSubaddress: (options: any) => Promise<string>; generateSubaddress: (options: UsernameGeneratorOptions) => Promise<string>;
generateCatchall: (options: any) => Promise<string>; generateCatchall: (options: UsernameGeneratorOptions) => Promise<string>;
generateForwarded: (options: any) => Promise<string>; generateForwarded: (options: UsernameGeneratorOptions) => Promise<string>;
getOptions: () => Promise<any>; getOptions: () => Promise<UsernameGeneratorOptions>;
saveOptions: (options: any) => Promise<void>; saveOptions: (options: UsernameGeneratorOptions) => Promise<void>;
} }

View File

@ -13,9 +13,10 @@ import {
ForwarderOptions, ForwarderOptions,
SimpleLoginForwarder, SimpleLoginForwarder,
} from "./email-forwarders"; } from "./email-forwarders";
import { UsernameGeneratorOptions } from "./username-generation-options";
import { UsernameGenerationServiceAbstraction } from "./username-generation.service.abstraction"; import { UsernameGenerationServiceAbstraction } from "./username-generation.service.abstraction";
const DefaultOptions = { const DefaultOptions: UsernameGeneratorOptions = {
type: "word", type: "word",
wordCapitalize: true, wordCapitalize: true,
wordIncludeNumber: true, wordIncludeNumber: true,
@ -33,7 +34,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr
private apiService: ApiService private apiService: ApiService
) {} ) {}
generateUsername(options: any): Promise<string> { generateUsername(options: UsernameGeneratorOptions): Promise<string> {
if (options.type === "catchall") { if (options.type === "catchall") {
return this.generateCatchall(options); return this.generateCatchall(options);
} else if (options.type === "subaddress") { } else if (options.type === "subaddress") {
@ -45,7 +46,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr
} }
} }
async generateWord(options: any): Promise<string> { async generateWord(options: UsernameGeneratorOptions): Promise<string> {
const o = Object.assign({}, DefaultOptions, options); const o = Object.assign({}, DefaultOptions, options);
if (o.wordCapitalize == null) { if (o.wordCapitalize == null) {
@ -67,7 +68,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr
return word; return word;
} }
async generateSubaddress(options: any): Promise<string> { async generateSubaddress(options: UsernameGeneratorOptions): Promise<string> {
const o = Object.assign({}, DefaultOptions, options); const o = Object.assign({}, DefaultOptions, options);
const subaddressEmail = o.subaddressEmail; const subaddressEmail = o.subaddressEmail;
@ -94,7 +95,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr
return emailBeginning + "+" + subaddressString + "@" + emailEnding; return emailBeginning + "+" + subaddressString + "@" + emailEnding;
} }
async generateCatchall(options: any): Promise<string> { async generateCatchall(options: UsernameGeneratorOptions): Promise<string> {
const o = Object.assign({}, DefaultOptions, options); const o = Object.assign({}, DefaultOptions, options);
if (o.catchallDomain == null || o.catchallDomain === "") { if (o.catchallDomain == null || o.catchallDomain === "") {
@ -113,7 +114,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr
return startString + "@" + o.catchallDomain; return startString + "@" + o.catchallDomain;
} }
async generateForwarded(options: any): Promise<string> { async generateForwarded(options: UsernameGeneratorOptions): Promise<string> {
const o = Object.assign({}, DefaultOptions, options); const o = Object.assign({}, DefaultOptions, options);
if (o.forwardedService == null) { if (o.forwardedService == null) {
@ -152,7 +153,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr
return forwarder.generate(this.apiService, forwarderOptions); return forwarder.generate(this.apiService, forwarderOptions);
} }
async getOptions(): Promise<any> { async getOptions(): Promise<UsernameGeneratorOptions> {
let options = await this.stateService.getUsernameGenerationOptions(); let options = await this.stateService.getUsernameGenerationOptions();
if (options == null) { if (options == null) {
options = Object.assign({}, DefaultOptions); options = Object.assign({}, DefaultOptions);
@ -163,7 +164,7 @@ export class UsernameGenerationService implements UsernameGenerationServiceAbstr
return options; return options;
} }
async saveOptions(options: any) { async saveOptions(options: UsernameGeneratorOptions) {
await this.stateService.setUsernameGenerationOptions(options); await this.stateService.setUsernameGenerationOptions(options);
} }