diff --git a/jslib b/jslib index 647b254a71..7a1e7b5474 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 647b254a71d0af105e1f6f1a1febeb15cd4181fb +Subproject commit 7a1e7b54743f065ee10eb27499186b629cce22b5 diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 592f6602a2..bfe5ee0cb9 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -1169,5 +1169,27 @@ }, "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?" + }, + "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." } } diff --git a/src/popup/accounts/lock.component.html b/src/popup/accounts/lock.component.html index fac87de88a..5ea4b87211 100644 --- a/src/popup/accounts/lock.component.html +++ b/src/popup/accounts/lock.component.html @@ -2,7 +2,7 @@
- {{'verifyMasterPassword' | i18n}} + {{(pinLock ? 'verifyPin' : 'verifyMasterPassword') | i18n}}
@@ -12,7 +12,12 @@
-
+
+ + +
+
@@ -27,7 +32,7 @@
diff --git a/src/popup/accounts/lock.component.ts b/src/popup/accounts/lock.component.ts index 6b1f276ba5..9cbe5ce4c7 100644 --- a/src/popup/accounts/lock.component.ts +++ b/src/popup/accounts/lock.component.ts @@ -3,8 +3,10 @@ import { Router } from '@angular/router'; import { CryptoService } from 'jslib/abstractions/crypto.service'; import { I18nService } from 'jslib/abstractions/i18n.service'; +import { LockService } from 'jslib/abstractions/lock.service'; import { MessagingService } from 'jslib/abstractions/messaging.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; +import { StorageService } from 'jslib/abstractions/storage.service'; import { UserService } from 'jslib/abstractions/user.service'; 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 { constructor(router: Router, i18nService: I18nService, platformUtilsService: PlatformUtilsService, messagingService: MessagingService, - userService: UserService, cryptoService: CryptoService) { - super(router, i18nService, platformUtilsService, messagingService, userService, cryptoService); + userService: UserService, cryptoService: CryptoService, + storageService: StorageService, lockService: LockService) { + super(router, i18nService, platformUtilsService, messagingService, userService, cryptoService, + storageService, lockService); this.successRoute = '/tabs/current'; } async ngOnInit() { await super.ngOnInit(); window.setTimeout(() => { - document.getElementById('masterPassword').focus(); + document.getElementById(this.pinLock ? 'pin' : 'masterPassword').focus(); }, 100); } } diff --git a/src/popup/scss/plugins.scss b/src/popup/scss/plugins.scss index e43841131f..b3a096c59f 100644 --- a/src/popup/scss/plugins.scss +++ b/src/popup/scss/plugins.scss @@ -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 { padding: 15px 10px 10px 10px; margin: 0; diff --git a/src/popup/settings/settings.component.html b/src/popup/settings/settings.component.html index 074625d127..e67f812295 100644 --- a/src/popup/settings/settings.component.html +++ b/src/popup/settings/settings.component.html @@ -31,6 +31,10 @@
+
+ + +
{{'lockNow' | i18n}}
diff --git a/src/popup/settings/settings.component.ts b/src/popup/settings/settings.component.ts index 12cdf4d9c7..7a390b0d4d 100644 --- a/src/popup/settings/settings.component.ts +++ b/src/popup/settings/settings.component.ts @@ -47,6 +47,7 @@ export class SettingsComponent implements OnInit { @ViewChild('lockOptionsSelect', { read: ElementRef }) lockOptionsSelectRef: ElementRef; lockOptions: any[]; lockOption: number = null; + pin: boolean = null; previousLockOption: number = null; constructor(private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, @@ -87,6 +88,8 @@ export class SettingsComponent implements OnInit { this.lockOption = option; } this.previousLockOption = this.lockOption; + + this.pin = await this.lockService.isPinLockSet(); } 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() { this.analytics.eventTrack.next({ action: 'Lock Now' }); await this.lockService.lock();