mirror of
https://github.com/bitwarden/browser
synced 2025-12-21 02:33:46 +00:00
Merge branch 'master' into patrickhlauke-a11y-patch2
This commit is contained in:
@@ -10,15 +10,19 @@
|
||||
<span class="title">{{'exportVault' | i18n}}</span>
|
||||
</div>
|
||||
<div class="right">
|
||||
<button appBlurClick type="submit">{{'submit' | i18n}}</button>
|
||||
<button appBlurClick type="submit" [disabled]="disabledByPolicy">{{'submit' | i18n}}</button>
|
||||
</div>
|
||||
</header>
|
||||
<content>
|
||||
<app-callout type="warning" title="{{'vaultExportDisabled' | i18n}}" *ngIf="disabledByPolicy">
|
||||
{{'personalVaultExportPolicyInEffect' | i18n}}
|
||||
</app-callout>
|
||||
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="format">{{'fileFormat' | i18n}}</label>
|
||||
<select id="format" name="Format" [(ngModel)]="format">
|
||||
<select id="format" name="Format" [(ngModel)]="format" [disabled]="disabledByPolicy">
|
||||
<option value="json">.json</option>
|
||||
<option value="csv">.csv</option>
|
||||
<option value="encrypted_json">.json (Encrypted)</option>
|
||||
@@ -28,7 +32,8 @@
|
||||
<div class="row-main">
|
||||
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}" name="MasterPassword"
|
||||
class="monospaced" [(ngModel)]="masterPassword" required appInputVerbatim appAutofocus>
|
||||
class="monospaced" [(ngModel)]="masterPassword" required appInputVerbatim appAutofocus
|
||||
[disabled]="disabledByPolicy">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<button type="button" class="row-btn" appStopClick appBlurClick
|
||||
|
||||
@@ -6,6 +6,7 @@ import { EventService } from 'jslib-common/abstractions/event.service';
|
||||
import { ExportService } from 'jslib-common/abstractions/export.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||
|
||||
import { ExportComponent as BaseExportComponent } from 'jslib-angular/components/export.component';
|
||||
|
||||
@@ -16,8 +17,8 @@ import { ExportComponent as BaseExportComponent } from 'jslib-angular/components
|
||||
export class ExportComponent extends BaseExportComponent {
|
||||
constructor(cryptoService: CryptoService, i18nService: I18nService,
|
||||
platformUtilsService: PlatformUtilsService, exportService: ExportService,
|
||||
eventService: EventService, private router: Router) {
|
||||
super(cryptoService, i18nService, platformUtilsService, exportService, eventService, window);
|
||||
eventService: EventService, policyService: PolicyService, private router: Router) {
|
||||
super(cryptoService, i18nService, platformUtilsService, exportService, eventService, policyService, window);
|
||||
}
|
||||
|
||||
protected saved() {
|
||||
|
||||
@@ -28,13 +28,7 @@
|
||||
<div class="box list">
|
||||
<div class="box-header">{{'security' | i18n}}</div>
|
||||
<div class="box-content single-line">
|
||||
<div class="box-content-row display-block" appBoxRow>
|
||||
<label for="vaultTimeout">{{'vaultTimeout' | i18n}}</label>
|
||||
<select #vaultTimeoutSelect id="vaultTimeout" name="VaultTimeouts" [ngModel]="vaultTimeout"
|
||||
(ngModelChange)="saveVaultTimeout($event)">
|
||||
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<app-vault-timeout-input [vaultTimeouts]="vaultTimeouts" [formControl]="vaultTimeout" ngDefaultControl></app-vault-timeout-input>
|
||||
<div class="box-content-row display-block" appBoxRow>
|
||||
<label for="vaultTimeoutAction">{{'vaultTimeoutAction' | i18n}}</label>
|
||||
<select #vaultTimeoutActionSelect id="vaultTimeoutAction" name="VaultTimeoutActions"
|
||||
@@ -50,6 +44,10 @@
|
||||
<label for="biometric">{{'unlockWithBiometrics' | i18n}}</label>
|
||||
<input id="biometric" type="checkbox" (change)="updateBiometric()" [(ngModel)]="biometric">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow *ngIf="supportsBiometric">
|
||||
<label for="autoBiometricsPrompt">{{'disableAutoBiometricsPrompt' | i18n}}</label>
|
||||
<input id="autoBiometricsPrompt" type="checkbox" (change)="updateAutoBiometricsPrompt()" [disabled]="!biometric" [(ngModel)]="disableAutoBiometricsPrompt">
|
||||
</div>
|
||||
<button type="button" class="box-content-row box-content-row-flex text-default" appStopClick appBlurClick
|
||||
(click)="lock()">
|
||||
<div class="row-main">{{'lockNow' | i18n}}</div>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import Swal from 'sweetalert2/src/sweetalert2.js';
|
||||
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
import Swal from 'sweetalert2/src/sweetalert2.js';
|
||||
|
||||
import { BrowserApi } from '../../browser/browserApi';
|
||||
|
||||
@@ -24,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',
|
||||
@@ -44,22 +49,24 @@ const RateUrls = {
|
||||
templateUrl: 'settings.component.html',
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
@ViewChild('vaultTimeoutSelect', { read: ElementRef, static: true }) vaultTimeoutSelectRef: ElementRef;
|
||||
@ViewChild('vaultTimeoutActionSelect', { read: ElementRef, static: true }) vaultTimeoutActionSelectRef: ElementRef;
|
||||
vaultTimeouts: any[];
|
||||
vaultTimeout: number = null;
|
||||
vaultTimeoutActions: any[];
|
||||
vaultTimeoutAction: string;
|
||||
pin: boolean = null;
|
||||
supportsBiometric: boolean;
|
||||
biometric: boolean = false;
|
||||
disableAutoBiometricsPrompt = true;
|
||||
previousVaultTimeout: number = null;
|
||||
|
||||
vaultTimeout: FormControl = new FormControl(null);
|
||||
|
||||
constructor(private platformUtilsService: PlatformUtilsService, private i18nService: I18nService,
|
||||
private vaultTimeoutService: VaultTimeoutService, private storageService: StorageService,
|
||||
public messagingService: MessagingService, private router: Router,
|
||||
private environmentService: EnvironmentService, private cryptoService: CryptoService,
|
||||
private userService: UserService, private popupUtilsService: PopupUtilsService) {
|
||||
private userService: UserService, private popupUtilsService: PopupUtilsService,
|
||||
private modalService: ModalService, private toasterService: ToasterService) {
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -89,14 +96,18 @@ export class SettingsComponent implements OnInit {
|
||||
{ name: this.i18nService.t('logOut'), value: 'logOut' },
|
||||
];
|
||||
|
||||
let timeout = await this.storageService.get<number>(ConstantsService.vaultTimeoutKey);
|
||||
let timeout = await this.vaultTimeoutService.getVaultTimeout();
|
||||
if (timeout != null) {
|
||||
if (timeout === -2 && !showOnLocked) {
|
||||
timeout = -1;
|
||||
}
|
||||
this.vaultTimeout = timeout;
|
||||
this.vaultTimeout.setValue(timeout);
|
||||
}
|
||||
this.previousVaultTimeout = this.vaultTimeout;
|
||||
this.previousVaultTimeout = this.vaultTimeout.value;
|
||||
this.vaultTimeout.valueChanges.subscribe(value => {
|
||||
this.saveVaultTimeout(value);
|
||||
});
|
||||
|
||||
const action = await this.storageService.get<string>(ConstantsService.vaultTimeoutActionKey);
|
||||
this.vaultTimeoutAction = action == null ? 'lock' : action;
|
||||
|
||||
@@ -105,6 +116,8 @@ export class SettingsComponent implements OnInit {
|
||||
|
||||
this.supportsBiometric = await this.platformUtilsService.supportsBiometric();
|
||||
this.biometric = await this.vaultTimeoutService.isBiometricLockSet();
|
||||
this.disableAutoBiometricsPrompt = await this.storageService.get<boolean>(
|
||||
ConstantsService.disableAutoBiometricsPromptKey) ?? true;
|
||||
}
|
||||
|
||||
async saveVaultTimeout(newValue: number) {
|
||||
@@ -113,18 +126,19 @@ export class SettingsComponent implements OnInit {
|
||||
this.i18nService.t('neverLockWarning'), null,
|
||||
this.i18nService.t('yes'), this.i18nService.t('cancel'), 'warning');
|
||||
if (!confirmed) {
|
||||
this.vaultTimeouts.forEach((option: any, i) => {
|
||||
if (option.value === this.vaultTimeout) {
|
||||
this.vaultTimeoutSelectRef.nativeElement.value = i + ': ' + this.vaultTimeout;
|
||||
}
|
||||
});
|
||||
this.vaultTimeout.setValue(this.previousVaultTimeout);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.previousVaultTimeout = this.vaultTimeout;
|
||||
this.vaultTimeout = newValue;
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout != null ? this.vaultTimeout : null,
|
||||
this.vaultTimeoutAction);
|
||||
|
||||
if (!this.vaultTimeout.valid) {
|
||||
this.toasterService.popAsync('error', null, this.i18nService.t('vaultTimeoutToLarge'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.previousVaultTimeout = this.vaultTimeout.value;
|
||||
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout.value, this.vaultTimeoutAction);
|
||||
if (this.previousVaultTimeout == null) {
|
||||
this.messagingService.send('bgReseedStorage');
|
||||
}
|
||||
@@ -145,65 +159,27 @@ export class SettingsComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.vaultTimeout.valid) {
|
||||
this.toasterService.popAsync('error', null, this.i18nService.t('vaultTimeoutToLarge'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.vaultTimeoutAction = newValue;
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout != null ? this.vaultTimeout : null,
|
||||
this.vaultTimeoutAction);
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout.value, this.vaultTimeoutAction);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -277,6 +253,10 @@ export class SettingsComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
async updateAutoBiometricsPrompt() {
|
||||
await this.storageService.save(ConstantsService.disableAutoBiometricsPromptKey, this.disableAutoBiometricsPrompt);
|
||||
}
|
||||
|
||||
async lock() {
|
||||
await this.vaultTimeoutService.lock(true);
|
||||
}
|
||||
|
||||
29
src/popup/settings/vault-timeout-input.component.html
Normal file
29
src/popup/settings/vault-timeout-input.component.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<app-callout type="info" *ngIf="vaultTimeoutPolicy">
|
||||
{{'vaultTimeoutPolicyInEffect' | i18n : vaultTimeoutPolicyHours : vaultTimeoutPolicyMinutes}}
|
||||
</app-callout>
|
||||
|
||||
<div [formGroup]="form">
|
||||
<div class="box-content-row last display-block" appBoxRow>
|
||||
<label for="vaultTimeout">{{'vaultTimeout' | i18n}}</label>
|
||||
<select id="vaultTimeout" name="VaultTimeout" formControlName="vaultTimeout" class="form-control">
|
||||
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row last" *ngIf="showCustom">
|
||||
<div formGroupName="custom" class="row">
|
||||
<div class="col">
|
||||
<div class="display-block" appBoxRow>
|
||||
<label for="customVaultTimeout">{{'hours' | i18n }}</label>
|
||||
<input id="hours" class="form-control" type="number" min="0" name="hours" formControlName="hours">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="display-block" appBoxRow>
|
||||
<label for="customVaultTimeout">{{'minutes' | i18n }}</label>
|
||||
<input id="minutes" class="form-control" type="number" min="0" max="59" name="minutes"
|
||||
formControlName="minutes">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
28
src/popup/settings/vault-timeout-input.component.ts
Normal file
28
src/popup/settings/vault-timeout-input.component.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
NG_VALIDATORS,
|
||||
NG_VALUE_ACCESSOR,
|
||||
} from '@angular/forms';
|
||||
|
||||
import {
|
||||
VaultTimeoutInputComponent as VaultTimeoutInputComponentBase
|
||||
} from 'jslib-angular/components/settings/vault-timeout-input.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-vault-timeout-input',
|
||||
templateUrl: 'vault-timeout-input.component.html',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
{
|
||||
provide: NG_VALIDATORS,
|
||||
multi: true,
|
||||
useExisting: VaultTimeoutInputComponent,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {
|
||||
}
|
||||
Reference in New Issue
Block a user