1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-11 10:10:25 +01:00
bitwarden-browser/apps/desktop/src/app/app.component.ts
Shane Melton 07c2c2af20
[AC-1070] Enforce master password policy on login (#4795)
* [EC-1070] Introduce flag for enforcing master password policy on login

* [EC-1070] Update master password policy form

Add the ability to toggle enforceOnLogin flag in web

* [EC-1070] Add API method to retrieve all policies for the current user

* [EC-1070] Refactor forcePasswordReset in state service to support more options

- Use an options class to provide a reason and optional organization id
- Use the OnDiskMemory storage location so the option persists between the same auth session

* [AC-1070] Retrieve single master password policy from identity token response

Additionally, store the policy in the login strategy for future use

* [EC-1070] Introduce master password evaluation in the password login strategy

- If a master password policy is returned from the identity result, evaluate the password.
- If the password does not meet the requirements, save the forcePasswordReset options
- Add support for 2FA by storing the results of the password evaluation on the login strategy instance
- Add unit tests to password login strategy

* [AC-1070] Modify admin password reset component to support update master password on login

- Modify the warning message to depend on the reason

- Use the forcePasswordResetOptions in the update temp password component

* [EC-1070] Require current master password when updating weak mp on login

- Inject user verification service to verify the user
- Conditionally show the current master password field only when updating a weak mp. Admin reset does not require the current master password.

* [EC-1070] Implement password policy check during vault unlock

Checking the master password during unlock is the only applicable place to enforce the master password policy check for SSO users.

* [EC-1070] CLI - Add ability to load MP policies on login

Inject policyApi and organization services into the login command

* [EC-1070] CLI - Refactor update temp password logic to support updating weak passwords

- Introduce new shared method for collecting a valid and confirmed master password from the CLI and generating a new encryption key
- Add separate methods for updating temp passwords and weak passwords.
- Utilize those methods during login flow if not using an API key

* [EC-1070] Add route guard to force password reset when required

* [AC-1070] Use master password policy from verify password response in lock component

* [EC-1070] Update labels in update password component

* [AC-1070] Fix policy service tests

* [AC-1070] CLI - Force sync before any password reset flow

Move up the call to sync the vault before attempting to collect a new master password. Ensures the master password policies are available.

* [AC-1070] Remove unused getAllPolicies method from policy api service

* [AC-1070] Fix missing enforceOnLogin copy in policy service

* [AC-1070] Include current master password on desktop/browser update password page templates

* [AC-1070] Check for forced password reset on account switch in Desktop

* [AC-1070] Rename WeakMasterPasswordOnLogin to WeakMasterPassword

* [AC-1070] Update AuthServiceInitOptions

* [AC-1070] Add None force reset password reason

* [AC-1070] Remove redundant ForcePasswordResetOptions class and replace with ForcePasswordResetReason enum

* [AC-1070] Rename ForceResetPasswordReason file

* [AC-1070] Simplify conditional

* [AC-1070] Refactor logic that saves password reset flag

* [AC-1070] Remove redundant constructors

* [AC-1070] Remove unnecessary state service call

* [AC-1070] Update master password policy component

- Use typed reactive form
- Use CL form components
- Remove bootstrap
- Update error component to support min/max
- Use Utils.minimumPasswordLength value for min value form validation

* [AC-1070] Cleanup leftover html comment

* [AC-1070] Remove overridden default values from MasterPasswordPolicyResponse

* [AC-1070] Hide current master password input in browser for admin password reset

* [AC-1070] Remove clientside user verification

* [AC-1070] Update temp password web component to use CL

- Use CL for form inputs in the Web component template
- Remove most of the bootstrap classes in the Web component template
- Use userVerificationService to build the password request
- Remove redundant current master password null check

* [AC-1070] Replace repeated user inputs email parsing helpers

- Update passwordStrength() method to accept an optional email argument that will be parsed into separate user inputs for use with zxcvbn
- Remove all other repeated getUserInput helper methods that parsed user emails and use the new passwordStrength signature

* [AC-1070] Fix broken login command after forcePasswordReset enum refactor

* [AC-1070] Reduce side effects in base login strategy

- Remove masterPasswordPolicy property from base login.strategy.ts
- Include an IdentityResponse in base startLogin() in addition to AuthResult
- Use the new IdentityResponse to parse the master password policy info only in the PasswordLoginStrategy

* [AC-1070] Cleanup password login strategy tests

* [AC-1070] Remove unused field

* [AC-1070] Strongly type postAccountVerifyPassword API service method

- Remove redundant verify master password response
- Use MasterPasswordPolicyResponse instead

* [AC-1070] Use ForceResetPassword.None during account switch check

* [AC-1070] Fix check for forcePasswordReset reason after addition of None

* [AC-1070] Redirect a user home if on the update temp password page without a reason

* [AC-1070] Use bit-select and bit-option

* [AC-1070] Reduce explicit form control definitions for readability

* [AC-1070] Import SelectModule in Shared web module

* [AC-1070] Add check for missing 'at' symbol

* [AC-1070] Remove redundant unpacking and null coalescing

* [AC-1070] Update passwordStrength signature and add jsdocs

* [AC-1070] Remove variable abbreviation

* [AC-1070] Restore Id attributes on form inputs

* [AC-1070] Clarify input value min/max error messages

* [AC-1070] Add input min/max value example to storybook

* [AC-1070] Add missing spinner to update temp password form

* [AC-1070] Add missing ids to form elements

* [AC-1070] Remove duplicate force sync and update comment

* [AC-1070] Switch backticks to quotation marks

---------

Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
2023-04-17 07:35:37 -07:00

651 lines
24 KiB
TypeScript

import {
Component,
NgZone,
OnDestroy,
OnInit,
SecurityContext,
Type,
ViewChild,
ViewContainerRef,
} from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser";
import { Router } from "@angular/router";
import { IndividualConfig, ToastrService } from "ngx-toastr";
import { firstValueFrom, Subject, takeUntil } from "rxjs";
import { ModalRef } from "@bitwarden/angular/components/modal/modal.ref";
import { ModalService } from "@bitwarden/angular/services/modal.service";
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { SystemService } from "@bitwarden/common/abstractions/system.service";
import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service";
import { CollectionService } from "@bitwarden/common/admin-console/abstractions/collection.service";
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ForceResetPasswordReason } from "@bitwarden/common/auth/models/domain/force-reset-password-reason";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
import { DeleteAccountComponent } from "../auth/delete-account.component";
import { LoginApprovalComponent } from "../auth/login/login-approval.component";
import { MenuUpdateRequest } from "../main/menu/menu.updater";
import { PremiumComponent } from "../vault/app/accounts/premium.component";
import { FolderAddEditComponent } from "../vault/app/vault/folder-add-edit.component";
import { SettingsComponent } from "./accounts/settings.component";
import { GeneratorComponent } from "./tools/generator.component";
import { PasswordGeneratorHistoryComponent } from "./tools/password-generator-history.component";
import { ExportComponent } from "./vault/export.component";
const BroadcasterSubscriptionId = "AppComponent";
const IdleTimeout = 60000 * 10; // 10 minutes
const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours
const systemTimeoutOptions = {
onLock: -2,
onSuspend: -3,
onIdle: -4,
};
@Component({
selector: "app-root",
styles: [],
template: `
<ng-template #settings></ng-template>
<ng-template #premium></ng-template>
<ng-template #passwordHistory></ng-template>
<ng-template #appFolderAddEdit></ng-template>
<ng-template #exportVault></ng-template>
<ng-template #appGenerator></ng-template>
<ng-template #loginApproval></ng-template>
<app-header></app-header>
<div id="container">
<div class="loading" *ngIf="loading">
<i class="bwi bwi-spinner bwi-spin bwi-3x" aria-hidden="true"></i>
</div>
<router-outlet *ngIf="!loading"></router-outlet>
</div>
`,
})
export class AppComponent implements OnInit, OnDestroy {
@ViewChild("settings", { read: ViewContainerRef, static: true }) settingsRef: ViewContainerRef;
@ViewChild("premium", { read: ViewContainerRef, static: true }) premiumRef: ViewContainerRef;
@ViewChild("passwordHistory", { read: ViewContainerRef, static: true })
passwordHistoryRef: ViewContainerRef;
@ViewChild("exportVault", { read: ViewContainerRef, static: true })
exportVaultModalRef: ViewContainerRef;
@ViewChild("appFolderAddEdit", { read: ViewContainerRef, static: true })
folderAddEditModalRef: ViewContainerRef;
@ViewChild("appGenerator", { read: ViewContainerRef, static: true })
generatorModalRef: ViewContainerRef;
@ViewChild("loginApproval", { read: ViewContainerRef, static: true })
loginApprovalModalRef: ViewContainerRef;
loading = false;
private lastActivity: number = null;
private modal: ModalRef = null;
private idleTimer: number = null;
private isIdle = false;
private activeUserId: string = null;
private destroy$ = new Subject<void>();
constructor(
private broadcasterService: BroadcasterService,
private folderService: InternalFolderService,
private settingsService: SettingsService,
private syncService: SyncService,
private passwordGenerationService: PasswordGenerationServiceAbstraction,
private cipherService: CipherService,
private authService: AuthService,
private router: Router,
private toastrService: ToastrService,
private i18nService: I18nService,
private sanitizer: DomSanitizer,
private ngZone: NgZone,
private vaultTimeoutService: VaultTimeoutService,
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
private cryptoService: CryptoService,
private logService: LogService,
private messagingService: MessagingService,
private collectionService: CollectionService,
private searchService: SearchService,
private notificationsService: NotificationsService,
private platformUtilsService: PlatformUtilsService,
private systemService: SystemService,
private stateService: StateService,
private eventUploadService: EventUploadService,
private policyService: InternalPolicyService,
private modalService: ModalService,
private keyConnectorService: KeyConnectorService
) {}
ngOnInit() {
this.stateService.activeAccount$.pipe(takeUntil(this.destroy$)).subscribe((userId) => {
this.activeUserId = userId;
});
this.ngZone.runOutsideAngular(() => {
setTimeout(async () => {
await this.updateAppMenu();
}, 1000);
window.ontouchstart = () => this.recordActivity();
window.onmousedown = () => this.recordActivity();
window.onscroll = () => this.recordActivity();
window.onkeypress = () => this.recordActivity();
});
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
this.ngZone.run(async () => {
switch (message.command) {
case "loggedIn":
case "unlocked":
this.recordActivity();
this.notificationsService.updateConnection();
this.updateAppMenu();
this.systemService.cancelProcessReload();
break;
case "loggedOut":
this.modalService.closeAll();
this.notificationsService.updateConnection();
this.updateAppMenu();
await this.systemService.clearPendingClipboard();
await this.systemService.startProcessReload(this.authService);
break;
case "authBlocked":
this.router.navigate(["login"]);
break;
case "logout":
this.loading = message.userId == null || message.userId === this.activeUserId;
await this.logOut(!!message.expired, message.userId);
this.loading = false;
break;
case "lockVault":
await this.vaultTimeoutService.lock(message.userId);
break;
case "lockAllVaults":
for (const userId in await firstValueFrom(this.stateService.accounts$)) {
if (userId != null) {
await this.vaultTimeoutService.lock(userId);
}
}
break;
case "locked":
this.modalService.closeAll();
if (
message.userId == null ||
message.userId === (await this.stateService.getUserId())
) {
await this.router.navigate(["lock"]);
}
this.notificationsService.updateConnection();
await this.updateAppMenu();
await this.systemService.clearPendingClipboard();
await this.systemService.startProcessReload(this.authService);
break;
case "startProcessReload":
this.systemService.startProcessReload(this.authService);
break;
case "cancelProcessReload":
this.systemService.cancelProcessReload();
break;
case "reloadProcess":
(window.location as any).reload(true);
break;
case "syncStarted":
break;
case "syncCompleted":
await this.updateAppMenu();
break;
case "openSettings":
await this.openModal<SettingsComponent>(SettingsComponent, this.settingsRef);
break;
case "openPremium":
await this.openModal<PremiumComponent>(PremiumComponent, this.premiumRef);
break;
case "showFingerprintPhrase": {
const fingerprint = await this.cryptoService.getFingerprint(
await this.stateService.getUserId()
);
const result = await this.platformUtilsService.showDialog(
this.i18nService.t("yourAccountsFingerprint") + ":\n" + fingerprint.join("-"),
this.i18nService.t("fingerprintPhrase"),
this.i18nService.t("learnMore"),
this.i18nService.t("close")
);
if (result) {
this.platformUtilsService.launchUri("https://bitwarden.com/help/fingerprint-phrase/");
}
break;
}
case "deleteAccount":
this.modalService.open(DeleteAccountComponent, { replaceTopModal: true });
break;
case "openPasswordHistory":
await this.openModal<PasswordGeneratorHistoryComponent>(
PasswordGeneratorHistoryComponent,
this.passwordHistoryRef
);
break;
case "showToast":
this.showToast(message);
break;
case "copiedToClipboard":
if (!message.clearing) {
this.systemService.clearClipboard(message.clipboardValue, message.clearMs);
}
break;
case "ssoCallback":
this.router.navigate(["sso"], {
queryParams: { code: message.code, state: message.state },
});
break;
case "premiumRequired": {
const premiumConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("premiumRequiredDesc"),
this.i18nService.t("premiumRequired"),
this.i18nService.t("learnMore"),
this.i18nService.t("cancel")
);
if (premiumConfirmed) {
await this.openModal<PremiumComponent>(PremiumComponent, this.premiumRef);
}
break;
}
case "emailVerificationRequired": {
const emailVerificationConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("emailVerificationRequiredDesc"),
this.i18nService.t("emailVerificationRequired"),
this.i18nService.t("learnMore"),
this.i18nService.t("cancel")
);
if (emailVerificationConfirmed) {
this.platformUtilsService.launchUri(
"https://bitwarden.com/help/create-bitwarden-account/"
);
}
break;
}
case "syncVault":
try {
await this.syncService.fullSync(true, true);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("syncingComplete")
);
} catch {
this.platformUtilsService.showToast(
"error",
null,
this.i18nService.t("syncingFailed")
);
}
break;
case "checkSyncVault":
try {
const lastSync = await this.syncService.getLastSync();
let lastSyncAgo = SyncInterval + 1;
if (lastSync != null) {
lastSyncAgo = new Date().getTime() - lastSync.getTime();
}
if (lastSyncAgo >= SyncInterval) {
await this.syncService.fullSync(false);
}
} catch (e) {
this.logService.error(e);
}
this.messagingService.send("scheduleNextSync");
break;
case "exportVault":
await this.openExportVault();
break;
case "newLogin":
this.routeToVault("add", CipherType.Login);
break;
case "newCard":
this.routeToVault("add", CipherType.Card);
break;
case "newIdentity":
this.routeToVault("add", CipherType.Identity);
break;
case "newSecureNote":
this.routeToVault("add", CipherType.SecureNote);
break;
default:
break;
case "newFolder":
await this.addFolder();
break;
case "openGenerator":
// openGenerator has extended functionality if called in the vault
if (!this.router.url.includes("vault")) {
await this.openGenerator();
}
break;
case "convertAccountToKeyConnector":
this.router.navigate(["/remove-password"]);
break;
case "switchAccount": {
if (message.userId != null) {
await this.stateService.setActiveUser(message.userId);
}
const locked =
(await this.authService.getAuthStatus(message.userId)) ===
AuthenticationStatus.Locked;
const forcedPasswordReset =
(await this.stateService.getForcePasswordResetReason({ userId: message.userId })) !=
ForceResetPasswordReason.None;
if (locked) {
this.messagingService.send("locked", { userId: message.userId });
} else if (forcedPasswordReset) {
this.router.navigate(["update-temp-password"]);
} else {
this.messagingService.send("unlocked");
this.loading = true;
await this.syncService.fullSync(true);
this.loading = false;
this.router.navigate(["vault"]);
}
break;
}
case "systemSuspended":
await this.checkForSystemTimeout(systemTimeoutOptions.onSuspend);
break;
case "systemLocked":
await this.checkForSystemTimeout(systemTimeoutOptions.onLock);
break;
case "systemIdle":
await this.checkForSystemTimeout(systemTimeoutOptions.onIdle);
break;
case "openLoginApproval":
if (message.notificationId != null) {
await this.openLoginApproval(message.notificationId);
}
break;
}
});
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
this.broadcasterService.unsubscribe(BroadcasterSubscriptionId);
}
async openExportVault() {
this.modalService.closeAll();
const [modal, childComponent] = await this.modalService.openViewRef(
ExportComponent,
this.exportVaultModalRef
);
this.modal = modal;
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
childComponent.onSaved.subscribe(() => {
this.modal.close();
});
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
this.modal.onClosed.subscribe(() => {
this.modal = null;
});
}
async addFolder() {
this.modalService.closeAll();
const [modal, childComponent] = await this.modalService.openViewRef(
FolderAddEditComponent,
this.folderAddEditModalRef,
(comp) => (comp.folderId = null)
);
this.modal = modal;
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
childComponent.onSavedFolder.subscribe(async () => {
this.modal.close();
this.syncService.fullSync(false);
});
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
this.modal.onClosed.subscribe(() => {
this.modal = null;
});
}
async openGenerator() {
this.modalService.closeAll();
[this.modal] = await this.modalService.openViewRef(
GeneratorComponent,
this.generatorModalRef,
(comp) => (comp.comingFromAddEdit = false)
);
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
this.modal.onClosed.subscribe(() => {
this.modal = null;
});
}
async openLoginApproval(notificationId: string) {
this.modalService.closeAll();
this.modal = await this.modalService.open(LoginApprovalComponent, {
data: { notificationId: notificationId },
});
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
this.modal.onClosed.subscribe(() => {
this.modal = null;
});
}
private async updateAppMenu() {
let updateRequest: MenuUpdateRequest;
const stateAccounts = await firstValueFrom(this.stateService.accounts$);
if (stateAccounts == null || Object.keys(stateAccounts).length < 1) {
updateRequest = {
accounts: null,
activeUserId: null,
hideChangeMasterPassword: true,
};
} else {
const accounts: { [userId: string]: any } = {};
for (const i in stateAccounts) {
if (i != null && stateAccounts[i]?.profile?.userId != null) {
const userId = stateAccounts[i].profile.userId;
accounts[userId] = {
isAuthenticated: await this.stateService.getIsAuthenticated({
userId: userId,
}),
isLocked:
(await this.authService.getAuthStatus(userId)) === AuthenticationStatus.Locked,
email: stateAccounts[i].profile.email,
userId: stateAccounts[i].profile.userId,
};
}
}
updateRequest = {
accounts: accounts,
activeUserId: await this.stateService.getUserId(),
hideChangeMasterPassword: await this.keyConnectorService.getUsesKeyConnector(),
};
}
this.messagingService.send("updateAppMenu", { updateRequest: updateRequest });
}
private async logOut(expired: boolean, userId?: string) {
const userBeingLoggedOut = await this.stateService.getUserId({ userId: userId });
await Promise.all([
this.eventUploadService.uploadEvents(userBeingLoggedOut),
this.syncService.setLastSync(new Date(0), userBeingLoggedOut),
this.cryptoService.clearKeys(userBeingLoggedOut),
this.settingsService.clear(userBeingLoggedOut),
this.cipherService.clear(userBeingLoggedOut),
this.folderService.clear(userBeingLoggedOut),
this.collectionService.clear(userBeingLoggedOut),
this.passwordGenerationService.clear(userBeingLoggedOut),
this.vaultTimeoutSettingsService.clear(userBeingLoggedOut),
this.policyService.clear(userBeingLoggedOut),
this.keyConnectorService.clear(),
]);
if (userBeingLoggedOut === this.activeUserId) {
this.searchService.clearIndex();
this.authService.logOut(async () => {
if (expired) {
this.platformUtilsService.showToast(
"warning",
this.i18nService.t("loggedOut"),
this.i18nService.t("loginExpired")
);
}
});
}
const preLogoutActiveUserId = this.activeUserId;
await this.stateService.clean({ userId: userBeingLoggedOut });
if (this.activeUserId == null) {
this.router.navigate(["login"]);
} else if (preLogoutActiveUserId !== this.activeUserId) {
this.messagingService.send("switchAccount");
}
await this.updateAppMenu();
}
private async recordActivity() {
if (this.activeUserId == null) {
return;
}
const now = new Date().getTime();
if (this.lastActivity != null && now - this.lastActivity < 250) {
return;
}
this.lastActivity = now;
await this.stateService.setLastActive(now, { userId: this.activeUserId });
// Idle states
if (this.isIdle) {
this.isIdle = false;
this.idleStateChanged();
}
if (this.idleTimer != null) {
window.clearTimeout(this.idleTimer);
this.idleTimer = null;
}
this.idleTimer = window.setTimeout(() => {
if (!this.isIdle) {
this.isIdle = true;
this.idleStateChanged();
}
}, IdleTimeout);
}
private idleStateChanged() {
if (this.isIdle) {
this.notificationsService.disconnectFromInactivity();
} else {
this.notificationsService.reconnectFromActivity();
}
}
private async openModal<T>(type: Type<T>, ref: ViewContainerRef) {
this.modalService.closeAll();
[this.modal] = await this.modalService.openViewRef(type, ref);
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
this.modal.onClosed.subscribe(() => {
this.modal = null;
});
}
private showToast(msg: any) {
let message = "";
const options: Partial<IndividualConfig> = {};
if (typeof msg.text === "string") {
message = msg.text;
} else if (msg.text.length === 1) {
message = msg.text[0];
} else {
msg.text.forEach(
(t: string) =>
(message += "<p>" + this.sanitizer.sanitize(SecurityContext.HTML, t) + "</p>")
);
options.enableHtml = true;
}
if (msg.options != null) {
if (msg.options.trustedHtml === true) {
options.enableHtml = true;
}
if (msg.options.timeout != null && msg.options.timeout > 0) {
options.timeOut = msg.options.timeout;
}
}
this.toastrService.show(message, msg.title, options, "toast-" + msg.type);
}
private routeToVault(action: string, cipherType: CipherType) {
if (!this.router.url.includes("vault")) {
this.router.navigate(["/vault"], {
queryParams: {
action: action,
addType: cipherType,
},
replaceUrl: true,
});
}
}
private async checkForSystemTimeout(timeout: number): Promise<void> {
const accounts = await firstValueFrom(this.stateService.accounts$);
for (const userId in accounts) {
if (userId == null) {
continue;
}
const options = await this.getVaultTimeoutOptions(userId);
if (options[0] === timeout) {
options[1] === "logOut"
? this.logOut(false, userId)
: await this.vaultTimeoutService.lock(userId);
}
}
}
private async getVaultTimeoutOptions(userId: string): Promise<[number, string]> {
const timeout = await this.stateService.getVaultTimeout({ userId: userId });
const action = await this.stateService.getVaultTimeoutAction({ userId: userId });
return [timeout, action];
}
}