1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-04-15 20:16:03 +02:00
bitwarden-browser/libs/common/src/models/domain/account.ts
2022-09-12 20:06:47 -04:00

310 lines
10 KiB
TypeScript

import { Jsonify } from "type-fest";
import { Utils } from "@bitwarden/common/misc/utils";
import { AuthenticationStatus } from "../../enums/authenticationStatus";
import { KdfType } from "../../enums/kdfType";
import { UriMatchType } from "../../enums/uriMatchType";
import { CipherData } from "../data/cipherData";
import { CollectionData } from "../data/collectionData";
import { EncryptedOrganizationKeyData } from "../data/encryptedOrganizationKeyData";
import { EventData } from "../data/eventData";
import { FolderData } from "../data/folderData";
import { OrganizationData } from "../data/organizationData";
import { PolicyData } from "../data/policyData";
import { ProviderData } from "../data/providerData";
import { SendData } from "../data/sendData";
import { CipherView } from "../view/cipherView";
import { CollectionView } from "../view/collectionView";
import { SendView } from "../view/sendView";
import { EncString } from "./encString";
import { EnvironmentUrls } from "./environmentUrls";
import { GeneratedPasswordHistory } from "./generatedPasswordHistory";
import { Policy } from "./policy";
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
export class EncryptionPair<TEncrypted, TDecrypted> {
encrypted?: TEncrypted;
decrypted?: TDecrypted;
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?: (decObj: Jsonify<TDecrypted>) => TDecrypted,
encryptedFromJson?: (encObj: 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> {
encrypted?: { [id: string]: TEncrypted };
decrypted?: TDecrypted[];
}
// This is a temporary structure to handle migrated `DataEncryptionPair` to
// avoid needing a data migration at this stage. It should be replaced with
// proper data migrations when `DataEncryptionPair` is deprecated.
export class TemporaryDataEncryption<TEncrypted> {
encrypted?: { [id: string]: TEncrypted };
}
export class AccountData {
ciphers?: DataEncryptionPair<CipherData, CipherView> = new DataEncryptionPair<
CipherData,
CipherView
>();
folders? = new TemporaryDataEncryption<FolderData>();
localData?: any;
sends?: DataEncryptionPair<SendData, SendView> = new DataEncryptionPair<SendData, SendView>();
collections?: DataEncryptionPair<CollectionData, CollectionView> = new DataEncryptionPair<
CollectionData,
CollectionView
>();
policies?: DataEncryptionPair<PolicyData, Policy> = new DataEncryptionPair<PolicyData, Policy>();
passwordGenerationHistory?: EncryptionPair<
GeneratedPasswordHistory[],
GeneratedPasswordHistory[]
> = new EncryptionPair<GeneratedPasswordHistory[], GeneratedPasswordHistory[]>();
addEditCipherInfo?: any;
eventCollection?: EventData[];
organizations?: { [id: string]: OrganizationData };
providers?: { [id: string]: ProviderData };
}
export class AccountKeys {
cryptoMasterKey?: SymmetricCryptoKey;
cryptoMasterKeyAuto?: string;
cryptoMasterKeyB64?: string;
cryptoMasterKeyBiometric?: string;
cryptoSymmetricKey?: EncryptionPair<string, SymmetricCryptoKey> = new EncryptionPair<
string,
SymmetricCryptoKey
>();
organizationKeys?: EncryptionPair<
{ [orgId: string]: EncryptedOrganizationKeyData },
Record<string, SymmetricCryptoKey>
> = new EncryptionPair<
{ [orgId: string]: EncryptedOrganizationKeyData },
Record<string, SymmetricCryptoKey>
>();
providerKeys?: EncryptionPair<Record<string, string>, Record<string, SymmetricCryptoKey>> =
new EncryptionPair<Record<string, string>, Record<string, SymmetricCryptoKey>>();
privateKey?: EncryptionPair<string, ArrayBuffer> = new EncryptionPair<string, ArrayBuffer>();
publicKey?: ArrayBuffer;
private publicKeySerialized?: string;
apiKeyClientSecret?: string;
toJSON() {
this.publicKeySerialized = Utils.fromBufferToByteString(this.publicKey);
return {
cryptoMasterKey: this.cryptoMasterKey?.toJSON(),
cryptoMasterKeyAuto: this.cryptoMasterKeyAuto,
cryptoMasterKeyB64: this.cryptoMasterKeyB64,
cryptoMasterKeyBiometric: this.cryptoMasterKeyBiometric,
cryptoSymmetricKey: this.cryptoSymmetricKey?.toJSON() as {
encrypted: string;
decrypted: Jsonify<SymmetricCryptoKey>;
decryptedSerialized: string;
},
organizationKeys: this.organizationKeys?.toJSON() as {
encrypted: { [orgId: string]: EncryptedOrganizationKeyData };
decrypted: Record<string, Jsonify<SymmetricCryptoKey>>;
decryptedSerialized: string;
},
providerKeys: this.providerKeys?.toJSON() as {
encrypted: any;
decrypted: Record<string, Jsonify<SymmetricCryptoKey>>;
decryptedSerialized: string;
},
privateKey: this.privateKey?.toJSON() as {
encrypted: string;
decrypted: Jsonify<ArrayBuffer>;
decryptedSerialized: string;
},
publicKeySerialized: this.publicKeySerialized,
};
}
static fromJSON(obj: Partial<Jsonify<AccountKeys>>): AccountKeys {
return Object.assign(
new AccountKeys(),
{ cryptoMasterKey: SymmetricCryptoKey.fromJSON(obj?.cryptoMasterKey) },
{
cryptoSymmetricKey: EncryptionPair.fromJSON(
obj?.cryptoSymmetricKey,
SymmetricCryptoKey.fromJSON
),
},
{ organizationKeys: AccountKeys.initRecordEncryptionPairsFromJSON(obj?.organizationKeys) },
{ providerKeys: AccountKeys.initRecordEncryptionPairsFromJSON(obj?.providerKeys) },
{ privateKey: EncryptionPair.fromJSON(obj?.privateKey) },
{
publicKey: Utils.fromByteStringToArray(obj?.publicKeySerialized)?.buffer,
}
);
}
// These `any` types are a result of Jsonify<Map<>> === {}
// Issue raised https://github.com/sindresorhus/type-fest/issues/457
static initRecordEncryptionPairsFromJSON(obj: any) {
return EncryptionPair.fromJSON(obj, (decObj: any) => {
const record: Record<string, SymmetricCryptoKey> = {};
for (const key in decObj) {
record[key] = SymmetricCryptoKey.fromJSON(decObj[key]);
}
return record;
});
}
}
export class AccountProfile {
apiKeyClientId?: string;
authenticationStatus?: AuthenticationStatus;
convertAccountToKeyConnector?: boolean;
email?: string;
emailVerified?: boolean;
entityId?: string;
entityType?: string;
everBeenUnlocked?: boolean;
forcePasswordReset?: boolean;
hasPremiumPersonally?: boolean;
hasPremiumFromOrganization?: boolean;
lastSync?: string;
userId?: string;
usesKeyConnector?: boolean;
keyHash?: string;
kdfIterations?: number;
kdfType?: KdfType;
static fromJSON(obj: Jsonify<AccountProfile>): AccountProfile {
return Object.assign(new AccountProfile(), obj);
}
}
export class AccountSettings {
autoConfirmFingerPrints?: boolean;
autoFillOnPageLoadDefault?: boolean;
biometricUnlock?: boolean;
clearClipboard?: number;
collapsedGroupings?: string[];
defaultUriMatch?: UriMatchType;
disableAddLoginNotification?: boolean;
disableAutoBiometricsPrompt?: boolean;
disableAutoTotpCopy?: boolean;
disableBadgeCounter?: boolean;
disableChangedPasswordNotification?: boolean;
disableContextMenuItem?: boolean;
disableGa?: boolean;
dontShowCardsCurrentTab?: boolean;
dontShowIdentitiesCurrentTab?: boolean;
enableAlwaysOnTop?: boolean;
enableAutoFillOnPageLoad?: boolean;
enableBiometric?: boolean;
enableFullWidth?: boolean;
enableGravitars?: boolean;
environmentUrls: EnvironmentUrls = new EnvironmentUrls();
equivalentDomains?: any;
minimizeOnCopyToClipboard?: boolean;
neverDomains?: { [id: string]: any };
passwordGenerationOptions?: any;
usernameGenerationOptions?: any;
generatorOptions?: any;
pinProtected?: EncryptionPair<string, EncString> = new EncryptionPair<string, EncString>();
protectedPin?: string;
settings?: AccountSettingsSettings; // TODO: Merge whatever is going on here into the AccountSettings model properly
vaultTimeout?: number;
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 = {
equivalentDomains?: { [id: string]: any };
};
export class AccountTokens {
accessToken?: string;
decodedToken?: any;
refreshToken?: string;
securityStamp?: string;
static fromJSON(obj: Jsonify<AccountTokens>): AccountTokens {
return Object.assign(new AccountTokens(), obj);
}
}
export class Account {
data?: AccountData = new AccountData();
keys?: AccountKeys = new AccountKeys();
profile?: AccountProfile = new AccountProfile();
settings?: AccountSettings = new AccountSettings();
tokens?: AccountTokens = new AccountTokens();
constructor(init: Partial<Account>) {
Object.assign(this, {
data: {
...new AccountData(),
...init?.data,
},
keys: {
...new AccountKeys(),
...init?.keys,
},
profile: {
...new AccountProfile(),
...init?.profile,
},
settings: {
...new AccountSettings(),
...init?.settings,
},
tokens: {
...new AccountTokens(),
...init?.tokens,
},
});
}
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),
});
}
}