mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-23 11:56:00 +01:00
added password generator tool
This commit is contained in:
parent
7979953f33
commit
367104f0a7
@ -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] },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -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,
|
||||
],
|
||||
|
38
src/app/tools/password-generator-history.component.html
Normal file
38
src/app/tools/password-generator-history.component.html
Normal 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">×</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>
|
24
src/app/tools/password-generator-history.component.ts
Normal file
24
src/app/tools/password-generator-history.component.ts
Normal 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);
|
||||
}
|
||||
}
|
63
src/app/tools/password-generator.component.html
Normal file
63
src/app/tools/password-generator.component.html
Normal 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">!@#$%^&*</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>
|
50
src/app/tools/password-generator.component.ts
Normal file
50
src/app/tools/password-generator.component.ts
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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> {
|
||||
|
Loading…
Reference in New Issue
Block a user