1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-19 17:53:39 +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

@@ -0,0 +1,15 @@
import { VaultTimeout } from "../../vault-timeout";
export abstract class SessionTimeoutTypeService {
/**
* Is provided timeout type available on this client type, OS ?
* @param timeout the timeout type
*/
abstract isAvailable(timeout: VaultTimeout): Promise<boolean>;
/**
* Returns the highest available and permissive timeout type, that is higher than or equals the provided timeout type.
* @param timeout the provided timeout type
*/
abstract getOrPromoteToAvailable(timeout: VaultTimeout): Promise<VaultTimeout>;
}

View File

@@ -0,0 +1,3 @@
export { SessionTimeoutTypeService } from "./abstractions/session-timeout-type.service";
export { MaximumSessionTimeoutPolicyData } from "./types/maximum-session-timeout-policy.type";
export { SessionTimeoutAction, SessionTimeoutType } from "./types/session-timeout.type";

View File

@@ -0,0 +1,7 @@
import { SessionTimeoutAction, SessionTimeoutType } from "./session-timeout.type";
export interface MaximumSessionTimeoutPolicyData {
type?: SessionTimeoutType;
minutes: number;
action?: SessionTimeoutAction;
}

View File

@@ -0,0 +1,8 @@
export type SessionTimeoutAction = null | "lock" | "logOut";
export type SessionTimeoutType =
| null
| "never"
| "onAppRestart"
| "onSystemLock"
| "immediately"
| "custom";

View File

@@ -4,8 +4,9 @@ export { VaultTimeoutService } from "./abstractions/vault-timeout.service";
export { VaultTimeoutService as DefaultVaultTimeoutService } from "./services/vault-timeout.service";
export { VaultTimeoutAction } from "./enums/vault-timeout-action.enum";
export {
isVaultTimeoutTypeNumeric,
VaultTimeout,
VaultTimeoutOption,
VaultTimeoutNumberType,
VaultTimeoutStringType,
} from "./types/vault-timeout.type";
export { MaximumVaultTimeoutPolicyData } from "./types/maximum-vault-timeout-policy.type";

View File

@@ -21,9 +21,14 @@ import { LogService } from "../../../platform/abstractions/log.service";
import { Utils } from "../../../platform/misc/utils";
import { UserId } from "../../../types/guid";
import { PinStateServiceAbstraction } from "../../pin/pin-state.service.abstraction";
import { SessionTimeoutTypeService } from "../../session-timeout";
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "../abstractions/vault-timeout-settings.service";
import { VaultTimeoutAction } from "../enums/vault-timeout-action.enum";
import { VaultTimeout, VaultTimeoutStringType } from "../types/vault-timeout.type";
import {
VaultTimeout,
VaultTimeoutNumberType,
VaultTimeoutStringType,
} from "../types/vault-timeout.type";
import { VaultTimeoutSettingsService } from "./vault-timeout-settings.service";
import { VAULT_TIMEOUT, VAULT_TIMEOUT_ACTION } from "./vault-timeout-settings.state";
@@ -40,9 +45,11 @@ describe("VaultTimeoutSettingsService", () => {
let userDecryptionOptionsSubject: BehaviorSubject<UserDecryptionOptions>;
const defaultVaultTimeout: VaultTimeout = 15; // default web vault timeout
const mockUserId = Utils.newGuid() as UserId;
let stateProvider: FakeStateProvider;
let logService: MockProxy<LogService>;
let sessionTimeoutTypeService: MockProxy<SessionTimeoutTypeService>;
beforeEach(() => {
accountService = mockAccountServiceWith(mockUserId);
@@ -67,8 +74,8 @@ describe("VaultTimeoutSettingsService", () => {
stateProvider = new FakeStateProvider(accountService);
logService = mock<LogService>();
sessionTimeoutTypeService = mock<SessionTimeoutTypeService>();
const defaultVaultTimeout: VaultTimeout = 15; // default web vault timeout
vaultTimeoutSettingsService = createVaultTimeoutSettingsService(defaultVaultTimeout);
biometricStateService.biometricUnlockEnabled$ = of(false);
@@ -259,40 +266,276 @@ describe("VaultTimeoutSettingsService", () => {
);
});
it.each([
// policy, vaultTimeout, expected
[null, null, 15], // no policy, no vault timeout, falls back to default
[30, 90, 30], // policy overrides vault timeout
[30, 15, 15], // policy doesn't override vault timeout when it's within acceptable range
[90, VaultTimeoutStringType.Never, 90], // policy overrides vault timeout when it's "never"
[null, VaultTimeoutStringType.Never, VaultTimeoutStringType.Never], // no policy, persist "never" vault timeout
[90, 0, 0], // policy doesn't override vault timeout when it's 0 (immediate)
[null, 0, 0], // no policy, persist 0 (immediate) vault timeout
[90, VaultTimeoutStringType.OnRestart, 90], // policy overrides vault timeout when it's "onRestart"
[null, VaultTimeoutStringType.OnRestart, VaultTimeoutStringType.OnRestart], // no policy, persist "onRestart" vault timeout
[90, VaultTimeoutStringType.OnLocked, 90], // policy overrides vault timeout when it's "onLocked"
[null, VaultTimeoutStringType.OnLocked, VaultTimeoutStringType.OnLocked], // no policy, persist "onLocked" vault timeout
[90, VaultTimeoutStringType.OnSleep, 90], // policy overrides vault timeout when it's "onSleep"
[null, VaultTimeoutStringType.OnSleep, VaultTimeoutStringType.OnSleep], // no policy, persist "onSleep" vault timeout
[90, VaultTimeoutStringType.OnIdle, 90], // policy overrides vault timeout when it's "onIdle"
[null, VaultTimeoutStringType.OnIdle, VaultTimeoutStringType.OnIdle], // no policy, persist "onIdle" vault timeout
])(
"when policy is %s, and vault timeout is %s, returns %s",
async (policy, vaultTimeout, expected) => {
describe("no policy", () => {
it("when vault timeout is null, returns default", async () => {
userDecryptionOptionsSubject.next(new UserDecryptionOptions({ hasMasterPassword: true }));
policyService.policiesByType$.mockReturnValue(
of(policy === null ? [] : ([{ data: { minutes: policy } }] as unknown as Policy[])),
policyService.policiesByType$.mockReturnValue(of([]));
await stateProvider.setUserState(VAULT_TIMEOUT, null, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(result).toBe(defaultVaultTimeout);
});
it.each([
VaultTimeoutNumberType.Immediately,
VaultTimeoutNumberType.OnMinute,
VaultTimeoutNumberType.EightHours,
VaultTimeoutStringType.Never,
VaultTimeoutStringType.OnRestart,
VaultTimeoutStringType.OnLocked,
VaultTimeoutStringType.OnSleep,
VaultTimeoutStringType.OnIdle,
])("when vault timeout is %s, returns unchanged", async (vaultTimeout) => {
userDecryptionOptionsSubject.next(new UserDecryptionOptions({ hasMasterPassword: true }));
policyService.policiesByType$.mockReturnValue(of([]));
await stateProvider.setUserState(VAULT_TIMEOUT, vaultTimeout, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(result).toBe(expected);
},
);
expect(result).toBe(vaultTimeout);
});
});
describe("policy type: custom", () => {
const policyMinutes = 30;
it.each([
VaultTimeoutNumberType.EightHours,
VaultTimeoutStringType.Never,
VaultTimeoutStringType.OnRestart,
VaultTimeoutStringType.OnLocked,
VaultTimeoutStringType.OnSleep,
VaultTimeoutStringType.OnIdle,
])(
"when vault timeout is %s and exceeds policy max, returns policy minutes",
async (vaultTimeout) => {
userDecryptionOptionsSubject.next(new UserDecryptionOptions({ hasMasterPassword: true }));
policyService.policiesByType$.mockReturnValue(
of([{ data: { type: "custom", minutes: policyMinutes } }] as unknown as Policy[]),
);
await stateProvider.setUserState(VAULT_TIMEOUT, vaultTimeout, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(result).toBe(policyMinutes);
},
);
it.each([VaultTimeoutNumberType.OnMinute, policyMinutes])(
"when vault timeout is %s and within policy max, returns unchanged",
async (vaultTimeout) => {
userDecryptionOptionsSubject.next(new UserDecryptionOptions({ hasMasterPassword: true }));
policyService.policiesByType$.mockReturnValue(
of([{ data: { type: "custom", minutes: policyMinutes } }] as unknown as Policy[]),
);
await stateProvider.setUserState(VAULT_TIMEOUT, vaultTimeout, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(result).toBe(vaultTimeout);
},
);
it("when vault timeout is Immediately, returns Immediately", async () => {
userDecryptionOptionsSubject.next(new UserDecryptionOptions({ hasMasterPassword: true }));
policyService.policiesByType$.mockReturnValue(
of([{ data: { type: "custom", minutes: policyMinutes } }] as unknown as Policy[]),
);
await stateProvider.setUserState(
VAULT_TIMEOUT,
VaultTimeoutNumberType.Immediately,
mockUserId,
);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(result).toBe(VaultTimeoutNumberType.Immediately);
});
});
describe("policy type: immediately", () => {
it.each([
VaultTimeoutStringType.Never,
VaultTimeoutStringType.OnRestart,
VaultTimeoutStringType.OnLocked,
VaultTimeoutStringType.OnIdle,
VaultTimeoutStringType.OnSleep,
VaultTimeoutNumberType.Immediately,
VaultTimeoutNumberType.OnMinute,
VaultTimeoutNumberType.EightHours,
])(
"when current timeout is %s, returns immediately or promoted value",
async (currentTimeout) => {
const expectedTimeout = VaultTimeoutNumberType.Immediately;
sessionTimeoutTypeService.getOrPromoteToAvailable.mockResolvedValue(expectedTimeout);
policyService.policiesByType$.mockReturnValue(
of([{ data: { type: "immediately" } }] as unknown as Policy[]),
);
await stateProvider.setUserState(VAULT_TIMEOUT, currentTimeout, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(sessionTimeoutTypeService.getOrPromoteToAvailable).toHaveBeenCalledWith(
VaultTimeoutNumberType.Immediately,
);
expect(result).toBe(expectedTimeout);
},
);
});
describe("policy type: onSystemLock", () => {
it.each([
VaultTimeoutStringType.Never,
VaultTimeoutStringType.OnRestart,
VaultTimeoutStringType.OnLocked,
VaultTimeoutStringType.OnIdle,
VaultTimeoutStringType.OnSleep,
])(
"when current timeout is %s, returns onLocked or promoted value",
async (currentTimeout) => {
const expectedTimeout = VaultTimeoutStringType.OnLocked;
sessionTimeoutTypeService.getOrPromoteToAvailable.mockResolvedValue(expectedTimeout);
policyService.policiesByType$.mockReturnValue(
of([{ data: { type: "onSystemLock" } }] as unknown as Policy[]),
);
await stateProvider.setUserState(VAULT_TIMEOUT, currentTimeout, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(sessionTimeoutTypeService.getOrPromoteToAvailable).toHaveBeenCalledWith(
VaultTimeoutStringType.OnLocked,
);
expect(result).toBe(expectedTimeout);
},
);
it.each([
VaultTimeoutNumberType.Immediately,
VaultTimeoutNumberType.OnMinute,
VaultTimeoutNumberType.EightHours,
])("when current timeout is numeric %s, returns unchanged", async (currentTimeout) => {
policyService.policiesByType$.mockReturnValue(
of([{ data: { type: "onSystemLock" } }] as unknown as Policy[]),
);
await stateProvider.setUserState(VAULT_TIMEOUT, currentTimeout, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(sessionTimeoutTypeService.getOrPromoteToAvailable).not.toHaveBeenCalled();
expect(result).toBe(currentTimeout);
});
});
describe("policy type: onAppRestart", () => {
it.each([
VaultTimeoutStringType.Never,
VaultTimeoutStringType.OnLocked,
VaultTimeoutStringType.OnIdle,
VaultTimeoutStringType.OnSleep,
])("when current timeout is %s, returns onRestart", async (currentTimeout) => {
policyService.policiesByType$.mockReturnValue(
of([{ data: { type: "onAppRestart" } }] as unknown as Policy[]),
);
await stateProvider.setUserState(VAULT_TIMEOUT, currentTimeout, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(sessionTimeoutTypeService.getOrPromoteToAvailable).not.toHaveBeenCalled();
expect(result).toBe(VaultTimeoutStringType.OnRestart);
});
it.each([
VaultTimeoutStringType.OnRestart,
VaultTimeoutNumberType.Immediately,
VaultTimeoutNumberType.OnMinute,
VaultTimeoutNumberType.EightHours,
])("when current timeout is %s, returns unchanged", async (currentTimeout) => {
policyService.policiesByType$.mockReturnValue(
of([{ data: { type: "onAppRestart" } }] as unknown as Policy[]),
);
await stateProvider.setUserState(VAULT_TIMEOUT, currentTimeout, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(sessionTimeoutTypeService.getOrPromoteToAvailable).not.toHaveBeenCalled();
expect(result).toBe(currentTimeout);
});
});
describe("policy type: never", () => {
it("when current timeout is never, returns never or promoted value", async () => {
const expectedTimeout = VaultTimeoutStringType.Never;
sessionTimeoutTypeService.getOrPromoteToAvailable.mockResolvedValue(expectedTimeout);
policyService.policiesByType$.mockReturnValue(
of([{ data: { type: "never" } }] as unknown as Policy[]),
);
await stateProvider.setUserState(VAULT_TIMEOUT, VaultTimeoutStringType.Never, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(sessionTimeoutTypeService.getOrPromoteToAvailable).toHaveBeenCalledWith(
VaultTimeoutStringType.Never,
);
expect(result).toBe(expectedTimeout);
});
it.each([
VaultTimeoutStringType.OnRestart,
VaultTimeoutStringType.OnLocked,
VaultTimeoutStringType.OnIdle,
VaultTimeoutStringType.OnSleep,
VaultTimeoutNumberType.Immediately,
VaultTimeoutNumberType.OnMinute,
VaultTimeoutNumberType.EightHours,
])("when current timeout is %s, returns unchanged", async (currentTimeout) => {
policyService.policiesByType$.mockReturnValue(
of([{ data: { type: "never" } }] as unknown as Policy[]),
);
await stateProvider.setUserState(VAULT_TIMEOUT, currentTimeout, mockUserId);
const result = await firstValueFrom(
vaultTimeoutSettingsService.getVaultTimeoutByUserId$(mockUserId),
);
expect(sessionTimeoutTypeService.getOrPromoteToAvailable).not.toHaveBeenCalled();
expect(result).toBe(currentTimeout);
});
});
});
describe("setVaultTimeoutOptions", () => {
@@ -405,6 +648,7 @@ describe("VaultTimeoutSettingsService", () => {
stateProvider,
logService,
defaultVaultTimeout,
sessionTimeoutTypeService,
);
}
});

View File

@@ -1,14 +1,15 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import {
EMPTY,
Observable,
catchError,
combineLatest,
defer,
distinctUntilChanged,
EMPTY,
firstValueFrom,
from,
map,
Observable,
shareReplay,
switchMap,
tap,
@@ -23,7 +24,6 @@ import { BiometricStateService, KeyService } from "@bitwarden/key-management";
import { PolicyService } from "../../../admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "../../../admin-console/enums";
import { Policy } from "../../../admin-console/models/domain/policy";
import { getFirstPolicy } from "../../../admin-console/services/policy/default-policy.service";
import { AccountService } from "../../../auth/abstractions/account.service";
import { TokenService } from "../../../auth/abstractions/token.service";
@@ -31,9 +31,15 @@ import { LogService } from "../../../platform/abstractions/log.service";
import { StateProvider } from "../../../platform/state";
import { UserId } from "../../../types/guid";
import { PinStateServiceAbstraction } from "../../pin/pin-state.service.abstraction";
import { MaximumSessionTimeoutPolicyData, SessionTimeoutTypeService } from "../../session-timeout";
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "../abstractions/vault-timeout-settings.service";
import { VaultTimeoutAction } from "../enums/vault-timeout-action.enum";
import { VaultTimeout, VaultTimeoutStringType } from "../types/vault-timeout.type";
import {
isVaultTimeoutTypeNumeric,
VaultTimeout,
VaultTimeoutNumberType,
VaultTimeoutStringType,
} from "../types/vault-timeout.type";
import { VAULT_TIMEOUT, VAULT_TIMEOUT_ACTION } from "./vault-timeout-settings.state";
@@ -49,6 +55,7 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA
private stateProvider: StateProvider,
private logService: LogService,
private defaultVaultTimeout: VaultTimeout,
private sessionTimeoutTypeService: SessionTimeoutTypeService,
) {}
async setVaultTimeoutOptions(
@@ -131,11 +138,25 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA
return combineLatest([
this.stateProvider.getUserState$(VAULT_TIMEOUT, userId),
this.getMaxVaultTimeoutPolicyByUserId$(userId),
this.getMaxSessionTimeoutPolicyDataByUserId$(userId),
]).pipe(
switchMap(([currentVaultTimeout, maxVaultTimeoutPolicy]) => {
return from(this.determineVaultTimeout(currentVaultTimeout, maxVaultTimeoutPolicy)).pipe(
switchMap(([currentVaultTimeout, maxSessionTimeoutPolicyData]) => {
this.logService.debug(
"[VaultTimeoutSettingsService] Current vault timeout is %o for user id %s, max session policy %o",
currentVaultTimeout,
userId,
maxSessionTimeoutPolicyData,
);
return from(
this.determineVaultTimeout(currentVaultTimeout, maxSessionTimeoutPolicyData),
).pipe(
tap((vaultTimeout: VaultTimeout) => {
this.logService.debug(
"[VaultTimeoutSettingsService] Determined vault timeout is %o for user id %s",
vaultTimeout,
userId,
);
// As a side effect, set the new value determined by determineVaultTimeout into state if it's different from the current
if (vaultTimeout !== currentVaultTimeout) {
return this.stateProvider.setUserState(VAULT_TIMEOUT, vaultTimeout, userId);
@@ -155,28 +176,63 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA
private async determineVaultTimeout(
currentVaultTimeout: VaultTimeout | null,
maxVaultTimeoutPolicy: Policy | null,
maxSessionTimeoutPolicyData: MaximumSessionTimeoutPolicyData | null,
): Promise<VaultTimeout | null> {
// if current vault timeout is null, apply the client specific default
currentVaultTimeout = currentVaultTimeout ?? this.defaultVaultTimeout;
// If no policy applies, return the current vault timeout
if (!maxVaultTimeoutPolicy) {
if (maxSessionTimeoutPolicyData == null) {
return currentVaultTimeout;
}
// User is subject to a max vault timeout policy
const maxVaultTimeoutPolicyData = maxVaultTimeoutPolicy.data;
// If the current vault timeout is not numeric, change it to the policy compliant value
if (typeof currentVaultTimeout === "string") {
return maxVaultTimeoutPolicyData.minutes;
switch (maxSessionTimeoutPolicyData.type) {
case "immediately":
return await this.sessionTimeoutTypeService.getOrPromoteToAvailable(
VaultTimeoutNumberType.Immediately,
);
case "custom":
case null:
case undefined:
if (currentVaultTimeout === VaultTimeoutNumberType.Immediately) {
return currentVaultTimeout;
}
if (isVaultTimeoutTypeNumeric(currentVaultTimeout)) {
return Math.min(currentVaultTimeout as number, maxSessionTimeoutPolicyData.minutes);
}
return maxSessionTimeoutPolicyData.minutes;
case "onSystemLock":
if (
currentVaultTimeout === VaultTimeoutStringType.Never ||
currentVaultTimeout === VaultTimeoutStringType.OnRestart ||
currentVaultTimeout === VaultTimeoutStringType.OnLocked ||
currentVaultTimeout === VaultTimeoutStringType.OnIdle ||
currentVaultTimeout === VaultTimeoutStringType.OnSleep
) {
return await this.sessionTimeoutTypeService.getOrPromoteToAvailable(
VaultTimeoutStringType.OnLocked,
);
}
break;
case "onAppRestart":
if (
currentVaultTimeout === VaultTimeoutStringType.Never ||
currentVaultTimeout === VaultTimeoutStringType.OnLocked ||
currentVaultTimeout === VaultTimeoutStringType.OnIdle ||
currentVaultTimeout === VaultTimeoutStringType.OnSleep
) {
return VaultTimeoutStringType.OnRestart;
}
break;
case "never":
if (currentVaultTimeout === VaultTimeoutStringType.Never) {
return await this.sessionTimeoutTypeService.getOrPromoteToAvailable(
VaultTimeoutStringType.Never,
);
}
break;
}
// For numeric vault timeouts, ensure they are smaller than maximum allowed value according to policy
const policyCompliantTimeout = Math.min(currentVaultTimeout, maxVaultTimeoutPolicyData.minutes);
return policyCompliantTimeout;
return currentVaultTimeout;
}
private async setVaultTimeoutAction(userId: UserId, action: VaultTimeoutAction): Promise<void> {
@@ -198,14 +254,14 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA
return combineLatest([
this.stateProvider.getUserState$(VAULT_TIMEOUT_ACTION, userId),
this.getMaxVaultTimeoutPolicyByUserId$(userId),
this.getMaxSessionTimeoutPolicyDataByUserId$(userId),
]).pipe(
switchMap(([currentVaultTimeoutAction, maxVaultTimeoutPolicy]) => {
switchMap(([currentVaultTimeoutAction, maxSessionTimeoutPolicyData]) => {
return from(
this.determineVaultTimeoutAction(
userId,
currentVaultTimeoutAction,
maxVaultTimeoutPolicy,
maxSessionTimeoutPolicyData,
),
).pipe(
tap((vaultTimeoutAction: VaultTimeoutAction) => {
@@ -235,7 +291,7 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA
private async determineVaultTimeoutAction(
userId: string,
currentVaultTimeoutAction: VaultTimeoutAction | null,
maxVaultTimeoutPolicy: Policy | null,
maxSessionTimeoutPolicyData: MaximumSessionTimeoutPolicyData | null,
): Promise<VaultTimeoutAction> {
const availableVaultTimeoutActions = await this.getAvailableVaultTimeoutActions(userId);
if (availableVaultTimeoutActions.length === 1) {
@@ -243,11 +299,13 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA
}
if (
maxVaultTimeoutPolicy?.data?.action &&
availableVaultTimeoutActions.includes(maxVaultTimeoutPolicy.data.action)
maxSessionTimeoutPolicyData?.action &&
availableVaultTimeoutActions.includes(
maxSessionTimeoutPolicyData.action as VaultTimeoutAction,
)
) {
// return policy defined vault timeout action
return maxVaultTimeoutPolicy.data.action;
// return policy defined session timeout action
return maxSessionTimeoutPolicyData.action as VaultTimeoutAction;
}
// No policy applies from here on
@@ -262,14 +320,17 @@ export class VaultTimeoutSettingsService implements VaultTimeoutSettingsServiceA
return currentVaultTimeoutAction;
}
private getMaxVaultTimeoutPolicyByUserId$(userId: UserId): Observable<Policy | null> {
private getMaxSessionTimeoutPolicyDataByUserId$(
userId: UserId,
): Observable<MaximumSessionTimeoutPolicyData | null> {
if (!userId) {
throw new Error("User id required. Cannot get max vault timeout policy.");
throw new Error("User id required. Cannot get max session timeout policy.");
}
return this.policyService
.policiesByType$(PolicyType.MaximumVaultTimeout, userId)
.pipe(getFirstPolicy);
return this.policyService.policiesByType$(PolicyType.MaximumVaultTimeout, userId).pipe(
getFirstPolicy,
map((policy) => (policy?.data ?? null) as MaximumSessionTimeoutPolicyData | null),
);
}
private async getAvailableVaultTimeoutActions(userId?: string): Promise<VaultTimeoutAction[]> {

View File

@@ -1,6 +0,0 @@
import { VaultTimeoutAction } from "../enums/vault-timeout-action.enum";
export interface MaximumVaultTimeoutPolicyData {
minutes: number;
action?: VaultTimeoutAction;
}

View File

@@ -5,13 +5,25 @@ export const VaultTimeoutStringType = {
OnLocked: "onLocked", // -2
OnSleep: "onSleep", // -3
OnIdle: "onIdle", // -4
Custom: "custom", // -100
} as const;
export const VaultTimeoutNumberType = {
Immediately: 0,
OnMinute: 1,
EightHours: 480,
} as const;
export type VaultTimeout =
| number // 0 or positive numbers only
| (typeof VaultTimeoutNumberType)[keyof typeof VaultTimeoutNumberType]
| number // 0 or positive numbers (in minutes). See VaultTimeoutNumberType for common numeric presets
| (typeof VaultTimeoutStringType)[keyof typeof VaultTimeoutStringType];
export interface VaultTimeoutOption {
name: string;
value: VaultTimeout;
}
export function isVaultTimeoutTypeNumeric(timeout: VaultTimeout): boolean {
return typeof timeout === "number";
}