mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
[PM-5273] Migrate state in CipherService (#8314)
* PM-5273 Initial migration work for localData
* PM-5273 Encrypted and Decrypted ciphers migration to state provider
* pm-5273 Update references
* pm5273 Ensure prototype on cipher
* PM-5273 Add CipherId
* PM-5273 Remove migrated methods and updated references
* pm-5273 Fix versions
* PM-5273 Added missing options
* Conflict resolution
* Revert "Conflict resolution"
This reverts commit 0c0c2039ed.
* PM-5273 Fix PR comments
* Pm-5273 Fix comments
* PM-5273 Changed decryptedCiphers to use ActiveUserState
* PM-5273 Fix tests
* PM-5273 Fix pr comments
This commit is contained in:
@@ -6,10 +6,6 @@ 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 { CipherData } from "../../vault/models/data/cipher.data";
|
||||
import { LocalData } from "../../vault/models/data/local.data";
|
||||
import { CipherView } from "../../vault/models/view/cipher.view";
|
||||
import { AddEditCipherInfo } from "../../vault/types/add-edit-cipher-info";
|
||||
import { KdfType } from "../enums";
|
||||
import { Account } from "../models/domain/account";
|
||||
import { EncString } from "../models/domain/enc-string";
|
||||
@@ -38,8 +34,6 @@ export abstract class StateService<T extends Account = Account> {
|
||||
clean: (options?: StorageOptions) => Promise<UserId>;
|
||||
init: (initOptions?: InitOptions) => Promise<void>;
|
||||
|
||||
getAddEditCipherInfo: (options?: StorageOptions) => Promise<AddEditCipherInfo>;
|
||||
setAddEditCipherInfo: (value: AddEditCipherInfo, options?: StorageOptions) => Promise<void>;
|
||||
/**
|
||||
* Gets the user's auto key
|
||||
*/
|
||||
@@ -104,8 +98,6 @@ export abstract class StateService<T extends Account = Account> {
|
||||
* @deprecated For migration purposes only, use setUserKeyBiometric instead
|
||||
*/
|
||||
setCryptoMasterKeyBiometric: (value: BiometricKey, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedCiphers: (options?: StorageOptions) => Promise<CipherView[]>;
|
||||
setDecryptedCiphers: (value: CipherView[], options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedPasswordGenerationHistory: (
|
||||
options?: StorageOptions,
|
||||
) => Promise<GeneratedPasswordHistory[]>;
|
||||
@@ -134,11 +126,6 @@ export abstract class StateService<T extends Account = Account> {
|
||||
value: boolean,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getEncryptedCiphers: (options?: StorageOptions) => Promise<{ [id: string]: CipherData }>;
|
||||
setEncryptedCiphers: (
|
||||
value: { [id: string]: CipherData },
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getEncryptedPasswordGenerationHistory: (
|
||||
options?: StorageOptions,
|
||||
) => Promise<GeneratedPasswordHistory[]>;
|
||||
@@ -165,11 +152,6 @@ export abstract class StateService<T extends Account = Account> {
|
||||
setLastActive: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getLastSync: (options?: StorageOptions) => Promise<string>;
|
||||
setLastSync: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getLocalData: (options?: StorageOptions) => Promise<{ [cipherId: string]: LocalData }>;
|
||||
setLocalData: (
|
||||
value: { [cipherId: string]: LocalData },
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getMinimizeOnCopyToClipboard: (options?: StorageOptions) => Promise<boolean>;
|
||||
setMinimizeOnCopyToClipboard: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getOrganizationInvitation: (options?: StorageOptions) => Promise<any>;
|
||||
|
||||
@@ -8,9 +8,6 @@ import {
|
||||
} from "../../../tools/generator/password";
|
||||
import { UsernameGeneratorOptions } from "../../../tools/generator/username/username-generation-options";
|
||||
import { DeepJsonify } from "../../../types/deep-jsonify";
|
||||
import { CipherData } from "../../../vault/models/data/cipher.data";
|
||||
import { CipherView } from "../../../vault/models/view/cipher.view";
|
||||
import { AddEditCipherInfo } from "../../../vault/types/add-edit-cipher-info";
|
||||
import { KdfType } from "../../enums";
|
||||
import { Utils } from "../../misc/utils";
|
||||
|
||||
@@ -61,28 +58,17 @@ export class DataEncryptionPair<TEncrypted, TDecrypted> {
|
||||
}
|
||||
|
||||
export class AccountData {
|
||||
ciphers?: DataEncryptionPair<CipherData, CipherView> = new DataEncryptionPair<
|
||||
CipherData,
|
||||
CipherView
|
||||
>();
|
||||
localData?: any;
|
||||
passwordGenerationHistory?: EncryptionPair<
|
||||
GeneratedPasswordHistory[],
|
||||
GeneratedPasswordHistory[]
|
||||
> = new EncryptionPair<GeneratedPasswordHistory[], GeneratedPasswordHistory[]>();
|
||||
addEditCipherInfo?: AddEditCipherInfo;
|
||||
|
||||
static fromJSON(obj: DeepJsonify<AccountData>): AccountData {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Object.assign(new AccountData(), obj, {
|
||||
addEditCipherInfo: {
|
||||
cipher: CipherView.fromJSON(obj?.addEditCipherInfo?.cipher),
|
||||
collectionIds: obj?.addEditCipherInfo?.collectionIds,
|
||||
},
|
||||
});
|
||||
return Object.assign(new AccountData(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,6 @@ 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 { CipherData } from "../../vault/models/data/cipher.data";
|
||||
import { LocalData } from "../../vault/models/data/local.data";
|
||||
import { CipherView } from "../../vault/models/view/cipher.view";
|
||||
import { AddEditCipherInfo } from "../../vault/types/add-edit-cipher-info";
|
||||
import { EnvironmentService } from "../abstractions/environment.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import {
|
||||
@@ -221,34 +217,6 @@ export class StateService<
|
||||
return currentUser as UserId;
|
||||
}
|
||||
|
||||
async getAddEditCipherInfo(options?: StorageOptions): Promise<AddEditCipherInfo> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
// ensure prototype on cipher
|
||||
const raw = account?.data?.addEditCipherInfo;
|
||||
return raw == null
|
||||
? null
|
||||
: {
|
||||
cipher:
|
||||
raw?.cipher.toJSON != null
|
||||
? raw.cipher
|
||||
: CipherView.fromJSON(raw?.cipher as Jsonify<CipherView>),
|
||||
collectionIds: raw?.collectionIds,
|
||||
};
|
||||
}
|
||||
|
||||
async setAddEditCipherInfo(value: AddEditCipherInfo, options?: StorageOptions): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
account.data.addEditCipherInfo = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* user key when using the "never" option of vault timeout
|
||||
*/
|
||||
@@ -465,24 +433,6 @@ export class StateService<
|
||||
await this.saveSecureStorageKey(partialKeys.biometricKey, value, options);
|
||||
}
|
||||
|
||||
@withPrototypeForArrayMembers(CipherView, CipherView.fromJSON)
|
||||
async getDecryptedCiphers(options?: StorageOptions): Promise<CipherView[]> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||
)?.data?.ciphers?.decrypted;
|
||||
}
|
||||
|
||||
async setDecryptedCiphers(value: CipherView[], options?: StorageOptions): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
account.data.ciphers.decrypted = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
@withPrototypeForArrayMembers(GeneratedPasswordHistory)
|
||||
async getDecryptedPasswordGenerationHistory(
|
||||
options?: StorageOptions,
|
||||
@@ -621,27 +571,6 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
@withPrototypeForObjectValues(CipherData)
|
||||
async getEncryptedCiphers(options?: StorageOptions): Promise<{ [id: string]: CipherData }> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()))
|
||||
)?.data?.ciphers?.encrypted;
|
||||
}
|
||||
|
||||
async setEncryptedCiphers(
|
||||
value: { [id: string]: CipherData },
|
||||
options?: StorageOptions,
|
||||
): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()),
|
||||
);
|
||||
account.data.ciphers.encrypted = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskMemoryOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use UserKey instead
|
||||
*/
|
||||
@@ -805,26 +734,6 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
async getLocalData(options?: StorageOptions): Promise<{ [cipherId: string]: LocalData }> {
|
||||
return (
|
||||
await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()))
|
||||
)?.data?.localData;
|
||||
}
|
||||
|
||||
async setLocalData(
|
||||
value: { [cipherId: string]: LocalData },
|
||||
options?: StorageOptions,
|
||||
): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()),
|
||||
);
|
||||
account.data.localData = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
async getMinimizeOnCopyToClipboard(options?: StorageOptions): Promise<boolean> {
|
||||
return (
|
||||
(await this.getAccount(this.reconcileOptions(options, await this.defaultOnDiskOptions())))
|
||||
@@ -1510,50 +1419,3 @@ function withPrototypeForArrayMembers<T>(
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function withPrototypeForObjectValues<T>(
|
||||
valuesConstructor: new (...args: any[]) => T,
|
||||
valuesConverter: (input: any) => T = (i) => i,
|
||||
): (
|
||||
target: any,
|
||||
propertyKey: string | symbol,
|
||||
descriptor: PropertyDescriptor,
|
||||
) => { value: (...args: any[]) => Promise<{ [key: string]: T }> } {
|
||||
return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
|
||||
const originalMethod = descriptor.value;
|
||||
|
||||
return {
|
||||
value: function (...args: any[]) {
|
||||
const originalResult: Promise<{ [key: string]: T }> = 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 {
|
||||
for (const [key, val] of Object.entries(result)) {
|
||||
result[key] =
|
||||
val == null || val.constructor.name === valuesConstructor.prototype.constructor.name
|
||||
? valuesConverter(val)
|
||||
: valuesConverter(
|
||||
Object.create(
|
||||
valuesConstructor.prototype,
|
||||
Object.getOwnPropertyDescriptors(val),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return result as { [key: string]: T };
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -135,3 +135,8 @@ export const VAULT_SETTINGS_DISK = new StateDefinition("vaultSettings", "disk",
|
||||
});
|
||||
export const VAULT_BROWSER_MEMORY = new StateDefinition("vaultBrowser", "memory");
|
||||
export const VAULT_SEARCH_MEMORY = new StateDefinition("vaultSearch", "memory");
|
||||
export const CIPHERS_DISK = new StateDefinition("ciphers", "disk", { web: "memory" });
|
||||
export const CIPHERS_DISK_LOCAL = new StateDefinition("ciphersLocal", "disk", {
|
||||
web: "disk-local",
|
||||
});
|
||||
export const CIPHERS_MEMORY = new StateDefinition("ciphersMemory", "memory");
|
||||
|
||||
Reference in New Issue
Block a user