1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 23:33:31 +00:00

Vault Timeout Policy (#1052)

This commit is contained in:
Oscar Hinton
2021-09-15 20:02:46 +02:00
committed by GitHub
parent da4af743f3
commit aa19e678f7
10 changed files with 116 additions and 18 deletions

View File

@@ -7,14 +7,7 @@
{{'security' | i18n}} {{'security' | i18n}}
</div> </div>
<div class="box-content box-content-padded"> <div class="box-content box-content-padded">
<div class="form-group"> <app-vault-timeout-input [vaultTimeouts]="vaultTimeouts" [formControl]="vaultTimeout" ngDefaultControl></app-vault-timeout-input>
<label for="vaultTimeouts">{{'vaultTimeout' | i18n}}</label>
<select id="vaultTimeouts" name="VaultTimeouts" [(ngModel)]="vaultTimeout"
(change)="saveVaultTimeoutOptions()">
<option *ngFor="let o of vaultTimeouts" [ngValue]="o.value">{{o.name}}</option>
</select>
<small class="help-block">{{'vaultTimeoutDesc' | i18n}}</small>
</div>
<div class="form-group"> <div class="form-group">
<label>{{'vaultTimeoutAction' | i18n}}</label> <label>{{'vaultTimeoutAction' | i18n}}</label>
<div class="radio radio-mt-2"> <div class="radio radio-mt-2">

View File

@@ -2,8 +2,8 @@ import {
Component, Component,
OnInit, OnInit,
} from '@angular/core'; } from '@angular/core';
import { FormControl } from '@angular/forms';
import Swal from 'sweetalert2/src/sweetalert2.js'; import { debounceTime } from 'rxjs/operators';
import { DeviceType } from 'jslib-common/enums/deviceType'; import { DeviceType } from 'jslib-common/enums/deviceType';
@@ -13,7 +13,6 @@ import { MessagingService } from 'jslib-common/abstractions/messaging.service';
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
import { StateService } from 'jslib-common/abstractions/state.service'; import { StateService } from 'jslib-common/abstractions/state.service';
import { StorageService } from 'jslib-common/abstractions/storage.service'; import { StorageService } from 'jslib-common/abstractions/storage.service';
import { UserService } from 'jslib-common/abstractions/user.service';
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service'; import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
import { ConstantsService } from 'jslib-common/services/constants.service'; import { ConstantsService } from 'jslib-common/services/constants.service';
@@ -32,7 +31,6 @@ import { SetPinComponent } from '../components/set-pin.component';
templateUrl: 'settings.component.html', templateUrl: 'settings.component.html',
}) })
export class SettingsComponent implements OnInit { export class SettingsComponent implements OnInit {
vaultTimeout: number = null;
vaultTimeoutAction: string; vaultTimeoutAction: string;
pin: boolean = null; pin: boolean = null;
disableFavicons: boolean = false; disableFavicons: boolean = false;
@@ -70,11 +68,12 @@ export class SettingsComponent implements OnInit {
startToTrayText: string; startToTrayText: string;
startToTrayDescText: string; startToTrayDescText: string;
vaultTimeout: FormControl = new FormControl(null);
constructor(private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, constructor(private i18nService: I18nService, private platformUtilsService: PlatformUtilsService,
private storageService: StorageService, private vaultTimeoutService: VaultTimeoutService, private storageService: StorageService, private vaultTimeoutService: VaultTimeoutService,
private stateService: StateService, private messagingService: MessagingService, private stateService: StateService, private messagingService: MessagingService,
private userService: UserService, private cryptoService: CryptoService, private cryptoService: CryptoService, private modalService: ModalService) {
private modalService: ModalService) {
const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop; const isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
// Workaround to avoid ghosting trays https://github.com/electron/electron/issues/17622 // Workaround to avoid ghosting trays https://github.com/electron/electron/issues/17622
@@ -117,6 +116,10 @@ export class SettingsComponent implements OnInit {
{ name: i18nService.t('never'), value: null }, { name: i18nService.t('never'), value: null },
]); ]);
this.vaultTimeout.valueChanges.pipe(debounceTime(500)).subscribe(() => {
this.saveVaultTimeoutOptions();
});
const localeOptions: any[] = []; const localeOptions: any[] = [];
i18nService.supportedTranslationLocales.forEach(locale => { i18nService.supportedTranslationLocales.forEach(locale => {
let name = locale; let name = locale;
@@ -149,7 +152,7 @@ export class SettingsComponent implements OnInit {
async ngOnInit() { async ngOnInit() {
this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop; this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop;
this.vaultTimeout = await this.storageService.get<number>(ConstantsService.vaultTimeoutKey); this.vaultTimeout.setValue(await this.vaultTimeoutService.getVaultTimeout());
this.vaultTimeoutAction = await this.storageService.get<string>(ConstantsService.vaultTimeoutActionKey); this.vaultTimeoutAction = await this.storageService.get<string>(ConstantsService.vaultTimeoutActionKey);
const pinSet = await this.vaultTimeoutService.isPinLockSet(); const pinSet = await this.vaultTimeoutService.isPinLockSet();
this.pin = pinSet[0] || pinSet[1]; this.pin = pinSet[0] || pinSet[1];
@@ -187,8 +190,18 @@ export class SettingsComponent implements OnInit {
return; return;
} }
} }
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout != null ? this.vaultTimeout : null,
this.vaultTimeoutAction); // Avoid saving 0 since it's useless as a timeout value.
if (this.vaultTimeout.value === 0) {
return;
}
if (!this.vaultTimeout.valid) {
this.platformUtilsService.showToast('error', null, this.i18nService.t('vaultTimeoutTooLarge'));
return;
}
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout.value, this.vaultTimeoutAction);
} }
async updatePin() { async updatePin() {

View File

@@ -0,0 +1,24 @@
<app-callout type="info" *ngIf="vaultTimeoutPolicy">
{{'vaultTimeoutPolicyInEffect' | i18n : vaultTimeoutPolicyHours : vaultTimeoutPolicyMinutes}}
</app-callout>
<div [formGroup]="form">
<div class="form-group">
<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>
<small class="form-text text-muted">{{'vaultTimeoutDesc' | i18n}}</small>
</div>
<div class="form-group row" *ngIf="showCustom" formGroupName="custom">
<div class="col">
<label for="hours">{{'hours' | i18n}}</label>
<input id="hours" class="form-control" type="number" min="0" name="hours" formControlName="hours">
</div>
<div class="col">
<label for="minutes">{{'minutes' | i18n}}</label>
<input id="minutes" class="form-control" type="number" min="0" max="59" name="minutes" formControlName="minutes">
</div>
</div>
<div class="form-group"></div> <!-- Styling fix -->
</div>

View 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 {
}

View File

@@ -28,6 +28,7 @@ import { SsoComponent } from './accounts/sso.component';
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component'; import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
import { TwoFactorComponent } from './accounts/two-factor.component'; import { TwoFactorComponent } from './accounts/two-factor.component';
import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component'; import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component';
import { VaultTimeoutInputComponent } from './accounts/vault-timeout-input.component';
import { CalloutComponent } from 'jslib-angular/components/callout.component'; import { CalloutComponent } from 'jslib-angular/components/callout.component';
import { IconComponent } from 'jslib-angular/components/icon.component'; import { IconComponent } from 'jslib-angular/components/icon.component';
@@ -224,6 +225,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
ViewComponent, ViewComponent,
PasswordRepromptComponent, PasswordRepromptComponent,
SetPinComponent, SetPinComponent,
VaultTimeoutInputComponent,
], ],
providers: [DatePipe], providers: [DatePipe],
bootstrap: [AppComponent], bootstrap: [AppComponent],

View File

@@ -120,7 +120,7 @@ const sendService = new SendService(cryptoService, userService, apiService, file
const policyService = new PolicyService(userService, storageService); const policyService = new PolicyService(userService, storageService);
const vaultTimeoutService = new VaultTimeoutService(cipherService, folderService, collectionService, const vaultTimeoutService = new VaultTimeoutService(cipherService, folderService, collectionService,
cryptoService, platformUtilsService, storageService, messagingService, searchService, userService, tokenService, cryptoService, platformUtilsService, storageService, messagingService, searchService, userService, tokenService,
null, async () => messagingService.send('logout', { expired: false })); policyService, null, async () => messagingService.send('logout', { expired: false }));
const syncService = new SyncService(userService, apiService, settingsService, const syncService = new SyncService(userService, apiService, settingsService,
folderService, cipherService, cryptoService, collectionService, storageService, messagingService, policyService, folderService, cipherService, cryptoService, collectionService, storageService, messagingService, policyService,
sendService, async (expired: boolean) => messagingService.send('logout', { expired: expired })); sendService, async (expired: boolean) => messagingService.send('logout', { expired: expired }));

View File

@@ -1703,6 +1703,28 @@
"updateMasterPasswordWarning": { "updateMasterPasswordWarning": {
"message": "Your Master Password was recently changed by an administrator in your organization. In order to access the vault, you must update it now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." "message": "Your Master Password was recently changed by an administrator in your organization. In order to access the vault, you must update it now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour."
}, },
"hours": {
"message": "Hours"
},
"minutes": {
"message": "Minutes"
},
"vaultTimeoutPolicyInEffect": {
"message": "Your organization policies are affecting your vault timeout. Maximum allowed Vault Timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)",
"placeholders": {
"hours": {
"content": "$1",
"example": "5"
},
"minutes": {
"content": "$2",
"example": "5"
}
}
},
"vaultTimeoutTooLarge": {
"message": "Your vault timeout exceeds the restrictions set by your organization."
},
"resetPasswordPolicyAutoEnroll": { "resetPasswordPolicyAutoEnroll": {
"message": "Automatic Enrollment" "message": "Automatic Enrollment"
}, },

10
src/scss/grid.scss Normal file
View File

@@ -0,0 +1,10 @@
.row {
display: flex;
margin: 0 -15px;
}
.col {
flex-basis: 0;
flex-grow: 1;
padding: 0 15px;
}

View File

@@ -253,6 +253,11 @@ form, .form {
border-color: themed('inputBorderColor'); border-color: themed('inputBorderColor');
} }
} }
input[type=text], input[type=number] {
padding: 5px;
width: 100%;
}
} }
.checkbox { .checkbox {

View File

@@ -1,6 +1,7 @@
@import "../css/webfonts.css"; @import "../css/webfonts.css";
@import "variables.scss"; @import "variables.scss";
@import "base.scss"; @import "base.scss";
@import "grid.scss";
@import "pages.scss"; @import "pages.scss";
@import "vault.scss"; @import "vault.scss";
@import "list.scss"; @import "list.scss";