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

View File

@ -43,6 +43,7 @@ import { CiphersComponent } from './vault/ciphers.component';
import { FolderAddEditComponent } from './vault/folder-add-edit.component';
import { GroupingsComponent } from './vault/groupings.component';
import { IconComponent } from './vault/icon.component';
import { PasswordGeneratorHistoryComponent } from './vault/password-generator-history.component';
import { PasswordGeneratorComponent } from './vault/password-generator.component';
import { VaultComponent } from './vault/vault.component';
import { ViewComponent } from './vault/view.component';
@ -81,6 +82,7 @@ import { ViewComponent } from './vault/view.component';
LoginComponent,
ModalComponent,
PasswordGeneratorComponent,
PasswordGeneratorHistoryComponent,
PremiumComponent,
RegisterComponent,
SearchCiphersPipe,
@ -98,6 +100,7 @@ import { ViewComponent } from './vault/view.component';
FolderAddEditComponent,
ModalComponent,
PasswordGeneratorComponent,
PasswordGeneratorHistoryComponent,
PremiumComponent,
SettingsComponent,
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';
@Component({
selector: 'password-generator',
selector: 'app-password-generator',
template: template,
})
export class PasswordGeneratorComponent implements OnInit {
@ -60,14 +60,13 @@ export class PasswordGeneratorComponent implements OnInit {
await this.passwordGenerationService.saveOptions(this.options);
if (regenerate) {
this.password = this.passwordGenerationService.generatePassword(this.options);
await this.passwordGenerationService.addHistory(this.password);
this.analytics.eventTrack.next({ action: 'Regenerated Password' });
await this.regenerate();
}
}
regenerate() {
async regenerate() {
this.password = this.passwordGenerationService.generatePassword(this.options);
await this.passwordGenerationService.addHistory(this.password);
this.analytics.eventTrack.next({ action: 'Regenerated Password' });
}

View File

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

View File

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

View File

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