mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
[PM-6426] Create TaskSchedulerService and update long lived timeouts in the extension to leverage the new service (#8566)
* [PM-6426] Create TaskSchedulerService and update usage of long lived timeouts * [PM-6426] Implementing nextSync timeout using TaskScheduler * [PM-6426] Implementing systemClearClipboard using TaskScheduler * [PM-6426] Fixing race condition with setting/unsetting active alarms * [PM-6426] Implementing clear clipboard call on generatePasswordToClipboard with the TaskSchedulerService * [PM-6426] Implementing abortTimeout for Fido2ClientService using TaskSchedulerService * [PM-6426] Implementing reconnect timer timeout for NotificationService using the TaskSchedulerService * [PM-6426] Implementing reconnect timer timeout for NotificationService using the TaskSchedulerService * [PM-6426] Implementing sessionTimeout for LoginStrategyService using TaskSchedulerService * [PM-6426] Implementing eventUploadInterval using TaskScheduler * [PM-6426] Adding jest tests for the base TaskSchedulerService class * [PM-6426] Updating jest tests for GeneratePasswordToClipboardCommand * [PM-6426] Setting up the full sync process as an interval rather than a timeout * [PM-6426] Renaming the scheduleNextSync alarm name * [PM-6426] Fixing dependency references in services.module.ts * [PM-6426] Adding jest tests for added BrowserApi methods * [PM-6426] Refactoring small detail for how we identify the clear clipboard timeout in SystemService * [PM-6426] Ensuring that we await clearing an established scheduled task for the notification service * [PM-6426] Changing the name of the state definition for the TaskScheduler * [PM-6426] Implementing jest tests for the BrowserTaskSchedulerService * [PM-6426] Implementing jest tests for the BrowserTaskSchedulerService * [PM-6426] Adding jest tests for the base TaskSchedulerService class * [PM-6426] Finalizing jest tests for BrowserTaskScheduler class * [PM-6426] Finalizing documentation on BrowserTaskSchedulerService * [PM-6426] Fixing jest test for LoginStrategyService * [PM-6426] Implementing compatibility for the browser.alarms api * [PM-6426] Fixing how we check for the browser alarms api * [PM-6426] Adding jest tests to the BrowserApi implementation * [PM-6426] Aligning the implementation with our code guidelines for Angular components * [PM-6426] Fixing jest tests and lint errors * [PM-6426] Moving alarms api calls out of BrowserApi and structuring them within the BrowserTaskSchedulerService * [PM-6426] Reworking implementation to register handlers separately from the call to those handlers * [PM-6426] Adjusting how we register the fullSync scheduled task * [PM-6426] Implementing approach for incorporating the user UUID when setting task handlers * [PM-6426] Attempting to re-work implementation to facilitate userId-spcific alarms * [PM-6426] Refactoring smaller details of the implementation * [PM-6426] Working through the details of the implementation and setting up final refinments * [PM-6426] Fixing some issues surrounding duplicate alarms triggering * [PM-6426] Adjusting name for generate password to clipboard command task name * [PM-6426] Fixing generate password to clipboard command jest tests * [PM-6426] Working through jest tests and implementing a method to guard against setting a task without having a registered callback * [PM-6426] Working through jest tests and implementing a method to guard against setting a task without having a registered callback * [PM-6426] Implementing methodology for having a fallback to setTimeout if the browser context is lost in some manner * [PM-6426] Working through jest tests * [PM-6426] Working through jest tests * [PM-6426] Working through jest tests * [PM-6426] Working through jest tests * [PM-6426] Finalizing stepped setInterval implementation * [PM-6426] Implementing Jest tests for DefaultTaskSchedulerService * [PM-6426] Adjusting jest tests * [PM-6426] Adjusting jest tests * [PM-6426] Adjusting jest tests * [PM-6426] Fixing issues identified in code review * [PM-6426] Fixing issues identified in code review * [PM-6426] Removing user-based alarms and fixing an issue found with setting steppedd alarm interavals * [PM-6426] Removing user-based alarms and fixing an issue found with setting steppedd alarm interavals * [PM-6426] Fixing issue with typing information on a test * [PM-6426] Using the getUpperBoundDelayInMinutes method to handle setting stepped alarms and setTimeout fallbacks * [PM-6426] Removing the potential for the TaskScheduler to be optional * [PM-6426] Reworking implementation to leverage subscription based deregistration of alarms * [PM-6426] Fixing jest tests * [PM-6426] Implementing foreground and background task scheduler services to avoid duplication of task scheudlers and to have the background setup as a fallback to the poopup tasks * [PM-6426] Implementing foreground and background task scheduler services to avoid duplication of task scheudlers and to have the background setup as a fallback to the poopup tasks * [PM-6426] Merging main into branch * [PM-6426] Fixing issues with the CLI Service Container implementation * [PM-6426] Reworking swallowed promises to contain a catch statement allow us to debug potential issues with registrations of alarms * [PM-6426] Adding jest tests to the ForegroundTaskSchedulerService and the BackgroundTaskSchedulerService * [PM-6426] Adding jest tests to the ForegroundTaskSchedulerService and the BackgroundTaskSchedulerService * [PM-6426] Adding jest tests to the ForegroundTaskSchedulerService and the BackgroundTaskSchedulerService * [PM-6426] Adding jest tests to the ForegroundTaskSchedulerService and the BackgroundTaskSchedulerService * [PM-6426] Adjusting implementation based on code review feedback * [PM-6426] Reworking file structure * [PM-6426] Reworking file structure * [PM-6426] Adding comments to provide clarity on how the login strategy cache experiation state is used * [PM-6426] Catching and logging erorrs that appear from methods that return a promise within VaultTimeoutService
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import { ScheduledTaskNames } from "../scheduling/scheduled-task-name.enum";
|
||||
|
||||
import { DefaultTaskSchedulerService } from "./default-task-scheduler.service";
|
||||
|
||||
describe("DefaultTaskSchedulerService", () => {
|
||||
const callback = jest.fn();
|
||||
const delayInMs = 1000;
|
||||
const intervalInMs = 1100;
|
||||
let logService: MockProxy<LogService>;
|
||||
let taskSchedulerService: DefaultTaskSchedulerService;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
logService = mock<LogService>();
|
||||
taskSchedulerService = new DefaultTaskSchedulerService(logService);
|
||||
taskSchedulerService.registerTaskHandler(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
callback,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it("triggers an error when setting a timeout for a task that is not registered", async () => {
|
||||
expect(() =>
|
||||
taskSchedulerService.setTimeout(ScheduledTaskNames.notificationsReconnectTimeout, 1000),
|
||||
).toThrow(
|
||||
`Task handler for ${ScheduledTaskNames.notificationsReconnectTimeout} not registered. Unable to schedule task.`,
|
||||
);
|
||||
});
|
||||
|
||||
it("triggers an error when setting an interval for a task that is not registered", async () => {
|
||||
expect(() =>
|
||||
taskSchedulerService.setInterval(ScheduledTaskNames.notificationsReconnectTimeout, 1000),
|
||||
).toThrow(
|
||||
`Task handler for ${ScheduledTaskNames.notificationsReconnectTimeout} not registered. Unable to schedule task.`,
|
||||
);
|
||||
});
|
||||
|
||||
it("overrides the handler for a previously registered task and provides a warning about the task registration", () => {
|
||||
taskSchedulerService.registerTaskHandler(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
callback,
|
||||
);
|
||||
|
||||
expect(logService.warning).toHaveBeenCalledWith(
|
||||
`Task handler for ${ScheduledTaskNames.loginStrategySessionTimeout} already exists. Overwriting.`,
|
||||
);
|
||||
expect(
|
||||
taskSchedulerService["taskHandlers"].get(ScheduledTaskNames.loginStrategySessionTimeout),
|
||||
).toBeDefined();
|
||||
});
|
||||
|
||||
it("sets a timeout and returns the timeout id", () => {
|
||||
const timeoutId = taskSchedulerService.setTimeout(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
delayInMs,
|
||||
);
|
||||
|
||||
expect(timeoutId).toBeDefined();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
jest.advanceTimersByTime(delayInMs);
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("sets an interval timeout and results the interval id", () => {
|
||||
const intervalId = taskSchedulerService.setInterval(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
intervalInMs,
|
||||
);
|
||||
|
||||
expect(intervalId).toBeDefined();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
jest.advanceTimersByTime(intervalInMs);
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
|
||||
jest.advanceTimersByTime(intervalInMs);
|
||||
|
||||
expect(callback).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("clears scheduled tasks using the timeout id", () => {
|
||||
const timeoutHandle = taskSchedulerService.setTimeout(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
delayInMs,
|
||||
);
|
||||
|
||||
expect(timeoutHandle).toBeDefined();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
timeoutHandle.unsubscribe();
|
||||
|
||||
jest.advanceTimersByTime(delayInMs);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("clears scheduled tasks using the interval id", () => {
|
||||
const intervalHandle = taskSchedulerService.setInterval(
|
||||
ScheduledTaskNames.loginStrategySessionTimeout,
|
||||
intervalInMs,
|
||||
);
|
||||
|
||||
expect(intervalHandle).toBeDefined();
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
intervalHandle.unsubscribe();
|
||||
|
||||
jest.advanceTimersByTime(intervalInMs);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
import { Subscription } from "rxjs";
|
||||
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import { ScheduledTaskName } from "../scheduling/scheduled-task-name.enum";
|
||||
import { TaskSchedulerService } from "../scheduling/task-scheduler.service";
|
||||
|
||||
export class DefaultTaskSchedulerService extends TaskSchedulerService {
|
||||
constructor(protected logService: LogService) {
|
||||
super();
|
||||
|
||||
this.taskHandlers = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a timeout and returns the timeout id.
|
||||
*
|
||||
* @param taskName - The name of the task. Unused in the base implementation.
|
||||
* @param delayInMs - The delay in milliseconds.
|
||||
*/
|
||||
setTimeout(taskName: ScheduledTaskName, delayInMs: number): Subscription {
|
||||
this.validateRegisteredTask(taskName);
|
||||
|
||||
const timeoutHandle = globalThis.setTimeout(() => this.triggerTask(taskName), delayInMs);
|
||||
return new Subscription(() => globalThis.clearTimeout(timeoutHandle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an interval and returns the interval id.
|
||||
*
|
||||
* @param taskName - The name of the task. Unused in the base implementation.
|
||||
* @param intervalInMs - The interval in milliseconds.
|
||||
* @param _initialDelayInMs - The initial delay in milliseconds. Unused in the base implementation.
|
||||
*/
|
||||
setInterval(
|
||||
taskName: ScheduledTaskName,
|
||||
intervalInMs: number,
|
||||
_initialDelayInMs?: number,
|
||||
): Subscription {
|
||||
this.validateRegisteredTask(taskName);
|
||||
|
||||
const intervalHandle = globalThis.setInterval(() => this.triggerTask(taskName), intervalInMs);
|
||||
|
||||
return new Subscription(() => globalThis.clearInterval(intervalHandle));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a task handler.
|
||||
*
|
||||
* @param taskName - The name of the task.
|
||||
* @param handler - The task handler.
|
||||
*/
|
||||
registerTaskHandler(taskName: ScheduledTaskName, handler: () => void) {
|
||||
const existingHandler = this.taskHandlers.get(taskName);
|
||||
if (existingHandler) {
|
||||
this.logService.warning(`Task handler for ${taskName} already exists. Overwriting.`);
|
||||
this.unregisterTaskHandler(taskName);
|
||||
}
|
||||
|
||||
this.taskHandlers.set(taskName, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a task handler.
|
||||
*
|
||||
* @param taskName - The name of the task.
|
||||
*/
|
||||
unregisterTaskHandler(taskName: ScheduledTaskName) {
|
||||
this.taskHandlers.delete(taskName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a task.
|
||||
*
|
||||
* @param taskName - The name of the task.
|
||||
* @param _periodInMinutes - The period in minutes. Unused in the base implementation.
|
||||
*/
|
||||
protected async triggerTask(
|
||||
taskName: ScheduledTaskName,
|
||||
_periodInMinutes?: number,
|
||||
): Promise<void> {
|
||||
const handler = this.taskHandlers.get(taskName);
|
||||
if (handler) {
|
||||
handler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a task handler is registered.
|
||||
*
|
||||
* @param taskName - The name of the task.
|
||||
*/
|
||||
protected validateRegisteredTask(taskName: ScheduledTaskName): void {
|
||||
if (!this.taskHandlers.has(taskName)) {
|
||||
throw new Error(`Task handler for ${taskName} not registered. Unable to schedule task.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
libs/common/src/platform/scheduling/index.ts
Normal file
3
libs/common/src/platform/scheduling/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { TaskSchedulerService } from "./task-scheduler.service";
|
||||
export { DefaultTaskSchedulerService } from "./default-task-scheduler.service";
|
||||
export { ScheduledTaskNames, ScheduledTaskName } from "./scheduled-task-name.enum";
|
||||
@@ -0,0 +1,12 @@
|
||||
export const ScheduledTaskNames = {
|
||||
generatePasswordClearClipboardTimeout: "generatePasswordClearClipboardTimeout",
|
||||
systemClearClipboardTimeout: "systemClearClipboardTimeout",
|
||||
loginStrategySessionTimeout: "loginStrategySessionTimeout",
|
||||
notificationsReconnectTimeout: "notificationsReconnectTimeout",
|
||||
fido2ClientAbortTimeout: "fido2ClientAbortTimeout",
|
||||
scheduleNextSyncInterval: "scheduleNextSyncInterval",
|
||||
eventUploadsInterval: "eventUploadsInterval",
|
||||
vaultTimeoutCheckInterval: "vaultTimeoutCheckInterval",
|
||||
} as const;
|
||||
|
||||
export type ScheduledTaskName = (typeof ScheduledTaskNames)[keyof typeof ScheduledTaskNames];
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Subscription } from "rxjs";
|
||||
|
||||
import { ScheduledTaskName } from "./scheduled-task-name.enum";
|
||||
|
||||
export abstract class TaskSchedulerService {
|
||||
protected taskHandlers: Map<string, () => void>;
|
||||
abstract setTimeout(taskName: ScheduledTaskName, delayInMs: number): Subscription;
|
||||
abstract setInterval(
|
||||
taskName: ScheduledTaskName,
|
||||
intervalInMs: number,
|
||||
initialDelayInMs?: number,
|
||||
): Subscription;
|
||||
abstract registerTaskHandler(taskName: ScheduledTaskName, handler: () => void): void;
|
||||
abstract unregisterTaskHandler(taskName: ScheduledTaskName): void;
|
||||
protected abstract triggerTask(taskName: ScheduledTaskName, periodInMinutes?: number): void;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { of } from "rxjs";
|
||||
import { AuthService } from "../../../auth/abstractions/auth.service";
|
||||
import { AuthenticationStatus } from "../../../auth/enums/authentication-status";
|
||||
import { DomainSettingsService } from "../../../autofill/services/domain-settings.service";
|
||||
import { Utils } from "../../../platform/misc/utils";
|
||||
import { VaultSettingsService } from "../../../vault/abstractions/vault-settings/vault-settings.service";
|
||||
import { ConfigService } from "../../abstractions/config/config.service";
|
||||
import {
|
||||
@@ -17,7 +18,7 @@ import {
|
||||
CreateCredentialParams,
|
||||
FallbackRequestedError,
|
||||
} from "../../abstractions/fido2/fido2-client.service.abstraction";
|
||||
import { Utils } from "../../misc/utils";
|
||||
import { TaskSchedulerService } from "../../scheduling/task-scheduler.service";
|
||||
|
||||
import * as DomainUtils from "./domain-utils";
|
||||
import { Fido2AuthenticatorService } from "./fido2-authenticator.service";
|
||||
@@ -35,6 +36,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
let authService!: MockProxy<AuthService>;
|
||||
let vaultSettingsService: MockProxy<VaultSettingsService>;
|
||||
let domainSettingsService: MockProxy<DomainSettingsService>;
|
||||
let taskSchedulerService: MockProxy<TaskSchedulerService>;
|
||||
let client!: Fido2ClientService;
|
||||
let tab!: chrome.tabs.Tab;
|
||||
let isValidRpId!: jest.SpyInstance;
|
||||
@@ -45,6 +47,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
authService = mock<AuthService>();
|
||||
vaultSettingsService = mock<VaultSettingsService>();
|
||||
domainSettingsService = mock<DomainSettingsService>();
|
||||
taskSchedulerService = mock<TaskSchedulerService>();
|
||||
|
||||
isValidRpId = jest.spyOn(DomainUtils, "isValidRpId");
|
||||
|
||||
@@ -54,6 +57,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
authService,
|
||||
vaultSettingsService,
|
||||
domainSettingsService,
|
||||
taskSchedulerService,
|
||||
);
|
||||
configService.serverConfig$ = of({ environment: { vault: VaultUrl } } as any);
|
||||
vaultSettingsService.enablePasskeys$ = of(true);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { firstValueFrom, Subscription } from "rxjs";
|
||||
import { parse } from "tldts";
|
||||
|
||||
import { AuthService } from "../../../auth/abstractions/auth.service";
|
||||
@@ -27,6 +27,8 @@ import {
|
||||
} from "../../abstractions/fido2/fido2-client.service.abstraction";
|
||||
import { LogService } from "../../abstractions/log.service";
|
||||
import { Utils } from "../../misc/utils";
|
||||
import { ScheduledTaskNames } from "../../scheduling/scheduled-task-name.enum";
|
||||
import { TaskSchedulerService } from "../../scheduling/task-scheduler.service";
|
||||
|
||||
import { isValidRpId } from "./domain-utils";
|
||||
import { Fido2Utils } from "./fido2-utils";
|
||||
@@ -38,14 +40,33 @@ import { Fido2Utils } from "./fido2-utils";
|
||||
* It is highly recommended that the W3C specification is used a reference when reading this code.
|
||||
*/
|
||||
export class Fido2ClientService implements Fido2ClientServiceAbstraction {
|
||||
private timeoutAbortController: AbortController;
|
||||
private readonly TIMEOUTS = {
|
||||
NO_VERIFICATION: {
|
||||
DEFAULT: 120000,
|
||||
MIN: 30000,
|
||||
MAX: 180000,
|
||||
},
|
||||
WITH_VERIFICATION: {
|
||||
DEFAULT: 300000,
|
||||
MIN: 30000,
|
||||
MAX: 600000,
|
||||
},
|
||||
};
|
||||
|
||||
constructor(
|
||||
private authenticator: Fido2AuthenticatorService,
|
||||
private configService: ConfigService,
|
||||
private authService: AuthService,
|
||||
private vaultSettingsService: VaultSettingsService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private taskSchedulerService: TaskSchedulerService,
|
||||
private logService?: LogService,
|
||||
) {}
|
||||
) {
|
||||
this.taskSchedulerService.registerTaskHandler(ScheduledTaskNames.fido2ClientAbortTimeout, () =>
|
||||
this.timeoutAbortController?.abort(),
|
||||
);
|
||||
}
|
||||
|
||||
async isFido2FeatureEnabled(hostname: string, origin: string): Promise<boolean> {
|
||||
const isUserLoggedIn =
|
||||
@@ -161,7 +182,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
|
||||
this.logService?.info(`[Fido2Client] Aborted with AbortController`);
|
||||
throw new DOMException("The operation either timed out or was not allowed.", "AbortError");
|
||||
}
|
||||
const timeout = setAbortTimeout(
|
||||
const timeoutSubscription = this.setAbortTimeout(
|
||||
abortController,
|
||||
params.authenticatorSelection?.userVerification,
|
||||
params.timeout,
|
||||
@@ -210,7 +231,8 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
|
||||
};
|
||||
}
|
||||
|
||||
clearTimeout(timeout);
|
||||
timeoutSubscription?.unsubscribe();
|
||||
|
||||
return {
|
||||
credentialId: Fido2Utils.bufferToString(makeCredentialResult.credentialId),
|
||||
attestationObject: Fido2Utils.bufferToString(makeCredentialResult.attestationObject),
|
||||
@@ -273,7 +295,11 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
|
||||
throw new DOMException("The operation either timed out or was not allowed.", "AbortError");
|
||||
}
|
||||
|
||||
const timeout = setAbortTimeout(abortController, params.userVerification, params.timeout);
|
||||
const timeoutSubscription = this.setAbortTimeout(
|
||||
abortController,
|
||||
params.userVerification,
|
||||
params.timeout,
|
||||
);
|
||||
|
||||
let getAssertionResult;
|
||||
try {
|
||||
@@ -310,7 +336,8 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
|
||||
this.logService?.info(`[Fido2Client] Aborted with AbortController`);
|
||||
throw new DOMException("The operation either timed out or was not allowed.", "AbortError");
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
|
||||
timeoutSubscription?.unsubscribe();
|
||||
|
||||
return {
|
||||
authenticatorData: Fido2Utils.bufferToString(getAssertionResult.authenticatorData),
|
||||
@@ -323,43 +350,29 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction {
|
||||
signature: Fido2Utils.bufferToString(getAssertionResult.signature),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const TIMEOUTS = {
|
||||
NO_VERIFICATION: {
|
||||
DEFAULT: 120000,
|
||||
MIN: 30000,
|
||||
MAX: 180000,
|
||||
},
|
||||
WITH_VERIFICATION: {
|
||||
DEFAULT: 300000,
|
||||
MIN: 30000,
|
||||
MAX: 600000,
|
||||
},
|
||||
};
|
||||
private setAbortTimeout = (
|
||||
abortController: AbortController,
|
||||
userVerification?: UserVerification,
|
||||
timeout?: number,
|
||||
): Subscription => {
|
||||
let clampedTimeout: number;
|
||||
|
||||
function setAbortTimeout(
|
||||
abortController: AbortController,
|
||||
userVerification?: UserVerification,
|
||||
timeout?: number,
|
||||
): number {
|
||||
let clampedTimeout: number;
|
||||
const { WITH_VERIFICATION, NO_VERIFICATION } = this.TIMEOUTS;
|
||||
if (userVerification === "required") {
|
||||
timeout = timeout ?? WITH_VERIFICATION.DEFAULT;
|
||||
clampedTimeout = Math.max(WITH_VERIFICATION.MIN, Math.min(timeout, WITH_VERIFICATION.MAX));
|
||||
} else {
|
||||
timeout = timeout ?? NO_VERIFICATION.DEFAULT;
|
||||
clampedTimeout = Math.max(NO_VERIFICATION.MIN, Math.min(timeout, NO_VERIFICATION.MAX));
|
||||
}
|
||||
|
||||
if (userVerification === "required") {
|
||||
timeout = timeout ?? TIMEOUTS.WITH_VERIFICATION.DEFAULT;
|
||||
clampedTimeout = Math.max(
|
||||
TIMEOUTS.WITH_VERIFICATION.MIN,
|
||||
Math.min(timeout, TIMEOUTS.WITH_VERIFICATION.MAX),
|
||||
this.timeoutAbortController = abortController;
|
||||
return this.taskSchedulerService.setTimeout(
|
||||
ScheduledTaskNames.fido2ClientAbortTimeout,
|
||||
clampedTimeout,
|
||||
);
|
||||
} else {
|
||||
timeout = timeout ?? TIMEOUTS.NO_VERIFICATION.DEFAULT;
|
||||
clampedTimeout = Math.max(
|
||||
TIMEOUTS.NO_VERIFICATION.MIN,
|
||||
Math.min(timeout, TIMEOUTS.NO_VERIFICATION.MAX),
|
||||
);
|
||||
}
|
||||
|
||||
return self.setTimeout(() => abortController.abort(), clampedTimeout);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { firstValueFrom, map, timeout } from "rxjs";
|
||||
import { firstValueFrom, map, Subscription, timeout } from "rxjs";
|
||||
|
||||
import { PinServiceAbstraction } from "../../../../auth/src/common/abstractions";
|
||||
import { VaultTimeoutSettingsService } from "../../abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
@@ -13,10 +13,12 @@ import { PlatformUtilsService } from "../abstractions/platform-utils.service";
|
||||
import { SystemService as SystemServiceAbstraction } from "../abstractions/system.service";
|
||||
import { BiometricStateService } from "../biometrics/biometric-state.service";
|
||||
import { Utils } from "../misc/utils";
|
||||
import { ScheduledTaskNames } from "../scheduling/scheduled-task-name.enum";
|
||||
import { TaskSchedulerService } from "../scheduling/task-scheduler.service";
|
||||
|
||||
export class SystemService implements SystemServiceAbstraction {
|
||||
private reloadInterval: any = null;
|
||||
private clearClipboardTimeout: any = null;
|
||||
private clearClipboardTimeoutSubscription: Subscription;
|
||||
private clearClipboardTimeoutFunction: () => Promise<any> = null;
|
||||
|
||||
constructor(
|
||||
@@ -28,7 +30,13 @@ export class SystemService implements SystemServiceAbstraction {
|
||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||
private biometricStateService: BiometricStateService,
|
||||
private accountService: AccountService,
|
||||
) {}
|
||||
private taskSchedulerService: TaskSchedulerService,
|
||||
) {
|
||||
this.taskSchedulerService.registerTaskHandler(
|
||||
ScheduledTaskNames.systemClearClipboardTimeout,
|
||||
() => this.clearPendingClipboard(),
|
||||
);
|
||||
}
|
||||
|
||||
async startProcessReload(authService: AuthService): Promise<void> {
|
||||
const accounts = await firstValueFrom(this.accountService.accounts$);
|
||||
@@ -111,25 +119,22 @@ export class SystemService implements SystemServiceAbstraction {
|
||||
}
|
||||
|
||||
async clearClipboard(clipboardValue: string, timeoutMs: number = null): Promise<void> {
|
||||
if (this.clearClipboardTimeout != null) {
|
||||
clearTimeout(this.clearClipboardTimeout);
|
||||
this.clearClipboardTimeout = null;
|
||||
}
|
||||
this.clearClipboardTimeoutSubscription?.unsubscribe();
|
||||
|
||||
if (Utils.isNullOrWhitespace(clipboardValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const clearClipboardDelay = await firstValueFrom(
|
||||
this.autofillSettingsService.clearClipboardDelay$,
|
||||
);
|
||||
|
||||
if (clearClipboardDelay == null) {
|
||||
return;
|
||||
let taskTimeoutInMs = timeoutMs;
|
||||
if (!taskTimeoutInMs) {
|
||||
const clearClipboardDelayInSeconds = await firstValueFrom(
|
||||
this.autofillSettingsService.clearClipboardDelay$,
|
||||
);
|
||||
taskTimeoutInMs = clearClipboardDelayInSeconds ? clearClipboardDelayInSeconds * 1000 : null;
|
||||
}
|
||||
|
||||
if (timeoutMs == null) {
|
||||
timeoutMs = clearClipboardDelay * 1000;
|
||||
if (!taskTimeoutInMs) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearClipboardTimeoutFunction = async () => {
|
||||
@@ -139,9 +144,10 @@ export class SystemService implements SystemServiceAbstraction {
|
||||
}
|
||||
};
|
||||
|
||||
this.clearClipboardTimeout = setTimeout(async () => {
|
||||
await this.clearPendingClipboard();
|
||||
}, timeoutMs);
|
||||
this.clearClipboardTimeoutSubscription = this.taskSchedulerService.setTimeout(
|
||||
ScheduledTaskNames.systemClearClipboardTimeout,
|
||||
taskTimeoutInMs,
|
||||
);
|
||||
}
|
||||
|
||||
async clearPendingClipboard() {
|
||||
|
||||
@@ -112,6 +112,7 @@ export const ENVIRONMENT_DISK = new StateDefinition("environment", "disk");
|
||||
export const ENVIRONMENT_MEMORY = new StateDefinition("environment", "memory");
|
||||
export const THEMING_DISK = new StateDefinition("theming", "disk", { web: "disk-local" });
|
||||
export const TRANSLATION_DISK = new StateDefinition("translation", "disk", { web: "disk-local" });
|
||||
export const TASK_SCHEDULER_DISK = new StateDefinition("taskScheduler", "disk");
|
||||
|
||||
// Secrets Manager
|
||||
|
||||
|
||||
Reference in New Issue
Block a user