Use a modal to set the unlock pin (#2060)
* Use separate modal for set pin * Fix modal style and layout * Minor fixes * Remove incorrect label * Fix initial focus and tab a11y * Fix unrelated linting * Update jslib
This commit is contained in:
parent
205b1153de
commit
8f700b0b45
|
@ -1,3 +1,4 @@
|
|||
import { A11yModule } from '@angular/cdk/a11y';
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { ToasterModule } from 'angular2-toaster';
|
||||
|
@ -76,6 +77,7 @@ import { CipherRowComponent } from './components/cipher-row.component';
|
|||
import { PasswordRepromptComponent } from './components/password-reprompt.component';
|
||||
import { PopOutComponent } from './components/pop-out.component';
|
||||
import { SendListComponent } from './components/send-list.component';
|
||||
import { SetPinComponent } from './components/set-pin.component';
|
||||
|
||||
import { CalloutComponent } from 'jslib-angular/components/callout.component';
|
||||
import { IconComponent } from 'jslib-angular/components/icon.component';
|
||||
|
@ -173,6 +175,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
|||
|
||||
@NgModule({
|
||||
imports: [
|
||||
A11yModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
BrowserModule,
|
||||
|
@ -243,6 +246,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
|||
UpdateTempPasswordComponent,
|
||||
ViewComponent,
|
||||
PasswordRepromptComponent,
|
||||
SetPinComponent,
|
||||
VaultTimeoutInputComponent,
|
||||
],
|
||||
entryComponents: [],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="modal fade" tabindex="-1" role="dialog" aria-modal="true" aria-labelledby="confirmUserTitle">
|
||||
<div class="modal fade" tabindex="-1" role="dialog" aria-modal="true">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<div class="modal fade" tabindex="-1" role="dialog" aria-modal="true" cdkTrapFocus cdkTrapFocusAutoCapture>
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()">
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
{{'setYourPinCode' | i18n}}
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="pin">{{'pin' | i18n}}</label>
|
||||
<input id="pin" type="{{showPin ? 'text' : 'password'}}" name="Pin"
|
||||
class="monospaced" [(ngModel)]="pin" required appInputVerbatim cdkFocusInitial>
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick appA11yTitle="{{'toggleVisibility' | i18n}}"
|
||||
(click)="toggleVisibility()">
|
||||
<i class="fa fa-lg" aria-hidden="true"
|
||||
[ngClass]="{'fa-eye': !showPin, 'fa-eye-slash': showPin}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label for="masterPasswordOnRestart">
|
||||
<input type="checkbox" id="masterPasswordOnRestart" name="MasterPasswordOnRestart"
|
||||
[(ngModel)]="masterPassOnRestart">
|
||||
<span>{{'lockWithMasterPassOnRestart' | i18n}}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary btn-submit" appBlurClick>
|
||||
<span>{{'ok' | i18n}}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">
|
||||
{{'cancel' | i18n}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,8 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
import { SetPinComponent as BaseSetPinComponent } from 'jslib-angular/components/set-pin.component';
|
||||
|
||||
@Component({
|
||||
templateUrl: 'set-pin.component.html',
|
||||
})
|
||||
export class SetPinComponent extends BaseSetPinComponent {}
|
|
@ -25,6 +25,10 @@ import { UserService } from 'jslib-common/abstractions/user.service';
|
|||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||
import { PopupUtilsService } from '../services/popup-utils.service';
|
||||
|
||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
||||
|
||||
import { SetPinComponent } from '../components/set-pin.component';
|
||||
|
||||
const RateUrls = {
|
||||
[DeviceType.ChromeExtension]:
|
||||
'https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews',
|
||||
|
@ -61,7 +65,7 @@ export class SettingsComponent implements OnInit {
|
|||
public messagingService: MessagingService, private router: Router,
|
||||
private environmentService: EnvironmentService, private cryptoService: CryptoService,
|
||||
private userService: UserService, private popupUtilsService: PopupUtilsService,
|
||||
private toasterService: ToasterService) {
|
||||
private modalService: ModalService, private toasterService: ToasterService) {
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
|
@ -164,58 +168,15 @@ export class SettingsComponent implements OnInit {
|
|||
|
||||
async updatePin() {
|
||||
if (this.pin) {
|
||||
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);
|
||||
const ref = this.modalService.open(SetPinComponent, { allowMultipleModals: true });
|
||||
|
||||
div.innerHTML =
|
||||
`<div class="swal2-text">${this.i18nService.t('setYourPinCode')}</div>` +
|
||||
'<input type="text" class="swal2-input" id="pin-val" autocomplete="off" ' +
|
||||
'autocapitalize="none" autocorrect="none" spellcheck="false" inputmode="verbatim">';
|
||||
|
||||
(div.querySelector('#pin-val') as HTMLInputElement).placeholder = this.i18nService.t('pin');
|
||||
div.appendChild(label);
|
||||
|
||||
const submitted = await Swal.fire({
|
||||
heightAuto: false,
|
||||
buttonsStyling: false,
|
||||
html: div,
|
||||
showCancelButton: true,
|
||||
cancelButtonText: this.i18nService.t('cancel'),
|
||||
showConfirmButton: true,
|
||||
confirmButtonText: this.i18nService.t('submit'),
|
||||
});
|
||||
|
||||
let pin: string = null;
|
||||
let masterPassOnRestart: boolean = null;
|
||||
if (submitted.value) {
|
||||
pin = (document.getElementById('pin-val') as HTMLInputElement).value;
|
||||
masterPassOnRestart = (document.getElementById('master-pass-restart') as HTMLInputElement).checked;
|
||||
}
|
||||
if (pin != null && pin.trim() !== '') {
|
||||
const kdf = await this.userService.getKdf();
|
||||
const kdfIterations = await this.userService.getKdfIterations();
|
||||
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);
|
||||
if (masterPassOnRestart) {
|
||||
const encPin = await this.cryptoService.encrypt(pin);
|
||||
await this.storageService.save(ConstantsService.protectedPin, encPin.encryptedString);
|
||||
this.vaultTimeoutService.pinProtectedKey = pinProtectedKey;
|
||||
} else {
|
||||
await this.storageService.save(ConstantsService.pinProtectedKey, pinProtectedKey.encryptedString);
|
||||
}
|
||||
} else {
|
||||
if (ref == null) {
|
||||
this.pin = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!this.pin) {
|
||||
|
||||
this.pin = await ref.onClosedPromise();
|
||||
} else {
|
||||
await this.cryptoService.clearPinProtectedKey();
|
||||
await this.vaultTimeoutService.clear();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue