mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-25 12:15:18 +01:00
Add to/fromJSON methods to State and Accounts
This is needed since all storage in manifest v3 is key-value-pair-based and session storage of most data is actually serialized into an encrypted string.
This commit is contained in:
parent
719a6d095e
commit
77bbd3a863
@ -116,8 +116,8 @@ describe("State Migration Service", () => {
|
|||||||
key: "orgThreeEncKey",
|
key: "orgThreeEncKey",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
} as any,
|
||||||
},
|
} as any,
|
||||||
});
|
});
|
||||||
|
|
||||||
const migratedAccount = await (stateMigrationService as any).migrateAccountFrom4To5(
|
const migratedAccount = await (stateMigrationService as any).migrateAccountFrom4To5(
|
||||||
|
9
libs/common/src/models/domain/account-profile.spec.ts
Normal file
9
libs/common/src/models/domain/account-profile.spec.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { AccountProfile } from "./account";
|
||||||
|
|
||||||
|
describe("AccountProfile", () => {
|
||||||
|
describe("fromJSON", () => {
|
||||||
|
it("should deserialize to an instance of itself", () => {
|
||||||
|
expect(AccountProfile.fromJSON({})).toBeInstanceOf(AccountProfile);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
25
libs/common/src/models/domain/account-settings.spec.ts
Normal file
25
libs/common/src/models/domain/account-settings.spec.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { AccountSettings, EncryptionPair } from "./account";
|
||||||
|
import { EncString } from "./encString";
|
||||||
|
|
||||||
|
describe("AccountSettings", () => {
|
||||||
|
describe("fromJSON", () => {
|
||||||
|
it("should deserialize to an instance of itself", () => {
|
||||||
|
expect(AccountSettings.fromJSON(JSON.parse("{}"))).toBeInstanceOf(AccountSettings);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deserialize pinProtected", () => {
|
||||||
|
const accountSettings = new AccountSettings();
|
||||||
|
accountSettings.pinProtected = EncryptionPair.fromJSON<string, EncString>({
|
||||||
|
encrypted: "encrypted",
|
||||||
|
decrypted: "3.data",
|
||||||
|
decryptedSerialized: null,
|
||||||
|
});
|
||||||
|
const jsonObj = JSON.parse(JSON.stringify(accountSettings));
|
||||||
|
const actual = AccountSettings.fromJSON(jsonObj);
|
||||||
|
|
||||||
|
expect(actual.pinProtected).toBeInstanceOf(EncryptionPair);
|
||||||
|
expect(actual.pinProtected.encrypted).toEqual("encrypted");
|
||||||
|
expect(actual.pinProtected.decrypted.encryptedString).toEqual("3.data");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
9
libs/common/src/models/domain/account-tokens.spec.ts
Normal file
9
libs/common/src/models/domain/account-tokens.spec.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { AccountTokens } from "./account";
|
||||||
|
|
||||||
|
describe("AccountTokens", () => {
|
||||||
|
describe("fromJSON", () => {
|
||||||
|
it("should deserialize to an instance of itself", () => {
|
||||||
|
expect(AccountTokens.fromJSON({})).toBeInstanceOf(AccountTokens);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
23
libs/common/src/models/domain/account.spec.ts
Normal file
23
libs/common/src/models/domain/account.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Account, AccountKeys, AccountProfile, AccountSettings, AccountTokens } from "./account";
|
||||||
|
|
||||||
|
describe("Account", () => {
|
||||||
|
describe("fromJSON", () => {
|
||||||
|
it("should deserialize to an instance of itself", () => {
|
||||||
|
expect(Account.fromJSON({})).toBeInstanceOf(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");
|
||||||
|
const tokensSpy = jest.spyOn(AccountTokens, "fromJSON");
|
||||||
|
|
||||||
|
Account.fromJSON({});
|
||||||
|
|
||||||
|
expect(keysSpy).toHaveBeenCalled();
|
||||||
|
expect(profileSpy).toHaveBeenCalled();
|
||||||
|
expect(settingsSpy).toHaveBeenCalled();
|
||||||
|
expect(tokensSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,3 +1,7 @@
|
|||||||
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
|
import { Utils } from "@bitwarden/common/misc/utils";
|
||||||
|
|
||||||
import { AuthenticationStatus } from "../../enums/authenticationStatus";
|
import { AuthenticationStatus } from "../../enums/authenticationStatus";
|
||||||
import { KdfType } from "../../enums/kdfType";
|
import { KdfType } from "../../enums/kdfType";
|
||||||
import { UriMatchType } from "../../enums/uriMatchType";
|
import { UriMatchType } from "../../enums/uriMatchType";
|
||||||
@ -23,7 +27,39 @@ import { SymmetricCryptoKey } from "./symmetricCryptoKey";
|
|||||||
export class EncryptionPair<TEncrypted, TDecrypted> {
|
export class EncryptionPair<TEncrypted, TDecrypted> {
|
||||||
encrypted?: TEncrypted;
|
encrypted?: TEncrypted;
|
||||||
decrypted?: TDecrypted;
|
decrypted?: TDecrypted;
|
||||||
decryptedSerialized?: string;
|
private decryptedSerialized?: string;
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
encrypted: this.encrypted,
|
||||||
|
decrypted: this.decrypted,
|
||||||
|
decryptedSerialized:
|
||||||
|
this.decrypted instanceof ArrayBuffer ? Utils.fromBufferToByteString(this.decrypted) : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON<TEncrypted, TDecrypted>(
|
||||||
|
obj: Jsonify<EncryptionPair<Jsonify<TEncrypted>, Jsonify<TDecrypted>>>,
|
||||||
|
decryptedFromJson?: (obj: Jsonify<TDecrypted>) => TDecrypted,
|
||||||
|
encryptedFromJson?: (obj: Jsonify<TEncrypted>) => TEncrypted
|
||||||
|
) {
|
||||||
|
const pair = new EncryptionPair<TEncrypted, TDecrypted>();
|
||||||
|
if (obj?.encrypted) {
|
||||||
|
pair.encrypted = encryptedFromJson
|
||||||
|
? encryptedFromJson(obj.encrypted as any)
|
||||||
|
: (obj.encrypted as TEncrypted);
|
||||||
|
}
|
||||||
|
if (obj?.decryptedSerialized) {
|
||||||
|
pair.decryptedSerialized = obj.decryptedSerialized;
|
||||||
|
// We only populate the decryptedSerialized if the decrypted is an arraybuffer.
|
||||||
|
pair.decrypted = Utils.fromByteStringToArray(obj.decryptedSerialized)?.buffer as any;
|
||||||
|
} else if (obj?.decrypted) {
|
||||||
|
pair.decrypted = decryptedFromJson
|
||||||
|
? decryptedFromJson(obj.decrypted as any)
|
||||||
|
: (obj.decrypted as TDecrypted);
|
||||||
|
}
|
||||||
|
return pair;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DataEncryptionPair<TEncrypted, TDecrypted> {
|
export class DataEncryptionPair<TEncrypted, TDecrypted> {
|
||||||
@ -83,8 +119,50 @@ export class AccountKeys {
|
|||||||
>();
|
>();
|
||||||
privateKey?: EncryptionPair<string, ArrayBuffer> = new EncryptionPair<string, ArrayBuffer>();
|
privateKey?: EncryptionPair<string, ArrayBuffer> = new EncryptionPair<string, ArrayBuffer>();
|
||||||
publicKey?: ArrayBuffer;
|
publicKey?: ArrayBuffer;
|
||||||
publicKeySerialized?: string;
|
private publicKeySerialized?: string;
|
||||||
apiKeyClientSecret?: string;
|
apiKeyClientSecret?: string;
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
this.publicKeySerialized = Utils.fromBufferToByteString(this.publicKey);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJSON(obj: any): AccountKeys {
|
||||||
|
return Object.assign(
|
||||||
|
new AccountKeys(),
|
||||||
|
{ cryptoMasterKey: SymmetricCryptoKey.fromJSON(obj?.cryptoMasterKey) },
|
||||||
|
{
|
||||||
|
cryptoSymmetricKey: EncryptionPair.fromJSON(
|
||||||
|
obj?.cryptoSymmetricKey,
|
||||||
|
SymmetricCryptoKey.fromJSON
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
organizationKeys: EncryptionPair.fromJSON(obj?.organizationKeys, (obj: any) => {
|
||||||
|
const map = new Map<string, SymmetricCryptoKey>();
|
||||||
|
for (const orgId in obj) {
|
||||||
|
map.set(orgId, SymmetricCryptoKey.fromJSON(obj[orgId]));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
providerKeys: EncryptionPair.fromJSON(obj?.providerKeys, (obj: any) => {
|
||||||
|
const map = new Map<string, SymmetricCryptoKey>();
|
||||||
|
for (const providerId in obj) {
|
||||||
|
map.set(providerId, SymmetricCryptoKey.fromJSON(obj[providerId]));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
privateKey: EncryptionPair.fromJSON(obj?.privateKey),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
publicKey: Utils.fromByteStringToArray(obj?.publicKeySerialized)?.buffer,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AccountProfile {
|
export class AccountProfile {
|
||||||
@ -105,6 +183,10 @@ export class AccountProfile {
|
|||||||
keyHash?: string;
|
keyHash?: string;
|
||||||
kdfIterations?: number;
|
kdfIterations?: number;
|
||||||
kdfType?: KdfType;
|
kdfType?: KdfType;
|
||||||
|
|
||||||
|
static fromJSON(obj: Jsonify<AccountProfile>): AccountProfile {
|
||||||
|
return Object.assign(new AccountProfile(), obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AccountSettings {
|
export class AccountSettings {
|
||||||
@ -140,6 +222,15 @@ export class AccountSettings {
|
|||||||
settings?: AccountSettingsSettings; // TODO: Merge whatever is going on here into the AccountSettings model properly
|
settings?: AccountSettingsSettings; // TODO: Merge whatever is going on here into the AccountSettings model properly
|
||||||
vaultTimeout?: number;
|
vaultTimeout?: number;
|
||||||
vaultTimeoutAction?: string = "lock";
|
vaultTimeoutAction?: string = "lock";
|
||||||
|
|
||||||
|
static fromJSON(obj: Jsonify<AccountSettings>): AccountSettings {
|
||||||
|
return Object.assign(new AccountSettings(), obj, {
|
||||||
|
pinProtected: EncryptionPair.fromJSON<string, EncString>(
|
||||||
|
obj?.pinProtected,
|
||||||
|
EncString.fromJSON
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AccountSettingsSettings = {
|
export type AccountSettingsSettings = {
|
||||||
@ -151,6 +242,10 @@ export class AccountTokens {
|
|||||||
decodedToken?: any;
|
decodedToken?: any;
|
||||||
refreshToken?: string;
|
refreshToken?: string;
|
||||||
securityStamp?: string;
|
securityStamp?: string;
|
||||||
|
|
||||||
|
static fromJSON(obj: Jsonify<AccountTokens>): AccountTokens {
|
||||||
|
return Object.assign(new AccountTokens(), obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Account {
|
export class Account {
|
||||||
@ -184,4 +279,13 @@ export class Account {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fromJSON(json: any): Account {
|
||||||
|
return Object.assign(new Account({}), json, {
|
||||||
|
keys: AccountKeys.fromJSON(json?.keys as any),
|
||||||
|
profile: AccountProfile.fromJSON(json?.profile),
|
||||||
|
settings: AccountSettings.fromJSON(json?.settings as any),
|
||||||
|
tokens: AccountTokens.fromJSON(json?.tokens as any),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
68
libs/common/src/models/domain/acount-keys.spec.ts
Normal file
68
libs/common/src/models/domain/acount-keys.spec.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { Utils } from "@bitwarden/common/misc/utils";
|
||||||
|
|
||||||
|
import { makeStaticByteArray } from "../../../spec/utils";
|
||||||
|
|
||||||
|
import { AccountKeys, EncryptionPair } from "./account";
|
||||||
|
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
|
||||||
|
|
||||||
|
describe("AccountKeys", () => {
|
||||||
|
describe("toJSON", () => {
|
||||||
|
it("should serialize itself", () => {
|
||||||
|
const keys = new AccountKeys();
|
||||||
|
const buffer = makeStaticByteArray(64).buffer;
|
||||||
|
const symmetricKey = new SymmetricCryptoKey(buffer);
|
||||||
|
keys.cryptoMasterKey = symmetricKey;
|
||||||
|
keys.publicKey = buffer;
|
||||||
|
keys.cryptoSymmetricKey = new EncryptionPair<string, SymmetricCryptoKey>();
|
||||||
|
keys.cryptoSymmetricKey.decrypted = symmetricKey;
|
||||||
|
|
||||||
|
const symmetricKeySpy = jest.spyOn(symmetricKey, "toJSON");
|
||||||
|
const actual = JSON.stringify(keys.toJSON());
|
||||||
|
expect(symmetricKeySpy).toHaveBeenCalled();
|
||||||
|
expect(actual).toContain(`"cryptoMasterKey":${JSON.stringify(symmetricKey.toJSON())}`);
|
||||||
|
expect(actual).toContain(
|
||||||
|
`"publicKeySerialized":${JSON.stringify(Utils.fromBufferToByteString(buffer))}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should serialize public key as a string", () => {
|
||||||
|
const keys = new AccountKeys();
|
||||||
|
keys.publicKey = Utils.fromByteStringToArray("hello").buffer;
|
||||||
|
const json = JSON.stringify(keys);
|
||||||
|
expect(json).toContain('"publicKeySerialized":"hello"');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("fromJSON", () => {
|
||||||
|
it("should deserialize public key to a buffer", () => {
|
||||||
|
const keys = AccountKeys.fromJSON({
|
||||||
|
publicKeySerialized: "hello",
|
||||||
|
});
|
||||||
|
expect(keys.publicKey).toEqual(Utils.fromByteStringToArray("hello").buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deserialize cryptoMasterKey", () => {
|
||||||
|
const spy = jest.spyOn(SymmetricCryptoKey, "fromJSON");
|
||||||
|
AccountKeys.fromJSON({});
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deserialize organizationKeys", () => {
|
||||||
|
const spy = jest.spyOn(SymmetricCryptoKey, "fromJSON");
|
||||||
|
AccountKeys.fromJSON({ organizationKeys: [{ orgId: "keyJSON" }] });
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deserialize providerKeys", () => {
|
||||||
|
const spy = jest.spyOn(SymmetricCryptoKey, "fromJSON");
|
||||||
|
AccountKeys.fromJSON({ providerKeys: [{ providerId: "keyJSON" }] });
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deserialize privateKey", () => {
|
||||||
|
const spy = jest.spyOn(EncryptionPair, "fromJSON");
|
||||||
|
AccountKeys.fromJSON({ privateKey: { encrypted: "encrypted", decrypted: "decrypted" } });
|
||||||
|
expect(spy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
44
libs/common/src/models/domain/encryption-pair.spec.ts
Normal file
44
libs/common/src/models/domain/encryption-pair.spec.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { Utils } from "@bitwarden/common/misc/utils";
|
||||||
|
|
||||||
|
import { EncryptionPair } from "./account";
|
||||||
|
|
||||||
|
describe("EncryptionPair", () => {
|
||||||
|
describe("toJSON", () => {
|
||||||
|
it("should populate decryptedSerialized for buffer arrays", () => {
|
||||||
|
const pair = new EncryptionPair<string, ArrayBuffer>();
|
||||||
|
pair.decrypted = Utils.fromByteStringToArray("hello").buffer;
|
||||||
|
const json = pair.toJSON();
|
||||||
|
expect(json.decryptedSerialized).toEqual("hello");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should serialize encrypted and decrypted", () => {
|
||||||
|
const pair = new EncryptionPair<string, string>();
|
||||||
|
pair.encrypted = "hello";
|
||||||
|
pair.decrypted = "world";
|
||||||
|
const json = pair.toJSON();
|
||||||
|
expect(json.encrypted).toEqual("hello");
|
||||||
|
expect(json.decrypted).toEqual("world");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("fromJSON", () => {
|
||||||
|
it("should deserialize encrypted and decrypted", () => {
|
||||||
|
const pair = EncryptionPair.fromJSON({
|
||||||
|
encrypted: "hello",
|
||||||
|
decrypted: "world",
|
||||||
|
decryptedSerialized: null,
|
||||||
|
});
|
||||||
|
expect(pair.encrypted).toEqual("hello");
|
||||||
|
expect(pair.decrypted).toEqual("world");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deserialize decryptedSerialized for buffer arrays", () => {
|
||||||
|
const pair = EncryptionPair.fromJSON<string, ArrayBuffer>({
|
||||||
|
encrypted: "encrypted",
|
||||||
|
decrypted: null,
|
||||||
|
decryptedSerialized: "hello",
|
||||||
|
});
|
||||||
|
expect(pair.decrypted).toEqual(Utils.fromByteStringToArray("hello").buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
28
libs/common/src/models/domain/state.spec.ts
Normal file
28
libs/common/src/models/domain/state.spec.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { Account } from "./account";
|
||||||
|
import { State } from "./state";
|
||||||
|
|
||||||
|
describe("state", () => {
|
||||||
|
describe("fromJSON", () => {
|
||||||
|
it("should deserialize to an instance of itself", () => {
|
||||||
|
expect(State.fromJSON({})).toBeInstanceOf(State);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should always assign an object to accounts", () => {
|
||||||
|
const state = State.fromJSON({});
|
||||||
|
expect(state.accounts).not.toBeNull();
|
||||||
|
expect(state.accounts).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should build an account map", () => {
|
||||||
|
const accountsSpy = jest.spyOn(Account, "fromJSON");
|
||||||
|
const state = State.fromJSON({
|
||||||
|
accounts: {
|
||||||
|
userId: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(state.accounts["userId"]).toBeInstanceOf(Account);
|
||||||
|
expect(accountsSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,3 +1,5 @@
|
|||||||
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
import { Account } from "./account";
|
import { Account } from "./account";
|
||||||
import { GlobalState } from "./globalState";
|
import { GlobalState } from "./globalState";
|
||||||
|
|
||||||
@ -14,4 +16,26 @@ export class State<
|
|||||||
constructor(globals: TGlobalState) {
|
constructor(globals: TGlobalState) {
|
||||||
this.globals = globals;
|
this.globals = globals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO, make Jsonify<State,TGlobalState,TAccount> work. It currently doesn't because Globals doesn't implement Jsonify.
|
||||||
|
static fromJSON<TGlobalState extends GlobalState, TAccount extends Account>(
|
||||||
|
obj: any
|
||||||
|
): State<TGlobalState, TAccount> {
|
||||||
|
return Object.assign(new State(null), obj, {
|
||||||
|
accounts: State.buildAccountMapFromJSON(obj?.accounts),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static buildAccountMapFromJSON(
|
||||||
|
jsonAccounts: Jsonify<{ [userId: string]: Jsonify<Account> }>
|
||||||
|
) {
|
||||||
|
if (!jsonAccounts) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const accounts: { [userId: string]: Account } = {};
|
||||||
|
for (const userId in jsonAccounts) {
|
||||||
|
accounts[userId] = Account.fromJSON(jsonAccounts[userId]);
|
||||||
|
}
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import { StorageLocation } from "../enums/storageLocation";
|
|||||||
import { ThemeType } from "../enums/themeType";
|
import { ThemeType } from "../enums/themeType";
|
||||||
import { UriMatchType } from "../enums/uriMatchType";
|
import { UriMatchType } from "../enums/uriMatchType";
|
||||||
import { StateFactory } from "../factories/stateFactory";
|
import { StateFactory } from "../factories/stateFactory";
|
||||||
import { Utils } from "../misc/utils";
|
|
||||||
import { CipherData } from "../models/data/cipherData";
|
import { CipherData } from "../models/data/cipherData";
|
||||||
import { CollectionData } from "../models/data/collectionData";
|
import { CollectionData } from "../models/data/collectionData";
|
||||||
import { EncryptedOrganizationKeyData } from "../models/data/encryptedOrganizationKeyData";
|
import { EncryptedOrganizationKeyData } from "../models/data/encryptedOrganizationKeyData";
|
||||||
@ -148,6 +147,9 @@ export class StateService<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await this.updateState(async (state) => {
|
await this.updateState(async (state) => {
|
||||||
|
if (state.accounts == null) {
|
||||||
|
state.accounts = {};
|
||||||
|
}
|
||||||
state.accounts[userId] = this.createAccount();
|
state.accounts[userId] = this.createAccount();
|
||||||
const diskAccount = await this.getAccountFromDisk({ userId: userId });
|
const diskAccount = await this.getAccountFromDisk({ userId: userId });
|
||||||
state.accounts[userId].profile = diskAccount.profile;
|
state.accounts[userId].profile = diskAccount.profile;
|
||||||
@ -494,9 +496,10 @@ export class StateService<
|
|||||||
|
|
||||||
@withPrototype(SymmetricCryptoKey, SymmetricCryptoKey.fromJSON)
|
@withPrototype(SymmetricCryptoKey, SymmetricCryptoKey.fromJSON)
|
||||||
async getCryptoMasterKey(options?: StorageOptions): Promise<SymmetricCryptoKey> {
|
async getCryptoMasterKey(options?: StorageOptions): Promise<SymmetricCryptoKey> {
|
||||||
return (
|
const account = await this.getAccount(
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
)?.keys?.cryptoMasterKey;
|
);
|
||||||
|
return account?.keys?.cryptoMasterKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setCryptoMasterKey(value: SymmetricCryptoKey, options?: StorageOptions): Promise<void> {
|
async setCryptoMasterKey(value: SymmetricCryptoKey, options?: StorageOptions): Promise<void> {
|
||||||
@ -657,9 +660,10 @@ export class StateService<
|
|||||||
|
|
||||||
@withPrototype(SymmetricCryptoKey, SymmetricCryptoKey.fromJSON)
|
@withPrototype(SymmetricCryptoKey, SymmetricCryptoKey.fromJSON)
|
||||||
async getDecryptedCryptoSymmetricKey(options?: StorageOptions): Promise<SymmetricCryptoKey> {
|
async getDecryptedCryptoSymmetricKey(options?: StorageOptions): Promise<SymmetricCryptoKey> {
|
||||||
return (
|
const account = await this.getAccount(
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
)?.keys?.cryptoSymmetricKey?.decrypted;
|
);
|
||||||
|
return account?.keys?.cryptoSymmetricKey?.decrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setDecryptedCryptoSymmetricKey(
|
async setDecryptedCryptoSymmetricKey(
|
||||||
@ -760,14 +764,9 @@ export class StateService<
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getDecryptedPrivateKey(options?: StorageOptions): Promise<ArrayBuffer> {
|
async getDecryptedPrivateKey(options?: StorageOptions): Promise<ArrayBuffer> {
|
||||||
const privateKey = (
|
return (
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||||
)?.keys?.privateKey;
|
)?.keys?.privateKey.decrypted;
|
||||||
let result = privateKey?.decrypted;
|
|
||||||
if (result == null && privateKey?.decryptedSerialized != null) {
|
|
||||||
result = Utils.fromByteStringToArray(privateKey.decryptedSerialized);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async setDecryptedPrivateKey(value: ArrayBuffer, options?: StorageOptions): Promise<void> {
|
async setDecryptedPrivateKey(value: ArrayBuffer, options?: StorageOptions): Promise<void> {
|
||||||
@ -775,8 +774,6 @@ export class StateService<
|
|||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
);
|
);
|
||||||
account.keys.privateKey.decrypted = value;
|
account.keys.privateKey.decrypted = value;
|
||||||
account.keys.privateKey.decryptedSerialized =
|
|
||||||
value == null ? null : Utils.fromBufferToByteString(value);
|
|
||||||
await this.saveAccount(
|
await this.saveAccount(
|
||||||
account,
|
account,
|
||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
@ -2015,11 +2012,7 @@ export class StateService<
|
|||||||
const keys = (
|
const keys = (
|
||||||
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
await this.getAccount(this.reconcileOptions(options, await this.defaultInMemoryOptions()))
|
||||||
)?.keys;
|
)?.keys;
|
||||||
let result = keys?.publicKey;
|
return keys?.publicKey;
|
||||||
if (result == null && keys?.publicKeySerialized != null) {
|
|
||||||
result = Utils.fromByteStringToArray(keys.publicKeySerialized);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async setPublicKey(value: ArrayBuffer, options?: StorageOptions): Promise<void> {
|
async setPublicKey(value: ArrayBuffer, options?: StorageOptions): Promise<void> {
|
||||||
@ -2027,7 +2020,6 @@ export class StateService<
|
|||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
);
|
);
|
||||||
account.keys.publicKey = value;
|
account.keys.publicKey = value;
|
||||||
account.keys.publicKeySerialized = value == null ? null : Utils.fromBufferToByteString(value);
|
|
||||||
await this.saveAccount(
|
await this.saveAccount(
|
||||||
account,
|
account,
|
||||||
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
this.reconcileOptions(options, await this.defaultInMemoryOptions())
|
||||||
@ -2718,8 +2710,11 @@ export class StateService<
|
|||||||
: await this.secureStorageService.save(`${options.userId}${key}`, value, options);
|
: await this.secureStorageService.save(`${options.userId}${key}`, value, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected state(): Promise<State<TGlobalState, TAccount>> {
|
protected async state(): Promise<State<TGlobalState, TAccount>> {
|
||||||
return this.memoryStorageService.get<State<TGlobalState, TAccount>>(keys.state);
|
const stateJson = await this.memoryStorageService.get<State<TGlobalState, TAccount>>(
|
||||||
|
keys.state
|
||||||
|
);
|
||||||
|
return State.fromJSON(stateJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setState(state: State<TGlobalState, TAccount>): Promise<void> {
|
private async setState(state: State<TGlobalState, TAccount>): Promise<void> {
|
||||||
|
@ -12,7 +12,13 @@ import { OrganizationData } from "../models/data/organizationData";
|
|||||||
import { PolicyData } from "../models/data/policyData";
|
import { PolicyData } from "../models/data/policyData";
|
||||||
import { ProviderData } from "../models/data/providerData";
|
import { ProviderData } from "../models/data/providerData";
|
||||||
import { SendData } from "../models/data/sendData";
|
import { SendData } from "../models/data/sendData";
|
||||||
import { Account, AccountSettings, AccountSettingsSettings } from "../models/domain/account";
|
import {
|
||||||
|
Account,
|
||||||
|
AccountSettings,
|
||||||
|
AccountSettingsSettings,
|
||||||
|
EncryptionPair,
|
||||||
|
} from "../models/domain/account";
|
||||||
|
import { EncString } from "../models/domain/encString";
|
||||||
import { EnvironmentUrls } from "../models/domain/environmentUrls";
|
import { EnvironmentUrls } from "../models/domain/environmentUrls";
|
||||||
import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory";
|
import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory";
|
||||||
import { GlobalState } from "../models/domain/globalState";
|
import { GlobalState } from "../models/domain/globalState";
|
||||||
@ -314,10 +320,10 @@ export class StateMigrationService<
|
|||||||
passwordGenerationOptions:
|
passwordGenerationOptions:
|
||||||
(await this.get<any>(v1Keys.passwordGenerationOptions)) ??
|
(await this.get<any>(v1Keys.passwordGenerationOptions)) ??
|
||||||
defaultAccount.settings.passwordGenerationOptions,
|
defaultAccount.settings.passwordGenerationOptions,
|
||||||
pinProtected: {
|
pinProtected: Object.assign(new EncryptionPair<string, EncString>(), {
|
||||||
decrypted: null,
|
decrypted: null,
|
||||||
encrypted: await this.get<string>(v1Keys.pinProtected),
|
encrypted: await this.get<string>(v1Keys.pinProtected),
|
||||||
},
|
}),
|
||||||
protectedPin: await this.get<string>(v1Keys.protectedPin),
|
protectedPin: await this.get<string>(v1Keys.protectedPin),
|
||||||
settings:
|
settings:
|
||||||
userId == null
|
userId == null
|
||||||
|
Loading…
Reference in New Issue
Block a user