diff --git a/libs/vault/src/cipher-form/abstractions/cipher-form-generation.service.ts b/libs/vault/src/cipher-form/abstractions/cipher-form-generation.service.ts new file mode 100644 index 0000000000..6ddd447344 --- /dev/null +++ b/libs/vault/src/cipher-form/abstractions/cipher-form-generation.service.ts @@ -0,0 +1,20 @@ +/** + * Service responsible for generating random passwords and usernames. + */ +export abstract class CipherFormGenerationService { + /** + * Generates a random password. Called when the user clicks the "Generate Password" button in the UI. + */ + abstract generatePassword(): Promise; + + /** + * Generates a random username. Called when the user clicks the "Generate Username" button in the UI. + */ + abstract generateUsername(): Promise; + + /** + * Generates an initial password for a new cipher. This should not involve any user interaction as it will + * be used to pre-fill the password field in the UI for new Login ciphers. + */ + abstract generateInitialPassword(): Promise; +} diff --git a/libs/vault/src/cipher-form/cipher-form.module.ts b/libs/vault/src/cipher-form/cipher-form.module.ts index 3552e050c0..b2a82d6766 100644 --- a/libs/vault/src/cipher-form/cipher-form.module.ts +++ b/libs/vault/src/cipher-form/cipher-form.module.ts @@ -1,7 +1,9 @@ import { NgModule } from "@angular/core"; +import { CipherFormGenerationService } from "./abstractions/cipher-form-generation.service"; import { CipherFormService } from "./abstractions/cipher-form.service"; import { CipherFormComponent } from "./components/cipher-form.component"; +import { DefaultCipherFormGenerationService } from "./services/default-cipher-form-generation.service"; import { DefaultCipherFormService } from "./services/default-cipher-form.service"; @NgModule({ @@ -11,6 +13,10 @@ import { DefaultCipherFormService } from "./services/default-cipher-form.service provide: CipherFormService, useClass: DefaultCipherFormService, }, + { + provide: CipherFormGenerationService, + useClass: DefaultCipherFormGenerationService, + }, ], exports: [CipherFormComponent], }) diff --git a/libs/vault/src/cipher-form/cipher-form.stories.ts b/libs/vault/src/cipher-form/cipher-form.stories.ts index acb4111a65..e90c46fa0a 100644 --- a/libs/vault/src/cipher-form/cipher-form.stories.ts +++ b/libs/vault/src/cipher-form/cipher-form.stories.ts @@ -19,10 +19,10 @@ import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { AsyncActionsModule, ButtonModule, ToastService } from "@bitwarden/components"; import { - PasswordGenerationServiceAbstraction, - UsernameGenerationServiceAbstraction, -} from "@bitwarden/generator-legacy"; -import { CipherFormConfig, PasswordRepromptService } from "@bitwarden/vault"; + CipherFormConfig, + CipherFormGenerationService, + PasswordRepromptService, +} from "@bitwarden/vault"; import { PreloadedEnglishI18nModule } from "@bitwarden/web-vault/src/app/core/tests"; import { CipherFormService } from "./abstractions/cipher-form.service"; @@ -132,23 +132,17 @@ export default { }, }, { - provide: PasswordGenerationServiceAbstraction, + provide: CipherFormGenerationService, useValue: { - getOptions: () => Promise.resolve([{}, {}]), + generateInitialPassword: () => Promise.resolve("initial-password"), generatePassword: () => Promise.resolve("random-password"), - }, - }, - { - provide: UsernameGenerationServiceAbstraction, - useValue: { - getOptions: () => Promise.resolve({}), generateUsername: () => Promise.resolve("random-username"), }, }, { provide: TotpCaptureService, useValue: { - captureTotpFromTab: () => Promise.resolve("some-value"), + captureTotpSecret: () => Promise.resolve("some-value"), }, }, { diff --git a/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts b/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts index d28db24a0d..c3e2684dc5 100644 --- a/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts +++ b/libs/vault/src/cipher-form/components/login-details-section/login-details-section.component.ts @@ -19,11 +19,8 @@ import { ToastService, TypographyModule, } from "@bitwarden/components"; -import { - PasswordGenerationServiceAbstraction, - UsernameGenerationServiceAbstraction, -} from "@bitwarden/generator-legacy"; +import { CipherFormGenerationService } from "../../abstractions/cipher-form-generation.service"; import { TotpCaptureService } from "../../abstractions/totp-capture.service"; import { CipherFormContainer } from "../../cipher-form-container"; @@ -88,8 +85,7 @@ export class LoginDetailsSectionComponent implements OnInit { private cipherFormContainer: CipherFormContainer, private formBuilder: FormBuilder, private i18nService: I18nService, - private passwordGenerationService: PasswordGenerationServiceAbstraction, - private usernameGenerationService: UsernameGenerationServiceAbstraction, + private generationService: CipherFormGenerationService, private auditService: AuditService, private toastService: ToastService, @Optional() private totpCaptureService?: TotpCaptureService, @@ -145,7 +141,9 @@ export class LoginDetailsSectionComponent implements OnInit { } private async initNewCipher() { - this.loginDetailsForm.controls.password.patchValue(await this.generateNewPassword()); + this.loginDetailsForm.controls.password.patchValue( + await this.generationService.generateInitialPassword(), + ); } captureTotp = async () => { @@ -184,8 +182,11 @@ export class LoginDetailsSectionComponent implements OnInit { * TODO: Browser extension needs a means to cache the current form so values are not lost upon navigating to the generator. */ generatePassword = async () => { - const newPassword = await this.generateNewPassword(); - this.loginDetailsForm.controls.password.patchValue(newPassword); + const newPassword = await this.generationService.generatePassword(); + + if (newPassword) { + this.loginDetailsForm.controls.password.patchValue(newPassword); + } }; /** @@ -193,9 +194,10 @@ export class LoginDetailsSectionComponent implements OnInit { * TODO: Browser extension needs a means to cache the current form so values are not lost upon navigating to the generator. */ generateUsername = async () => { - const options = await this.usernameGenerationService.getOptions(); - const newUsername = await this.usernameGenerationService.generateUsername(options); - this.loginDetailsForm.controls.username.patchValue(newUsername); + const newUsername = await this.generationService.generateUsername(); + if (newUsername) { + this.loginDetailsForm.controls.username.patchValue(newUsername); + } }; /** @@ -224,9 +226,4 @@ export class LoginDetailsSectionComponent implements OnInit { }); } }; - - private async generateNewPassword() { - const [options] = await this.passwordGenerationService.getOptions(); - return await this.passwordGenerationService.generatePassword(options); - } } diff --git a/libs/vault/src/cipher-form/index.ts b/libs/vault/src/cipher-form/index.ts index 7c9032435e..1d275029df 100644 --- a/libs/vault/src/cipher-form/index.ts +++ b/libs/vault/src/cipher-form/index.ts @@ -6,4 +6,5 @@ export { OptionalInitialValues, } from "./abstractions/cipher-form-config.service"; export { TotpCaptureService } from "./abstractions/totp-capture.service"; +export { CipherFormGenerationService } from "./abstractions/cipher-form-generation.service"; export { DefaultCipherFormConfigService } from "./services/default-cipher-form-config.service"; diff --git a/libs/vault/src/cipher-form/services/default-cipher-form-generation.service.ts b/libs/vault/src/cipher-form/services/default-cipher-form-generation.service.ts new file mode 100644 index 0000000000..181590e841 --- /dev/null +++ b/libs/vault/src/cipher-form/services/default-cipher-form-generation.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from "@angular/core"; + +import { + PasswordGenerationServiceAbstraction, + UsernameGenerationServiceAbstraction, +} from "@bitwarden/generator-legacy"; + +import { CipherFormGenerationService } from "../abstractions/cipher-form-generation.service"; + +@Injectable() +export class DefaultCipherFormGenerationService implements CipherFormGenerationService { + constructor( + private passwordGenerationService: PasswordGenerationServiceAbstraction, + private usernameGenerationService: UsernameGenerationServiceAbstraction, + ) {} + + async generatePassword(): Promise { + const [options] = await this.passwordGenerationService.getOptions(); + return await this.passwordGenerationService.generatePassword(options); + } + + async generateUsername(): Promise { + const options = await this.usernameGenerationService.getOptions(); + return await this.usernameGenerationService.generateUsername(options); + } + + async generateInitialPassword(): Promise { + return await this.generatePassword(); + } +}