lock with master pass on restart option on PIN lock

This commit is contained in:
Kyle Spearrin 2019-02-14 00:46:28 -05:00
parent a2064939d2
commit f28d7a1af6
7 changed files with 62 additions and 20 deletions

2
jslib

@ -1 +1 @@
Subproject commit f67fac3eebc21b8935a54a28b7a21152c8513322 Subproject commit 43872f82ccd7453926c75240ecde196151e89ce9

View File

@ -1191,5 +1191,8 @@
}, },
"yourVaultIsLockedPinCode": { "yourVaultIsLockedPinCode": {
"message": "Your vault is locked. Verify your PIN code to continue." "message": "Your vault is locked. Verify your PIN code to continue."
},
"lockWithMasterPassOnRestart": {
"message": "Lock with master password on browser restart"
} }
} }

View File

@ -42,7 +42,7 @@ export default class IdleBackground {
if (newState === 'locked') { if (newState === 'locked') {
const lockOption = await this.storageService.get<number>(ConstantsService.lockOptionKey); const lockOption = await this.storageService.get<number>(ConstantsService.lockOptionKey);
if (lockOption === -2) { if (lockOption === -2) {
this.lockService.lock(); this.lockService.lock(true);
} }
} }
}); });

View File

@ -160,7 +160,7 @@ export default class MainBackground {
this.auditService = new AuditService(cryptoFunctionService, this.apiService); this.auditService = new AuditService(cryptoFunctionService, this.apiService);
this.exportService = new ExportService(this.folderService, this.cipherService, this.apiService); this.exportService = new ExportService(this.folderService, this.cipherService, this.apiService);
this.notificationsService = new NotificationsService(this.userService, this.syncService, this.appIdService, this.notificationsService = new NotificationsService(this.userService, this.syncService, this.appIdService,
this.apiService, this.cryptoService, () => this.logout(true)); this.apiService, this.lockService, () => this.logout(true));
this.environmentService = new EnvironmentService(this.apiService, this.storageService, this.environmentService = new EnvironmentService(this.apiService, this.storageService,
this.notificationsService); this.notificationsService);
this.analytics = new Analytics(window, () => BrowserApi.gaFilter(), this.platformUtilsService, this.analytics = new Analytics(window, () => BrowserApi.gaFilter(), this.platformUtilsService,
@ -223,12 +223,12 @@ export default class MainBackground {
} }
const isAuthenticated = await this.userService.isAuthenticated(); const isAuthenticated = await this.userService.isAuthenticated();
const hasKey = await this.cryptoService.hasKey(); const locked = await this.lockService.isLocked();
let suffix = ''; let suffix = '';
if (!isAuthenticated) { if (!isAuthenticated) {
suffix = '_gray'; suffix = '_gray';
} else if (!hasKey) { } else if (locked) {
suffix = '_locked'; suffix = '_locked';
} }
@ -273,8 +273,10 @@ export default class MainBackground {
this.folderService.clear(userId), this.folderService.clear(userId),
this.collectionService.clear(userId), this.collectionService.clear(userId),
this.passwordGenerationService.clear(), this.passwordGenerationService.clear(),
this.lockService.clear(),
]); ]);
this.lockService.pinLocked = false;
this.searchService.clearIndex(); this.searchService.clearIndex();
this.messagingService.send('doneLoggingOut', { expired: expired }); this.messagingService.send('doneLoggingOut', { expired: expired });

View File

@ -121,6 +121,7 @@ $fa-font-path: "~font-awesome/fonts";
.swal-content { .swal-content {
margin: 15px 0 0 0; margin: 15px 0 0 0;
padding: 0 10px; padding: 0 10px;
font-size: $font-size-base;
.swal-text { .swal-text {
&:last-child { &:last-child {
@ -132,6 +133,17 @@ $fa-font-path: "~font-awesome/fonts";
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
} }
label.checkbox {
margin-top: 10px;
display: flex;
text-align: left;
align-items: top;
input {
margin: 3px 5px 0 1px;
}
}
} }
i.swal-custom-icon { i.swal-custom-icon {

View File

@ -6,12 +6,12 @@ import {
Router, Router,
} from '@angular/router'; } from '@angular/router';
import { CryptoService } from 'jslib/abstractions/crypto.service'; import { LockService } from 'jslib/abstractions/lock.service';
import { UserService } from 'jslib/abstractions/user.service'; import { UserService } from 'jslib/abstractions/user.service';
@Injectable() @Injectable()
export class LaunchGuardService implements CanActivate { export class LaunchGuardService implements CanActivate {
constructor(private cryptoService: CryptoService, private userService: UserService, private router: Router) { } constructor(private lockService: LockService, private userService: UserService, private router: Router) { }
async canActivate() { async canActivate() {
if (BrowserApi.getBackgroundPage() == null) { if (BrowserApi.getBackgroundPage() == null) {
@ -28,8 +28,8 @@ export class LaunchGuardService implements CanActivate {
return true; return true;
} }
const hasKey = await this.cryptoService.hasKey(); const locked = await this.lockService.isLocked();
if (!hasKey) { if (locked) {
this.router.navigate(['lock']); this.router.navigate(['lock']);
} else { } else {
this.router.navigate(['tabs/current']); this.router.navigate(['tabs/current']);

View File

@ -89,7 +89,8 @@ export class SettingsComponent implements OnInit {
} }
this.previousLockOption = this.lockOption; this.previousLockOption = this.lockOption;
this.pin = await this.lockService.isPinLockSet(); const pinSet = await this.lockService.isPinLockSet();
this.pin = pinSet[0] || pinSet[1];
} }
async saveLockOption(newValue: number) { async saveLockOption(newValue: number) {
@ -116,31 +117,55 @@ export class SettingsComponent implements OnInit {
async updatePin() { async updatePin() {
if (this.pin) { if (this.pin) {
const pin = await swal({ const div = document.createElement('div');
const label = document.createElement('label');
label.className = 'checkbox';
const checkboxText = document.createElement('span');
const restartText = document.createTextNode(this.i18nService.t('lockWithMasterPassOnRestart'));
checkboxText.appendChild(restartText);
label.innerHTML = '<input type="checkbox" id="master-pass-restart" checked>';
label.appendChild(checkboxText);
div.innerHTML = '<input type="text" class="swal-content__input" id="pin-val">';
(div.querySelector('#pin-val') as HTMLInputElement).placeholder = this.i18nService.t('pin');
div.appendChild(label);
const submitted = await swal({
text: this.i18nService.t('setYourPinCode'), text: this.i18nService.t('setYourPinCode'),
content: { element: 'input' }, content: { element: div },
buttons: [this.i18nService.t('cancel'), this.i18nService.t('submit')], buttons: [this.i18nService.t('cancel'), this.i18nService.t('submit')],
}); });
let pin: string = null;
let masterPassOnRestart: boolean = null;
if (submitted) {
pin = (document.getElementById('pin-val') as HTMLInputElement).value;
masterPassOnRestart = (document.getElementById('master-pass-restart') as HTMLInputElement).checked;
}
if (pin != null && pin.trim() !== '') { if (pin != null && pin.trim() !== '') {
const kdf = await this.userService.getKdf(); if (masterPassOnRestart) {
const kdfIterations = await this.userService.getKdfIterations(); const encPin = await this.cryptoService.encrypt(pin);
const email = await this.userService.getEmail(); await this.storageService.save(ConstantsService.protectedPin, encPin.encryptedString);
const pinKey = await this.cryptoService.makePinKey(pin, email, kdf, kdfIterations); } else {
const key = await this.cryptoService.getKey(); const kdf = await this.userService.getKdf();
const pinProtectedKey = await this.cryptoService.encrypt(key.key, pinKey); const kdfIterations = await this.userService.getKdfIterations();
await this.storageService.save(ConstantsService.pinProtectedKey, pinProtectedKey.encryptedString); const email = await this.userService.getEmail();
const pinKey = await this.cryptoService.makePinKey(pin, email, kdf, kdfIterations);
const key = await this.cryptoService.getKey();
const pinProtectedKey = await this.cryptoService.encrypt(key.key, pinKey);
await this.storageService.save(ConstantsService.pinProtectedKey, pinProtectedKey.encryptedString);
}
} else { } else {
this.pin = false; this.pin = false;
} }
} }
if (!this.pin) { if (!this.pin) {
await this.storageService.remove(ConstantsService.pinProtectedKey); await this.storageService.remove(ConstantsService.pinProtectedKey);
await this.storageService.remove(ConstantsService.protectedPin);
} }
} }
async lock() { async lock() {
this.analytics.eventTrack.next({ action: 'Lock Now' }); this.analytics.eventTrack.next({ action: 'Lock Now' });
await this.lockService.lock(); await this.lockService.lock(true);
this.router.navigate(['lock']); this.router.navigate(['lock']);
} }