1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[PM-26057] Enforce session timeout policy (#17424)

* enforce session timeout policy

* better angular validation

* lint fix

* missing switch break

* fallback when timeout not supported with highest available timeout

* failing unit tests

* incorrect policy message

* vault timeout type adjustments

* fallback to "on browser refresh" for browser, when policy is set to "on system locked", but not available (Safari)

* docs, naming improvements

* fallback for current user session timeout to "on refresh", when policy is set to "on system locked", but not available.

* don't display policy message when the policy does not affect available timeout options

* 8 hours default when changing from non-numeric timeout to Custom.

* failing unit test

* missing locales, changing functions access to private, docs

* removal of redundant magic number

* missing await

* await once for available timeout options

* adjusted messaging

* unit test coverage

* vault timeout numeric module exports

* unit test coverage
This commit is contained in:
Maciej Zieniuk
2025-12-05 14:55:59 +01:00
committed by GitHub
parent c036ffd775
commit bbea11388a
48 changed files with 3344 additions and 569 deletions

View File

@@ -7,16 +7,16 @@ import { mock } from "jest-mock-extended";
import { Observable, of } from "rxjs";
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
import {
SessionTimeoutAction,
SessionTimeoutType,
} from "@bitwarden/common/key-management/session-timeout";
import { VaultTimeoutAction } from "@bitwarden/common/key-management/vault-timeout";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogRef, DialogService } from "@bitwarden/components";
import { SessionTimeoutConfirmationNeverComponent } from "./session-timeout-confirmation-never.component";
import {
SessionTimeoutAction,
SessionTimeoutPolicyComponent,
SessionTimeoutType,
} from "./session-timeout.component";
import { SessionTimeoutPolicyComponent } from "./session-timeout.component";
// Mock DialogRef, so we can mock "readonly closed" property.
class MockDialogRef extends DialogRef {

View File

@@ -10,26 +10,22 @@ import {
} from "rxjs";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import {
MaximumSessionTimeoutPolicyData,
SessionTimeoutAction,
SessionTimeoutType,
} from "@bitwarden/common/key-management/session-timeout";
import { VaultTimeoutAction } from "@bitwarden/common/key-management/vault-timeout";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { DialogService } from "@bitwarden/components";
import {
BasePolicyEditDefinition,
BasePolicyEditComponent,
BasePolicyEditDefinition,
} from "@bitwarden/web-vault/app/admin-console/organizations/policies";
import { SharedModule } from "@bitwarden/web-vault/app/shared";
import { SessionTimeoutConfirmationNeverComponent } from "./session-timeout-confirmation-never.component";
export type SessionTimeoutAction = null | "lock" | "logOut";
export type SessionTimeoutType =
| null
| "never"
| "onAppRestart"
| "onSystemLock"
| "immediately"
| "custom";
export class SessionTimeoutPolicy extends BasePolicyEditDefinition {
name = "sessionTimeoutPolicyTitle";
description = "sessionTimeoutPolicyDescription";
@@ -50,9 +46,6 @@ export class SessionTimeoutPolicyComponent
extends BasePolicyEditComponent
implements OnInit, OnDestroy
{
private destroy$ = new Subject<void>();
private lastConfirmedType$ = new BehaviorSubject<SessionTimeoutType>(null);
actionOptions: { name: string; value: SessionTimeoutAction }[];
typeOptions: { name: string; value: SessionTimeoutType }[];
data = this.formBuilder.group({
@@ -74,6 +67,9 @@ export class SessionTimeoutPolicyComponent
action: new FormControl<SessionTimeoutAction>(null),
});
private destroy$ = new Subject<void>();
private lastConfirmedType$ = new BehaviorSubject<SessionTimeoutType>(null);
constructor(
private formBuilder: FormBuilder,
private i18nService: I18nService,
@@ -123,12 +119,10 @@ export class SessionTimeoutPolicyComponent
}
protected override loadData() {
const minutes: number | null = this.policyResponse?.data?.minutes ?? null;
const action: SessionTimeoutAction =
this.policyResponse?.data?.action ?? (null satisfies SessionTimeoutAction);
const minutes: number | null = this.policyData?.minutes ?? null;
const action: SessionTimeoutAction = this.policyData?.action ?? null;
// For backward compatibility, the "type" field might not exist, hence we initialize it based on the presence of "minutes"
const type: SessionTimeoutType =
this.policyResponse?.data?.type ?? ((minutes ? "custom" : null) satisfies SessionTimeoutType);
const type: SessionTimeoutType = this.policyData?.type ?? (minutes ? "custom" : null);
this.updateFormControls(type);
this.data.patchValue({
@@ -165,7 +159,11 @@ export class SessionTimeoutPolicyComponent
type,
minutes,
action: this.data.value.action,
};
} satisfies MaximumSessionTimeoutPolicyData;
}
private get policyData(): MaximumSessionTimeoutPolicyData | null {
return this.policyResponse?.data ?? null;
}
private async confirmTypeChange(newType: SessionTimeoutType): Promise<boolean> {