From 367104f0a72cf7bf5d7e7ae2c82c61295dc8624a Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 20 Jun 2018 18:16:20 -0400 Subject: [PATCH] added password generator tool --- src/app/app-routing.module.ts | 4 +- src/app/app.module.ts | 6 +- .../password-generator-history.component.html | 38 +++++++++++ .../password-generator-history.component.ts | 24 +++++++ .../tools/password-generator.component.html | 63 +++++++++++++++++++ src/app/tools/password-generator.component.ts | 50 +++++++++++++++ src/app/tools/tools.component.html | 6 +- src/app/vault/add-edit.component.ts | 13 +++- src/locales/en/messages.json | 31 +++++++++ src/scss/styles.scss | 29 ++++++++- src/services/htmlStorage.service.ts | 6 +- 11 files changed, 262 insertions(+), 8 deletions(-) create mode 100644 src/app/tools/password-generator-history.component.html create mode 100644 src/app/tools/password-generator-history.component.ts create mode 100644 src/app/tools/password-generator.component.html create mode 100644 src/app/tools/password-generator.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 96fb60102d..a9e3794b65 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -16,6 +16,7 @@ import { TwoFactorComponent } from './accounts/two-factor.component'; import { ExportComponent } from './tools/export.component'; import { ImportComponent } from './tools/import.component'; +import { PasswordGeneratorComponent } from './tools/password-generator.component'; import { ToolsComponent } from './tools/tools.component'; import { VaultComponent } from './vault/vault.component'; @@ -45,9 +46,10 @@ const routes: Routes = [ path: 'tools', component: ToolsComponent, children: [ - { path: '', pathMatch: 'full', redirectTo: 'import' }, + { path: '', pathMatch: 'full', redirectTo: 'generator' }, { path: 'import', component: ImportComponent, canActivate: [AuthGuardService] }, { path: 'export', component: ExportComponent, canActivate: [AuthGuardService] }, + { path: 'generator', component: PasswordGeneratorComponent, canActivate: [AuthGuardService] }, ], }, ], diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ec927de868..eb6ed514f9 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -31,6 +31,8 @@ import { TwoFactorComponent } from './accounts/two-factor.component'; import { ExportComponent } from './tools/export.component'; import { ImportComponent } from './tools/import.component'; +import { PasswordGeneratorHistoryComponent } from './tools/password-generator-history.component'; +import { PasswordGeneratorComponent } from './tools/password-generator.component'; import { ToolsComponent } from './tools/tools.component'; import { AddEditComponent } from './vault/add-edit.component'; @@ -60,7 +62,6 @@ import { TrueFalseValueDirective } from 'jslib/angular/directives/true-false-val import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe'; import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe'; -import { Folder } from 'jslib/models/domain'; @NgModule({ imports: [ @@ -106,6 +107,8 @@ import { Folder } from 'jslib/models/domain'; NavbarComponent, OrganizationsComponent, OrganizationLayoutComponent, + PasswordGeneratorComponent, + PasswordGeneratorHistoryComponent, RegisterComponent, SearchCiphersPipe, ShareComponent, @@ -127,6 +130,7 @@ import { Folder } from 'jslib/models/domain'; CollectionsComponent, FolderAddEditComponent, ModalComponent, + PasswordGeneratorHistoryComponent, ShareComponent, TwoFactorOptionsComponent, ], diff --git a/src/app/tools/password-generator-history.component.html b/src/app/tools/password-generator-history.component.html new file mode 100644 index 0000000000..5eed695725 --- /dev/null +++ b/src/app/tools/password-generator-history.component.html @@ -0,0 +1,38 @@ + diff --git a/src/app/tools/password-generator-history.component.ts b/src/app/tools/password-generator-history.component.ts new file mode 100644 index 0000000000..e1d47a41f8 --- /dev/null +++ b/src/app/tools/password-generator-history.component.ts @@ -0,0 +1,24 @@ +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { Component } from '@angular/core'; + +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; +import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; + +import { + PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent, +} from 'jslib/angular/components/password-generator-history.component'; + +@Component({ + selector: 'app-password-generator-history', + templateUrl: 'password-generator-history.component.html', +}) +export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent { + constructor(passwordGenerationService: PasswordGenerationService, analytics: Angulartics2, + platformUtilsService: PlatformUtilsService, i18nService: I18nService, + toasterService: ToasterService) { + super(passwordGenerationService, analytics, platformUtilsService, i18nService, toasterService, window); + } +} diff --git a/src/app/tools/password-generator.component.html b/src/app/tools/password-generator.component.html new file mode 100644 index 0000000000..0658ee902d --- /dev/null +++ b/src/app/tools/password-generator.component.html @@ -0,0 +1,63 @@ + +
+
+ {{password}} +
+
+

{{'options' | i18n}}

+
+
+ + + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ +
+
+ diff --git a/src/app/tools/password-generator.component.ts b/src/app/tools/password-generator.component.ts new file mode 100644 index 0000000000..c541eabd2c --- /dev/null +++ b/src/app/tools/password-generator.component.ts @@ -0,0 +1,50 @@ +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { + Component, + ComponentFactoryResolver, + ViewChild, + ViewContainerRef, +} from '@angular/core'; + +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; +import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; + +import { + PasswordGeneratorComponent as BasePasswordGeneratorComponent, +} from 'jslib/angular/components/password-generator.component'; + +import { ModalComponent } from '../modal.component'; +import { PasswordGeneratorHistoryComponent } from './password-generator-history.component'; + +@Component({ + selector: 'app-password-generator', + templateUrl: 'password-generator.component.html', +}) +export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent { + @ViewChild('historyTemplate', { read: ViewContainerRef }) historyModalRef: ViewContainerRef; + + private modal: ModalComponent = null; + + constructor(passwordGenerationService: PasswordGenerationService, analytics: Angulartics2, + platformUtilsService: PlatformUtilsService, i18nService: I18nService, + toasterService: ToasterService, private componentFactoryResolver: ComponentFactoryResolver) { + super(passwordGenerationService, analytics, platformUtilsService, i18nService, toasterService, window); + } + + history() { + if (this.modal != null) { + this.modal.close(); + } + + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + this.modal = this.historyModalRef.createComponent(factory).instance; + this.modal.show(PasswordGeneratorHistoryComponent, this.historyModalRef); + + this.modal.onClosed.subscribe(async () => { + this.modal = null; + }); + } +} diff --git a/src/app/tools/tools.component.html b/src/app/tools/tools.component.html index a3789f8bff..6d765e620d 100644 --- a/src/app/tools/tools.component.html +++ b/src/app/tools/tools.component.html @@ -4,11 +4,15 @@ diff --git a/src/app/vault/add-edit.component.ts b/src/app/vault/add-edit.component.ts index b1b96ae415..b82d845a77 100644 --- a/src/app/vault/add-edit.component.ts +++ b/src/app/vault/add-edit.component.ts @@ -12,6 +12,7 @@ import { AuditService } from 'jslib/abstractions/audit.service'; import { CipherService } from 'jslib/abstractions/cipher.service'; import { FolderService } from 'jslib/abstractions/folder.service'; import { I18nService } from 'jslib/abstractions/i18n.service'; +import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { StateService } from 'jslib/abstractions/state.service'; import { TokenService } from 'jslib/abstractions/token.service'; @@ -38,7 +39,8 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit { i18nService: I18nService, platformUtilsService: PlatformUtilsService, analytics: Angulartics2, toasterService: ToasterService, auditService: AuditService, stateService: StateService, - private tokenService: TokenService, private totpService: TotpService) { + private tokenService: TokenService, private totpService: TotpService, + private passwordGenerationService: PasswordGenerationService) { super(cipherService, folderService, i18nService, platformUtilsService, analytics, toasterService, auditService, stateService); } @@ -83,6 +85,15 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit { this.i18nService.t('valueCopied', this.i18nService.t(typeI18nKey))); } + async generatePassword(): Promise { + const confirmed = await super.generatePassword(); + if (confirmed) { + const options = await this.passwordGenerationService.getOptions(); + this.cipher.login.password = await this.passwordGenerationService.generatePassword(options); + } + return confirmed; + } + private cleanUp() { if (this.totpInterval) { window.clearInterval(this.totpInterval); diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 5b4fb67361..46327efb50 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -732,5 +732,36 @@ }, "exportSuccess": { "message": "Your vault data has been exported." + }, + "passwordGenerator": { + "message": "Password Generator" + }, + "minNumbers": { + "message": "Minimum Numbers" + }, + "minSpecial": { + "message": "Minimum Special", + "description": "Minimum Special Characters" + }, + "ambiguous": { + "message": "Avoid Ambiguous Characters" + }, + "regeneratePassword": { + "message": "Regenerate Password" + }, + "length": { + "message": "Length" + }, + "options": { + "message": "Options" + }, + "passwordHistory": { + "message": "Password History" + }, + "noPasswordsInList": { + "message": "There are no passwords to list." + }, + "clear": { + "message": "Clear" } } diff --git a/src/scss/styles.scss b/src/scss/styles.scss index fc439ed3f7..ab54c33bd4 100644 --- a/src/scss/styles.scss +++ b/src/scss/styles.scss @@ -159,7 +159,7 @@ body { @include border-radius($modal-content-border-radius); } -form label:not(.form-check-label) { +label:not(.form-check-label) { font-weight: bold; } @@ -296,6 +296,33 @@ app-login { } } +app-password-generator { + #lengthRange { + width: 100%; + } + + .card-password { + span { + word-break: break-all; + display: block; + text-align: center; + font-size: $font-size-lg; + font-family: $font-family-monospace; + } + } +} + +app-password-generator-history { + .list-group-item { + line-height: 1; + + .password { + word-break: break-all; + font-family: $font-family-monospace; + } + } +} + #duo-frame { background: url('../images/loading.svg') 0 0 no-repeat; height: 330px; diff --git a/src/services/htmlStorage.service.ts b/src/services/htmlStorage.service.ts index 684de60f0d..973207cb7d 100644 --- a/src/services/htmlStorage.service.ts +++ b/src/services/htmlStorage.service.ts @@ -2,9 +2,9 @@ import { StorageService } from 'jslib/abstractions/storage.service'; import { ConstantsService } from 'jslib/services'; export class HtmlStorageService implements StorageService { - private localStorageKeys = new Set(['appId', 'anonymousAppId', 'rememberedEmail', - ConstantsService.disableFaviconKey, ConstantsService.lockOptionKey, ConstantsService.localeKey, - ConstantsService.lockOptionKey]); + private localStorageKeys = new Set(['appId', 'anonymousAppId', 'rememberedEmail', 'passwordGenerationOptions', + ConstantsService.disableFaviconKey, ConstantsService.lockOptionKey, + ConstantsService.localeKey, ConstantsService.lockOptionKey]); private localStorageStartsWithKeys = ['twoFactorToken_']; get(key: string): Promise {