support for unlocking with PIN code
This commit is contained in:
parent
7b395ba4ff
commit
d5cbae7803
2
jslib
2
jslib
|
@ -1 +1 @@
|
||||||
Subproject commit 647b254a71d0af105e1f6f1a1febeb15cd4181fb
|
Subproject commit 7a1e7b54743f065ee10eb27499186b629cce22b5
|
|
@ -1169,5 +1169,27 @@
|
||||||
},
|
},
|
||||||
"weakMasterPasswordDesc": {
|
"weakMasterPasswordDesc": {
|
||||||
"message": "The master password you have chosen is weak. You should use a strong master password (or a passphrase) to properly protect your Bitwarden account. Are you sure you want to use this master password?"
|
"message": "The master password you have chosen is weak. You should use a strong master password (or a passphrase) to properly protect your Bitwarden account. Are you sure you want to use this master password?"
|
||||||
|
},
|
||||||
|
"pin": {
|
||||||
|
"message": "PIN",
|
||||||
|
"description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device."
|
||||||
|
},
|
||||||
|
"unlockWithPin": {
|
||||||
|
"message": "Unlock with PIN"
|
||||||
|
},
|
||||||
|
"setYourPinCode": {
|
||||||
|
"message": "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application."
|
||||||
|
},
|
||||||
|
"pinRequired": {
|
||||||
|
"message": "PIN code is required."
|
||||||
|
},
|
||||||
|
"invalidPin": {
|
||||||
|
"message": "Invalid PIN code."
|
||||||
|
},
|
||||||
|
"verifyPin": {
|
||||||
|
"message": "Verify PIN"
|
||||||
|
},
|
||||||
|
"yourVaultIsLockedPinCode": {
|
||||||
|
"message": "Your vault is locked. Verify your PIN code to continue."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<header>
|
<header>
|
||||||
<div class="left"></div>
|
<div class="left"></div>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<span class="title">{{'verifyMasterPassword' | i18n}}</span>
|
<span class="title">{{(pinLock ? 'verifyPin' : 'verifyMasterPassword') | i18n}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button type="submit" appBlurClick>{{'unlock' | i18n}}</button>
|
<button type="submit" appBlurClick>{{'unlock' | i18n}}</button>
|
||||||
|
@ -12,7 +12,12 @@
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-content">
|
<div class="box-content">
|
||||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||||
<div class="row-main">
|
<div class="row-main" *ngIf="pinLock">
|
||||||
|
<label for="pin">{{'pin' | i18n}}</label>
|
||||||
|
<input id="pin" type="{{showPassword ? 'text' : 'password'}}" name="PIN"
|
||||||
|
class="monospaced" [(ngModel)]="pin" required appInputVerbatim>
|
||||||
|
</div>
|
||||||
|
<div class="row-main" *ngIf="!pinLock">
|
||||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||||
class="monospaced" [(ngModel)]="masterPassword" required appInputVerbatim>
|
class="monospaced" [(ngModel)]="masterPassword" required appInputVerbatim>
|
||||||
|
@ -27,7 +32,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<p>{{'yourVaultIsLocked' | i18n}}</p>
|
<p>{{(pinLock ? 'yourVaultIsLockedPinCode' : 'yourVaultIsLocked') | i18n}}</p>
|
||||||
{{'loggedInAs' | i18n : email}}
|
{{'loggedInAs' | i18n : email}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,8 +3,10 @@ import { Router } from '@angular/router';
|
||||||
|
|
||||||
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';
|
||||||
|
import { LockService } from 'jslib/abstractions/lock.service';
|
||||||
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
import { MessagingService } from 'jslib/abstractions/messaging.service';
|
||||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
|
import { StorageService } from 'jslib/abstractions/storage.service';
|
||||||
import { UserService } from 'jslib/abstractions/user.service';
|
import { UserService } from 'jslib/abstractions/user.service';
|
||||||
|
|
||||||
import { LockComponent as BaseLockComponent } from 'jslib/angular/components/lock.component';
|
import { LockComponent as BaseLockComponent } from 'jslib/angular/components/lock.component';
|
||||||
|
@ -16,15 +18,17 @@ import { LockComponent as BaseLockComponent } from 'jslib/angular/components/loc
|
||||||
export class LockComponent extends BaseLockComponent {
|
export class LockComponent extends BaseLockComponent {
|
||||||
constructor(router: Router, i18nService: I18nService,
|
constructor(router: Router, i18nService: I18nService,
|
||||||
platformUtilsService: PlatformUtilsService, messagingService: MessagingService,
|
platformUtilsService: PlatformUtilsService, messagingService: MessagingService,
|
||||||
userService: UserService, cryptoService: CryptoService) {
|
userService: UserService, cryptoService: CryptoService,
|
||||||
super(router, i18nService, platformUtilsService, messagingService, userService, cryptoService);
|
storageService: StorageService, lockService: LockService) {
|
||||||
|
super(router, i18nService, platformUtilsService, messagingService, userService, cryptoService,
|
||||||
|
storageService, lockService);
|
||||||
this.successRoute = '/tabs/current';
|
this.successRoute = '/tabs/current';
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
await super.ngOnInit();
|
await super.ngOnInit();
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
document.getElementById('masterPassword').focus();
|
document.getElementById(this.pinLock ? 'pin' : 'masterPassword').focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,6 +158,20 @@ $fa-font-path: "~font-awesome/fonts";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .swal-text:first-child {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.swal-content__input, .swal-content__textarea {
|
||||||
|
border: 1px solid #000000;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
@include themify($themes) {
|
||||||
|
border-color: themed('inputBorderColor');
|
||||||
|
color: themed('textColor');
|
||||||
|
background-color: themed('inputBackgroundColor');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.swal-footer {
|
.swal-footer {
|
||||||
padding: 15px 10px 10px 10px;
|
padding: 15px 10px 10px 10px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
|
@ -31,6 +31,10 @@
|
||||||
<option *ngFor="let o of lockOptions" [ngValue]="o.value">{{o.name}}</option>
|
<option *ngFor="let o of lockOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||||
|
<label for="pin">{{'unlockWithPin' | i18n}}</label>
|
||||||
|
<input id="pin" type="checkbox" (change)="updatePin()" [(ngModel)]="pin">
|
||||||
|
</div>
|
||||||
<a class="box-content-row box-content-row-flex text-default" href="#"
|
<a class="box-content-row box-content-row-flex text-default" href="#"
|
||||||
appStopClick appBlurClick (click)="lock()">
|
appStopClick appBlurClick (click)="lock()">
|
||||||
<div class="row-main">{{'lockNow' | i18n}}</div>
|
<div class="row-main">{{'lockNow' | i18n}}</div>
|
||||||
|
|
|
@ -47,6 +47,7 @@ export class SettingsComponent implements OnInit {
|
||||||
@ViewChild('lockOptionsSelect', { read: ElementRef }) lockOptionsSelectRef: ElementRef;
|
@ViewChild('lockOptionsSelect', { read: ElementRef }) lockOptionsSelectRef: ElementRef;
|
||||||
lockOptions: any[];
|
lockOptions: any[];
|
||||||
lockOption: number = null;
|
lockOption: number = null;
|
||||||
|
pin: boolean = null;
|
||||||
previousLockOption: number = null;
|
previousLockOption: number = null;
|
||||||
|
|
||||||
constructor(private platformUtilsService: PlatformUtilsService, private i18nService: I18nService,
|
constructor(private platformUtilsService: PlatformUtilsService, private i18nService: I18nService,
|
||||||
|
@ -87,6 +88,8 @@ export class SettingsComponent implements OnInit {
|
||||||
this.lockOption = option;
|
this.lockOption = option;
|
||||||
}
|
}
|
||||||
this.previousLockOption = this.lockOption;
|
this.previousLockOption = this.lockOption;
|
||||||
|
|
||||||
|
this.pin = await this.lockService.isPinLockSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveLockOption(newValue: number) {
|
async saveLockOption(newValue: number) {
|
||||||
|
@ -111,6 +114,27 @@ export class SettingsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updatePin() {
|
||||||
|
if (this.pin) {
|
||||||
|
const pin = await swal({
|
||||||
|
text: this.i18nService.t('setYourPinCode'),
|
||||||
|
content: { element: 'input' },
|
||||||
|
buttons: [this.i18nService.t('cancel'), this.i18nService.t('submit')],
|
||||||
|
});
|
||||||
|
if (pin != null && pin.trim() !== '') {
|
||||||
|
const pinKey = await this.cryptoService.makePinKey(pin, await this.userService.getEmail());
|
||||||
|
const key = await this.cryptoService.getKey();
|
||||||
|
const pinProtectedKey = await this.cryptoService.encrypt(key.key, pinKey);
|
||||||
|
await this.storageService.save(ConstantsService.pinProtectedKey, pinProtectedKey.encryptedString);
|
||||||
|
} else {
|
||||||
|
this.pin = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.pin) {
|
||||||
|
await this.storageService.remove(ConstantsService.pinProtectedKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
Loading…
Reference in New Issue