1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-18 11:05:41 +01:00

password generator

This commit is contained in:
Kyle Spearrin 2018-04-09 17:35:16 -04:00
parent 82f3c39167
commit 58f920821d
17 changed files with 316 additions and 6 deletions

View File

@ -1085,5 +1085,8 @@
},
"allItems": {
"message": "All Items"
},
"noPasswordsInList": {
"message": "There are no passwords to list."
}
}

View File

@ -103,4 +103,10 @@ export const routerTransition = trigger('routerTransition', [
transition('tabs => add-cipher, ciphers => add-cipher', inSlideUp),
transition('add-cipher => tabs, add-cipher => ciphers', outSlideDown),
transition('generator => generator-history', inSlideLeft),
transition('generator-history => generator', outSlideRight),
transition('add-cipher => generator, edit-cipher => generator, tabs => generator', inSlideUp),
transition('generator => add-cipher, generator => edit-cipher, generator => tabs', outSlideDown),
]);

View File

@ -14,7 +14,11 @@ import { LoginComponent } from './accounts/login.component';
import { RegisterComponent } from './accounts/register.component';
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
import { TwoFactorComponent } from './accounts/two-factor.component';
import { SettingsComponent } from './settings/settings.component';
import { TabsComponent } from './tabs.component';
import { PasswordGeneratorComponent } from './tools/password-generator.component';
import { PasswordGeneratorHistoryComponent } from './tools/password-generator-history.component';
import { ToolsComponent } from './tools/tools.component';
import { AddEditComponent } from './vault/add-edit.component';
import { CiphersComponent } from './vault/ciphers.component';
import { CurrentTabComponent } from './vault/current-tab.component';
@ -36,6 +40,8 @@ const routes: Routes = [
{ path: 'view-cipher', component: ViewComponent, data: { state: 'view-cipher' } },
{ path: 'add-cipher', component: AddEditComponent, data: { state: 'add-cipher' } },
{ path: 'edit-cipher', component: AddEditComponent, data: { state: 'edit-cipher' } },
{ path: 'generator', component: PasswordGeneratorComponent, data: { state: 'generator' } },
{ path: 'generator-history', component: PasswordGeneratorHistoryComponent, data: { state: 'generator-history' } },
{
path: 'tabs', component: TabsComponent,
data: { state: 'tabs' },
@ -52,7 +58,19 @@ const routes: Routes = [
component: GroupingsComponent,
canActivate: [AuthGuardService],
data: { state: 'tabs_vault' },
}
},
{
path: 'tools',
component: ToolsComponent,
canActivate: [AuthGuardService],
data: { state: 'tabs_tools' },
},
{
path: 'settings',
component: SettingsComponent,
canActivate: [AuthGuardService],
data: { state: 'tabs_settings' },
},
]
}
];

View File

@ -92,6 +92,9 @@ export class AppComponent implements OnInit {
this.stateService.remove('GroupingsComponent');
this.stateService.remove('CiphersComponent');
}
if (url.startsWith('/tabs/')) {
this.stateService.remove('addEditCipher');
}
this.previousUrl = url;
}
});

View File

@ -23,7 +23,11 @@ import { LoginComponent } from './accounts/login.component';
import { RegisterComponent } from './accounts/register.component';
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
import { TwoFactorComponent } from './accounts/two-factor.component';
import { SettingsComponent } from './settings/settings.component';
import { TabsComponent } from './tabs.component';
import { PasswordGeneratorComponent } from './tools/password-generator.component';
import { PasswordGeneratorHistoryComponent } from './tools/password-generator-history.component';
import { ToolsComponent } from './tools/tools.component';
import { AddEditComponent } from './vault/add-edit.component';
import { CiphersComponent } from './vault/ciphers.component';
import { CurrentTabComponent } from './vault/current-tab.component';
@ -81,12 +85,16 @@ import { IconComponent } from 'jslib/angular/components/icon.component';
IconComponent,
LockComponent,
LoginComponent,
PasswordGeneratorComponent,
PasswordGeneratorHistoryComponent,
PopOutComponent,
RegisterComponent,
SearchCiphersPipe,
SettingsComponent,
StopClickDirective,
StopPropDirective,
TabsComponent,
ToolsComponent,
TwoFactorOptionsComponent,
TwoFactorComponent,
ViewComponent,

View File

@ -0,0 +1,16 @@
<header>
<div class="left">
<app-pop-out></app-pop-out>
</div>
<div class="center">
<span class="title">{{'settings' | i18n}}</span>
</div>
<div class="right"></div>
</header>
<content>
<div class="box list">
<div class="box-content">
</div>
</div>
</content>

View File

@ -0,0 +1,7 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-settings',
templateUrl: 'settings.component.html',
})
export class SettingsComponent { }

View File

@ -0,0 +1,39 @@
<header>
<div class="left">
<button appBlurClick type="button" (click)="close()">
<i class="fa fa-chevron-left"></i>
<span>{{'back' | i18n}}</span>
</button>
</div>
<div class="center">
<span class="title">{{'passwordHistory' | i18n}}</span>
</div>
<div class="right">
<button appBlurClick type="button" (click)="clear()">
{{'clear' | i18n}}
</button>
</div>
</header>
<content>
<div class="box list full-list" *ngIf="history && history.length">
<div class="box-content">
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
<div class="row-main">
<span class="text monospaced no-ellipsis">
{{h.password}}
</span>
<span class="detail">{{h.date | date:'medium'}}</span>
</div>
<div class="action-buttons">
<a class="row-btn" href="#" appStopClick title="{{'copyPassword' | i18n}}"
(click)="copy(h.password)">
<i class="fa fa-lg fa-clipboard"></i>
</a>
</div>
</div>
</div>
</div>
<div class="no-items" *ngIf="!history || !history.length">
<p>{{'noPasswordsInList' | i18n}}</p>
</div>
</content>

View File

@ -0,0 +1,29 @@
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { Location } from '@angular/common';
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, private location: Location) {
super(passwordGenerationService, analytics, platformUtilsService, i18nService, toasterService);
}
close() {
this.location.back();
}
}

View File

@ -0,0 +1,79 @@
<header>
<div class="left">
<button type="button" appBlurClick (click)="close()">{{'close' | i18n}}</button>
</div>
<div class="center">
<span class="title">{{'passGen' | i18n}}</span>
</div>
<div class="right">
<button type="button" appBlurClick (click)="select()" *ngIf="showSelect">{{'select' | i18n}}</button>
</div>
</header>
<content>
<div class="password-block">{{password}}</div>
<div class="box">
<div class="box-content">
<a class="box-content-row" href="#" appStopClick appBlurClick
(click)="regenerate()">{{'regeneratePassword' | i18n}}</a>
<a class="box-content-row" href="#" appStopClick appBlurClick
(click)="copy()">{{'copyPassword' | i18n}}</a>
<a class="box-content-row box-content-row-flex" routerLink="/generator-history">
<div class="row-main">{{'passwordHistory' | i18n}}</div>
<i class="fa fa-chevron-right row-sub-icon"></i>
</a>
</div>
</div>
<div class="box">
<div class="box-header">
{{'options' | i18n}}
</div>
<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)]="options.length"
(input)="saveOptions()">
<input id="lengthRange" type="range" min="5" max="128" step="1"
[(ngModel)]="options.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)="saveOptions()"
[(ngModel)]="options.uppercase">
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="lowercase">a-z</label>
<input id="lowercase" type="checkbox" (change)="saveOptions()"
[(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()"
[(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()"
[(ngModel)]="options.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" (input)="saveOptions()"
[(ngModel)]="options.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" (input)="saveOptions()"
[(ngModel)]="options.minSpecial">
</div>
<div class="box-content-row box-content-row-checkbox" appBoxRow>
<label for="ambiguous">{{'avoidAmbChar' | i18n}}</label>
<input id="ambiguous" type="checkbox" (change)="saveOptions()"
[(ngModel)]="avoidAmbiguous">
</div>
</div>
</div>
</content>

View File

@ -0,0 +1,52 @@
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { Location } from '@angular/common';
import {
Component,
} from '@angular/core';
import {
Router,
} from '@angular/router';
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 { CipherView } from 'jslib/models/view/cipherView';
import {
PasswordGeneratorComponent as BasePasswordGeneratorComponent
} from 'jslib/angular/components/password-generator.component';
@Component({
selector: 'app-password-generator',
templateUrl: 'password-generator.component.html',
})
export class PasswordGeneratorComponent extends BasePasswordGeneratorComponent {
private cipherState: CipherView;
constructor(passwordGenerationService: PasswordGenerationService, analytics: Angulartics2,
platformUtilsService: PlatformUtilsService, i18nService: I18nService,
toasterService: ToasterService, private stateService: StateService,
private router: Router, private location: Location) {
super(passwordGenerationService, analytics, platformUtilsService, i18nService, toasterService);
}
async ngOnInit() {
await super.ngOnInit();
this.cipherState = await this.stateService.get<CipherView>('addEditCipher');
super.showSelect = this.cipherState != null;
}
select() {
super.select();
this.cipherState.login.password = this.password;
this.close();
}
close() {
this.location.back();
}
}

View File

@ -0,0 +1,19 @@
<header>
<div class="left">
<app-pop-out></app-pop-out>
</div>
<div class="center">
<span class="title">{{'tools' | i18n}}</span>
</div>
<div class="right"></div>
</header>
<content>
<div class="box list">
<div class="box-content">
<a routerLink="/generator" class="box-content-row box-content-row-flex text-default">
<div class="row-main">{{'passGen' | i18n}}</div>
<i class="fa fa-chevron-right row-sub-icon"></i>
</a>
</div>
</div>
</content>

View File

@ -0,0 +1,7 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-tools',
templateUrl: 'tools.component.html',
})
export class ToolsComponent { }

View File

@ -16,6 +16,7 @@ import { CipherService } from 'jslib/abstractions/cipher.service';
import { FolderService } from 'jslib/abstractions/folder.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StateService } from 'jslib/abstractions/state.service';
import { AddEditComponent as BaseAddEditComponent } from 'jslib/angular/components/add-edit.component';
@ -27,10 +28,11 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit {
constructor(cipherService: CipherService, folderService: FolderService,
i18nService: I18nService, platformUtilsService: PlatformUtilsService,
analytics: Angulartics2, toasterService: ToasterService,
auditService: AuditService, private route: ActivatedRoute,
private router: Router, private location: Location) {
auditService: AuditService, stateService: StateService,
private route: ActivatedRoute, private router: Router,
private location: Location) {
super(cipherService, folderService, i18nService, platformUtilsService, analytics,
toasterService, auditService);
toasterService, auditService, stateService);
}
ngOnInit() {
@ -56,4 +58,10 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit {
super.cancel();
this.location.back();
}
async generatePassword() {
await super.generatePassword();
this.stateService.save('addEditCipher', this.cipher);
this.router.navigate(['generator']);
}
}

View File

@ -125,7 +125,7 @@
<div class="no-items" *ngIf="!searchedCiphers.length">
<p>{{'noItemsInList' | i18n}}</p>
</div>
<div class="box list only-list" *ngIf="searchedCiphers.length > 0">
<div class="box list full-list" *ngIf="searchedCiphers.length > 0">
<div class="box-content">
<app-ciphers-list [ciphers]="searchedCiphers" title="{{'viewItem' | i18n}}"
(onSelected)="selectCipher($event)"></app-ciphers-list>

View File

@ -368,7 +368,7 @@
}
}
.text, .detail {
.text:not(.no-ellipsis), .detail {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@ -391,4 +391,12 @@
border-bottom: none;
}
}
&.full-list {
margin: 0;
.box-content {
border: none;
}
}
}

View File

@ -97,3 +97,11 @@ app-root > #loading {
width: 100%;
color: $text-muted;
}
app-password-generator .password-block {
font-size: $font-size-large;
word-break: break-all;
font-family: $font-family-monospace;
text-align: center;
margin: 20px;
}