[feat(Account Switching)] Allow for extending application state (#584)
* [feat(Account Switching)] Allow for extending application state * [bug(Account Switching)] Remove hardcoded dev urls * [bug(Account Switching)] Init Account when signing in * [bug(Account Switching)] Check for state migration version in local storage for web * [bug(Account Switching)] Fix never lock configurations * [chore] Prettier merge * [bug] Move environmentUrls to global state * [chore] Ran prettier * [bug]change storage location for enityId and type * [style] Ran prettier Co-authored-by: Robyn MacCallum <robyntmaccallum@gmail.com>
This commit is contained in:
parent
59a5300458
commit
9e26336549
|
@ -24,11 +24,11 @@ import { CollectionView } from "../models/view/collectionView";
|
||||||
import { FolderView } from "../models/view/folderView";
|
import { FolderView } from "../models/view/folderView";
|
||||||
import { SendView } from "../models/view/sendView";
|
import { SendView } from "../models/view/sendView";
|
||||||
|
|
||||||
export abstract class StateService {
|
export abstract class StateService<T extends Account = Account> {
|
||||||
accounts: BehaviorSubject<{ [userId: string]: Account }>;
|
accounts: BehaviorSubject<{ [userId: string]: T }>;
|
||||||
activeAccount: BehaviorSubject<string>;
|
activeAccount: BehaviorSubject<string>;
|
||||||
|
|
||||||
addAccount: (account: Account) => Promise<void>;
|
addAccount: (account: T) => Promise<void>;
|
||||||
setActiveUser: (userId: string) => Promise<void>;
|
setActiveUser: (userId: string) => Promise<void>;
|
||||||
clean: (options?: StorageOptions) => Promise<void>;
|
clean: (options?: StorageOptions) => Promise<void>;
|
||||||
init: () => Promise<void>;
|
init: () => Promise<void>;
|
||||||
|
|
|
@ -130,9 +130,6 @@ export class AccountSettings {
|
||||||
enableMinimizeToTray?: boolean;
|
enableMinimizeToTray?: boolean;
|
||||||
enableStartToTray?: boolean;
|
enableStartToTray?: boolean;
|
||||||
enableTray?: boolean;
|
enableTray?: boolean;
|
||||||
environmentUrls?: any = {
|
|
||||||
server: "bitwarden.com",
|
|
||||||
};
|
|
||||||
equivalentDomains?: any;
|
equivalentDomains?: any;
|
||||||
minimizeOnCopyToClipboard?: boolean;
|
minimizeOnCopyToClipboard?: boolean;
|
||||||
neverDomains?: { [id: string]: any };
|
neverDomains?: { [id: string]: any };
|
||||||
|
|
|
@ -24,4 +24,7 @@ export class GlobalState {
|
||||||
noAutoPromptBiometrics?: boolean;
|
noAutoPromptBiometrics?: boolean;
|
||||||
noAutoPromptBiometricsText?: string;
|
noAutoPromptBiometricsText?: string;
|
||||||
stateVersion: number;
|
stateVersion: number;
|
||||||
|
environmentUrls?: any = {
|
||||||
|
server: "bitwarden.com",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Account } from "./account";
|
import { Account } from "./account";
|
||||||
import { GlobalState } from "./globalState";
|
import { GlobalState } from "./globalState";
|
||||||
|
|
||||||
export class State {
|
export class State<TAccount extends Account = Account> {
|
||||||
accounts: { [userId: string]: Account } = {};
|
accounts: { [userId: string]: TAccount } = {};
|
||||||
globals: GlobalState = new GlobalState();
|
globals: GlobalState = new GlobalState();
|
||||||
activeUserId: string;
|
activeUserId: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,13 @@ import { HashPurpose } from "../enums/hashPurpose";
|
||||||
import { KdfType } from "../enums/kdfType";
|
import { KdfType } from "../enums/kdfType";
|
||||||
import { TwoFactorProviderType } from "../enums/twoFactorProviderType";
|
import { TwoFactorProviderType } from "../enums/twoFactorProviderType";
|
||||||
|
|
||||||
import { Account, AccountData, AccountProfile, AccountTokens } from "../models/domain/account";
|
import {
|
||||||
|
Account,
|
||||||
|
AccountData,
|
||||||
|
AccountKeys,
|
||||||
|
AccountProfile,
|
||||||
|
AccountTokens,
|
||||||
|
} from "../models/domain/account";
|
||||||
import { AuthResult } from "../models/domain/authResult";
|
import { AuthResult } from "../models/domain/authResult";
|
||||||
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
|
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
|
||||||
|
|
||||||
|
@ -538,27 +544,34 @@ export class AuthService implements AuthServiceAbstraction {
|
||||||
result.forcePasswordReset = tokenResponse.forcePasswordReset;
|
result.forcePasswordReset = tokenResponse.forcePasswordReset;
|
||||||
|
|
||||||
const accountInformation = await this.tokenService.decodeToken(tokenResponse.accessToken);
|
const accountInformation = await this.tokenService.decodeToken(tokenResponse.accessToken);
|
||||||
await this.stateService.addAccount({
|
await this.stateService.addAccount(
|
||||||
profile: {
|
new Account({
|
||||||
...new AccountProfile(),
|
profile: {
|
||||||
...{
|
...new AccountProfile(),
|
||||||
userId: accountInformation.sub,
|
...{
|
||||||
email: accountInformation.email,
|
userId: accountInformation.sub,
|
||||||
apiKeyClientId: clientId,
|
email: accountInformation.email,
|
||||||
apiKeyClientSecret: clientSecret,
|
apiKeyClientId: clientId,
|
||||||
hasPremiumPersonally: accountInformation.premium,
|
hasPremiumPersonally: accountInformation.premium,
|
||||||
kdfIterations: tokenResponse.kdfIterations,
|
kdfIterations: tokenResponse.kdfIterations,
|
||||||
kdfType: tokenResponse.kdf,
|
kdfType: tokenResponse.kdf,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
keys: {
|
||||||
tokens: {
|
...new AccountKeys(),
|
||||||
...new AccountTokens(),
|
...{
|
||||||
...{
|
apiKeyClientSecret: clientSecret,
|
||||||
accessToken: tokenResponse.accessToken,
|
},
|
||||||
refreshToken: tokenResponse.refreshToken,
|
|
||||||
},
|
},
|
||||||
},
|
tokens: {
|
||||||
});
|
...new AccountTokens(),
|
||||||
|
...{
|
||||||
|
accessToken: tokenResponse.accessToken,
|
||||||
|
refreshToken: tokenResponse.refreshToken,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
if (tokenResponse.twoFactorToken != null) {
|
if (tokenResponse.twoFactorToken != null) {
|
||||||
await this.tokenService.setTwoFactorToken(tokenResponse.twoFactorToken, email);
|
await this.tokenService.setTwoFactorToken(tokenResponse.twoFactorToken, email);
|
||||||
|
|
|
@ -761,13 +761,13 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
protected async storeKey(key: SymmetricCryptoKey, userId?: string) {
|
protected async storeKey(key: SymmetricCryptoKey, userId?: string) {
|
||||||
if (
|
if (await this.shouldStoreKey(KeySuffixOptions.Auto, userId)) {
|
||||||
(await this.shouldStoreKey(KeySuffixOptions.Auto, userId)) ||
|
await this.stateService.setCryptoMasterKeyAuto(key.keyB64, { userId: userId });
|
||||||
(await this.shouldStoreKey(KeySuffixOptions.Biometric, userId))
|
} else if (await this.shouldStoreKey(KeySuffixOptions.Biometric, userId)) {
|
||||||
) {
|
await this.stateService.setCryptoMasterKeyBiometric(key.keyB64, { userId: userId });
|
||||||
await this.stateService.setCryptoMasterKeyB64(key.keyB64, { userId: userId });
|
|
||||||
} else {
|
} else {
|
||||||
await this.stateService.setCryptoMasterKeyB64(null, { userId: userId });
|
await this.stateService.setCryptoMasterKeyAuto(null, { userId: userId });
|
||||||
|
await this.stateService.setCryptoMasterKeyBiometric(null, { userId: userId });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,18 +109,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
||||||
}
|
}
|
||||||
|
|
||||||
async setUrlsFromStorage(): Promise<void> {
|
async setUrlsFromStorage(): Promise<void> {
|
||||||
const urlsObj: any = await this.stateService.getEnvironmentUrls();
|
const urls: any = await this.stateService.getEnvironmentUrls();
|
||||||
const urls = urlsObj || {
|
|
||||||
base: null,
|
|
||||||
api: null,
|
|
||||||
identity: null,
|
|
||||||
icons: null,
|
|
||||||
notifications: null,
|
|
||||||
events: null,
|
|
||||||
webVault: null,
|
|
||||||
keyConnector: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const envUrls = new EnvironmentUrls();
|
const envUrls = new EnvironmentUrls();
|
||||||
|
|
||||||
if (urls.base) {
|
if (urls.base) {
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import { StateService as StateServiceAbstraction } from "../abstractions/state.service";
|
import { StateService as StateServiceAbstraction } from "../abstractions/state.service";
|
||||||
|
|
||||||
import { Account } from "../models/domain/account";
|
import {
|
||||||
|
Account,
|
||||||
|
AccountData,
|
||||||
|
AccountKeys,
|
||||||
|
AccountProfile,
|
||||||
|
AccountTokens,
|
||||||
|
} from "../models/domain/account";
|
||||||
|
|
||||||
import { LogService } from "../abstractions/log.service";
|
import { LogService } from "../abstractions/log.service";
|
||||||
import { StorageService } from "../abstractions/storage.service";
|
import { StorageService } from "../abstractions/storage.service";
|
||||||
|
@ -34,19 +40,21 @@ import { SendData } from "../models/data/sendData";
|
||||||
|
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
import { StateMigrationService } from "./stateMigration.service";
|
import { StateMigrationService } from "../abstractions/stateMigration.service";
|
||||||
|
|
||||||
export class StateService implements StateServiceAbstraction {
|
export class StateService<TAccount extends Account = Account>
|
||||||
accounts = new BehaviorSubject<{ [userId: string]: Account }>({});
|
implements StateServiceAbstraction<TAccount>
|
||||||
|
{
|
||||||
|
accounts = new BehaviorSubject<{ [userId: string]: TAccount }>({});
|
||||||
activeAccount = new BehaviorSubject<string>(null);
|
activeAccount = new BehaviorSubject<string>(null);
|
||||||
|
|
||||||
private state: State = new State();
|
protected state: State<TAccount> = new State<TAccount>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private storageService: StorageService,
|
protected storageService: StorageService,
|
||||||
private secureStorageService: StorageService,
|
protected secureStorageService: StorageService,
|
||||||
private logService: LogService,
|
protected logService: LogService,
|
||||||
private stateMigrationService: StateMigrationService
|
protected stateMigrationService: StateMigrationService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
|
@ -60,7 +68,7 @@ export class StateService implements StateServiceAbstraction {
|
||||||
|
|
||||||
async loadStateFromDisk() {
|
async loadStateFromDisk() {
|
||||||
if ((await this.getActiveUserIdFromStorage()) != null) {
|
if ((await this.getActiveUserIdFromStorage()) != null) {
|
||||||
const diskState = await this.storageService.get<State>(
|
const diskState = await this.storageService.get<State<TAccount>>(
|
||||||
"state",
|
"state",
|
||||||
await this.defaultOnDiskOptions()
|
await this.defaultOnDiskOptions()
|
||||||
);
|
);
|
||||||
|
@ -71,10 +79,7 @@ export class StateService implements StateServiceAbstraction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async addAccount(account: Account) {
|
async addAccount(account: TAccount) {
|
||||||
if (account?.profile?.userId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.state.accounts[account.profile.userId] = account;
|
this.state.accounts[account.profile.userId] = account;
|
||||||
await this.scaffoldNewAccountStorage(account);
|
await this.scaffoldNewAccountStorage(account);
|
||||||
await this.setActiveUser(account.profile.userId);
|
await this.setActiveUser(account.profile.userId);
|
||||||
|
@ -83,7 +88,7 @@ export class StateService implements StateServiceAbstraction {
|
||||||
|
|
||||||
async setActiveUser(userId: string): Promise<void> {
|
async setActiveUser(userId: string): Promise<void> {
|
||||||
this.state.activeUserId = userId;
|
this.state.activeUserId = userId;
|
||||||
const storedState = await this.storageService.get<State>(
|
const storedState = await this.storageService.get<State<TAccount>>(
|
||||||
"state",
|
"state",
|
||||||
await this.defaultOnDiskOptions()
|
await this.defaultOnDiskOptions()
|
||||||
);
|
);
|
||||||
|
@ -1321,47 +1326,64 @@ export class StateService implements StateServiceAbstraction {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEntityId(options?: StorageOptions): Promise<string> {
|
async getEntityId(options?: StorageOptions): Promise<string> {
|
||||||
return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)))
|
return (
|
||||||
?.profile?.entityId;
|
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||||
|
)?.profile?.entityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setEntityId(value: string, options?: StorageOptions): Promise<void> {
|
async setEntityId(value: string, options?: StorageOptions): Promise<void> {
|
||||||
const account = await this.getAccount(
|
const account = await this.getAccount(
|
||||||
this.reconcileOptions(options, this.defaultInMemoryOptions)
|
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
|
||||||
);
|
);
|
||||||
account.profile.entityId = value;
|
account.profile.entityId = value;
|
||||||
await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions));
|
await this.saveAccount(
|
||||||
|
account,
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEntityType(options?: StorageOptions): Promise<any> {
|
async getEntityType(options?: StorageOptions): Promise<any> {
|
||||||
return (await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)))
|
return (
|
||||||
?.profile?.entityType;
|
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||||
|
)?.profile?.entityType;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setEntityType(value: string, options?: StorageOptions): Promise<void> {
|
async setEntityType(value: string, options?: StorageOptions): Promise<void> {
|
||||||
const account = await this.getAccount(
|
const account = await this.getAccount(
|
||||||
this.reconcileOptions(options, this.defaultInMemoryOptions)
|
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
|
||||||
);
|
);
|
||||||
account.profile.entityType = value;
|
account.profile.entityType = value;
|
||||||
await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions));
|
await this.saveAccount(
|
||||||
|
account,
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEnvironmentUrls(options?: StorageOptions): Promise<any> {
|
async getEnvironmentUrls(options?: StorageOptions): Promise<any> {
|
||||||
return (
|
return (
|
||||||
(await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
|
(await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
|
||||||
?.settings?.environmentUrls ?? {
|
?.environmentUrls ?? {
|
||||||
|
base: null,
|
||||||
|
api: null,
|
||||||
|
identity: null,
|
||||||
|
icons: null,
|
||||||
|
notifications: null,
|
||||||
|
events: null,
|
||||||
|
webVault: null,
|
||||||
|
keyConnector: null,
|
||||||
|
// TODO: this is a bug and we should use base instead for the server detail in the account switcher, otherwise self hosted urls will not show correctly
|
||||||
server: "bitwarden.com",
|
server: "bitwarden.com",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setEnvironmentUrls(value: any, options?: StorageOptions): Promise<void> {
|
async setEnvironmentUrls(value: any, options?: StorageOptions): Promise<void> {
|
||||||
const account = await this.getAccount(
|
const globals = await this.getGlobals(
|
||||||
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||||
);
|
);
|
||||||
account.settings.environmentUrls = value;
|
globals.environmentUrls = value;
|
||||||
await this.saveAccount(
|
await this.saveGlobals(
|
||||||
account,
|
globals,
|
||||||
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1402,17 +1424,20 @@ export class StateService implements StateServiceAbstraction {
|
||||||
|
|
||||||
async getEverBeenUnlocked(options?: StorageOptions): Promise<boolean> {
|
async getEverBeenUnlocked(options?: StorageOptions): Promise<boolean> {
|
||||||
return (
|
return (
|
||||||
(await this.getAccount(this.reconcileOptions(options, this.defaultInMemoryOptions)))?.profile
|
(await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
|
||||||
?.everBeenUnlocked ?? false
|
?.profile?.everBeenUnlocked ?? false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setEverBeenUnlocked(value: boolean, options?: StorageOptions): Promise<void> {
|
async setEverBeenUnlocked(value: boolean, options?: StorageOptions): Promise<void> {
|
||||||
const account = await this.getAccount(
|
const account = await this.getAccount(
|
||||||
this.reconcileOptions(options, this.defaultInMemoryOptions)
|
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||||
);
|
);
|
||||||
account.profile.everBeenUnlocked = value;
|
account.profile.everBeenUnlocked = value;
|
||||||
await this.saveAccount(account, this.reconcileOptions(options, this.defaultInMemoryOptions));
|
await this.saveAccount(
|
||||||
|
account,
|
||||||
|
this.reconcileOptions(options, await this.defaultOnDiskOptions())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getForcePasswordReset(options?: StorageOptions): Promise<boolean> {
|
async getForcePasswordReset(options?: StorageOptions): Promise<boolean> {
|
||||||
|
@ -1981,10 +2006,7 @@ export class StateService implements StateServiceAbstraction {
|
||||||
const accountVaultTimeout = (
|
const accountVaultTimeout = (
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||||
)?.settings?.vaultTimeout;
|
)?.settings?.vaultTimeout;
|
||||||
const globalVaultTimeout = (
|
return accountVaultTimeout;
|
||||||
await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
|
||||||
)?.vaultTimeout;
|
|
||||||
return accountVaultTimeout ?? globalVaultTimeout ?? 15;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async setVaultTimeout(value: number, options?: StorageOptions): Promise<void> {
|
async setVaultTimeout(value: number, options?: StorageOptions): Promise<void> {
|
||||||
|
@ -2047,7 +2069,7 @@ export class StateService implements StateServiceAbstraction {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getGlobals(options: StorageOptions): Promise<GlobalState> {
|
protected async getGlobals(options: StorageOptions): Promise<GlobalState> {
|
||||||
let globals: GlobalState;
|
let globals: GlobalState;
|
||||||
if (this.useMemory(options.storageLocation)) {
|
if (this.useMemory(options.storageLocation)) {
|
||||||
globals = this.getGlobalsFromMemory();
|
globals = this.getGlobalsFromMemory();
|
||||||
|
@ -2060,39 +2082,41 @@ export class StateService implements StateServiceAbstraction {
|
||||||
return globals ?? new GlobalState();
|
return globals ?? new GlobalState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveGlobals(globals: GlobalState, options: StorageOptions) {
|
protected async saveGlobals(globals: GlobalState, options: StorageOptions) {
|
||||||
return this.useMemory(options.storageLocation)
|
return this.useMemory(options.storageLocation)
|
||||||
? this.saveGlobalsToMemory(globals)
|
? this.saveGlobalsToMemory(globals)
|
||||||
: await this.saveGlobalsToDisk(globals, options);
|
: await this.saveGlobalsToDisk(globals, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getGlobalsFromMemory(): GlobalState {
|
protected getGlobalsFromMemory(): GlobalState {
|
||||||
return this.state.globals;
|
return this.state.globals;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getGlobalsFromDisk(options: StorageOptions): Promise<GlobalState> {
|
protected async getGlobalsFromDisk(options: StorageOptions): Promise<GlobalState> {
|
||||||
return (await this.storageService.get<State>("state", options))?.globals;
|
return (await this.storageService.get<State<TAccount>>("state", options))?.globals;
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveGlobalsToMemory(globals: GlobalState): void {
|
protected saveGlobalsToMemory(globals: GlobalState): void {
|
||||||
this.state.globals = globals;
|
this.state.globals = globals;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveGlobalsToDisk(globals: GlobalState, options: StorageOptions): Promise<void> {
|
protected async saveGlobalsToDisk(globals: GlobalState, options: StorageOptions): Promise<void> {
|
||||||
if (options.useSecureStorage) {
|
if (options.useSecureStorage) {
|
||||||
const state = (await this.secureStorageService.get<State>("state", options)) ?? new State();
|
const state =
|
||||||
|
(await this.secureStorageService.get<State<TAccount>>("state", options)) ?? new State();
|
||||||
state.globals = globals;
|
state.globals = globals;
|
||||||
await this.secureStorageService.save("state", state, options);
|
await this.secureStorageService.save("state", state, options);
|
||||||
} else {
|
} else {
|
||||||
const state = (await this.storageService.get<State>("state", options)) ?? new State();
|
const state =
|
||||||
|
(await this.storageService.get<State<TAccount>>("state", options)) ?? new State();
|
||||||
state.globals = globals;
|
state.globals = globals;
|
||||||
await this.saveStateToStorage(state, options);
|
await this.saveStateToStorage(state, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getAccount(options: StorageOptions): Promise<Account> {
|
protected async getAccount(options: StorageOptions): Promise<TAccount> {
|
||||||
try {
|
try {
|
||||||
let account: Account;
|
let account: TAccount;
|
||||||
if (this.useMemory(options.storageLocation)) {
|
if (this.useMemory(options.storageLocation)) {
|
||||||
account = this.getAccountFromMemory(options);
|
account = this.getAccountFromMemory(options);
|
||||||
}
|
}
|
||||||
|
@ -2101,51 +2125,51 @@ export class StateService implements StateServiceAbstraction {
|
||||||
account = await this.getAccountFromDisk(options);
|
account = await this.getAccountFromDisk(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
return account != null ? new Account(account) : null;
|
return account;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getAccountFromMemory(options: StorageOptions): Account {
|
protected getAccountFromMemory(options: StorageOptions): TAccount {
|
||||||
if (this.state.accounts == null) {
|
if (this.state.accounts == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.state.accounts[this.getUserIdFromMemory(options)];
|
return this.state.accounts[this.getUserIdFromMemory(options)];
|
||||||
}
|
}
|
||||||
|
|
||||||
private getUserIdFromMemory(options: StorageOptions): string {
|
protected getUserIdFromMemory(options: StorageOptions): string {
|
||||||
return options?.userId != null
|
return options?.userId != null
|
||||||
? this.state.accounts[options.userId]?.profile?.userId
|
? this.state.accounts[options.userId]?.profile?.userId
|
||||||
: this.state.activeUserId;
|
: this.state.activeUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getAccountFromDisk(options: StorageOptions): Promise<Account> {
|
protected async getAccountFromDisk(options: StorageOptions): Promise<TAccount> {
|
||||||
if (options?.userId == null && this.state.activeUserId == null) {
|
if (options?.userId == null && this.state.activeUserId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = options?.useSecureStorage
|
const state = options?.useSecureStorage
|
||||||
? (await this.secureStorageService.get<State>("state", options)) ??
|
? (await this.secureStorageService.get<State<TAccount>>("state", options)) ??
|
||||||
(await this.storageService.get<State>(
|
(await this.storageService.get<State<TAccount>>(
|
||||||
"state",
|
"state",
|
||||||
this.reconcileOptions(options, { htmlStorageLocation: HtmlStorageLocation.Local })
|
this.reconcileOptions(options, { htmlStorageLocation: HtmlStorageLocation.Local })
|
||||||
))
|
))
|
||||||
: await this.storageService.get<State>("state", options);
|
: await this.storageService.get<State<TAccount>>("state", options);
|
||||||
|
|
||||||
return state?.accounts[options?.userId ?? this.state.activeUserId];
|
return state?.accounts[options?.userId ?? this.state.activeUserId];
|
||||||
}
|
}
|
||||||
|
|
||||||
private useMemory(storageLocation: StorageLocation) {
|
protected useMemory(storageLocation: StorageLocation) {
|
||||||
return storageLocation === StorageLocation.Memory || storageLocation === StorageLocation.Both;
|
return storageLocation === StorageLocation.Memory || storageLocation === StorageLocation.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
private useDisk(storageLocation: StorageLocation) {
|
protected useDisk(storageLocation: StorageLocation) {
|
||||||
return storageLocation === StorageLocation.Disk || storageLocation === StorageLocation.Both;
|
return storageLocation === StorageLocation.Disk || storageLocation === StorageLocation.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveAccount(
|
protected async saveAccount(
|
||||||
account: Account,
|
account: TAccount,
|
||||||
options: StorageOptions = {
|
options: StorageOptions = {
|
||||||
storageLocation: StorageLocation.Both,
|
storageLocation: StorageLocation.Both,
|
||||||
useSecureStorage: false,
|
useSecureStorage: false,
|
||||||
|
@ -2156,86 +2180,75 @@ export class StateService implements StateServiceAbstraction {
|
||||||
: await this.saveAccountToDisk(account, options);
|
: await this.saveAccountToDisk(account, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveAccountToDisk(account: Account, options: StorageOptions): Promise<void> {
|
protected async saveAccountToDisk(account: TAccount, options: StorageOptions): Promise<void> {
|
||||||
const storageLocation = options.useSecureStorage
|
const storageLocation = options.useSecureStorage
|
||||||
? this.secureStorageService
|
? this.secureStorageService
|
||||||
: this.storageService;
|
: this.storageService;
|
||||||
|
|
||||||
const state = (await storageLocation.get<State>("state", options)) ?? new State();
|
const state =
|
||||||
|
(await storageLocation.get<State<TAccount>>("state", options)) ?? new State<TAccount>();
|
||||||
state.accounts[account.profile.userId] = account;
|
state.accounts[account.profile.userId] = account;
|
||||||
|
|
||||||
await storageLocation.save("state", state, options);
|
await storageLocation.save("state", state, options);
|
||||||
await this.pushAccounts();
|
await this.pushAccounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveAccountToMemory(account: Account): Promise<void> {
|
protected async saveAccountToMemory(account: TAccount): Promise<void> {
|
||||||
if (this.getAccountFromMemory({ userId: account.profile.userId }) !== null) {
|
if (this.getAccountFromMemory({ userId: account.profile.userId }) !== null) {
|
||||||
this.state.accounts[account.profile.userId] = account;
|
this.state.accounts[account.profile.userId] = account;
|
||||||
}
|
}
|
||||||
await this.pushAccounts();
|
await this.pushAccounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async scaffoldNewAccountStorage(account: Account): Promise<void> {
|
protected async scaffoldNewAccountStorage(account: TAccount): Promise<void> {
|
||||||
await this.scaffoldNewAccountLocalStorage(account);
|
await this.scaffoldNewAccountLocalStorage(account);
|
||||||
await this.scaffoldNewAccountSessionStorage(account);
|
await this.scaffoldNewAccountSessionStorage(account);
|
||||||
await this.scaffoldNewAccountMemoryStorage(account);
|
await this.scaffoldNewAccountMemoryStorage(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async scaffoldNewAccountLocalStorage(account: Account): Promise<void> {
|
protected async scaffoldNewAccountLocalStorage(account: TAccount): Promise<void> {
|
||||||
const storedState =
|
const storedState =
|
||||||
(await this.storageService.get<State>("state", await this.defaultOnDiskLocalOptions())) ??
|
(await this.storageService.get<State<TAccount>>(
|
||||||
new State();
|
"state",
|
||||||
|
await this.defaultOnDiskLocalOptions()
|
||||||
|
)) ?? new State<TAccount>();
|
||||||
const storedAccount = storedState.accounts[account.profile.userId];
|
const storedAccount = storedState.accounts[account.profile.userId];
|
||||||
if (storedAccount != null) {
|
if (storedAccount != null) {
|
||||||
account = {
|
account.settings = storedAccount.settings;
|
||||||
settings: storedAccount.settings,
|
|
||||||
profile: account.profile,
|
|
||||||
tokens: account.tokens,
|
|
||||||
keys: account.keys,
|
|
||||||
data: account.data,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
storedState.accounts[account.profile.userId] = account;
|
storedState.accounts[account.profile.userId] = account;
|
||||||
await this.saveStateToStorage(storedState, await this.defaultOnDiskLocalOptions());
|
await this.saveStateToStorage(storedState, await this.defaultOnDiskLocalOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async scaffoldNewAccountMemoryStorage(account: Account): Promise<void> {
|
protected async scaffoldNewAccountMemoryStorage(account: TAccount): Promise<void> {
|
||||||
const storedState =
|
const storedState =
|
||||||
(await this.storageService.get<State>("state", await this.defaultOnDiskMemoryOptions())) ??
|
(await this.storageService.get<State<TAccount>>(
|
||||||
new State();
|
"state",
|
||||||
|
await this.defaultOnDiskMemoryOptions()
|
||||||
|
)) ?? new State<TAccount>();
|
||||||
const storedAccount = storedState.accounts[account.profile.userId];
|
const storedAccount = storedState.accounts[account.profile.userId];
|
||||||
if (storedAccount != null) {
|
if (storedAccount != null) {
|
||||||
account = {
|
account.settings = storedAccount.settings;
|
||||||
settings: storedAccount.settings,
|
|
||||||
profile: account.profile,
|
|
||||||
tokens: account.tokens,
|
|
||||||
keys: account.keys,
|
|
||||||
data: account.data,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
storedState.accounts[account.profile.userId] = account;
|
storedState.accounts[account.profile.userId] = account;
|
||||||
await this.saveStateToStorage(storedState, await this.defaultOnDiskMemoryOptions());
|
await this.saveStateToStorage(storedState, await this.defaultOnDiskMemoryOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async scaffoldNewAccountSessionStorage(account: Account): Promise<void> {
|
protected async scaffoldNewAccountSessionStorage(account: TAccount): Promise<void> {
|
||||||
const storedState =
|
const storedState =
|
||||||
(await this.storageService.get<State>("state", await this.defaultOnDiskOptions())) ??
|
(await this.storageService.get<State<TAccount>>(
|
||||||
new State();
|
"state",
|
||||||
|
await this.defaultOnDiskOptions()
|
||||||
|
)) ?? new State<TAccount>();
|
||||||
const storedAccount = storedState.accounts[account.profile.userId];
|
const storedAccount = storedState.accounts[account.profile.userId];
|
||||||
if (storedAccount != null) {
|
if (storedAccount != null) {
|
||||||
account = {
|
account.settings = storedAccount.settings;
|
||||||
settings: storedAccount.settings,
|
|
||||||
profile: account.profile,
|
|
||||||
tokens: account.tokens,
|
|
||||||
keys: account.keys,
|
|
||||||
data: account.data,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
storedState.accounts[account.profile.userId] = account;
|
storedState.accounts[account.profile.userId] = account;
|
||||||
await this.saveStateToStorage(storedState, await this.defaultOnDiskOptions());
|
await this.saveStateToStorage(storedState, await this.defaultOnDiskOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async pushAccounts(): Promise<void> {
|
protected async pushAccounts(): Promise<void> {
|
||||||
await this.pruneInMemoryAccounts();
|
await this.pruneInMemoryAccounts();
|
||||||
if (this.state?.accounts == null || Object.keys(this.state.accounts).length < 1) {
|
if (this.state?.accounts == null || Object.keys(this.state.accounts).length < 1) {
|
||||||
this.accounts.next(null);
|
this.accounts.next(null);
|
||||||
|
@ -2245,7 +2258,7 @@ export class StateService implements StateServiceAbstraction {
|
||||||
this.accounts.next(this.state.accounts);
|
this.accounts.next(this.state.accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
private reconcileOptions(
|
protected reconcileOptions(
|
||||||
requestedOptions: StorageOptions,
|
requestedOptions: StorageOptions,
|
||||||
defaultOptions: StorageOptions
|
defaultOptions: StorageOptions
|
||||||
): StorageOptions {
|
): StorageOptions {
|
||||||
|
@ -2263,11 +2276,11 @@ export class StateService implements StateServiceAbstraction {
|
||||||
return requestedOptions;
|
return requestedOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get defaultInMemoryOptions(): StorageOptions {
|
protected get defaultInMemoryOptions(): StorageOptions {
|
||||||
return { storageLocation: StorageLocation.Memory, userId: this.state.activeUserId };
|
return { storageLocation: StorageLocation.Memory, userId: this.state.activeUserId };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async defaultOnDiskOptions(): Promise<StorageOptions> {
|
protected async defaultOnDiskOptions(): Promise<StorageOptions> {
|
||||||
return {
|
return {
|
||||||
storageLocation: StorageLocation.Disk,
|
storageLocation: StorageLocation.Disk,
|
||||||
htmlStorageLocation: HtmlStorageLocation.Session,
|
htmlStorageLocation: HtmlStorageLocation.Session,
|
||||||
|
@ -2276,7 +2289,7 @@ export class StateService implements StateServiceAbstraction {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async defaultOnDiskLocalOptions(): Promise<StorageOptions> {
|
protected async defaultOnDiskLocalOptions(): Promise<StorageOptions> {
|
||||||
return {
|
return {
|
||||||
storageLocation: StorageLocation.Disk,
|
storageLocation: StorageLocation.Disk,
|
||||||
htmlStorageLocation: HtmlStorageLocation.Local,
|
htmlStorageLocation: HtmlStorageLocation.Local,
|
||||||
|
@ -2285,7 +2298,7 @@ export class StateService implements StateServiceAbstraction {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async defaultOnDiskMemoryOptions(): Promise<StorageOptions> {
|
protected async defaultOnDiskMemoryOptions(): Promise<StorageOptions> {
|
||||||
return {
|
return {
|
||||||
storageLocation: StorageLocation.Disk,
|
storageLocation: StorageLocation.Disk,
|
||||||
htmlStorageLocation: HtmlStorageLocation.Memory,
|
htmlStorageLocation: HtmlStorageLocation.Memory,
|
||||||
|
@ -2294,7 +2307,7 @@ export class StateService implements StateServiceAbstraction {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async defaultSecureStorageOptions(): Promise<StorageOptions> {
|
protected async defaultSecureStorageOptions(): Promise<StorageOptions> {
|
||||||
return {
|
return {
|
||||||
storageLocation: StorageLocation.Disk,
|
storageLocation: StorageLocation.Disk,
|
||||||
useSecureStorage: true,
|
useSecureStorage: true,
|
||||||
|
@ -2302,46 +2315,38 @@ export class StateService implements StateServiceAbstraction {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getActiveUserIdFromStorage(): Promise<string> {
|
protected async getActiveUserIdFromStorage(): Promise<string> {
|
||||||
const state = await this.storageService.get<State>("state");
|
const state = await this.storageService.get<State<TAccount>>("state");
|
||||||
return state?.activeUserId;
|
return state?.activeUserId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async removeAccountFromLocalStorage(
|
protected async removeAccountFromLocalStorage(
|
||||||
userId: string = this.state.activeUserId
|
userId: string = this.state.activeUserId
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const state = await this.storageService.get<State>("state", {
|
const state = await this.storageService.get<State<TAccount>>("state", {
|
||||||
htmlStorageLocation: HtmlStorageLocation.Local,
|
htmlStorageLocation: HtmlStorageLocation.Local,
|
||||||
});
|
});
|
||||||
if (state?.accounts[userId] == null) {
|
if (state?.accounts[userId] == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
state.accounts[userId] = this.resetAccount(state.accounts[userId]);
|
||||||
state.accounts[userId] = new Account({
|
|
||||||
settings: state.accounts[userId].settings,
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.saveStateToStorage(state, await this.defaultOnDiskLocalOptions());
|
await this.saveStateToStorage(state, await this.defaultOnDiskLocalOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async removeAccountFromSessionStorage(
|
protected async removeAccountFromSessionStorage(
|
||||||
userId: string = this.state.activeUserId
|
userId: string = this.state.activeUserId
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const state = await this.storageService.get<State>("state", {
|
const state = await this.storageService.get<State<TAccount>>("state", {
|
||||||
htmlStorageLocation: HtmlStorageLocation.Session,
|
htmlStorageLocation: HtmlStorageLocation.Session,
|
||||||
});
|
});
|
||||||
if (state?.accounts[userId] == null) {
|
if (state?.accounts[userId] == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
state.accounts[userId] = this.resetAccount(state.accounts[userId]);
|
||||||
state.accounts[userId] = new Account({
|
|
||||||
settings: state.accounts[userId].settings,
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.saveStateToStorage(state, await this.defaultOnDiskOptions());
|
await this.saveStateToStorage(state, await this.defaultOnDiskOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async removeAccountFromSecureStorage(
|
protected async removeAccountFromSecureStorage(
|
||||||
userId: string = this.state.activeUserId
|
userId: string = this.state.activeUserId
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.setCryptoMasterKeyAuto(null, { userId: userId });
|
await this.setCryptoMasterKeyAuto(null, { userId: userId });
|
||||||
|
@ -2349,15 +2354,18 @@ export class StateService implements StateServiceAbstraction {
|
||||||
await this.setCryptoMasterKeyB64(null, { userId: userId });
|
await this.setCryptoMasterKeyB64(null, { userId: userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeAccountFromMemory(userId: string = this.state.activeUserId): void {
|
protected removeAccountFromMemory(userId: string = this.state.activeUserId): void {
|
||||||
delete this.state.accounts[userId];
|
delete this.state.accounts[userId];
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveStateToStorage(state: State, options: StorageOptions): Promise<void> {
|
protected async saveStateToStorage(
|
||||||
|
state: State<TAccount>,
|
||||||
|
options: StorageOptions
|
||||||
|
): Promise<void> {
|
||||||
await this.storageService.save("state", state, options);
|
await this.storageService.save("state", state, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async pruneInMemoryAccounts() {
|
protected async pruneInMemoryAccounts() {
|
||||||
// We preserve settings for logged out accounts, but we don't want to consider them when thinking about active account state
|
// We preserve settings for logged out accounts, but we don't want to consider them when thinking about active account state
|
||||||
for (const userId in this.state.accounts) {
|
for (const userId in this.state.accounts) {
|
||||||
if (!(await this.getIsAuthenticated({ userId: userId }))) {
|
if (!(await this.getIsAuthenticated({ userId: userId }))) {
|
||||||
|
@ -2365,4 +2373,13 @@ export class StateService implements StateServiceAbstraction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// settings persist even on reset
|
||||||
|
protected resetAccount(account: TAccount) {
|
||||||
|
account.data = new AccountData();
|
||||||
|
account.keys = new AccountKeys();
|
||||||
|
account.profile = new AccountProfile();
|
||||||
|
account.tokens = new AccountTokens();
|
||||||
|
return account;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,19 +114,22 @@ export class StateMigrationService {
|
||||||
readonly latestVersion: number = 2;
|
readonly latestVersion: number = 2;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private storageService: StorageService,
|
protected storageService: StorageService,
|
||||||
private secureStorageService: StorageService
|
protected secureStorageService: StorageService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async needsMigration(): Promise<boolean> {
|
async needsMigration(): Promise<boolean> {
|
||||||
const currentStateVersion = (await this.storageService.get<State>("state"))?.globals
|
const currentStateVersion = (
|
||||||
?.stateVersion;
|
await this.storageService.get<State<Account>>("state", {
|
||||||
|
htmlStorageLocation: HtmlStorageLocation.Local,
|
||||||
|
})
|
||||||
|
)?.globals?.stateVersion;
|
||||||
return currentStateVersion == null || currentStateVersion < this.latestVersion;
|
return currentStateVersion == null || currentStateVersion < this.latestVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
async migrate(): Promise<void> {
|
async migrate(): Promise<void> {
|
||||||
let currentStateVersion =
|
let currentStateVersion =
|
||||||
(await this.storageService.get<State>("state"))?.globals?.stateVersion ?? 1;
|
(await this.storageService.get<State<Account>>("state"))?.globals?.stateVersion ?? 1;
|
||||||
while (currentStateVersion < this.latestVersion) {
|
while (currentStateVersion < this.latestVersion) {
|
||||||
switch (currentStateVersion) {
|
switch (currentStateVersion) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -138,10 +141,10 @@ export class StateMigrationService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async migrateStateFrom1To2(): Promise<void> {
|
protected async migrateStateFrom1To2(): Promise<void> {
|
||||||
const options: StorageOptions = { htmlStorageLocation: HtmlStorageLocation.Local };
|
const options: StorageOptions = { htmlStorageLocation: HtmlStorageLocation.Local };
|
||||||
const userId = await this.storageService.get<string>("userId");
|
const userId = await this.storageService.get<string>("userId");
|
||||||
const initialState: State =
|
const initialState: State<Account> =
|
||||||
userId == null
|
userId == null
|
||||||
? {
|
? {
|
||||||
globals: {
|
globals: {
|
||||||
|
@ -174,6 +177,7 @@ export class StateMigrationService {
|
||||||
v1Keys.enableBiometric,
|
v1Keys.enableBiometric,
|
||||||
options
|
options
|
||||||
),
|
),
|
||||||
|
environmentUrls: await this.storageService.get<any>(v1Keys.environmentUrls, options),
|
||||||
installedVersion: await this.storageService.get<string>(
|
installedVersion: await this.storageService.get<string>(
|
||||||
v1Keys.installedVersion,
|
v1Keys.installedVersion,
|
||||||
options
|
options
|
||||||
|
@ -439,10 +443,6 @@ export class StateMigrationService {
|
||||||
options
|
options
|
||||||
),
|
),
|
||||||
enableTray: await this.storageService.get<boolean>(v1Keys.enableTray, options),
|
enableTray: await this.storageService.get<boolean>(v1Keys.enableTray, options),
|
||||||
environmentUrls: await this.storageService.get<any>(
|
|
||||||
v1Keys.environmentUrls,
|
|
||||||
options
|
|
||||||
),
|
|
||||||
equivalentDomains: await this.storageService.get<any>(
|
equivalentDomains: await this.storageService.get<any>(
|
||||||
v1Keys.equivalentDomains,
|
v1Keys.equivalentDomains,
|
||||||
options
|
options
|
||||||
|
|
|
@ -53,7 +53,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
||||||
async isLocked(userId?: string): Promise<boolean> {
|
async isLocked(userId?: string): Promise<boolean> {
|
||||||
const neverLock =
|
const neverLock =
|
||||||
(await this.cryptoService.hasKeyStored(KeySuffixOptions.Auto, userId)) &&
|
(await this.cryptoService.hasKeyStored(KeySuffixOptions.Auto, userId)) &&
|
||||||
!(await this.stateService.getEverBeenUnlocked({ userId: userId }));
|
(await this.stateService.getEverBeenUnlocked({ userId: userId }));
|
||||||
if (neverLock) {
|
if (neverLock) {
|
||||||
// TODO: This also _sets_ the key so when we check memory in the next line it finds a key.
|
// TODO: This also _sets_ the key so when we check memory in the next line it finds a key.
|
||||||
// We should refactor here.
|
// We should refactor here.
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { StringResponse } from "./models/response/stringResponse";
|
||||||
|
|
||||||
export abstract class BaseProgram {
|
export abstract class BaseProgram {
|
||||||
constructor(
|
constructor(
|
||||||
private stateService: StateService,
|
protected stateService: StateService,
|
||||||
private writeLn: (s: string, finalLine: boolean, error: boolean) => void
|
private writeLn: (s: string, finalLine: boolean, error: boolean) => void
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue