mirror of
https://github.com/bitwarden/desktop.git
synced 2024-11-24 11:55:50 +01:00
password history
This commit is contained in:
parent
22d5b23e5b
commit
8dafca3706
@ -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;
|
||||||
|
@ -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,
|
||||||
|
40
src/app/vault/password-generator-history.component.html
Normal file
40
src/app/vault/password-generator-history.component.html
Normal 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>
|
38
src/app/vault/password-generator-history.component.ts
Normal file
38
src/app/vault/password-generator-history.component.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,5 +868,11 @@
|
|||||||
},
|
},
|
||||||
"refreshComplete": {
|
"refreshComplete": {
|
||||||
"message": "Refresh complete"
|
"message": "Refresh complete"
|
||||||
|
},
|
||||||
|
"passwordHistory": {
|
||||||
|
"message": "Password History"
|
||||||
|
},
|
||||||
|
"clear": {
|
||||||
|
"message": "Clear"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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+=' },
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user