Vault Timeout Policy (#1171)
This commit is contained in:
parent
17166dad4d
commit
a1c1fea976
|
@ -0,0 +1,20 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
import { AppComponent as BaseAppComponent } from 'src/app/app.component';
|
||||
import { MaximumVaultTimeoutPolicy } from './policies/maximum-vault-timeout.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: '../../../src/app/app.component.html',
|
||||
})
|
||||
export class AppComponent extends BaseAppComponent {
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
|
||||
this.policyListService.addPolicies([
|
||||
new MaximumVaultTimeoutPolicy(),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
|
@ -3,27 +3,36 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
|||
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { MaximumVaultTimeoutPolicyComponent } from './policies/maximum-vault-timeout.component';
|
||||
|
||||
import { AppComponent } from 'src/app/app.component';
|
||||
import { OssRoutingModule } from 'src/app/oss-routing.module';
|
||||
import { OssModule } from 'src/app/oss.module';
|
||||
import { ServicesModule } from 'src/app/services/services.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
OssModule,
|
||||
BrowserAnimationsModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
ServicesModule,
|
||||
ToasterModule.forRoot(),
|
||||
InfiniteScrollModule,
|
||||
DragDropModule,
|
||||
AppRoutingModule,
|
||||
OssRoutingModule,
|
||||
RouterModule,
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
MaximumVaultTimeoutPolicyComponent,
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<app-callout type="tip" title="{{'prerequisite' | i18n}}">
|
||||
{{'requireSsoPolicyReq' | i18n}}
|
||||
</app-callout>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="enabled" [formControl]="enabled" name="Enabled">
|
||||
<label class="form-check-label" for="enabled">{{'enabled' | i18n}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div [formGroup]="data">
|
||||
<div class="form-group">
|
||||
<label for="hours">{{'maximumVaultTimeoutLabel' | i18n}}</label>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<input id="hours" class="form-control" type="number" min="0" name="hours" formControlName="hours">
|
||||
<small>{{'hours' | i18n }}</small>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<input id="minutes" class="form-control" type="number" min="0" max="59" name="minutes"
|
||||
formControlName="minutes">
|
||||
<small>{{'minutes' | i18n }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,65 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
|
||||
import { PolicyType } from 'jslib-common/enums/policyType';
|
||||
|
||||
import { PolicyRequest } from 'jslib-common/models/request/policyRequest';
|
||||
|
||||
import { BasePolicy, BasePolicyComponent } from 'src/app/organizations/policies/base-policy.component';
|
||||
|
||||
export class MaximumVaultTimeoutPolicy extends BasePolicy {
|
||||
name = 'maximumVaultTimeout';
|
||||
description = 'maximumVaultTimeoutDesc';
|
||||
type = PolicyType.MaximumVaultTimeout;
|
||||
component = MaximumVaultTimeoutPolicyComponent;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'policy-maximum-timeout',
|
||||
templateUrl: 'maximum-vault-timeout.component.html',
|
||||
})
|
||||
export class MaximumVaultTimeoutPolicyComponent extends BasePolicyComponent {
|
||||
|
||||
data = this.fb.group({
|
||||
hours: [null],
|
||||
minutes: [null],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder, private i18nService: I18nService) {
|
||||
super();
|
||||
}
|
||||
|
||||
loadData() {
|
||||
const minutes = this.policyResponse.data?.minutes;
|
||||
|
||||
if (minutes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.data.patchValue({
|
||||
hours: Math.floor(minutes / 60),
|
||||
minutes: minutes % 60,
|
||||
});
|
||||
}
|
||||
|
||||
buildRequestData() {
|
||||
if (this.data.value.hours == null && this.data.value.minutes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
minutes: this.data.value.hours * 60 + this.data.value.minutes,
|
||||
};
|
||||
}
|
||||
|
||||
buildRequest(policiesEnabledMap: Map<PolicyType, boolean>): Promise<PolicyRequest> {
|
||||
const singleOrgEnabled = policiesEnabledMap.get(PolicyType.SingleOrg) ?? false;
|
||||
if (this.enabled.value && !singleOrgEnabled) {
|
||||
throw new Error(this.i18nService.t('requireSsoPolicyReqError'));
|
||||
}
|
||||
|
||||
return super.buildRequest(policiesEnabledMap);
|
||||
}
|
||||
}
|
2
jslib
2
jslib
|
@ -1 +1 @@
|
|||
Subproject commit 5f64d956520612a681611a27c5f4f2e5f27b640e
|
||||
Subproject commit 32774561f37bdcf9abb80276c5d1958b7ec192de
|
|
@ -22,6 +22,9 @@ import { ServicesModule } from './services/services.module';
|
|||
DragDropModule,
|
||||
OssRoutingModule,
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule { }
|
||||
|
|
|
@ -35,19 +35,28 @@ export abstract class BasePolicyComponent implements OnInit {
|
|||
ngOnInit(): void {
|
||||
this.enabled.setValue(this.policyResponse.enabled);
|
||||
|
||||
if (this.data != null) {
|
||||
this.data.patchValue(this.policyResponse.data ?? {});
|
||||
if (this.policyResponse.data != null) {
|
||||
this.loadData();
|
||||
}
|
||||
}
|
||||
|
||||
loadData() {
|
||||
this.data.patchValue(this.policyResponse.data ?? {});
|
||||
}
|
||||
|
||||
buildRequestData() {
|
||||
if (this.data != null) {
|
||||
return this.data.value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
buildRequest(policiesEnabledMap: Map<PolicyType, boolean>) {
|
||||
const request = new PolicyRequest();
|
||||
request.enabled = this.enabled.value;
|
||||
request.type = this.policy.type;
|
||||
|
||||
if (this.data != null) {
|
||||
request.data = this.data.value;
|
||||
}
|
||||
request.data = this.buildRequestData();
|
||||
|
||||
return Promise.resolve(request);
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@ import { RouterModule } from '@angular/router';
|
|||
import { ToasterModule } from 'angular2-toaster';
|
||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
import { AvatarComponent } from './components/avatar.component';
|
||||
import { PasswordRepromptComponent } from './components/password-reprompt.component';
|
||||
import { PasswordStrengthComponent } from './components/password-strength.component';
|
||||
|
@ -144,6 +142,7 @@ import { UpdateKeyComponent } from './settings/update-key.component';
|
|||
import { UpdateLicenseComponent } from './settings/update-license.component';
|
||||
import { UserBillingComponent } from './settings/user-billing.component';
|
||||
import { UserSubscriptionComponent } from './settings/user-subscription.component';
|
||||
import { VaultTimeoutInputComponent } from './settings/vault-timeout-input.component';
|
||||
import { VerifyEmailComponent } from './settings/verify-email.component';
|
||||
|
||||
import { BreachReportComponent } from './tools/breach-report.component';
|
||||
|
@ -307,7 +306,6 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
|||
AdjustStorageComponent,
|
||||
ApiActionDirective,
|
||||
ApiKeyComponent,
|
||||
AppComponent,
|
||||
AttachmentsComponent,
|
||||
AutofocusDirective,
|
||||
AvatarComponent,
|
||||
|
@ -457,6 +455,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
|||
DisableSendPolicyComponent,
|
||||
SendOptionsPolicyComponent,
|
||||
ResetPasswordPolicyComponent,
|
||||
VaultTimeoutInputComponent,
|
||||
],
|
||||
exports: [
|
||||
A11yTitleDirective,
|
||||
|
|
|
@ -124,7 +124,7 @@ const sendService = new SendService(cryptoService, userService, apiService, file
|
|||
i18nService, cryptoFunctionService);
|
||||
const vaultTimeoutService = new VaultTimeoutService(cipherService, folderService, collectionService,
|
||||
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,
|
||||
folderService, cipherService, cryptoService, collectionService, storageService, messagingService, policyService,
|
||||
sendService, async (expired: boolean) => messagingService.send('logout', { expired: expired }));
|
||||
|
|
|
@ -5,13 +5,8 @@
|
|||
<form (ngSubmit)="submit()" ngNativeValidate>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<label for="vaultTimeout">{{'vaultTimeout' | i18n}}</label>
|
||||
<select id="vaultTimeout" name="VaultTimeout" [(ngModel)]="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>
|
||||
<app-vault-timeout-input [vaultTimeouts]="vaultTimeouts" [formControl]="vaultTimeout" ngDefaultControl>
|
||||
</app-vault-timeout-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
|
|
@ -2,6 +2,7 @@ import {
|
|||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
|
||||
import { ToasterService } from 'angular2-toaster';
|
||||
|
||||
|
@ -21,15 +22,16 @@ import { Utils } from 'jslib-common/misc/utils';
|
|||
templateUrl: 'options.component.html',
|
||||
})
|
||||
export class OptionsComponent implements OnInit {
|
||||
vaultTimeout: number = null;
|
||||
vaultTimeoutAction: string = 'lock';
|
||||
disableIcons: boolean;
|
||||
enableGravatars: boolean;
|
||||
enableFullWidth: boolean;
|
||||
locale: string;
|
||||
vaultTimeouts: any[];
|
||||
vaultTimeouts: { name: string; value: number; }[];
|
||||
localeOptions: any[];
|
||||
|
||||
vaultTimeout: FormControl = new FormControl(null);
|
||||
|
||||
private startingLocale: string;
|
||||
|
||||
constructor(private storageService: StorageService, private stateService: StateService,
|
||||
|
@ -63,7 +65,7 @@ export class OptionsComponent implements OnInit {
|
|||
}
|
||||
|
||||
async ngOnInit() {
|
||||
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.disableIcons = await this.storageService.get<boolean>(ConstantsService.disableFaviconKey);
|
||||
this.enableGravatars = await this.storageService.get<boolean>('enableGravatars');
|
||||
|
@ -72,8 +74,12 @@ export class OptionsComponent implements OnInit {
|
|||
}
|
||||
|
||||
async submit() {
|
||||
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;
|
||||
}
|
||||
|
||||
await this.vaultTimeoutService.setVaultTimeoutOptions(this.vaultTimeout.value, this.vaultTimeoutAction);
|
||||
await this.storageService.save(ConstantsService.disableFaviconKey, this.disableIcons);
|
||||
await this.stateService.save(ConstantsService.disableFaviconKey, this.disableIcons);
|
||||
await this.storageService.save('enableGravatars', this.enableGravatars);
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<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" *ngIf="showCustom" formGroupName="custom">
|
||||
<label for="customVaultTimeout">{{'customVaultTimeout' | i18n}}</label>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<input id="hours" class="form-control" type="number" min="0" name="hours"
|
||||
formControlName="hours">
|
||||
<small>{{'hours' | i18n }}</small>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<input id="minutes" class="form-control" type="number" min="0" max="59" name="minutes"
|
||||
formControlName="minutes">
|
||||
<small>{{'minutes' | i18n }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -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 {
|
||||
}
|
|
@ -4198,5 +4198,39 @@
|
|||
},
|
||||
"updateMasterPasswordWarning": {
|
||||
"message": "Your Master Password was recently changed by an administrator in your organization. In order to access the vault, you must update your Master Password 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."
|
||||
},
|
||||
"maximumVaultTimeout": {
|
||||
"message": "Vault Timeout"
|
||||
},
|
||||
"maximumVaultTimeoutDesc": {
|
||||
"message": "Configure a maximum vault timeout for all users."
|
||||
},
|
||||
"maximumVaultTimeoutLabel": {
|
||||
"message": "Maximum Vault Timeout"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"customVaultTimeout": {
|
||||
"message": "Custom Vault Timeout"
|
||||
},
|
||||
"vaultTimeoutToLarge": {
|
||||
"message": "Your vault timeout exceeds the restriction set by your organization."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,10 +82,6 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
|
|||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
lockTimeout(): number {
|
||||
return null;
|
||||
}
|
||||
|
||||
launchUri(uri: string, options?: any): void {
|
||||
const a = document.createElement('a');
|
||||
a.href = uri;
|
||||
|
|
Loading…
Reference in New Issue