password breach check moved to view page

This commit is contained in:
Kyle Spearrin 2018-02-28 11:35:48 -05:00
parent df9d79ab7d
commit 093071c8f3
7 changed files with 32 additions and 26 deletions

2
jslib

@ -1 +1 @@
Subproject commit ab00dfd3991c258065d452dfb14b11e90898143e Subproject commit e3b3e444dbff7e4541fa5367ee26bc7ed4d73b26

View File

@ -35,10 +35,6 @@
[(ngModel)]="cipher.login.password"> [(ngModel)]="cipher.login.password">
</div> </div>
<div class="action-buttons"> <div class="action-buttons">
<a class="row-btn" href="#" appStopClick appBlurClick
title="{{'checkPassword' | i18n}}" (click)="checkPassword()">
<i class="fa fa-lg fa-check-circle"></i>
</a>
<a class="row-btn" href="#" appStopClick appBlurClick <a class="row-btn" href="#" appStopClick appBlurClick
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword()"> title="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
<i class="fa fa-lg" <i class="fa fa-lg"

View File

@ -15,7 +15,6 @@ import { CipherType } from 'jslib/enums/cipherType';
import { FieldType } from 'jslib/enums/fieldType'; import { FieldType } from 'jslib/enums/fieldType';
import { SecureNoteType } from 'jslib/enums/secureNoteType'; import { SecureNoteType } from 'jslib/enums/secureNoteType';
import { AuditService } from 'jslib/abstractions/audit.service';
import { CipherService } from 'jslib/abstractions/cipher.service'; import { CipherService } from 'jslib/abstractions/cipher.service';
import { FolderService } from 'jslib/abstractions/folder.service'; import { FolderService } from 'jslib/abstractions/folder.service';
import { I18nService } from 'jslib/abstractions/i18n.service'; import { I18nService } from 'jslib/abstractions/i18n.service';
@ -61,8 +60,7 @@ export class AddEditComponent implements OnChanges {
constructor(private cipherService: CipherService, private folderService: FolderService, constructor(private cipherService: CipherService, private folderService: FolderService,
private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService,
private analytics: Angulartics2, private toasterService: ToasterService, private analytics: Angulartics2, private toasterService: ToasterService) {
private auditService: AuditService) {
this.typeOptions = [ this.typeOptions = [
{ name: i18nService.t('typeLogin'), value: CipherType.Login }, { name: i18nService.t('typeLogin'), value: CipherType.Login },
{ name: i18nService.t('typeCard'), value: CipherType.Card }, { name: i18nService.t('typeCard'), value: CipherType.Card },
@ -214,19 +212,6 @@ export class AddEditComponent implements OnChanges {
document.getElementById('loginPassword').focus(); document.getElementById('loginPassword').focus();
} }
async checkPassword() {
this.analytics.eventTrack.next({ action: 'Check Password' });
const match = await this.auditService.passwordLeaked(this.cipher.login.password);
if (match > 0) {
this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'),
this.i18nService.t('passwordExposed', match.toString()));
} else {
this.toasterService.popAsync('success', null, this.i18nService.t('passwordSafe'));
}
}
toggleFieldValue(field: FieldView) { toggleFieldValue(field: FieldView) {
const f = (field as any); const f = (field as any);
f.showValue = !f.showValue; f.showValue = !f.showValue;

View File

@ -47,6 +47,12 @@
<span [hidden]="!showPassword" class="monospaced">{{cipher.login.password}}</span> <span [hidden]="!showPassword" class="monospaced">{{cipher.login.password}}</span>
</div> </div>
<div class="action-buttons"> <div class="action-buttons">
<button type="button" #checkPasswordBtn class="row-btn btn" appBlurClick
title="{{'checkPassword' | i18n}}" (click)="checkPassword()"
[appApiAction]="checkPasswordPromise" [disabled]="checkPasswordBtn.loading">
<i class="fa fa-lg fa-check-circle" [hidden]="checkPasswordBtn.loading"></i>
<i class="fa fa-lg fa-spinner fa-spin" [hidden]="!checkPasswordBtn.loading"></i>
</button>
<a class="row-btn" href="#" appStopClick title="{{'toggleVisibility' | i18n}}" <a class="row-btn" href="#" appStopClick title="{{'toggleVisibility' | i18n}}"
(click)="togglePassword()"> (click)="togglePassword()">
<i class="fa fa-lg" <i class="fa fa-lg"

View File

@ -15,6 +15,7 @@ import { Angulartics2 } from 'angulartics2';
import { CipherType } from 'jslib/enums/cipherType'; import { CipherType } from 'jslib/enums/cipherType';
import { FieldType } from 'jslib/enums/fieldType'; import { FieldType } from 'jslib/enums/fieldType';
import { AuditService } from 'jslib/abstractions/audit.service';
import { CipherService } from 'jslib/abstractions/cipher.service'; import { CipherService } from 'jslib/abstractions/cipher.service';
import { CryptoService } from 'jslib/abstractions/crypto.service'; import { CryptoService } from 'jslib/abstractions/crypto.service';
import { I18nService } from 'jslib/abstractions/i18n.service'; import { I18nService } from 'jslib/abstractions/i18n.service';
@ -42,13 +43,15 @@ export class ViewComponent implements OnChanges, OnDestroy {
totpSec: number; totpSec: number;
totpLow: boolean; totpLow: boolean;
fieldType = FieldType; fieldType = FieldType;
checkPasswordPromise: Promise<number>;
private totpInterval: any; private totpInterval: any;
constructor(private cipherService: CipherService, private totpService: TotpService, constructor(private cipherService: CipherService, private totpService: TotpService,
private tokenService: TokenService, private toasterService: ToasterService, private tokenService: TokenService, private toasterService: ToasterService,
private cryptoService: CryptoService, private platformUtilsService: PlatformUtilsService, private cryptoService: CryptoService, private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService, private analytics: Angulartics2) { } private i18nService: I18nService, private analytics: Angulartics2,
private auditService: AuditService) { }
async ngOnChanges() { async ngOnChanges() {
this.cleanUp(); this.cleanUp();
@ -82,6 +85,22 @@ export class ViewComponent implements OnChanges, OnDestroy {
this.showPassword = !this.showPassword; this.showPassword = !this.showPassword;
} }
async checkPassword() {
if (this.cipher.login == null || this.cipher.login.password == null || this.cipher.login.password === '') {
return;
}
this.analytics.eventTrack.next({ action: 'Check Password' });
this.checkPasswordPromise = this.auditService.passwordLeaked(this.cipher.login.password);
const matches = await this.checkPasswordPromise;
if (matches > 0) {
this.toasterService.popAsync('warning', null, this.i18nService.t('passwordExposed', matches.toString()));
} else {
this.toasterService.popAsync('success', null, this.i18nService.t('passwordSafe'));
}
}
toggleFieldValue(field: FieldView) { toggleFieldValue(field: FieldView) {
const f = (field as any); const f = (field as any);
f.showValue = !f.showValue; f.showValue = !f.showValue;

View File

@ -959,14 +959,14 @@
"message": "Window" "message": "Window"
}, },
"checkPassword": { "checkPassword": {
"message": "Check if this password has been previously exposed." "message": "Check if this password has been exposed."
}, },
"passwordExposed": { "passwordExposed": {
"message": "This password has been exposed in $VALUE$ data breach(es)! You should change it.", "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.",
"placeholders": { "placeholders": {
"value": { "value": {
"content": "$1", "content": "$1",
"example": "1" "example": "2"
} }
} }
}, },

View File

@ -20,7 +20,7 @@ $brand-primary: #3c8dbc;
$brand-danger: #dd4b39; $brand-danger: #dd4b39;
$brand-success: #00a65a; $brand-success: #00a65a;
$brand-info: #555555; $brand-info: #555555;
$brand-warning: #f39c12; $brand-warning: #bf7e16;
$brand-primary-accent: #286090; $brand-primary-accent: #286090;
$background-color: white; $background-color: white;