1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-06-30 11:15:36 +02:00

password history

This commit is contained in:
Kyle Spearrin 2018-02-17 22:37:43 -05:00
parent 22d5b23e5b
commit 8dafca3706
8 changed files with 119 additions and 29 deletions

View File

@ -10,6 +10,7 @@ import {
NgZone, NgZone,
OnDestroy, OnDestroy,
OnInit, OnInit,
Type,
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
} from '@angular/core'; } from '@angular/core';
@ -19,6 +20,7 @@ import { ModalComponent } from './modal.component';
import { PremiumComponent } from './accounts/premium.component'; import { PremiumComponent } from './accounts/premium.component';
import { SettingsComponent } from './accounts/settings.component'; import { SettingsComponent } from './accounts/settings.component';
import { PasswordGeneratorHistoryComponent } from './vault/password-generator-history.component';
import { ToasterService } from 'angular2-toaster'; import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2'; import { Angulartics2 } from 'angulartics2';
@ -51,11 +53,13 @@ const BroadcasterSubscriptionId = 'AppComponent';
<toaster-container [toasterconfig]="toasterConfig"></toaster-container> <toaster-container [toasterconfig]="toasterConfig"></toaster-container>
<ng-template #settings></ng-template> <ng-template #settings></ng-template>
<ng-template #premium></ng-template> <ng-template #premium></ng-template>
<ng-template #passwordHistory></ng-template>
<router-outlet></router-outlet>`, <router-outlet></router-outlet>`,
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
@ViewChild('settings', { read: ViewContainerRef }) settingsRef: ViewContainerRef; @ViewChild('settings', { read: ViewContainerRef }) settingsRef: ViewContainerRef;
@ViewChild('premium', { read: ViewContainerRef }) premiumRef: ViewContainerRef; @ViewChild('premium', { read: ViewContainerRef }) premiumRef: ViewContainerRef;
@ViewChild('passwordHistory', { read: ViewContainerRef }) passwordHistoryRef: ViewContainerRef;
toasterConfig: ToasterConfig = new ToasterConfig({ toasterConfig: ToasterConfig = new ToasterConfig({
showCloseButton: true, showCloseButton: true,
@ -114,10 +118,14 @@ export class AppComponent implements OnInit {
case 'syncCompleted': case 'syncCompleted':
break; break;
case 'openSettings': case 'openSettings':
this.openSettings(); this.openModal<SettingsComponent>(SettingsComponent, this.settingsRef);
break; break;
case 'openPremium': case 'openPremium':
this.openPremium(); this.openModal<PremiumComponent>(PremiumComponent, this.premiumRef);
break;
case 'openPasswordHistory':
this.openModal<PasswordGeneratorHistoryComponent>(
PasswordGeneratorHistoryComponent, this.passwordHistoryRef);
break; break;
default: default:
} }
@ -170,28 +178,14 @@ export class AppComponent implements OnInit {
this.storageService.save(ConstantsService.lastActiveKey, now); this.storageService.save(ConstantsService.lastActiveKey, now);
} }
private openSettings() { private openModal<T>(type: Type<T>, ref: ViewContainerRef) {
if (this.modal != null) { if (this.modal != null) {
this.modal.close(); this.modal.close();
} }
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
this.modal = this.settingsRef.createComponent(factory).instance; this.modal = ref.createComponent(factory).instance;
const childComponent = this.modal.show<SettingsComponent>(SettingsComponent, this.settingsRef); const childComponent = this.modal.show<T>(type, ref);
this.modal.onClosed.subscribe(() => {
this.modal = null;
});
}
private openPremium() {
if (this.modal != null) {
this.modal.close();
}
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
this.modal = this.premiumRef.createComponent(factory).instance;
const childComponent = this.modal.show<PremiumComponent>(PremiumComponent, this.premiumRef);
this.modal.onClosed.subscribe(() => { this.modal.onClosed.subscribe(() => {
this.modal = null; this.modal = null;

View File

@ -43,6 +43,7 @@ import { CiphersComponent } from './vault/ciphers.component';
import { FolderAddEditComponent } from './vault/folder-add-edit.component'; import { FolderAddEditComponent } from './vault/folder-add-edit.component';
import { GroupingsComponent } from './vault/groupings.component'; import { GroupingsComponent } from './vault/groupings.component';
import { IconComponent } from './vault/icon.component'; import { IconComponent } from './vault/icon.component';
import { PasswordGeneratorHistoryComponent } from './vault/password-generator-history.component';
import { PasswordGeneratorComponent } from './vault/password-generator.component'; import { PasswordGeneratorComponent } from './vault/password-generator.component';
import { VaultComponent } from './vault/vault.component'; import { VaultComponent } from './vault/vault.component';
import { ViewComponent } from './vault/view.component'; import { ViewComponent } from './vault/view.component';
@ -81,6 +82,7 @@ import { ViewComponent } from './vault/view.component';
LoginComponent, LoginComponent,
ModalComponent, ModalComponent,
PasswordGeneratorComponent, PasswordGeneratorComponent,
PasswordGeneratorHistoryComponent,
PremiumComponent, PremiumComponent,
RegisterComponent, RegisterComponent,
SearchCiphersPipe, SearchCiphersPipe,
@ -98,6 +100,7 @@ import { ViewComponent } from './vault/view.component';
FolderAddEditComponent, FolderAddEditComponent,
ModalComponent, ModalComponent,
PasswordGeneratorComponent, PasswordGeneratorComponent,
PasswordGeneratorHistoryComponent,
PremiumComponent, PremiumComponent,
SettingsComponent, SettingsComponent,
TwoFactorOptionsComponent, TwoFactorOptionsComponent,

View File

@ -0,0 +1,40 @@
<div class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div class="box">
<div class="box-header">
{{'passwordHistory' | i18n}}
</div>
<div class="box-content condensed">
<div class="box-content-row box-content-row-flex" *ngFor="let h of history">
<div class="row-main">
<span class="text monospaced">
{{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 class="box-content-row" *ngIf="!history.length">
{{'noItemsInList' | i18n}}
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
<div class="right">
<button appBlurClick type="button" (click)="clear()" class="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,38 @@
import * as template from './password-generator-history.component.html';
import { Angulartics2 } from 'angulartics2';
import {
Component,
OnInit,
} from '@angular/core';
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { PasswordHistory } from 'jslib/models/domain/passwordHistory';
@Component({
selector: 'app-password-generator-history',
template: template,
})
export class PasswordGeneratorHistoryComponent implements OnInit {
history: PasswordHistory[];
constructor(private passwordGenerationService: PasswordGenerationService, private analytics: Angulartics2,
private platformUtilsService: PlatformUtilsService) { }
async ngOnInit() {
this.history = await this.passwordGenerationService.getHistory();
}
clear() {
this.history = [];
this.passwordGenerationService.clear();
}
copy(password: string) {
this.analytics.eventTrack.next({ action: 'Copied Historical Password' });
this.platformUtilsService.copyToClipboard(password);
}
}

View File

@ -15,7 +15,7 @@ import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
@Component({ @Component({
selector: 'password-generator', selector: 'app-password-generator',
template: template, template: template,
}) })
export class PasswordGeneratorComponent implements OnInit { export class PasswordGeneratorComponent implements OnInit {
@ -60,14 +60,13 @@ export class PasswordGeneratorComponent implements OnInit {
await this.passwordGenerationService.saveOptions(this.options); await this.passwordGenerationService.saveOptions(this.options);
if (regenerate) { if (regenerate) {
this.password = this.passwordGenerationService.generatePassword(this.options); await this.regenerate();
await this.passwordGenerationService.addHistory(this.password);
this.analytics.eventTrack.next({ action: 'Regenerated Password' });
} }
} }
regenerate() { async regenerate() {
this.password = this.passwordGenerationService.generatePassword(this.options); this.password = this.passwordGenerationService.generatePassword(this.options);
await this.passwordGenerationService.addHistory(this.password);
this.analytics.eventTrack.next({ action: 'Regenerated Password' }); this.analytics.eventTrack.next({ action: 'Regenerated Password' });
} }

View File

@ -868,5 +868,11 @@
}, },
"refreshComplete": { "refreshComplete": {
"message": "Refresh complete" "message": "Refresh complete"
},
"passwordHistory": {
"message": "Password History"
},
"clear": {
"message": "Clear"
} }
} }

View File

@ -27,6 +27,7 @@ export class MenuMain {
changeMasterPass: MenuItem; changeMasterPass: MenuItem;
premiumMembership: MenuItem; premiumMembership: MenuItem;
passwordGenerator: MenuItem; passwordGenerator: MenuItem;
passwordHistory: MenuItem;
searchVault: MenuItem; searchVault: MenuItem;
unlockedRequiredMenuItems: MenuItem[] = []; unlockedRequiredMenuItems: MenuItem[] = [];
@ -49,12 +50,14 @@ export class MenuMain {
this.changeMasterPass = this.menu.getMenuItemById('changeMasterPass'); this.changeMasterPass = this.menu.getMenuItemById('changeMasterPass');
this.premiumMembership = this.menu.getMenuItemById('premiumMembership'); this.premiumMembership = this.menu.getMenuItemById('premiumMembership');
this.passwordGenerator = this.menu.getMenuItemById('passwordGenerator'); this.passwordGenerator = this.menu.getMenuItemById('passwordGenerator');
this.passwordHistory = this.menu.getMenuItemById('passwordHistory');
this.searchVault = this.menu.getMenuItemById('searchVault'); this.searchVault = this.menu.getMenuItemById('searchVault');
this.unlockedRequiredMenuItems = [ this.unlockedRequiredMenuItems = [
this.addNewLogin, this.addNewItem, this.addNewFolder, this.addNewLogin, this.addNewItem, this.addNewFolder,
this.syncVault, this.settings, this.lockNow, this.twoStepLogin, this.changeEmail, this.syncVault, this.settings, this.lockNow, this.twoStepLogin, this.changeEmail,
this.changeMasterPass, this.premiumMembership, this.passwordGenerator, this.searchVault]; this.changeMasterPass, this.premiumMembership, this.passwordGenerator, this.passwordHistory,
this.searchVault];
this.updateApplicationMenuState(false, true); this.updateApplicationMenuState(false, true);
} }
@ -174,6 +177,13 @@ export class MenuMain {
{ {
label: this.main.i18nService.t('view'), label: this.main.i18nService.t('view'),
submenu: [ submenu: [
{
label: this.main.i18nService.t('searchVault'),
id: 'searchVault',
click: () => this.main.messagingService.send('focusSearch'),
accelerator: 'CmdOrCtrl+F',
},
{ type: 'separator' },
{ {
label: this.main.i18nService.t('passwordGenerator'), label: this.main.i18nService.t('passwordGenerator'),
id: 'passwordGenerator', id: 'passwordGenerator',
@ -181,10 +191,9 @@ export class MenuMain {
accelerator: 'CmdOrCtrl+G', accelerator: 'CmdOrCtrl+G',
}, },
{ {
label: this.main.i18nService.t('searchVault'), label: this.main.i18nService.t('passwordHistory'),
id: 'searchVault', id: 'passwordHistory',
click: () => this.main.messagingService.send('focusSearch'), click: () => this.main.messagingService.send('openPasswordHistory'),
accelerator: 'CmdOrCtrl+F',
}, },
{ type: 'separator' }, { type: 'separator' },
{ role: 'zoomin', accelerator: 'CmdOrCtrl+=' }, { role: 'zoomin', accelerator: 'CmdOrCtrl+=' },

View File

@ -98,6 +98,7 @@
&.box-content-row-slider, &.box-content-row-cf { &.box-content-row-slider, &.box-content-row-cf {
display: flex; display: flex;
align-items: center; align-items: center;
word-break: break-word;
} }
&.box-content-row-cf { &.box-content-row-cf {