From bf081e0322c997bab71fa884ac2fdcca9dff4502 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 30 Mar 2022 17:59:58 -0400 Subject: [PATCH] username generator (#2468) * username generator * pass usernameWebsite * update jslib ref * update jslib ref * update jslib ref * update jslib ref * Update jslib to point to jslib master * Updated package-lock.json after running npm i * add missing translations * pr feedback Co-authored-by: Daniel James Smith --- jslib | 2 +- package-lock.json | 34 +- src/_locales/en/messages.json | 52 +- src/background/main.background.ts | 9 +- src/popup/components/pop-out.component.html | 2 +- .../password-generator.component.html | 487 +++++++++++++----- .../generator/password-generator.component.ts | 38 +- src/popup/scss/box.scss | 9 + src/popup/scss/pages.scss | 21 +- src/popup/services/services.module.ts | 6 + src/popup/vault/add-edit.component.html | 34 +- src/popup/vault/add-edit.component.ts | 29 +- 12 files changed, 532 insertions(+), 191 deletions(-) diff --git a/jslib b/jslib index 9950fb42a1..fa73c13b8c 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 9950fb42a15bad434a4b404419ff4a87af67a27b +Subproject commit fa73c13b8c9ed35cbb9909e342b143fa8a57f1a0 diff --git a/package-lock.json b/package-lock.json index a9dfccc7f7..bc1bc5f657 100644 --- a/package-lock.json +++ b/package-lock.json @@ -121,7 +121,7 @@ "big-integer": "1.6.48", "browser-hrtime": "^1.1.8", "lunr": "^2.3.9", - "node-forge": "^0.10.0", + "node-forge": "^1.2.1", "papaparse": "^5.3.0", "rxjs": "^7.4.0", "tldjs": "^2.3.1", @@ -130,7 +130,7 @@ "devDependencies": { "@types/lunr": "^2.3.3", "@types/node": "^16.11.12", - "@types/node-forge": "^0.9.7", + "@types/node-forge": "^1.0.1", "@types/papaparse": "^5.2.5", "@types/tldjs": "^2.3.0", "@types/zxcvbn": "^4.4.1", @@ -1037,9 +1037,9 @@ "dev": true }, "node_modules/@types/node-forge": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-0.9.10.tgz", - "integrity": "sha512-+BbPlhZeYs/WETWftQi2LeRx9VviWSwawNo+Pid5qNrSZHb60loYjpph3OrbwXMMseadu9rE9NeK34r4BHT+QQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz", + "integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==", "dev": true, "dependencies": { "@types/node": "*" @@ -8281,11 +8281,11 @@ } }, "node_modules/node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz", + "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==", "engines": { - "node": ">= 6.0.0" + "node": ">= 6.13.0" } }, "node_modules/node-releases": { @@ -12566,14 +12566,14 @@ "@microsoft/signalr-protocol-msgpack": "5.0.10", "@types/lunr": "^2.3.3", "@types/node": "^16.11.12", - "@types/node-forge": "^0.9.7", + "@types/node-forge": "^1.0.1", "@types/papaparse": "^5.2.5", "@types/tldjs": "^2.3.0", "@types/zxcvbn": "^4.4.1", "big-integer": "1.6.48", "browser-hrtime": "^1.1.8", "lunr": "^2.3.9", - "node-forge": "^0.10.0", + "node-forge": "^1.2.1", "papaparse": "^5.3.0", "rimraf": "^3.0.2", "rxjs": "^7.4.0", @@ -12894,9 +12894,9 @@ "dev": true }, "@types/node-forge": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-0.9.10.tgz", - "integrity": "sha512-+BbPlhZeYs/WETWftQi2LeRx9VviWSwawNo+Pid5qNrSZHb60loYjpph3OrbwXMMseadu9rE9NeK34r4BHT+QQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz", + "integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==", "dev": true, "requires": { "@types/node": "*" @@ -18471,9 +18471,9 @@ } }, "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz", + "integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==" }, "node-releases": { "version": "2.0.1", diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 19daa712f7..9695f4e58b 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -542,6 +542,12 @@ "overwritePasswordConfirmation": { "message": "Are you sure you want to overwrite the current password?" }, + "overwriteUsername": { + "message": "Overwrite Username" + }, + "overwriteUsernameConfirmation": { + "message": "Are you sure you want to overwrite the current username?" + }, "searchFolder": { "message": "Search folder" }, @@ -1233,7 +1239,12 @@ "message": "This password was not found in any known data breaches. It should be safe to use." }, "baseDomain": { - "message": "Base domain" + "message": "Base domain", + "description": "Domain name. Ex. website.com" + }, + "domainName": { + "message": "Domain Name", + "description": "Domain name. Ex. website.com" }, "host": { "message": "Host", @@ -1887,5 +1898,44 @@ }, "error": { "message": "Error" + }, + "regenerateUsername": { + "message": "Regenerate Username" + }, + "generateUsername": { + "message": "Generate Username" + }, + "usernameType": { + "message": "Username Type" + }, + "plusAddressedEmail": { + "message": "Plus Addressed Email" + }, + "plusAddressedEmailDesc": { + "message": "Use your email provider's sub-addressing capabilities." + }, + "catchallEmail": { + "message": "Catch-all Email" + }, + "catchallEmailDesc": { + "message": "Use your domain's configured catch-all inbox." + }, + "random": { + "message": "Random" + }, + "randomWord": { + "message": "Random Word" + }, + "websiteName": { + "message": "Website Name" + }, + "whatWouldYouLikeToGenerate": { + "message": "What would you like to generate?" + }, + "passwordType": { + "message": "Password Type" + }, + "service": { + "message": "Service" } } diff --git a/src/background/main.background.ts b/src/background/main.background.ts index 40b50de7e2..e355216216 100644 --- a/src/background/main.background.ts +++ b/src/background/main.background.ts @@ -31,6 +31,7 @@ import { TokenService as TokenServiceAbstraction } from "jslib-common/abstractio import { TotpService as TotpServiceAbstraction } from "jslib-common/abstractions/totp.service"; import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service"; import { UserVerificationService as UserVerificationServiceAbstraction } from "jslib-common/abstractions/userVerification.service"; +import { UsernameGenerationService as UsernameGenerationServiceAbstraction } from "jslib-common/abstractions/usernameGeneration.service"; import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service"; import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType"; import { CipherType } from "jslib-common/enums/cipherType"; @@ -66,6 +67,7 @@ import { TokenService } from "jslib-common/services/token.service"; import { TotpService } from "jslib-common/services/totp.service"; import { TwoFactorService } from "jslib-common/services/twoFactor.service"; import { UserVerificationService } from "jslib-common/services/userVerification.service"; +import { UsernameGenerationService } from "jslib-common/services/usernameGeneration.service"; import { WebCryptoFunctionService } from "jslib-common/services/webCryptoFunction.service"; import { BrowserApi } from "../browser/browserApi"; @@ -136,6 +138,7 @@ export default class MainBackground { keyConnectorService: KeyConnectorServiceAbstraction; userVerificationService: UserVerificationServiceAbstraction; twoFactorService: TwoFactorServiceAbstraction; + usernameGenerationService: UsernameGenerationServiceAbstraction; onUpdatedRan: boolean; onReplacedRan: boolean; @@ -200,7 +203,7 @@ export default class MainBackground { } ); this.i18nService = new I18nService(BrowserApi.getUILanguage(window)); - this.cryptoFunctionService = new WebCryptoFunctionService(window, this.platformUtilsService); + this.cryptoFunctionService = new WebCryptoFunctionService(window); this.cryptoService = new BrowserCryptoService( this.cryptoFunctionService, this.platformUtilsService, @@ -475,6 +478,10 @@ export default class MainBackground { this.twoFactorService, this.i18nService ); + this.usernameGenerationService = new UsernameGenerationService( + this.cryptoService, + this.stateService + ); } async bootstrap() { diff --git a/src/popup/components/pop-out.component.html b/src/popup/components/pop-out.component.html index 08d8fb75e0..73bf76941d 100644 --- a/src/popup/components/pop-out.component.html +++ b/src/popup/components/pop-out.component.html @@ -1,4 +1,4 @@ - + diff --git a/src/popup/generator/password-generator.component.html b/src/popup/generator/password-generator.component.html index f2337ea015..a30c7dfcd1 100644 --- a/src/popup/generator/password-generator.component.html +++ b/src/popup/generator/password-generator.component.html @@ -6,7 +6,7 @@

- {{ "passGen" | i18n }} + {{ "generator" | i18n }}

- + {{ "passwordGeneratorPolicyInEffect" | i18n }} -
-
-
-
-
+
+
+
-
-
- -
{{ "passwordHistory" | i18n }}
- -
+
+
+
+ +
-

- {{ "options" | i18n }} -

- -
+ +
-
-
-
- - -
-
- - -
-
- - -
-
- - + +
+

+ {{ "options" | i18n }} +

+
+
+ +
+ + +
+
-
- -
+
-
- +
+ +
+
+
- +
- + -
-
- - -
-
- -
+ +
+
+
+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+

+ {{ "options" | i18n }} +

-
- +
+ +
+ + +
+
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
-
- +
+ +
+ + +
+
+
+ +
+
+
+
+
+
+ + +
+
+ +
+ + +
+
+
+ + +
+
+
+
+
+
+ +
- +
diff --git a/src/popup/generator/password-generator.component.ts b/src/popup/generator/password-generator.component.ts index 9faae10330..9103ad4391 100644 --- a/src/popup/generator/password-generator.component.ts +++ b/src/popup/generator/password-generator.component.ts @@ -1,11 +1,13 @@ import { Location } from "@angular/common"; import { Component } from "@angular/core"; +import { ActivatedRoute } from "@angular/router"; import { PasswordGeneratorComponent as BasePasswordGeneratorComponent } from "jslib-angular/components/password-generator.component"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { StateService } from "jslib-common/abstractions/state.service"; +import { UsernameGenerationService } from "jslib-common/abstractions/usernameGeneration.service"; import { CipherView } from "jslib-common/models/view/cipherView"; @Component({ @@ -13,30 +15,52 @@ import { CipherView } from "jslib-common/models/view/cipherView"; templateUrl: "password-generator.component.html", }) export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent { + private addEditCipherInfo: any; private cipherState: CipherView; constructor( passwordGenerationService: PasswordGenerationService, + usernameGenerationService: UsernameGenerationService, platformUtilsService: PlatformUtilsService, i18nService: I18nService, - private stateService: StateService, + stateService: StateService, + route: ActivatedRoute, private location: Location ) { - super(passwordGenerationService, platformUtilsService, i18nService, window); + super( + passwordGenerationService, + usernameGenerationService, + platformUtilsService, + stateService, + i18nService, + route, + window + ); } async ngOnInit() { - await super.ngOnInit(); - const addEditCipherInfo = await this.stateService.getAddEditCipherInfo(); - if (addEditCipherInfo != null) { - this.cipherState = addEditCipherInfo.cipher; + this.addEditCipherInfo = await this.stateService.getAddEditCipherInfo(); + if (this.addEditCipherInfo != null) { + this.cipherState = this.addEditCipherInfo.cipher; } this.showSelect = this.cipherState != null; + this.showWebsiteOption = + this.cipherState?.login?.hasUris && this.cipherState.login.uris[0].hostname != null; + if (this.showWebsiteOption) { + this.usernameWebsite = this.cipherState.login.uris[0].hostname; + } + await super.ngOnInit(); } select() { super.select(); - this.cipherState.login.password = this.password; + if (this.type === "password") { + this.cipherState.login.password = this.password; + } else if (this.type === "username") { + this.cipherState.login.username = this.username; + } + this.addEditCipherInfo.cipher = this.cipherState; + this.stateService.setAddEditCipherInfo(this.addEditCipherInfo); this.close(); } diff --git a/src/popup/scss/box.scss b/src/popup/scss/box.scss index 782952be16..da7a3ab789 100644 --- a/src/popup/scss/box.scss +++ b/src/popup/scss/box.scss @@ -677,5 +677,14 @@ color: themed("textColor"); } } + + &.align-start { + align-items: start; + margin-top: 10px; + + label { + margin-top: -4px; + } + } } } diff --git a/src/popup/scss/pages.scss b/src/popup/scss/pages.scss index 061b7a8b23..9cba02e100 100644 --- a/src/popup/scss/pages.scss +++ b/src/popup/scss/pages.scss @@ -8,13 +8,28 @@ app-sync { } } -app-password-generator .password-block { +app-password-generator .generated-block { font-size: $font-size-large; font-family: $font-family-monospace; margin: 20px; + display: flex; - .password-wrapper { - text-align: center; + .generated-wrapper { + text-align: left; + width: 100%; + min-width: 0; + white-space: pre-wrap; + word-break: break-all; + } + + .action-buttons { + display: flex; + align-self: center; + + button { + padding-left: 5px; + margin-left: 10px; + } } } diff --git a/src/popup/services/services.module.ts b/src/popup/services/services.module.ts index 3b8dc70c85..8229cf6d3f 100644 --- a/src/popup/services/services.module.ts +++ b/src/popup/services/services.module.ts @@ -37,6 +37,7 @@ import { TokenService } from "jslib-common/abstractions/token.service"; import { TotpService } from "jslib-common/abstractions/totp.service"; import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service"; import { UserVerificationService } from "jslib-common/abstractions/userVerification.service"; +import { UsernameGenerationService } from "jslib-common/abstractions/usernameGeneration.service"; import { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service"; import { ThemeType } from "jslib-common/enums/themeType"; import { AuthService } from "jslib-common/services/auth.service"; @@ -311,6 +312,11 @@ export function initFactory( useFactory: getBgService("stateService"), deps: [], }, + { + provide: UsernameGenerationService, + useFactory: getBgService("usernameGenerationService"), + deps: [], + }, { provide: BaseStateServiceAbstraction, useExisting: StateServiceAbstraction, diff --git a/src/popup/vault/add-edit.component.html b/src/popup/vault/add-edit.component.html index 8fd413ffc2..982d839b24 100644 --- a/src/popup/vault/add-edit.component.html +++ b/src/popup/vault/add-edit.component.html @@ -34,16 +34,30 @@
-
- - +
+
+ + +
+
+ +
diff --git a/src/popup/vault/add-edit.component.ts b/src/popup/vault/add-edit.component.ts index 8d0c7b37bc..c8524da5a2 100644 --- a/src/popup/vault/add-edit.component.ts +++ b/src/popup/vault/add-edit.component.ts @@ -182,17 +182,20 @@ export class AddEditComponent extends BaseAddEditComponent { this.location.back(); } + async generateUsername(): Promise { + const confirmed = await super.generateUsername(); + if (confirmed) { + await this.saveCipherState(); + this.router.navigate(["generator"], { queryParams: { type: "username" } }); + } + return confirmed; + } + async generatePassword(): Promise { const confirmed = await super.generatePassword(); if (confirmed) { - this.stateService.setAddEditCipherInfo({ - cipher: this.cipher, - collectionIds: - this.collections == null - ? [] - : this.collections.filter((c) => (c as any).checked).map((c) => c.id), - }); - this.router.navigate(["generator"]); + await this.saveCipherState(); + this.router.navigate(["generator"], { queryParams: { type: "password" } }); } return confirmed; } @@ -217,4 +220,14 @@ export class AddEditComponent extends BaseAddEditComponent { (this.ownershipOptions.length > 1 || !this.allowPersonal) ); } + + private saveCipherState() { + return this.stateService.setAddEditCipherInfo({ + cipher: this.cipher, + collectionIds: + this.collections == null + ? [] + : this.collections.filter((c) => (c as any).checked).map((c) => c.id), + }); + } }