mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
Delete Unused Bits of StateService (#9858)
* Delete Unused Bits of StateService * Fix Tests
This commit is contained in:
@@ -1,8 +1,4 @@
|
||||
import { BiometricKey } from "../../auth/types/biometric-key";
|
||||
import { GeneratorOptions } from "../../tools/generator/generator-options";
|
||||
import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password";
|
||||
import { UsernameGeneratorOptions } from "../../tools/generator/username";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { Account } from "../models/domain/account";
|
||||
import { StorageOptions } from "../models/domain/storage-options";
|
||||
|
||||
@@ -22,7 +18,6 @@ export type InitOptions = {
|
||||
|
||||
export abstract class StateService<T extends Account = Account> {
|
||||
addAccount: (account: T) => Promise<void>;
|
||||
clearDecryptedData: (userId: UserId) => Promise<void>;
|
||||
clean: (options?: StorageOptions) => Promise<void>;
|
||||
init: (initOptions?: InitOptions) => Promise<void>;
|
||||
|
||||
@@ -73,36 +68,10 @@ export abstract class StateService<T extends Account = Account> {
|
||||
* @deprecated For migration purposes only, use setUserKeyBiometric instead
|
||||
*/
|
||||
setCryptoMasterKeyBiometric: (value: BiometricKey, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedPasswordGenerationHistory: (
|
||||
options?: StorageOptions,
|
||||
) => Promise<GeneratedPasswordHistory[]>;
|
||||
setDecryptedPasswordGenerationHistory: (
|
||||
value: GeneratedPasswordHistory[],
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getDuckDuckGoSharedKey: (options?: StorageOptions) => Promise<string>;
|
||||
setDuckDuckGoSharedKey: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedPasswordGenerationHistory: (
|
||||
options?: StorageOptions,
|
||||
) => Promise<GeneratedPasswordHistory[]>;
|
||||
setEncryptedPasswordGenerationHistory: (
|
||||
value: GeneratedPasswordHistory[],
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getIsAuthenticated: (options?: StorageOptions) => Promise<boolean>;
|
||||
getLastSync: (options?: StorageOptions) => Promise<string>;
|
||||
setLastSync: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getPasswordGenerationOptions: (options?: StorageOptions) => Promise<PasswordGeneratorOptions>;
|
||||
setPasswordGenerationOptions: (
|
||||
value: PasswordGeneratorOptions,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getUsernameGenerationOptions: (options?: StorageOptions) => Promise<UsernameGeneratorOptions>;
|
||||
setUsernameGenerationOptions: (
|
||||
value: UsernameGeneratorOptions,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getGeneratorOptions: (options?: StorageOptions) => Promise<GeneratorOptions>;
|
||||
setGeneratorOptions: (value: GeneratorOptions, options?: StorageOptions) => Promise<void>;
|
||||
getUserId: (options?: StorageOptions) => Promise<string>;
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { AccountSettings } from "./account";
|
||||
|
||||
describe("AccountSettings", () => {
|
||||
describe("fromJSON", () => {
|
||||
it("should deserialize to an instance of itself", () => {
|
||||
expect(AccountSettings.fromJSON(JSON.parse("{}"))).toBeInstanceOf(AccountSettings);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Account, AccountKeys, AccountProfile, AccountSettings } from "./account";
|
||||
import { Account, AccountKeys, AccountProfile } from "./account";
|
||||
|
||||
describe("Account", () => {
|
||||
describe("fromJSON", () => {
|
||||
@@ -9,13 +9,11 @@ describe("Account", () => {
|
||||
it("should call all the sub-fromJSONs", () => {
|
||||
const keysSpy = jest.spyOn(AccountKeys, "fromJSON");
|
||||
const profileSpy = jest.spyOn(AccountProfile, "fromJSON");
|
||||
const settingsSpy = jest.spyOn(AccountSettings, "fromJSON");
|
||||
|
||||
Account.fromJSON({});
|
||||
|
||||
expect(keysSpy).toHaveBeenCalled();
|
||||
expect(profileSpy).toHaveBeenCalled();
|
||||
expect(settingsSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { UriMatchStrategySetting } from "../../../models/domain/domain-service";
|
||||
import { GeneratorOptions } from "../../../tools/generator/generator-options";
|
||||
import {
|
||||
GeneratedPasswordHistory,
|
||||
PasswordGeneratorOptions,
|
||||
} from "../../../tools/generator/password";
|
||||
import { UsernameGeneratorOptions } from "../../../tools/generator/username/username-generation-options";
|
||||
import { DeepJsonify } from "../../../types/deep-jsonify";
|
||||
import { KdfType } from "../../enums";
|
||||
import { Utils } from "../../misc/utils";
|
||||
|
||||
import { SymmetricCryptoKey } from "./symmetric-crypto-key";
|
||||
@@ -51,26 +43,6 @@ export class EncryptionPair<TEncrypted, TDecrypted> {
|
||||
}
|
||||
}
|
||||
|
||||
export class DataEncryptionPair<TEncrypted, TDecrypted> {
|
||||
encrypted?: Record<string, TEncrypted>;
|
||||
decrypted?: TDecrypted[];
|
||||
}
|
||||
|
||||
export class AccountData {
|
||||
passwordGenerationHistory?: EncryptionPair<
|
||||
GeneratedPasswordHistory[],
|
||||
GeneratedPasswordHistory[]
|
||||
> = new EncryptionPair<GeneratedPasswordHistory[], GeneratedPasswordHistory[]>();
|
||||
|
||||
static fromJSON(obj: DeepJsonify<AccountData>): AccountData {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Object.assign(new AccountData(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
export class AccountKeys {
|
||||
publicKey?: Uint8Array;
|
||||
|
||||
@@ -127,10 +99,6 @@ export class AccountProfile {
|
||||
emailVerified?: boolean;
|
||||
lastSync?: string;
|
||||
userId?: string;
|
||||
kdfIterations?: number;
|
||||
kdfMemory?: number;
|
||||
kdfParallelism?: number;
|
||||
kdfType?: KdfType;
|
||||
|
||||
static fromJSON(obj: Jsonify<AccountProfile>): AccountProfile {
|
||||
if (obj == null) {
|
||||
@@ -141,33 +109,12 @@ export class AccountProfile {
|
||||
}
|
||||
}
|
||||
|
||||
export class AccountSettings {
|
||||
defaultUriMatch?: UriMatchStrategySetting;
|
||||
passwordGenerationOptions?: PasswordGeneratorOptions;
|
||||
usernameGenerationOptions?: UsernameGeneratorOptions;
|
||||
generatorOptions?: GeneratorOptions;
|
||||
|
||||
static fromJSON(obj: Jsonify<AccountSettings>): AccountSettings {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Object.assign(new AccountSettings(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
export class Account {
|
||||
data?: AccountData = new AccountData();
|
||||
keys?: AccountKeys = new AccountKeys();
|
||||
profile?: AccountProfile = new AccountProfile();
|
||||
settings?: AccountSettings = new AccountSettings();
|
||||
|
||||
constructor(init: Partial<Account>) {
|
||||
Object.assign(this, {
|
||||
data: {
|
||||
...new AccountData(),
|
||||
...init?.data,
|
||||
},
|
||||
keys: {
|
||||
...new AccountKeys(),
|
||||
...init?.keys,
|
||||
@@ -176,10 +123,6 @@ export class Account {
|
||||
...new AccountProfile(),
|
||||
...init?.profile,
|
||||
},
|
||||
settings: {
|
||||
...new AccountSettings(),
|
||||
...init?.settings,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -190,9 +133,7 @@ export class Account {
|
||||
|
||||
return Object.assign(new Account({}), json, {
|
||||
keys: AccountKeys.fromJSON(json?.keys),
|
||||
data: AccountData.fromJSON(json?.data),
|
||||
profile: AccountProfile.fromJSON(json?.profile),
|
||||
settings: AccountSettings.fromJSON(json?.settings),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@ import { Jsonify, JsonValue } from "type-fest";
|
||||
import { AccountService } from "../../auth/abstractions/account.service";
|
||||
import { TokenService } from "../../auth/abstractions/token.service";
|
||||
import { BiometricKey } from "../../auth/types/biometric-key";
|
||||
import { GeneratorOptions } from "../../tools/generator/generator-options";
|
||||
import { GeneratedPasswordHistory, PasswordGeneratorOptions } from "../../tools/generator/password";
|
||||
import { UsernameGeneratorOptions } from "../../tools/generator/username";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { EnvironmentService } from "../abstractions/environment.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
@@ -17,8 +14,7 @@ import {
|
||||
import { AbstractStorageService } from "../abstractions/storage.service";
|
||||
import { HtmlStorageLocation, StorageLocation } from "../enums";
|
||||
import { StateFactory } from "../factories/state-factory";
|
||||
import { Utils } from "../misc/utils";
|
||||
import { Account, AccountData, AccountSettings } from "../models/domain/account";
|
||||
import { Account } from "../models/domain/account";
|
||||
import { GlobalState } from "../models/domain/global-state";
|
||||
import { State } from "../models/domain/state";
|
||||
import { StorageOptions } from "../models/domain/storage-options";
|
||||
@@ -306,29 +302,6 @@ export class StateService<
|
||||
await this.saveSecureStorageKey(partialKeys.biometricKey, value, options);
|
||||
}
|
||||
|
||||
@withPrototypeForArrayMembers(GeneratedPasswordHistory)
|
||||
async getDecryptedPasswordGenerationHistory(
|
||||
options?: StorageOptions,
|
||||
): Promise<GeneratedPasswordHistory[]> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
)?.data?.passwordGenerationHistory?.decrypted;
|
||||
}
|
||||
|
||||
async setDecryptedPasswordGenerationHistory(
|
||||
value: GeneratedPasswordHistory[],
|
||||
options?: StorageOptions,
|
||||
): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
account.data.passwordGenerationHistory.decrypted = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getDuckDuckGoSharedKey(options?: StorageOptions): Promise<string> {
|
||||
options = this.reconcileOptions(options, await this.defaultSecureStorageOptions());
|
||||
if (options?.userId == null) {
|
||||
@@ -370,29 +343,6 @@ export class StateService<
|
||||
)?.keys.cryptoSymmetricKey.encrypted;
|
||||
}
|
||||
|
||||
@withPrototypeForArrayMembers(GeneratedPasswordHistory)
|
||||
async getEncryptedPasswordGenerationHistory(
|
||||
options?: StorageOptions,
|
||||
): Promise<GeneratedPasswordHistory[]> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
|
||||
)?.data?.passwordGenerationHistory?.encrypted;
|
||||
}
|
||||
|
||||
async setEncryptedPasswordGenerationHistory(
|
||||
value: GeneratedPasswordHistory[],
|
||||
options?: StorageOptions,
|
||||
): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
account.data.passwordGenerationHistory.encrypted = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getIsAuthenticated(options?: StorageOptions): Promise<boolean> {
|
||||
return (
|
||||
(await this.tokenService.getAccessToken(options?.userId as UserId)) != null &&
|
||||
@@ -417,63 +367,6 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
async getPasswordGenerationOptions(options?: StorageOptions): Promise<PasswordGeneratorOptions> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||
)?.settings?.passwordGenerationOptions;
|
||||
}
|
||||
|
||||
async setPasswordGenerationOptions(
|
||||
value: PasswordGeneratorOptions,
|
||||
options?: StorageOptions,
|
||||
): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()),
|
||||
);
|
||||
account.settings.passwordGenerationOptions = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getUsernameGenerationOptions(options?: StorageOptions): Promise<UsernameGeneratorOptions> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||
)?.settings?.usernameGenerationOptions;
|
||||
}
|
||||
|
||||
async setUsernameGenerationOptions(
|
||||
value: UsernameGeneratorOptions,
|
||||
options?: StorageOptions,
|
||||
): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()),
|
||||
);
|
||||
account.settings.usernameGenerationOptions = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getGeneratorOptions(options?: StorageOptions): Promise<GeneratorOptions> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||
)?.settings?.generatorOptions;
|
||||
}
|
||||
|
||||
async setGeneratorOptions(value: GeneratorOptions, options?: StorageOptions): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()),
|
||||
);
|
||||
account.settings.generatorOptions = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getUserId(options?: StorageOptions): Promise<string> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions()))
|
||||
@@ -629,19 +522,6 @@ export class StateService<
|
||||
// TODO: There is a tech debt item for splitting up these methods - only Web uses multiple storage locations in its storageService.
|
||||
// For now these methods exist with some redundancy to facilitate this special web requirement.
|
||||
protected async scaffoldNewAccountLocalStorage(account: TAccount): Promise<void> {
|
||||
const storedAccount = await this.getAccount(
|
||||
this.reconcileOptions(
|
||||
{ userId: account.profile.userId },
|
||||
await this.defaultOnDiskLocalOptions(),
|
||||
),
|
||||
);
|
||||
if (storedAccount?.settings != null) {
|
||||
account.settings = storedAccount.settings;
|
||||
} else if (await this.storageService.has(keys.tempAccountSettings)) {
|
||||
account.settings = await this.storageService.get<AccountSettings>(keys.tempAccountSettings);
|
||||
await this.storageService.remove(keys.tempAccountSettings);
|
||||
}
|
||||
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(
|
||||
@@ -652,15 +532,6 @@ export class StateService<
|
||||
}
|
||||
|
||||
protected async scaffoldNewAccountMemoryStorage(account: TAccount): Promise<void> {
|
||||
const storedAccount = await this.getAccount(
|
||||
this.reconcileOptions(
|
||||
{ userId: account.profile.userId },
|
||||
await this.defaultOnDiskMemoryOptions(),
|
||||
),
|
||||
);
|
||||
if (storedAccount?.settings != null) {
|
||||
account.settings = storedAccount.settings;
|
||||
}
|
||||
await this.storageService.save(
|
||||
account.profile.userId,
|
||||
account,
|
||||
@@ -676,12 +547,6 @@ export class StateService<
|
||||
}
|
||||
|
||||
protected async scaffoldNewAccountSessionStorage(account: TAccount): Promise<void> {
|
||||
const storedAccount = await this.getAccount(
|
||||
this.reconcileOptions({ userId: account.profile.userId }, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
if (storedAccount?.settings != null) {
|
||||
account.settings = storedAccount.settings;
|
||||
}
|
||||
await this.storageService.save(
|
||||
account.profile.userId,
|
||||
account,
|
||||
@@ -830,20 +695,8 @@ export class StateService<
|
||||
|
||||
// settings persist even on reset, and are not affected by this method
|
||||
protected resetAccount(account: TAccount) {
|
||||
const persistentAccountInformation = {
|
||||
settings: account.settings,
|
||||
};
|
||||
return Object.assign(this.createAccount(), persistentAccountInformation);
|
||||
}
|
||||
|
||||
async clearDecryptedData(userId: UserId): Promise<void> {
|
||||
await this.updateState(async (state) => {
|
||||
if (userId != null && state?.accounts[userId]?.data != null) {
|
||||
state.accounts[userId].data = new AccountData();
|
||||
}
|
||||
|
||||
return state;
|
||||
});
|
||||
// All settings have been moved to StateProviders
|
||||
return this.createAccount();
|
||||
}
|
||||
|
||||
protected createAccount(init: Partial<TAccount> = null): TAccount {
|
||||
@@ -904,51 +757,3 @@ export class StateService<
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function withPrototypeForArrayMembers<T>(
|
||||
memberConstructor: new (...args: any[]) => T,
|
||||
memberConverter: (input: any) => T = (i) => i,
|
||||
): (
|
||||
target: any,
|
||||
propertyKey: string | symbol,
|
||||
descriptor: PropertyDescriptor,
|
||||
) => { value: (...args: any[]) => Promise<T[]> } {
|
||||
return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
|
||||
const originalMethod = descriptor.value;
|
||||
|
||||
return {
|
||||
value: function (...args: any[]) {
|
||||
const originalResult: Promise<any[]> = originalMethod.apply(this, args);
|
||||
|
||||
if (!Utils.isPromise(originalResult)) {
|
||||
throw new Error(
|
||||
`Error applying prototype to stored value -- result is not a promise for method ${String(
|
||||
propertyKey,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
return originalResult.then((result) => {
|
||||
if (result == null) {
|
||||
return null;
|
||||
} else if (!(result instanceof Array)) {
|
||||
throw new Error(
|
||||
`Attempted to retrieve non array type from state as an array for method ${String(
|
||||
propertyKey,
|
||||
)}`,
|
||||
);
|
||||
} else {
|
||||
return result.map((r) => {
|
||||
return r == null ||
|
||||
r.constructor.name === memberConstructor.prototype.constructor.name
|
||||
? r
|
||||
: memberConverter(
|
||||
Object.create(memberConstructor.prototype, Object.getOwnPropertyDescriptors(r)),
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import { VaultTimeoutAction } from "../../enums/vault-timeout-action.enum";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { MessagingService } from "../abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "../abstractions/platform-utils.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { SystemService as SystemServiceAbstraction } from "../abstractions/system.service";
|
||||
import { BiometricStateService } from "../biometrics/biometric-state.service";
|
||||
import { Utils } from "../misc/utils";
|
||||
@@ -25,7 +24,6 @@ export class SystemService implements SystemServiceAbstraction {
|
||||
private messagingService: MessagingService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private reloadCallback: () => Promise<void> = null,
|
||||
private stateService: StateService,
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||
private biometricStateService: BiometricStateService,
|
||||
@@ -90,8 +88,6 @@ export class SystemService implements SystemServiceAbstraction {
|
||||
const nextUser = await firstValueFrom(
|
||||
this.accountService.nextUpAccount$.pipe(map((account) => account?.id ?? null)),
|
||||
);
|
||||
// Can be removed once we migrate password generation history to state providers
|
||||
await this.stateService.clearDecryptedData(activeUserId);
|
||||
await this.accountService.switchAccount(nextUser);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user