added password generator tool

This commit is contained in:
Kyle Spearrin 2018-06-20 18:16:20 -04:00
parent 7979953f33
commit 367104f0a7
11 changed files with 262 additions and 8 deletions

View File

@ -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] },
],
},
],

View File

@ -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,
],

View File

@ -0,0 +1,38 @@
<div class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">{{'passwordHistory' | i18n}}</h2>
<button type="button" class="close" data-dismiss="modal" attr.aria-label="{{'close' | i18n}}">
<span aria-hidden="true">&times;</span>
</button>
</div>
<ul class="list-group list-group-flush" *ngIf="history.length">
<li class="list-group-item d-flex" *ngFor="let h of history">
<div>
<div class="password">{{h.password}}</div>
<small class="text-muted">{{h.date | date:'medium'}}</small>
</div>
<div class="ml-auto">
<button class="btn btn-link" appBlurClick title="{{'copyPassword' | i18n}}" (click)="copy(h.password)">
<i class="fa fa-lg fa-clipboard"></i>
</button>
</div>
</li>
</ul>
<div class="modal-body" *ngIf="!history.length">
{{'noPasswordsInList' | i18n}}
</div>
<div class="modal-footer">
<button appBlurClick type="button" class="btn btn-outline-secondary" data-dismiss="modal" title="{{'close' | i18n}}">
{{'close' | i18n}}
</button>
<div class="ml-auto">
<button appBlurClick type="button" (click)="clear()" class="btn btn-outline-danger" title="{{'clear' | i18n}}">
<i class="fa fa-trash-o fa-lg fa-fw"></i>
</button>
</div>
</div>
</div>
</div>
</div>

View File

@ -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);
}
}

View File

@ -0,0 +1,63 @@
<div class="page-header">
<h1>{{'passwordGenerator' | i18n}}</h1>
</div>
<div class="card card-password bg-light my-4">
<div class="card-body">
<span>{{password}}</span>
</div>
</div>
<h2>{{'options' | i18n}}</h2>
<div class="row">
<div class="form-group col-4">
<label for="length">{{'length' | i18n}}</label>
<input id="length" class="form-control" type="number" min="5" max="128" [(ngModel)]="options.length" (input)="saveOptions()">
<input id="lengthRange" class="mt-2" type="range" min="5" max="128" step="1" [(ngModel)]="options.length" (change)="sliderChanged()"
(input)="sliderInput()">
</div>
<div class="form-group col-4">
<label for="min-number">{{'minNumbers' | i18n}}</label>
<input id="min-number" class="form-control" type="number" min="0" max="9" (input)="saveOptions()" [(ngModel)]="options.minNumber">
</div>
<div class="form-group col-4">
<label for="min-special">{{'minSpecial' | i18n}}</label>
<input id="min-special" class="form-control" type="number" min="0" max="9" (input)="saveOptions()" [(ngModel)]="options.minSpecial">
</div>
</div>
<div class="form-group">
<div class="form-check">
<input id="uppercase" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.uppercase">
<label for="uppercase" class="form-check-label">A-Z</label>
</div>
<div class="form-check">
<input id="lowercase" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.lowercase">
<label for="lowercase" class="form-check-label">a-z</label>
</div>
<div class="form-check">
<input id="numbers" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.number">
<label for="numbers" class="form-check-label">0-9</label>
</div>
<div class="form-check">
<input id="special" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="options.special">
<label for="special" class="form-check-label">!@#$%^&amp;*</label>
</div>
<div class="form-check">
<input id="ambiguous" class="form-check-input" type="checkbox" (change)="saveOptions()" [(ngModel)]="avoidAmbiguous">
<label for="ambiguous" class="form-check-label">{{'ambiguous' | i18n}}</label>
</div>
</div>
<div class="d-flex">
<div>
<button type="button" class="btn btn-outline-primary" appBlurClick (click)="regenerate()">
{{'regeneratePassword' | i18n}}
</button>
<button type="button" class="btn btn-outline-primary" appBlurClick (click)="copy()">
{{'copyPassword' | i18n}}
</button>
</div>
<div class="ml-auto">
<button type="button" class="btn btn-link" appBlurClick (click)="history()">
{{'passwordHistory' | i18n}}
</button>
</div>
</div>
<ng-template #historyTemplate></ng-template>

View File

@ -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>(PasswordGeneratorHistoryComponent, this.historyModalRef);
this.modal.onClosed.subscribe(async () => {
this.modal = null;
});
}
}

View File

@ -4,11 +4,15 @@
<div class="card">
<div class="card-header">Tools</div>
<div class="list-group list-group-flush">
<a routerLink="generator" class="list-group-item" routerLinkActive="active">
Password Generator
</a>
<a routerLink="import" class="list-group-item" routerLinkActive="active">
Import
</a>
<a routerLink="export" class="list-group-item" routerLinkActive="active">
Export</a>
Export
</a>
</div>
</div>
</div>

View File

@ -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<boolean> {
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);

View File

@ -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"
}
}

View File

@ -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;

View File

@ -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<T>(key: string): Promise<T> {