mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 16:23:44 +00:00
[PM-23524] Port desktop settings to CL vault timeout, and drop old non-CL vault timeout components (#15513)
* Remove unused old vault timeout component * Drop desktop specific vault timeout component and replace it with shared CL implementation * Fix tests * Fix test * Fix build on desktop * Fix tests * Fix margin
This commit is contained in:
@@ -1,44 +0,0 @@
|
|||||||
<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 vaultTimeoutOptions" [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>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { Component } from "@angular/core";
|
|
||||||
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from "@angular/forms";
|
|
||||||
|
|
||||||
import { VaultTimeoutInputComponent as VaultTimeoutInputComponentBase } from "@bitwarden/auth/angular";
|
|
||||||
|
|
||||||
@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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
standalone: false,
|
|
||||||
})
|
|
||||||
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {}
|
|
||||||
@@ -27,7 +27,6 @@ import {
|
|||||||
import { AccountComponent } from "../auth/popup/account-switching/account.component";
|
import { AccountComponent } from "../auth/popup/account-switching/account.component";
|
||||||
import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component";
|
import { CurrentAccountComponent } from "../auth/popup/account-switching/current-account.component";
|
||||||
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
|
import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component";
|
||||||
import { VaultTimeoutInputComponent } from "../auth/popup/settings/vault-timeout-input.component";
|
|
||||||
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
|
import { AutofillComponent } from "../autofill/popup/settings/autofill.component";
|
||||||
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
|
import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component";
|
||||||
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
|
||||||
@@ -96,7 +95,6 @@ import "../platform/popup/locales";
|
|||||||
ColorPasswordCountPipe,
|
ColorPasswordCountPipe,
|
||||||
TabsV2Component,
|
TabsV2Component,
|
||||||
UserVerificationComponent,
|
UserVerificationComponent,
|
||||||
VaultTimeoutInputComponent,
|
|
||||||
RemovePasswordComponent,
|
RemovePasswordComponent,
|
||||||
EnvironmentSelectorComponent,
|
EnvironmentSelectorComponent,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -30,88 +30,39 @@
|
|||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<ng-container *ngIf="showSecurity">
|
<ng-container *ngIf="showSecurity">
|
||||||
<bit-callout type="info" *ngIf="vaultTimeoutPolicyCallout | async as policy">
|
<bit-section disableMargin>
|
||||||
<span *ngIf="policy.timeout && policy.action">
|
<bit-section-header>
|
||||||
{{
|
<h2 bitTypography="h6">{{ "vaultTimeoutHeader" | i18n }}</h2>
|
||||||
"vaultTimeoutPolicyWithActionInEffect"
|
</bit-section-header>
|
||||||
| i18n: policy.timeout.hours : policy.timeout.minutes : (policy.action | i18n)
|
|
||||||
}}
|
<auth-vault-timeout-input
|
||||||
</span>
|
|
||||||
<span *ngIf="policy.timeout && !policy.action">
|
|
||||||
{{
|
|
||||||
"vaultTimeoutPolicyInEffect"
|
|
||||||
| i18n: policy.timeout.hours : policy.timeout.minutes
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
<span *ngIf="!policy.timeout && policy.action">
|
|
||||||
{{ "vaultTimeoutActionPolicyInEffect" | i18n: (policy.action | i18n) }}
|
|
||||||
</span>
|
|
||||||
</bit-callout>
|
|
||||||
<app-vault-timeout-input
|
|
||||||
[vaultTimeoutOptions]="vaultTimeoutOptions"
|
[vaultTimeoutOptions]="vaultTimeoutOptions"
|
||||||
[formControl]="form.controls.vaultTimeout"
|
[formControl]="form.controls.vaultTimeout"
|
||||||
ngDefaultControl
|
ngDefaultControl
|
||||||
></app-vault-timeout-input>
|
|
||||||
<ng-container
|
|
||||||
*ngIf="availableVaultTimeoutActions$ | async as availableVaultTimeoutActions"
|
|
||||||
>
|
>
|
||||||
<div class="form-group">
|
</auth-vault-timeout-input>
|
||||||
<label>{{ "vaultTimeoutAction" | i18n }}</label>
|
|
||||||
<div class="radio radio-mt-2">
|
<bit-form-field disableMargin>
|
||||||
<label for="vaultTimeoutActionLock">
|
<bit-label for="vaultTimeoutAction">{{ "vaultTimeoutAction1" | i18n }}</bit-label>
|
||||||
<!--
|
<bit-select id="vaultTimeoutAction" formControlName="vaultTimeoutAction">
|
||||||
Using [attr.disabled] because reactive forms don't support disabling individual radio buttons, see:
|
<bit-option
|
||||||
https://github.com/angular/angular/issues/11763
|
*ngFor="let action of availableVaultTimeoutActions"
|
||||||
-->
|
[value]="action"
|
||||||
<input
|
[label]="action | i18n"
|
||||||
type="radio"
|
|
||||||
id="vaultTimeoutActionLock"
|
|
||||||
value="{{ VaultTimeoutAction.Lock }}"
|
|
||||||
aria-describedby="vaultTimeoutActionLockHelp"
|
|
||||||
formControlName="vaultTimeoutAction"
|
|
||||||
[attr.disabled]="
|
|
||||||
!availableVaultTimeoutActions.includes(VaultTimeoutAction.Lock)
|
|
||||||
? true
|
|
||||||
: null
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
{{ "lock" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<small id="vaultTimeoutActionLockHelp" class="help-block">{{
|
|
||||||
"vaultTimeoutActionLockDesc" | i18n
|
|
||||||
}}</small>
|
|
||||||
<div class="radio">
|
|
||||||
<label for="vaultTimeoutActionLogOut">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
id="vaultTimeoutActionLogOut"
|
|
||||||
value="{{ VaultTimeoutAction.LogOut }}"
|
|
||||||
aria-describedby="vaultTimeoutActionLogOutHelp"
|
|
||||||
formControlName="vaultTimeoutAction"
|
|
||||||
[attr.disabled]="
|
|
||||||
!availableVaultTimeoutActions.includes(VaultTimeoutAction.LogOut)
|
|
||||||
? true
|
|
||||||
: null
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
{{ "logOut" | i18n }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<small id="vaultTimeoutActionLogOutHelp" class="help-block">{{
|
|
||||||
"vaultTimeoutActionLogOutDesc" | i18n
|
|
||||||
}}</small>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
*ngIf="!availableVaultTimeoutActions.includes(VaultTimeoutAction.Lock)"
|
|
||||||
class="form-group"
|
|
||||||
>
|
>
|
||||||
<small id="unlockMethodNeededToChangeTimeoutActionHelp" class="help-block">{{
|
</bit-option>
|
||||||
"unlockMethodNeededToChangeTimeoutActionDesc" | i18n
|
</bit-select>
|
||||||
}}</small>
|
|
||||||
</div>
|
<bit-hint *ngIf="!availableVaultTimeoutActions.includes(VaultTimeoutAction.Lock)">
|
||||||
</ng-container>
|
{{ "unlockMethodNeededToChangeTimeoutActionDesc" | i18n }}<br />
|
||||||
<div class="form-group" *ngIf="(pinEnabled$ | async) || this.form.value.pin">
|
</bit-hint>
|
||||||
|
</bit-form-field>
|
||||||
|
|
||||||
|
<bit-hint *ngIf="hasVaultTimeoutPolicy" class="tw-mt-4">
|
||||||
|
{{ "vaultTimeoutPolicyAffectingOptions" | i18n }}
|
||||||
|
</bit-hint>
|
||||||
|
</bit-section>
|
||||||
|
<div class="form-group tw-mt-4" *ngIf="(pinEnabled$ | async) || this.form.value.pin">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label for="pin">
|
<label for="pin">
|
||||||
<input id="pin" type="checkbox" formControlName="pin" />
|
<input id="pin" type="checkbox" formControlName="pin" />
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { By } from "@angular/platform-browser";
|
|||||||
import { mock } from "jest-mock-extended";
|
import { mock } from "jest-mock-extended";
|
||||||
import { firstValueFrom, of } from "rxjs";
|
import { firstValueFrom, of } from "rxjs";
|
||||||
|
|
||||||
import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe";
|
|
||||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
@@ -33,7 +32,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|||||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||||
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { DialogRef, DialogService } from "@bitwarden/components";
|
import { DialogRef, DialogService, ToastService } from "@bitwarden/components";
|
||||||
import { BiometricStateService, BiometricsStatus, KeyService } from "@bitwarden/key-management";
|
import { BiometricStateService, BiometricsStatus, KeyService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
import { SetPinComponent } from "../../auth/components/set-pin.component";
|
import { SetPinComponent } from "../../auth/components/set-pin.component";
|
||||||
@@ -92,7 +91,7 @@ describe("SettingsComponent", () => {
|
|||||||
i18nService.supportedTranslationLocales = [];
|
i18nService.supportedTranslationLocales = [];
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [SettingsComponent, I18nPipe],
|
imports: [],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: AutofillSettingsServiceAbstraction,
|
provide: AutofillSettingsServiceAbstraction,
|
||||||
@@ -126,11 +125,26 @@ describe("SettingsComponent", () => {
|
|||||||
{ provide: VaultTimeoutSettingsService, useValue: vaultTimeoutSettingsService },
|
{ provide: VaultTimeoutSettingsService, useValue: vaultTimeoutSettingsService },
|
||||||
{ provide: ValidationService, useValue: validationService },
|
{ provide: ValidationService, useValue: validationService },
|
||||||
{ provide: MessagingService, useValue: messagingService },
|
{ provide: MessagingService, useValue: messagingService },
|
||||||
|
{ provide: ToastService, useValue: mock<ToastService>() },
|
||||||
{ provide: DesktopAutotypeService, useValue: desktopAutotypeService },
|
{ provide: DesktopAutotypeService, useValue: desktopAutotypeService },
|
||||||
],
|
],
|
||||||
schemas: [NO_ERRORS_SCHEMA],
|
schemas: [NO_ERRORS_SCHEMA],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
|
TestBed.overrideComponent(SettingsComponent, {
|
||||||
|
add: {
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: DialogService,
|
||||||
|
useValue: dialogService,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
providers: [DialogService],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
fixture = TestBed.createComponent(SettingsComponent);
|
fixture = TestBed.createComponent(SettingsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -163,6 +177,7 @@ describe("SettingsComponent", () => {
|
|||||||
themeStateService.selectedTheme$ = of(ThemeType.System);
|
themeStateService.selectedTheme$ = of(ThemeType.System);
|
||||||
i18nService.userSetLocale$ = of("en");
|
i18nService.userSetLocale$ = of("en");
|
||||||
pinServiceAbstraction.isPinSet.mockResolvedValue(false);
|
pinServiceAbstraction.isPinSet.mockResolvedValue(false);
|
||||||
|
policyService.policiesByType$.mockReturnValue(of([null]));
|
||||||
desktopAutotypeService.autotypeEnabled$ = of(false);
|
desktopAutotypeService.autotypeEnabled$ = of(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -580,7 +595,6 @@ describe("SettingsComponent", () => {
|
|||||||
component["form"].controls.vaultTimeoutAction.setValue(DEFAULT_VAULT_TIMEOUT_ACTION, {
|
component["form"].controls.vaultTimeoutAction.setValue(DEFAULT_VAULT_TIMEOUT_ACTION, {
|
||||||
emitEvent: false,
|
emitEvent: false,
|
||||||
});
|
});
|
||||||
component["previousVaultTimeout"] = DEFAULT_VAULT_TIMEOUT;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
@@ -594,14 +608,13 @@ describe("SettingsComponent", () => {
|
|||||||
])("should save vault timeout", async (vaultTimeout: VaultTimeout) => {
|
])("should save vault timeout", async (vaultTimeout: VaultTimeout) => {
|
||||||
dialogService.openSimpleDialog.mockResolvedValue(true);
|
dialogService.openSimpleDialog.mockResolvedValue(true);
|
||||||
|
|
||||||
await component.saveVaultTimeout(vaultTimeout);
|
await component.saveVaultTimeout(DEFAULT_VAULT_TIMEOUT, vaultTimeout);
|
||||||
|
|
||||||
expect(vaultTimeoutSettingsService.setVaultTimeoutOptions).toHaveBeenCalledWith(
|
expect(vaultTimeoutSettingsService.setVaultTimeoutOptions).toHaveBeenCalledWith(
|
||||||
mockUserId,
|
mockUserId,
|
||||||
vaultTimeout,
|
vaultTimeout,
|
||||||
DEFAULT_VAULT_TIMEOUT_ACTION,
|
DEFAULT_VAULT_TIMEOUT_ACTION,
|
||||||
);
|
);
|
||||||
expect(component["previousVaultTimeout"]).toEqual(DEFAULT_VAULT_TIMEOUT);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should save vault timeout when vault timeout action is disabled", async () => {
|
it("should save vault timeout when vault timeout action is disabled", async () => {
|
||||||
@@ -610,24 +623,25 @@ describe("SettingsComponent", () => {
|
|||||||
});
|
});
|
||||||
component["form"].controls.vaultTimeoutAction.disable({ emitEvent: false });
|
component["form"].controls.vaultTimeoutAction.disable({ emitEvent: false });
|
||||||
|
|
||||||
await component.saveVaultTimeout(DEFAULT_VAULT_TIMEOUT);
|
await component.saveVaultTimeout(DEFAULT_VAULT_TIMEOUT, DEFAULT_VAULT_TIMEOUT);
|
||||||
|
|
||||||
expect(vaultTimeoutSettingsService.setVaultTimeoutOptions).toHaveBeenCalledWith(
|
expect(vaultTimeoutSettingsService.setVaultTimeoutOptions).toHaveBeenCalledWith(
|
||||||
mockUserId,
|
mockUserId,
|
||||||
DEFAULT_VAULT_TIMEOUT,
|
DEFAULT_VAULT_TIMEOUT,
|
||||||
VaultTimeoutAction.LogOut,
|
VaultTimeoutAction.LogOut,
|
||||||
);
|
);
|
||||||
expect(component["previousVaultTimeout"]).toEqual(DEFAULT_VAULT_TIMEOUT);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not save vault timeout when vault timeout is 'never' and dialog is cancelled", async () => {
|
it("should not save vault timeout when vault timeout is 'never' and dialog is cancelled", async () => {
|
||||||
dialogService.openSimpleDialog.mockResolvedValue(false);
|
dialogService.openSimpleDialog.mockResolvedValue(false);
|
||||||
|
|
||||||
await component.saveVaultTimeout(VaultTimeoutStringType.Never);
|
await component.saveVaultTimeout(DEFAULT_VAULT_TIMEOUT, VaultTimeoutStringType.Never);
|
||||||
|
|
||||||
expect(vaultTimeoutSettingsService.setVaultTimeoutOptions).not.toHaveBeenCalled();
|
expect(vaultTimeoutSettingsService.setVaultTimeoutOptions).not.toHaveBeenCalledWith(
|
||||||
expect(component["form"].getRawValue().vaultTimeout).toEqual(DEFAULT_VAULT_TIMEOUT);
|
mockUserId,
|
||||||
expect(component["previousVaultTimeout"]).toEqual(DEFAULT_VAULT_TIMEOUT);
|
VaultTimeoutStringType.Never,
|
||||||
|
DEFAULT_VAULT_TIMEOUT_ACTION,
|
||||||
|
);
|
||||||
expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({
|
expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({
|
||||||
title: { key: "warning" },
|
title: { key: "warning" },
|
||||||
content: { key: "neverLockWarning" },
|
content: { key: "neverLockWarning" },
|
||||||
@@ -637,21 +651,27 @@ describe("SettingsComponent", () => {
|
|||||||
|
|
||||||
it("should not save vault timeout when vault timeout is 0", async () => {
|
it("should not save vault timeout when vault timeout is 0", async () => {
|
||||||
component["form"].controls.vaultTimeout.setValue(0, { emitEvent: false });
|
component["form"].controls.vaultTimeout.setValue(0, { emitEvent: false });
|
||||||
await component.saveVaultTimeout(0);
|
await component.saveVaultTimeout(DEFAULT_VAULT_TIMEOUT, 0);
|
||||||
|
|
||||||
expect(vaultTimeoutSettingsService.setVaultTimeoutOptions).not.toHaveBeenCalled();
|
expect(vaultTimeoutSettingsService.setVaultTimeoutOptions).not.toHaveBeenCalledWith(
|
||||||
|
mockUserId,
|
||||||
|
0,
|
||||||
|
DEFAULT_VAULT_TIMEOUT_ACTION,
|
||||||
|
);
|
||||||
expect(component["form"].getRawValue().vaultTimeout).toEqual(0);
|
expect(component["form"].getRawValue().vaultTimeout).toEqual(0);
|
||||||
expect(component["previousVaultTimeout"]).toEqual(DEFAULT_VAULT_TIMEOUT);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not save vault timeout when vault timeout is invalid", async () => {
|
it("should not save vault timeout when vault timeout is invalid", async () => {
|
||||||
i18nService.t.mockReturnValue("Number too large test error");
|
i18nService.t.mockReturnValue("Number too large test error");
|
||||||
component["form"].controls.vaultTimeout.setErrors({}, { emitEvent: false });
|
component["form"].controls.vaultTimeout.setErrors({}, { emitEvent: false });
|
||||||
await component.saveVaultTimeout(999_999_999);
|
await component.saveVaultTimeout(DEFAULT_VAULT_TIMEOUT, 999_999_999);
|
||||||
|
|
||||||
expect(vaultTimeoutSettingsService.setVaultTimeoutOptions).not.toHaveBeenCalled();
|
expect(vaultTimeoutSettingsService.setVaultTimeoutOptions).not.toHaveBeenCalledWith(
|
||||||
|
mockUserId,
|
||||||
|
999_999_999,
|
||||||
|
DEFAULT_VAULT_TIMEOUT_ACTION,
|
||||||
|
);
|
||||||
expect(component["form"].getRawValue().vaultTimeout).toEqual(DEFAULT_VAULT_TIMEOUT);
|
expect(component["form"].getRawValue().vaultTimeout).toEqual(DEFAULT_VAULT_TIMEOUT);
|
||||||
expect(component["previousVaultTimeout"]).toEqual(DEFAULT_VAULT_TIMEOUT);
|
|
||||||
expect(platformUtilsService.showToast).toHaveBeenCalledWith(
|
expect(platformUtilsService.showToast).toHaveBeenCalledWith(
|
||||||
"error",
|
"error",
|
||||||
null,
|
null,
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { CommonModule } from "@angular/common";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { BehaviorSubject, Observable, Subject, firstValueFrom, of } from "rxjs";
|
import { FormBuilder, FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||||
import {
|
import { RouterModule } from "@angular/router";
|
||||||
concatMap,
|
import { BehaviorSubject, Observable, Subject, combineLatest, firstValueFrom, of } from "rxjs";
|
||||||
debounceTime,
|
import { concatMap, map, pairwise, startWith, switchMap, takeUntil, timeout } from "rxjs/operators";
|
||||||
filter,
|
|
||||||
map,
|
|
||||||
switchMap,
|
|
||||||
takeUntil,
|
|
||||||
tap,
|
|
||||||
timeout,
|
|
||||||
} from "rxjs/operators";
|
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { VaultTimeoutInputComponent } from "@bitwarden/auth/angular";
|
||||||
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
import { PinServiceAbstraction } from "@bitwarden/auth/common";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
@@ -43,7 +38,19 @@ import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums/theme-type.e
|
|||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import {
|
||||||
|
CheckboxModule,
|
||||||
|
DialogService,
|
||||||
|
FormFieldModule,
|
||||||
|
IconButtonModule,
|
||||||
|
ItemModule,
|
||||||
|
LinkModule,
|
||||||
|
SectionComponent,
|
||||||
|
SectionHeaderComponent,
|
||||||
|
SelectModule,
|
||||||
|
ToastService,
|
||||||
|
TypographyModule,
|
||||||
|
} from "@bitwarden/components";
|
||||||
import { KeyService, BiometricStateService, BiometricsStatus } from "@bitwarden/key-management";
|
import { KeyService, BiometricStateService, BiometricsStatus } from "@bitwarden/key-management";
|
||||||
|
|
||||||
import { SetPinComponent } from "../../auth/components/set-pin.component";
|
import { SetPinComponent } from "../../auth/components/set-pin.component";
|
||||||
@@ -57,14 +64,30 @@ import { NativeMessagingManifestService } from "../services/native-messaging-man
|
|||||||
@Component({
|
@Component({
|
||||||
selector: "app-settings",
|
selector: "app-settings",
|
||||||
templateUrl: "settings.component.html",
|
templateUrl: "settings.component.html",
|
||||||
standalone: false,
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CheckboxModule,
|
||||||
|
CommonModule,
|
||||||
|
FormFieldModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
IconButtonModule,
|
||||||
|
ItemModule,
|
||||||
|
JslibModule,
|
||||||
|
LinkModule,
|
||||||
|
RouterModule,
|
||||||
|
SectionComponent,
|
||||||
|
SectionHeaderComponent,
|
||||||
|
SelectModule,
|
||||||
|
TypographyModule,
|
||||||
|
VaultTimeoutInputComponent,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class SettingsComponent implements OnInit, OnDestroy {
|
export class SettingsComponent implements OnInit, OnDestroy {
|
||||||
// For use in template
|
// For use in template
|
||||||
protected readonly VaultTimeoutAction = VaultTimeoutAction;
|
protected readonly VaultTimeoutAction = VaultTimeoutAction;
|
||||||
|
|
||||||
showMinToTray = false;
|
showMinToTray = false;
|
||||||
vaultTimeoutOptions: VaultTimeoutOption[] = [];
|
|
||||||
localeOptions: any[];
|
localeOptions: any[];
|
||||||
themeOptions: any[];
|
themeOptions: any[];
|
||||||
clearClipboardOptions: any[];
|
clearClipboardOptions: any[];
|
||||||
@@ -96,12 +119,9 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
currentUserEmail: string;
|
currentUserEmail: string;
|
||||||
currentUserId: UserId;
|
currentUserId: UserId;
|
||||||
|
|
||||||
availableVaultTimeoutActions$: Observable<VaultTimeoutAction[]>;
|
availableVaultTimeoutActions: VaultTimeoutAction[] = [];
|
||||||
vaultTimeoutPolicyCallout: Observable<{
|
vaultTimeoutOptions: VaultTimeoutOption[] = [];
|
||||||
timeout: { hours: number; minutes: number };
|
hasVaultTimeoutPolicy = false;
|
||||||
action: "lock" | "logOut";
|
|
||||||
}>;
|
|
||||||
previousVaultTimeout: VaultTimeout = null;
|
|
||||||
|
|
||||||
userHasMasterPassword: boolean;
|
userHasMasterPassword: boolean;
|
||||||
userHasPinSet: boolean;
|
userHasPinSet: boolean;
|
||||||
@@ -170,6 +190,8 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
private nativeMessagingManifestService: NativeMessagingManifestService,
|
private nativeMessagingManifestService: NativeMessagingManifestService,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private validationService: ValidationService,
|
private validationService: ValidationService,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private toastService: ToastService,
|
||||||
) {
|
) {
|
||||||
this.isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
this.isMac = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||||
this.isLinux = this.platformUtilsService.getDevice() === DeviceType.LinuxDesktop;
|
this.isLinux = this.platformUtilsService.getDevice() === DeviceType.LinuxDesktop;
|
||||||
@@ -255,38 +277,54 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
this.currentUserEmail = activeAccount.email;
|
this.currentUserEmail = activeAccount.email;
|
||||||
this.currentUserId = activeAccount.id;
|
this.currentUserId = activeAccount.id;
|
||||||
|
|
||||||
this.availableVaultTimeoutActions$ = this.refreshTimeoutSettings$.pipe(
|
const maximumVaultTimeoutPolicy = this.accountService.activeAccount$.pipe(
|
||||||
switchMap(() =>
|
|
||||||
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(activeAccount.id),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load timeout policy
|
|
||||||
this.vaultTimeoutPolicyCallout = this.accountService.activeAccount$.pipe(
|
|
||||||
getUserId,
|
getUserId,
|
||||||
switchMap((userId) =>
|
switchMap((userId) =>
|
||||||
this.policyService.policiesByType$(PolicyType.MaximumVaultTimeout, userId),
|
this.policyService.policiesByType$(PolicyType.MaximumVaultTimeout, userId),
|
||||||
),
|
),
|
||||||
getFirstPolicy,
|
getFirstPolicy,
|
||||||
filter((policy) => policy != null),
|
);
|
||||||
map((policy) => {
|
if ((await firstValueFrom(maximumVaultTimeoutPolicy)) != null) {
|
||||||
let timeout;
|
this.hasVaultTimeoutPolicy = true;
|
||||||
if (policy.data?.minutes) {
|
|
||||||
timeout = {
|
|
||||||
hours: Math.floor(policy.data?.minutes / 60),
|
|
||||||
minutes: policy.data?.minutes % 60,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return { timeout: timeout, action: policy.data?.action };
|
|
||||||
}),
|
this.refreshTimeoutSettings$
|
||||||
tap((policy) => {
|
.pipe(
|
||||||
if (policy.action) {
|
switchMap(() =>
|
||||||
|
combineLatest([
|
||||||
|
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(),
|
||||||
|
this.vaultTimeoutSettingsService.getVaultTimeoutActionByUserId$(activeAccount.id),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe(([availableActions, action]) => {
|
||||||
|
this.availableVaultTimeoutActions = availableActions;
|
||||||
|
this.form.controls.vaultTimeoutAction.setValue(action, { emitEvent: false });
|
||||||
|
// NOTE: The UI doesn't properly update without detect changes.
|
||||||
|
// I've even tried using an async pipe, but it still doesn't work. I'm not sure why.
|
||||||
|
// Using an async pipe means that we can't call `detectChanges` AFTER the data has change
|
||||||
|
// meaning that we are forced to use regular class variables instead of observables.
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.refreshTimeoutSettings$
|
||||||
|
.pipe(
|
||||||
|
switchMap(() =>
|
||||||
|
combineLatest([
|
||||||
|
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(),
|
||||||
|
maximumVaultTimeoutPolicy,
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
takeUntil(this.destroy$),
|
||||||
|
)
|
||||||
|
.subscribe(([availableActions, policy]) => {
|
||||||
|
if (policy?.data?.action || availableActions.length <= 1) {
|
||||||
this.form.controls.vaultTimeoutAction.disable({ emitEvent: false });
|
this.form.controls.vaultTimeoutAction.disable({ emitEvent: false });
|
||||||
} else {
|
} else {
|
||||||
this.form.controls.vaultTimeoutAction.enable({ emitEvent: false });
|
this.form.controls.vaultTimeoutAction.enable({ emitEvent: false });
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// Load initial values
|
// Load initial values
|
||||||
this.userHasPinSet = await this.pinService.isPinSet(activeAccount.id);
|
this.userHasPinSet = await this.pinService.isPinSet(activeAccount.id);
|
||||||
@@ -354,7 +392,6 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
// Non-form values
|
// Non-form values
|
||||||
this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop;
|
this.showMinToTray = this.platformUtilsService.getDevice() !== DeviceType.LinuxDesktop;
|
||||||
this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
this.showAlwaysShowDock = this.platformUtilsService.getDevice() === DeviceType.MacOsDesktop;
|
||||||
this.previousVaultTimeout = this.form.value.vaultTimeout;
|
|
||||||
|
|
||||||
this.refreshTimeoutSettings$
|
this.refreshTimeoutSettings$
|
||||||
.pipe(
|
.pipe(
|
||||||
@@ -370,9 +407,10 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
// Form events
|
// Form events
|
||||||
this.form.controls.vaultTimeout.valueChanges
|
this.form.controls.vaultTimeout.valueChanges
|
||||||
.pipe(
|
.pipe(
|
||||||
debounceTime(500),
|
startWith(initialValues.vaultTimeout), // emit to init pairwise
|
||||||
concatMap(async (value) => {
|
pairwise(),
|
||||||
await this.saveVaultTimeout(value);
|
concatMap(async ([previousValue, newValue]) => {
|
||||||
|
await this.saveVaultTimeout(previousValue, newValue);
|
||||||
}),
|
}),
|
||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
)
|
)
|
||||||
@@ -423,7 +461,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveVaultTimeout(newValue: VaultTimeout) {
|
async saveVaultTimeout(previousValue: VaultTimeout, newValue: VaultTimeout) {
|
||||||
if (newValue === VaultTimeoutStringType.Never) {
|
if (newValue === VaultTimeoutStringType.Never) {
|
||||||
const confirmed = await this.dialogService.openSimpleDialog({
|
const confirmed = await this.dialogService.openSimpleDialog({
|
||||||
title: { key: "warning" },
|
title: { key: "warning" },
|
||||||
@@ -432,7 +470,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
this.form.controls.vaultTimeout.setValue(this.previousVaultTimeout);
|
this.form.controls.vaultTimeout.setValue(previousValue, { emitEvent: false });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -451,8 +489,6 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.previousVaultTimeout = this.form.value.vaultTimeout;
|
|
||||||
|
|
||||||
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
|
const activeAccount = await firstValueFrom(this.accountService.activeAccount$);
|
||||||
|
|
||||||
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
|
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
|
||||||
@@ -460,10 +496,11 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
newValue,
|
newValue,
|
||||||
this.form.getRawValue().vaultTimeoutAction,
|
this.form.getRawValue().vaultTimeoutAction,
|
||||||
);
|
);
|
||||||
|
this.refreshTimeoutSettings$.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveVaultTimeoutAction(newValue: VaultTimeoutAction) {
|
async saveVaultTimeoutAction(value: VaultTimeoutAction) {
|
||||||
if (newValue === "logOut") {
|
if (value === VaultTimeoutAction.LogOut) {
|
||||||
const confirmed = await this.dialogService.openSimpleDialog({
|
const confirmed = await this.dialogService.openSimpleDialog({
|
||||||
title: { key: "vaultTimeoutLogOutConfirmationTitle" },
|
title: { key: "vaultTimeoutLogOutConfirmationTitle" },
|
||||||
content: { key: "vaultTimeoutLogOutConfirmation" },
|
content: { key: "vaultTimeoutLogOutConfirmation" },
|
||||||
@@ -471,7 +508,7 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
this.form.controls.vaultTimeoutAction.patchValue(VaultTimeoutAction.Lock, {
|
this.form.controls.vaultTimeoutAction.setValue(VaultTimeoutAction.Lock, {
|
||||||
emitEvent: false,
|
emitEvent: false,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@@ -479,11 +516,11 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.form.controls.vaultTimeout.hasError("policyError")) {
|
if (this.form.controls.vaultTimeout.hasError("policyError")) {
|
||||||
this.platformUtilsService.showToast(
|
this.toastService.showToast({
|
||||||
"error",
|
variant: "error",
|
||||||
null,
|
title: null,
|
||||||
this.i18nService.t("vaultTimeoutTooLarge"),
|
message: this.i18nService.t("vaultTimeoutTooLarge"),
|
||||||
);
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,8 +529,9 @@ export class SettingsComponent implements OnInit, OnDestroy {
|
|||||||
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
|
await this.vaultTimeoutSettingsService.setVaultTimeoutOptions(
|
||||||
activeAccount.id,
|
activeAccount.id,
|
||||||
this.form.value.vaultTimeout,
|
this.form.value.vaultTimeout,
|
||||||
newValue,
|
value,
|
||||||
);
|
);
|
||||||
|
this.refreshTimeoutSettings$.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePinHandler(value: boolean) {
|
async updatePinHandler(value: boolean) {
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
<div [formGroup]="form">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="vaultTimeout">{{ "vaultTimeout" | i18n }}</label>
|
|
||||||
<select
|
|
||||||
id="vaultTimeout"
|
|
||||||
name="VaultTimeout"
|
|
||||||
aria-describedby="vaultTimeoutHelp"
|
|
||||||
formControlName="vaultTimeout"
|
|
||||||
class="form-control"
|
|
||||||
>
|
|
||||||
<option *ngFor="let o of vaultTimeoutOptions" [ngValue]="o.value">{{ o.name }}</option>
|
|
||||||
</select>
|
|
||||||
<small id="vaultTimeoutHelp" class="help-block">{{ "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>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { Component } from "@angular/core";
|
|
||||||
import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from "@angular/forms";
|
|
||||||
|
|
||||||
import { VaultTimeoutInputComponent as VaultTimeoutInputComponentBase } from "@bitwarden/auth/angular";
|
|
||||||
|
|
||||||
@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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
standalone: false,
|
|
||||||
})
|
|
||||||
export class VaultTimeoutInputComponent extends VaultTimeoutInputComponentBase {}
|
|
||||||
@@ -19,8 +19,6 @@ import { RemovePasswordComponent } from "../key-management/key-connector/remove-
|
|||||||
import { VaultFilterModule } from "../vault/app/vault/vault-filter/vault-filter.module";
|
import { VaultFilterModule } from "../vault/app/vault/vault-filter/vault-filter.module";
|
||||||
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
|
import { VaultV2Component } from "../vault/app/vault/vault-v2.component";
|
||||||
|
|
||||||
import { SettingsComponent } from "./accounts/settings.component";
|
|
||||||
import { VaultTimeoutInputComponent } from "./accounts/vault-timeout-input.component";
|
|
||||||
import { AppRoutingModule } from "./app-routing.module";
|
import { AppRoutingModule } from "./app-routing.module";
|
||||||
import { AppComponent } from "./app.component";
|
import { AppComponent } from "./app.component";
|
||||||
import { UserVerificationComponent } from "./components/user-verification.component";
|
import { UserVerificationComponent } from "./components/user-verification.component";
|
||||||
@@ -55,8 +53,6 @@ import { SharedModule } from "./shared/shared.module";
|
|||||||
PremiumComponent,
|
PremiumComponent,
|
||||||
RemovePasswordComponent,
|
RemovePasswordComponent,
|
||||||
SearchComponent,
|
SearchComponent,
|
||||||
SettingsComponent,
|
|
||||||
VaultTimeoutInputComponent,
|
|
||||||
],
|
],
|
||||||
providers: [SshAgentService],
|
providers: [SshAgentService],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
|
|||||||
@@ -1225,12 +1225,18 @@
|
|||||||
"twoStepLogin": {
|
"twoStepLogin": {
|
||||||
"message": "Two-step login"
|
"message": "Two-step login"
|
||||||
},
|
},
|
||||||
|
"vaultTimeoutHeader": {
|
||||||
|
"message": "Vault timeout"
|
||||||
|
},
|
||||||
"vaultTimeout": {
|
"vaultTimeout": {
|
||||||
"message": "Vault timeout"
|
"message": "Vault timeout"
|
||||||
},
|
},
|
||||||
"vaultTimeout1": {
|
"vaultTimeout1": {
|
||||||
"message": "Timeout"
|
"message": "Timeout"
|
||||||
},
|
},
|
||||||
|
"vaultTimeoutAction1": {
|
||||||
|
"message": "Timeout action"
|
||||||
|
},
|
||||||
"vaultTimeoutDesc": {
|
"vaultTimeoutDesc": {
|
||||||
"message": "Choose when your vault will take the vault timeout action."
|
"message": "Choose when your vault will take the vault timeout action."
|
||||||
},
|
},
|
||||||
@@ -2511,6 +2517,35 @@
|
|||||||
"vaultTimeoutTooLarge": {
|
"vaultTimeoutTooLarge": {
|
||||||
"message": "Your vault timeout exceeds the restrictions set by your organization."
|
"message": "Your vault timeout exceeds the restrictions set by your organization."
|
||||||
},
|
},
|
||||||
|
"vaultTimeoutPolicyAffectingOptions": {
|
||||||
|
"message": "Enterprise policy requirements have been applied to your timeout options"
|
||||||
|
},
|
||||||
|
"vaultTimeoutPolicyInEffect": {
|
||||||
|
"message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).",
|
||||||
|
"placeholders": {
|
||||||
|
"hours": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "5"
|
||||||
|
},
|
||||||
|
"minutes": {
|
||||||
|
"content": "$2",
|
||||||
|
"example": "5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vaultTimeoutPolicyMaximumError": {
|
||||||
|
"message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum",
|
||||||
|
"placeholders": {
|
||||||
|
"hours": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "5"
|
||||||
|
},
|
||||||
|
"minutes": {
|
||||||
|
"content": "$2",
|
||||||
|
"example": "5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"inviteAccepted": {
|
"inviteAccepted": {
|
||||||
"message": "Invitation accepted"
|
"message": "Invitation accepted"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user