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 <djsmith@web.de>
This commit is contained in:
parent
4607e9d0ba
commit
bf081e0322
2
jslib
2
jslib
|
@ -1 +1 @@
|
||||||
Subproject commit 9950fb42a15bad434a4b404419ff4a87af67a27b
|
Subproject commit fa73c13b8c9ed35cbb9909e342b143fa8a57f1a0
|
|
@ -121,7 +121,7 @@
|
||||||
"big-integer": "1.6.48",
|
"big-integer": "1.6.48",
|
||||||
"browser-hrtime": "^1.1.8",
|
"browser-hrtime": "^1.1.8",
|
||||||
"lunr": "^2.3.9",
|
"lunr": "^2.3.9",
|
||||||
"node-forge": "^0.10.0",
|
"node-forge": "^1.2.1",
|
||||||
"papaparse": "^5.3.0",
|
"papaparse": "^5.3.0",
|
||||||
"rxjs": "^7.4.0",
|
"rxjs": "^7.4.0",
|
||||||
"tldjs": "^2.3.1",
|
"tldjs": "^2.3.1",
|
||||||
|
@ -130,7 +130,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/lunr": "^2.3.3",
|
"@types/lunr": "^2.3.3",
|
||||||
"@types/node": "^16.11.12",
|
"@types/node": "^16.11.12",
|
||||||
"@types/node-forge": "^0.9.7",
|
"@types/node-forge": "^1.0.1",
|
||||||
"@types/papaparse": "^5.2.5",
|
"@types/papaparse": "^5.2.5",
|
||||||
"@types/tldjs": "^2.3.0",
|
"@types/tldjs": "^2.3.0",
|
||||||
"@types/zxcvbn": "^4.4.1",
|
"@types/zxcvbn": "^4.4.1",
|
||||||
|
@ -1037,9 +1037,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node-forge": {
|
"node_modules/@types/node-forge": {
|
||||||
"version": "0.9.10",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-0.9.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz",
|
||||||
"integrity": "sha512-+BbPlhZeYs/WETWftQi2LeRx9VviWSwawNo+Pid5qNrSZHb60loYjpph3OrbwXMMseadu9rE9NeK34r4BHT+QQ==",
|
"integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
|
@ -8281,11 +8281,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-forge": {
|
"node_modules/node-forge": {
|
||||||
"version": "0.10.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz",
|
||||||
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
|
"integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6.0.0"
|
"node": ">= 6.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
|
@ -12566,14 +12566,14 @@
|
||||||
"@microsoft/signalr-protocol-msgpack": "5.0.10",
|
"@microsoft/signalr-protocol-msgpack": "5.0.10",
|
||||||
"@types/lunr": "^2.3.3",
|
"@types/lunr": "^2.3.3",
|
||||||
"@types/node": "^16.11.12",
|
"@types/node": "^16.11.12",
|
||||||
"@types/node-forge": "^0.9.7",
|
"@types/node-forge": "^1.0.1",
|
||||||
"@types/papaparse": "^5.2.5",
|
"@types/papaparse": "^5.2.5",
|
||||||
"@types/tldjs": "^2.3.0",
|
"@types/tldjs": "^2.3.0",
|
||||||
"@types/zxcvbn": "^4.4.1",
|
"@types/zxcvbn": "^4.4.1",
|
||||||
"big-integer": "1.6.48",
|
"big-integer": "1.6.48",
|
||||||
"browser-hrtime": "^1.1.8",
|
"browser-hrtime": "^1.1.8",
|
||||||
"lunr": "^2.3.9",
|
"lunr": "^2.3.9",
|
||||||
"node-forge": "^0.10.0",
|
"node-forge": "^1.2.1",
|
||||||
"papaparse": "^5.3.0",
|
"papaparse": "^5.3.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rxjs": "^7.4.0",
|
"rxjs": "^7.4.0",
|
||||||
|
@ -12894,9 +12894,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node-forge": {
|
"@types/node-forge": {
|
||||||
"version": "0.9.10",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-0.9.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.0.1.tgz",
|
||||||
"integrity": "sha512-+BbPlhZeYs/WETWftQi2LeRx9VviWSwawNo+Pid5qNrSZHb60loYjpph3OrbwXMMseadu9rE9NeK34r4BHT+QQ==",
|
"integrity": "sha512-96ELNKv9tQJ19afdBUiM5iDw7OYEc53iUc51gAPR2aGaqRsO1DBROjqgZRjZa1tkPj7TnEOR0EnyAX6iryGkzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
|
@ -18471,9 +18471,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node-forge": {
|
"node-forge": {
|
||||||
"version": "0.10.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz",
|
||||||
"integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
|
"integrity": "sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA=="
|
||||||
},
|
},
|
||||||
"node-releases": {
|
"node-releases": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
|
|
|
@ -542,6 +542,12 @@
|
||||||
"overwritePasswordConfirmation": {
|
"overwritePasswordConfirmation": {
|
||||||
"message": "Are you sure you want to overwrite the current password?"
|
"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": {
|
"searchFolder": {
|
||||||
"message": "Search folder"
|
"message": "Search folder"
|
||||||
},
|
},
|
||||||
|
@ -1233,7 +1239,12 @@
|
||||||
"message": "This password was not found in any known data breaches. It should be safe to use."
|
"message": "This password was not found in any known data breaches. It should be safe to use."
|
||||||
},
|
},
|
||||||
"baseDomain": {
|
"baseDomain": {
|
||||||
"message": "Base domain"
|
"message": "Base domain",
|
||||||
|
"description": "Domain name. Ex. website.com"
|
||||||
|
},
|
||||||
|
"domainName": {
|
||||||
|
"message": "Domain Name",
|
||||||
|
"description": "Domain name. Ex. website.com"
|
||||||
},
|
},
|
||||||
"host": {
|
"host": {
|
||||||
"message": "Host",
|
"message": "Host",
|
||||||
|
@ -1887,5 +1898,44 @@
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"message": "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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import { TokenService as TokenServiceAbstraction } from "jslib-common/abstractio
|
||||||
import { TotpService as TotpServiceAbstraction } from "jslib-common/abstractions/totp.service";
|
import { TotpService as TotpServiceAbstraction } from "jslib-common/abstractions/totp.service";
|
||||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service";
|
import { TwoFactorService as TwoFactorServiceAbstraction } from "jslib-common/abstractions/twoFactor.service";
|
||||||
import { UserVerificationService as UserVerificationServiceAbstraction } from "jslib-common/abstractions/userVerification.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 { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "jslib-common/abstractions/vaultTimeout.service";
|
||||||
import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType";
|
import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType";
|
||||||
import { CipherType } from "jslib-common/enums/cipherType";
|
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 { TotpService } from "jslib-common/services/totp.service";
|
||||||
import { TwoFactorService } from "jslib-common/services/twoFactor.service";
|
import { TwoFactorService } from "jslib-common/services/twoFactor.service";
|
||||||
import { UserVerificationService } from "jslib-common/services/userVerification.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 { WebCryptoFunctionService } from "jslib-common/services/webCryptoFunction.service";
|
||||||
|
|
||||||
import { BrowserApi } from "../browser/browserApi";
|
import { BrowserApi } from "../browser/browserApi";
|
||||||
|
@ -136,6 +138,7 @@ export default class MainBackground {
|
||||||
keyConnectorService: KeyConnectorServiceAbstraction;
|
keyConnectorService: KeyConnectorServiceAbstraction;
|
||||||
userVerificationService: UserVerificationServiceAbstraction;
|
userVerificationService: UserVerificationServiceAbstraction;
|
||||||
twoFactorService: TwoFactorServiceAbstraction;
|
twoFactorService: TwoFactorServiceAbstraction;
|
||||||
|
usernameGenerationService: UsernameGenerationServiceAbstraction;
|
||||||
|
|
||||||
onUpdatedRan: boolean;
|
onUpdatedRan: boolean;
|
||||||
onReplacedRan: boolean;
|
onReplacedRan: boolean;
|
||||||
|
@ -200,7 +203,7 @@ export default class MainBackground {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.i18nService = new I18nService(BrowserApi.getUILanguage(window));
|
this.i18nService = new I18nService(BrowserApi.getUILanguage(window));
|
||||||
this.cryptoFunctionService = new WebCryptoFunctionService(window, this.platformUtilsService);
|
this.cryptoFunctionService = new WebCryptoFunctionService(window);
|
||||||
this.cryptoService = new BrowserCryptoService(
|
this.cryptoService = new BrowserCryptoService(
|
||||||
this.cryptoFunctionService,
|
this.cryptoFunctionService,
|
||||||
this.platformUtilsService,
|
this.platformUtilsService,
|
||||||
|
@ -475,6 +478,10 @@ export default class MainBackground {
|
||||||
this.twoFactorService,
|
this.twoFactorService,
|
||||||
this.i18nService
|
this.i18nService
|
||||||
);
|
);
|
||||||
|
this.usernameGenerationService = new UsernameGenerationService(
|
||||||
|
this.cryptoService,
|
||||||
|
this.stateService
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async bootstrap() {
|
async bootstrap() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<ng-container>
|
<ng-container *ngIf="show">
|
||||||
<button type="button" (click)="expand()" appA11yTitle="{{ 'popOutNewWindow' | i18n }}">
|
<button type="button" (click)="expand()" appA11yTitle="{{ 'popOutNewWindow' | i18n }}">
|
||||||
<i class="bwi bwi-external-link bwi-rotate-270 bwi-lg bwi-fw" aria-hidden="true"></i>
|
<i class="bwi bwi-external-link bwi-rotate-270 bwi-lg bwi-fw" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="center">
|
<h1 class="center">
|
||||||
<span class="title">{{ "passGen" | i18n }}</span>
|
<span class="title">{{ "generator" | i18n }}</span>
|
||||||
</h1>
|
</h1>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button type="button" appBlurClick (click)="select()" *ngIf="showSelect">
|
<button type="button" appBlurClick (click)="select()" *ngIf="showSelect">
|
||||||
|
@ -15,58 +15,69 @@
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<content>
|
<content>
|
||||||
<app-callout type="info" *ngIf="enforcedPolicyOptions?.inEffect()">
|
<app-callout type="info" *ngIf="enforcedPasswordPolicyOptions?.inEffect() && type === 'password'">
|
||||||
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
{{ "passwordGeneratorPolicyInEffect" | i18n }}
|
||||||
</app-callout>
|
</app-callout>
|
||||||
<div class="password-block">
|
<div class="generated-block" *ngIf="type === 'password'">
|
||||||
<div class="password-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
<div class="generated-wrapper" [innerHTML]="password | colorPassword" appSelectCopy></div>
|
||||||
</div>
|
<div class="action-buttons">
|
||||||
<div class="box list">
|
|
||||||
<div class="box-content single-line">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="box-content-row text-primary"
|
class="row-btn"
|
||||||
appStopClick
|
appStopClick
|
||||||
appBlurClick
|
appA11yTitle="{{ 'copyPassword' | i18n }}"
|
||||||
(click)="regenerate()"
|
(click)="copy()"
|
||||||
>
|
>
|
||||||
{{ "regeneratePassword" | i18n }}
|
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="box-content-row text-primary"
|
|
||||||
appStopClick
|
appStopClick
|
||||||
appBlurClick
|
appBlurClick
|
||||||
(click)="copy()"
|
appA11yTitle="{{ 'regeneratePassword' | i18n }}"
|
||||||
|
(click)="regenerate()"
|
||||||
>
|
>
|
||||||
{{ "copyPassword" | i18n }}
|
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box list">
|
<div class="generated-block" *ngIf="type === 'username'">
|
||||||
<div class="box-content single-line">
|
<div class="generated-wrapper" [innerHTML]="username | colorPassword" appSelectCopy></div>
|
||||||
<a class="box-content-row box-content-row-flex" routerLink="/generator-history">
|
<div class="action-buttons">
|
||||||
<div class="row-main">{{ "passwordHistory" | i18n }}</div>
|
<button
|
||||||
<i class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i>
|
type="button"
|
||||||
</a>
|
class="row-btn"
|
||||||
|
appStopClick
|
||||||
|
appA11yTitle="{{ 'copyUsername' | i18n }}"
|
||||||
|
(click)="copy()"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
appA11yTitle="{{ 'regenerateUsername' | i18n }}"
|
||||||
|
(click)="regenerate()"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<h2 class="box-header">
|
|
||||||
{{ "options" | i18n }}
|
|
||||||
</h2>
|
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row">
|
<div class="box-content-row">
|
||||||
<label class="sr-only radio-header">{{ "type" | i18n }}</label>
|
<label class="radio-header">{{ "whatWouldYouLikeToGenerate" | i18n }}</label>
|
||||||
<div class="radio-group text-default" appBoxRow *ngFor="let o of passTypeOptions">
|
<div class="radio-group text-default" appBoxRow *ngFor="let o of typeOptions">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
[(ngModel)]="options.type"
|
[(ngModel)]="type"
|
||||||
name="Type_{{ o.value }}"
|
name="Type_{{ o.value }}"
|
||||||
id="type_{{ o.value }}"
|
id="type_{{ o.value }}"
|
||||||
[value]="o.value"
|
[value]="o.value"
|
||||||
(change)="saveOptions()"
|
(change)="typeChanged()"
|
||||||
[checked]="options.type === o.value"
|
[checked]="type === o.value"
|
||||||
|
[disabled]="showSelect"
|
||||||
/>
|
/>
|
||||||
<label for="type_{{ o.value }}">
|
<label for="type_{{ o.value }}">
|
||||||
{{ o.name }}
|
{{ o.name }}
|
||||||
|
@ -75,152 +86,344 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box" *ngIf="options.type === 'passphrase'">
|
<ng-container *ngIf="type === 'password'">
|
||||||
<div class="box-content">
|
<div class="box">
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<h2 class="box-header">
|
||||||
<label for="num-words">{{ "numWords" | i18n }}</label>
|
{{ "options" | i18n }}
|
||||||
<input
|
</h2>
|
||||||
id="num-words"
|
<div class="box-content">
|
||||||
type="number"
|
<div class="box-content-row">
|
||||||
min="3"
|
<label class="radio-header">{{ "passwordType" | i18n }}</label>
|
||||||
max="20"
|
<div class="radio-group text-default" appBoxRow *ngFor="let o of passTypeOptions">
|
||||||
(change)="saveOptions()"
|
<input
|
||||||
[(ngModel)]="options.numWords"
|
type="radio"
|
||||||
/>
|
[(ngModel)]="passwordOptions.type"
|
||||||
</div>
|
name="PasswordType_{{ o.value }}"
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
id="passwordtype_{{ o.value }}"
|
||||||
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
|
[value]="o.value"
|
||||||
<input
|
(change)="savePasswordOptions()"
|
||||||
id="word-separator"
|
[checked]="passwordOptions.type === o.value"
|
||||||
type="text"
|
/>
|
||||||
maxlength="1"
|
<label for="passwordtype_{{ o.value }}">
|
||||||
(input)="saveOptions()"
|
{{ o.name }}
|
||||||
[(ngModel)]="options.wordSeparator"
|
</label>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="capitalize"
|
|
||||||
type="checkbox"
|
|
||||||
(change)="saveOptions()"
|
|
||||||
[(ngModel)]="options.capitalize"
|
|
||||||
[disabled]="enforcedPolicyOptions?.capitalize"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
|
||||||
<input
|
|
||||||
id="include-number"
|
|
||||||
type="checkbox"
|
|
||||||
(change)="saveOptions()"
|
|
||||||
[(ngModel)]="options.includeNumber"
|
|
||||||
[disabled]="enforcedPolicyOptions?.includeNumber"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="box" *ngIf="passwordOptions.type === 'passphrase'">
|
||||||
<ng-container *ngIf="options.type === 'password'">
|
|
||||||
<div class="box">
|
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-slider" appBoxRow>
|
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||||
<label for="length">{{ "length" | i18n }}</label>
|
<label for="num-words">{{ "numWords" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="length"
|
id="num-words"
|
||||||
type="number"
|
type="number"
|
||||||
min="5"
|
min="3"
|
||||||
max="128"
|
max="20"
|
||||||
[(ngModel)]="options.length"
|
(change)="savePasswordOptions()"
|
||||||
(change)="saveOptions()"
|
[(ngModel)]="passwordOptions.numWords"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||||
|
<label for="word-separator">{{ "wordSeparator" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="lengthRange"
|
id="word-separator"
|
||||||
type="range"
|
type="text"
|
||||||
min="5"
|
maxlength="1"
|
||||||
max="128"
|
(input)="savePasswordOptions()"
|
||||||
step="1"
|
[(ngModel)]="passwordOptions.wordSeparator"
|
||||||
[(ngModel)]="options.length"
|
|
||||||
(change)="sliderChanged()"
|
|
||||||
(input)="sliderInput()"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="uppercase">A-Z</label>
|
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="uppercase"
|
id="capitalize"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
(change)="saveOptions()"
|
(change)="savePasswordOptions()"
|
||||||
attr.aria-label="{{ 'uppercase' | i18n }}"
|
[(ngModel)]="passwordOptions.capitalize"
|
||||||
[disabled]="enforcedPolicyOptions.useUppercase"
|
[disabled]="enforcedPasswordPolicyOptions?.capitalize"
|
||||||
[(ngModel)]="options.uppercase"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="lowercase">a-z</label>
|
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="lowercase"
|
id="include-number"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
(change)="saveOptions()"
|
(change)="savePasswordOptions()"
|
||||||
attr.aria-label="{{ 'lowercase' | i18n }}"
|
[(ngModel)]="passwordOptions.includeNumber"
|
||||||
[disabled]="enforcedPolicyOptions.useLowercase"
|
[disabled]="enforcedPasswordPolicyOptions?.includeNumber"
|
||||||
[(ngModel)]="options.lowercase"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="numbers">0-9</label>
|
|
||||||
<input
|
|
||||||
id="numbers"
|
|
||||||
type="checkbox"
|
|
||||||
(change)="saveOptions()"
|
|
||||||
attr.aria-label="{{ 'numbers' | i18n }}"
|
|
||||||
[disabled]="enforcedPolicyOptions.useNumbers"
|
|
||||||
[(ngModel)]="options.number"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
|
||||||
<label for="special">!@#$%^&*</label>
|
|
||||||
<input
|
|
||||||
id="special"
|
|
||||||
type="checkbox"
|
|
||||||
(change)="saveOptions()"
|
|
||||||
attr.aria-label="{{ 'specialCharacters' | i18n }}"
|
|
||||||
[disabled]="enforcedPolicyOptions.useSpecial"
|
|
||||||
[(ngModel)]="options.special"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ng-container *ngIf="passwordOptions.type === 'password'">
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row box-content-row-slider" appBoxRow>
|
||||||
|
<label for="length">{{ "length" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="length"
|
||||||
|
type="number"
|
||||||
|
min="5"
|
||||||
|
max="128"
|
||||||
|
[(ngModel)]="passwordOptions.length"
|
||||||
|
(change)="savePasswordOptions()"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
id="lengthRange"
|
||||||
|
type="range"
|
||||||
|
min="5"
|
||||||
|
max="128"
|
||||||
|
step="1"
|
||||||
|
[(ngModel)]="passwordOptions.length"
|
||||||
|
(change)="sliderChanged()"
|
||||||
|
(input)="sliderInput()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="uppercase">A-Z</label>
|
||||||
|
<input
|
||||||
|
id="uppercase"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="savePasswordOptions()"
|
||||||
|
attr.aria-label="{{ 'uppercase' | i18n }}"
|
||||||
|
[disabled]="enforcedPasswordPolicyOptions.useUppercase"
|
||||||
|
[(ngModel)]="passwordOptions.uppercase"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="lowercase">a-z</label>
|
||||||
|
<input
|
||||||
|
id="lowercase"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="savePasswordOptions()"
|
||||||
|
attr.aria-label="{{ 'lowercase' | i18n }}"
|
||||||
|
[disabled]="enforcedPasswordPolicyOptions.useLowercase"
|
||||||
|
[(ngModel)]="passwordOptions.lowercase"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="numbers">0-9</label>
|
||||||
|
<input
|
||||||
|
id="numbers"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="savePasswordOptions()"
|
||||||
|
attr.aria-label="{{ 'numbers' | i18n }}"
|
||||||
|
[disabled]="enforcedPasswordPolicyOptions.useNumbers"
|
||||||
|
[(ngModel)]="passwordOptions.number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="special">!@#$%^&*</label>
|
||||||
|
<input
|
||||||
|
id="special"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="savePasswordOptions()"
|
||||||
|
attr.aria-label="{{ 'specialCharacters' | i18n }}"
|
||||||
|
[disabled]="enforcedPasswordPolicyOptions.useSpecial"
|
||||||
|
[(ngModel)]="passwordOptions.special"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||||
|
<label for="min-number">{{ "minNumbers" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="min-number"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="9"
|
||||||
|
(change)="savePasswordOptions()"
|
||||||
|
[(ngModel)]="passwordOptions.minNumber"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-input" appBoxRow>
|
||||||
|
<label for="min-special">{{ "minSpecial" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="min-special"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="9"
|
||||||
|
(change)="savePasswordOptions()"
|
||||||
|
[(ngModel)]="passwordOptions.minSpecial"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="ambiguous">{{ "avoidAmbChar" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="ambiguous"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="savePasswordOptions()"
|
||||||
|
[(ngModel)]="avoidAmbiguous"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<div class="box list">
|
||||||
|
<div class="box-content single-line">
|
||||||
|
<a class="box-content-row box-content-row-flex" routerLink="/generator-history">
|
||||||
|
<div class="row-main">{{ "passwordHistory" | i18n }}</div>
|
||||||
|
<i class="bwi bwi-angle-right bwi-lg row-sub-icon" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="type === 'username'">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
|
<h2 class="box-header">
|
||||||
|
{{ "options" | i18n }}
|
||||||
|
</h2>
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<div class="box-content-row">
|
||||||
<label for="min-number">{{ "minNumbers" | i18n }}</label>
|
<label class="radio-header">{{ "usernameType" | i18n }}</label>
|
||||||
|
<div
|
||||||
|
class="radio-group align-start text-default"
|
||||||
|
appBoxRow
|
||||||
|
*ngFor="let o of usernameTypeOptions"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
[(ngModel)]="usernameOptions.type"
|
||||||
|
name="Type_{{ o.value }}"
|
||||||
|
id="type_{{ o.value }}"
|
||||||
|
[value]="o.value"
|
||||||
|
(change)="saveUsernameOptions()"
|
||||||
|
[checked]="usernameOptions.type === o.value"
|
||||||
|
/>
|
||||||
|
<label for="type_{{ o.value }}">
|
||||||
|
{{ o.name }}
|
||||||
|
<div class="small text-muted" *ngIf="o.desc">{{ o.desc }}</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box" *ngIf="usernameOptions.type === 'forwarded'">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row">
|
||||||
|
<label class="radio-header">{{ "service" | i18n }}</label>
|
||||||
|
<div class="radio-group text-default" appBoxRow *ngFor="let o of forwardOptions">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
[(ngModel)]="usernameOptions.forwardedService"
|
||||||
|
name="ForwardType_{{ o.value }}"
|
||||||
|
id="forwardtype_{{ o.value }}"
|
||||||
|
[value]="o.value"
|
||||||
|
(change)="saveUsernameOptions()"
|
||||||
|
[checked]="usernameOptions.forwardedService === o.value"
|
||||||
|
/>
|
||||||
|
<label for="forwardtype_{{ o.value }}">
|
||||||
|
{{ o.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box" *ngIf="usernameOptions.type === 'subaddress'">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="subaddress-email">{{ "emailAddress" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="min-number"
|
id="subaddress-email"
|
||||||
type="number"
|
type="text"
|
||||||
min="0"
|
name="SubaddressEmail"
|
||||||
max="9"
|
[(ngModel)]="usernameOptions.subaddressEmail"
|
||||||
(change)="saveOptions()"
|
(blur)="saveUsernameOptions()"
|
||||||
[(ngModel)]="options.minNumber"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-input" appBoxRow>
|
<div class="box-content-row" *ngIf="subaddressOptions.length > 1">
|
||||||
<label for="min-special">{{ "minSpecial" | i18n }}</label>
|
<label class="radio-header">{{ "type" | i18n }}</label>
|
||||||
|
<div class="radio-group text-default" appBoxRow *ngFor="let o of subaddressOptions">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
[(ngModel)]="usernameOptions.subaddressType"
|
||||||
|
name="SubaddressType_{{ o.value }}"
|
||||||
|
id="subaddresstype_{{ o.value }}"
|
||||||
|
[value]="o.value"
|
||||||
|
(change)="saveUsernameOptions()"
|
||||||
|
[checked]="usernameOptions.subaddressType === o.value"
|
||||||
|
/>
|
||||||
|
<label for="subaddresstype_{{ o.value }}">
|
||||||
|
{{ o.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" appBoxRow *ngIf="showWebsiteOption">
|
||||||
|
<label for="subaddress-website">{{ "website" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="min-special"
|
id="subaddress-website"
|
||||||
type="number"
|
type="text"
|
||||||
min="0"
|
name="SubaddressWebsite"
|
||||||
max="9"
|
[value]="usernameOptions.website"
|
||||||
(change)="saveOptions()"
|
disabled
|
||||||
[(ngModel)]="options.minSpecial"
|
readonly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box" *ngIf="usernameOptions.type === 'catchall'">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row" appBoxRow>
|
||||||
|
<label for="catchall-domain">{{ "domainName" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="catchall-domain"
|
||||||
|
type="text"
|
||||||
|
name="CatchallDomain"
|
||||||
|
[(ngModel)]="usernameOptions.catchallDomain"
|
||||||
|
(blur)="saveUsernameOptions()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" *ngIf="catchallOptions.length > 1">
|
||||||
|
<label class="radio-header">{{ "type" | i18n }}</label>
|
||||||
|
<div class="radio-group text-default" appBoxRow *ngFor="let o of catchallOptions">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
[(ngModel)]="usernameOptions.catchallType"
|
||||||
|
name="CatchallType_{{ o.value }}"
|
||||||
|
id="catchalltype_{{ o.value }}"
|
||||||
|
[value]="o.value"
|
||||||
|
(change)="saveUsernameOptions()"
|
||||||
|
[checked]="usernameOptions.catchallType === o.value"
|
||||||
|
/>
|
||||||
|
<label for="catchalltype_{{ o.value }}">
|
||||||
|
{{ o.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-content-row" appBoxRow *ngIf="showWebsiteOption">
|
||||||
|
<label for="catchall-website">{{ "website" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="catchall-website"
|
||||||
|
type="text"
|
||||||
|
name="CatchallWebsite"
|
||||||
|
[value]="usernameOptions.website"
|
||||||
|
disabled
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box" *ngIf="usernameOptions.type === 'word'">
|
||||||
|
<div class="box-content">
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="capitalize">{{ "capitalize" | i18n }}</label>
|
||||||
|
<input
|
||||||
|
id="capitalize"
|
||||||
|
type="checkbox"
|
||||||
|
(change)="saveUsernameOptions()"
|
||||||
|
[(ngModel)]="usernameOptions.wordCapitalize"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
<label for="ambiguous">{{ "avoidAmbChar" | i18n }}</label>
|
<label for="include-number">{{ "includeNumber" | i18n }}</label>
|
||||||
<input
|
<input
|
||||||
id="ambiguous"
|
id="include-number"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
(change)="saveOptions()"
|
(change)="saveUsernameOptions()"
|
||||||
[(ngModel)]="avoidAmbiguous"
|
[(ngModel)]="usernameOptions.wordIncludeNumber"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { Location } from "@angular/common";
|
import { Location } from "@angular/common";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
import { PasswordGeneratorComponent as BasePasswordGeneratorComponent } from "jslib-angular/components/password-generator.component";
|
import { PasswordGeneratorComponent as BasePasswordGeneratorComponent } from "jslib-angular/components/password-generator.component";
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
import { PasswordGenerationService } from "jslib-common/abstractions/passwordGeneration.service";
|
||||||
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
|
||||||
import { StateService } from "jslib-common/abstractions/state.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";
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -13,30 +15,52 @@ import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
templateUrl: "password-generator.component.html",
|
templateUrl: "password-generator.component.html",
|
||||||
})
|
})
|
||||||
export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent {
|
export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent {
|
||||||
|
private addEditCipherInfo: any;
|
||||||
private cipherState: CipherView;
|
private cipherState: CipherView;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
passwordGenerationService: PasswordGenerationService,
|
passwordGenerationService: PasswordGenerationService,
|
||||||
|
usernameGenerationService: UsernameGenerationService,
|
||||||
platformUtilsService: PlatformUtilsService,
|
platformUtilsService: PlatformUtilsService,
|
||||||
i18nService: I18nService,
|
i18nService: I18nService,
|
||||||
private stateService: StateService,
|
stateService: StateService,
|
||||||
|
route: ActivatedRoute,
|
||||||
private location: Location
|
private location: Location
|
||||||
) {
|
) {
|
||||||
super(passwordGenerationService, platformUtilsService, i18nService, window);
|
super(
|
||||||
|
passwordGenerationService,
|
||||||
|
usernameGenerationService,
|
||||||
|
platformUtilsService,
|
||||||
|
stateService,
|
||||||
|
i18nService,
|
||||||
|
route,
|
||||||
|
window
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
await super.ngOnInit();
|
this.addEditCipherInfo = await this.stateService.getAddEditCipherInfo();
|
||||||
const addEditCipherInfo = await this.stateService.getAddEditCipherInfo();
|
if (this.addEditCipherInfo != null) {
|
||||||
if (addEditCipherInfo != null) {
|
this.cipherState = this.addEditCipherInfo.cipher;
|
||||||
this.cipherState = addEditCipherInfo.cipher;
|
|
||||||
}
|
}
|
||||||
this.showSelect = this.cipherState != null;
|
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() {
|
select() {
|
||||||
super.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();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -677,5 +677,14 @@
|
||||||
color: themed("textColor");
|
color: themed("textColor");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.align-start {
|
||||||
|
align-items: start;
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-top: -4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,28 @@ app-sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app-password-generator .password-block {
|
app-password-generator .generated-block {
|
||||||
font-size: $font-size-large;
|
font-size: $font-size-large;
|
||||||
font-family: $font-family-monospace;
|
font-family: $font-family-monospace;
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
.password-wrapper {
|
.generated-wrapper {
|
||||||
text-align: center;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import { TokenService } from "jslib-common/abstractions/token.service";
|
||||||
import { TotpService } from "jslib-common/abstractions/totp.service";
|
import { TotpService } from "jslib-common/abstractions/totp.service";
|
||||||
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
|
import { TwoFactorService } from "jslib-common/abstractions/twoFactor.service";
|
||||||
import { UserVerificationService } from "jslib-common/abstractions/userVerification.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 { VaultTimeoutService } from "jslib-common/abstractions/vaultTimeout.service";
|
||||||
import { ThemeType } from "jslib-common/enums/themeType";
|
import { ThemeType } from "jslib-common/enums/themeType";
|
||||||
import { AuthService } from "jslib-common/services/auth.service";
|
import { AuthService } from "jslib-common/services/auth.service";
|
||||||
|
@ -311,6 +312,11 @@ export function initFactory(
|
||||||
useFactory: getBgService<StateServiceAbstraction>("stateService"),
|
useFactory: getBgService<StateServiceAbstraction>("stateService"),
|
||||||
deps: [],
|
deps: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: UsernameGenerationService,
|
||||||
|
useFactory: getBgService<UsernameGenerationService>("usernameGenerationService"),
|
||||||
|
deps: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: BaseStateServiceAbstraction,
|
provide: BaseStateServiceAbstraction,
|
||||||
useExisting: StateServiceAbstraction,
|
useExisting: StateServiceAbstraction,
|
||||||
|
|
|
@ -34,16 +34,30 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- Login -->
|
<!-- Login -->
|
||||||
<div *ngIf="cipher.type === cipherType.Login">
|
<div *ngIf="cipher.type === cipherType.Login">
|
||||||
<div class="box-content-row" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<label for="loginUsername">{{ "username" | i18n }}</label>
|
<div class="row-main">
|
||||||
<input
|
<label for="loginUsername">{{ "username" | i18n }}</label>
|
||||||
id="loginUsername"
|
<input
|
||||||
type="text"
|
id="loginUsername"
|
||||||
name="Login.Username"
|
type="text"
|
||||||
[(ngModel)]="cipher.login.username"
|
name="Login.Username"
|
||||||
inputmode="email"
|
[(ngModel)]="cipher.login.username"
|
||||||
appInputVerbatim
|
inputmode="email"
|
||||||
/>
|
appInputVerbatim
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="row-btn"
|
||||||
|
appStopClick
|
||||||
|
appBlurClick
|
||||||
|
appA11yTitle="{{ 'generateUsername' | i18n }}"
|
||||||
|
(click)="generateUsername()"
|
||||||
|
>
|
||||||
|
<i class="bwi bwi-lg bwi-generate" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main">
|
||||||
|
|
|
@ -182,17 +182,20 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||||
this.location.back();
|
this.location.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async generateUsername(): Promise<boolean> {
|
||||||
|
const confirmed = await super.generateUsername();
|
||||||
|
if (confirmed) {
|
||||||
|
await this.saveCipherState();
|
||||||
|
this.router.navigate(["generator"], { queryParams: { type: "username" } });
|
||||||
|
}
|
||||||
|
return confirmed;
|
||||||
|
}
|
||||||
|
|
||||||
async generatePassword(): Promise<boolean> {
|
async generatePassword(): Promise<boolean> {
|
||||||
const confirmed = await super.generatePassword();
|
const confirmed = await super.generatePassword();
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
this.stateService.setAddEditCipherInfo({
|
await this.saveCipherState();
|
||||||
cipher: this.cipher,
|
this.router.navigate(["generator"], { queryParams: { type: "password" } });
|
||||||
collectionIds:
|
|
||||||
this.collections == null
|
|
||||||
? []
|
|
||||||
: this.collections.filter((c) => (c as any).checked).map((c) => c.id),
|
|
||||||
});
|
|
||||||
this.router.navigate(["generator"]);
|
|
||||||
}
|
}
|
||||||
return confirmed;
|
return confirmed;
|
||||||
}
|
}
|
||||||
|
@ -217,4 +220,14 @@ export class AddEditComponent extends BaseAddEditComponent {
|
||||||
(this.ownershipOptions.length > 1 || !this.allowPersonal)
|
(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),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue