import { Except, Jsonify } from "type-fest"; import { AuthenticationStatus } from "../../enums/authenticationStatus"; import { KdfType } from "../../enums/kdfType"; import { UriMatchType } from "../../enums/uriMatchType"; import { Utils } from "../../misc/utils"; import { DeepJsonify } from "../../types/deep-jsonify"; import { CipherData } from "../data/cipher.data"; import { CollectionData } from "../data/collection.data"; import { EncryptedOrganizationKeyData } from "../data/encrypted-organization-key.data"; import { EventData } from "../data/event.data"; import { FolderData } from "../data/folder.data"; import { OrganizationData } from "../data/organization.data"; import { PolicyData } from "../data/policy.data"; import { ProviderData } from "../data/provider.data"; import { SendData } from "../data/send.data"; import { ServerConfigData } from "../data/server-config.data"; import { CipherView } from "../view/cipher.view"; import { CollectionView } from "../view/collection.view"; import { SendView } from "../view/send.view"; import { EncString } from "./enc-string"; import { EnvironmentUrls } from "./environment-urls"; import { GeneratedPasswordHistory } from "./generated-password-history"; import { Policy } from "./policy"; import { SymmetricCryptoKey } from "./symmetric-crypto-key"; export class EncryptionPair { encrypted?: TEncrypted; decrypted?: TDecrypted; toJSON() { return { encrypted: this.encrypted, decrypted: this.decrypted instanceof ArrayBuffer ? Utils.fromBufferToByteString(this.decrypted) : this.decrypted, }; } static fromJSON( obj: Jsonify, Jsonify>>, decryptedFromJson?: (decObj: Jsonify | string) => TDecrypted, encryptedFromJson?: (encObj: Jsonify) => TEncrypted ) { if (obj == null) { return null; } const pair = new EncryptionPair(); if (obj?.encrypted != null) { pair.encrypted = encryptedFromJson ? encryptedFromJson(obj.encrypted) : (obj.encrypted as TEncrypted); } if (obj?.decrypted != null) { pair.decrypted = decryptedFromJson ? decryptedFromJson(obj.decrypted) : (obj.decrypted as TDecrypted); } return pair; } } export class DataEncryptionPair { 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 { encrypted?: { [id: string]: TEncrypted }; } export class AccountData { ciphers?: DataEncryptionPair = new DataEncryptionPair< CipherData, CipherView >(); folders? = new TemporaryDataEncryption(); localData?: any; sends?: DataEncryptionPair = new DataEncryptionPair(); collections?: DataEncryptionPair = new DataEncryptionPair< CollectionData, CollectionView >(); policies?: DataEncryptionPair = new DataEncryptionPair(); passwordGenerationHistory?: EncryptionPair< GeneratedPasswordHistory[], GeneratedPasswordHistory[] > = new EncryptionPair(); 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 = new EncryptionPair< string, SymmetricCryptoKey >(); organizationKeys?: EncryptionPair< { [orgId: string]: EncryptedOrganizationKeyData }, Record > = new EncryptionPair< { [orgId: string]: EncryptedOrganizationKeyData }, Record >(); providerKeys?: EncryptionPair> = new EncryptionPair< any, Record >(); privateKey?: EncryptionPair = new EncryptionPair(); publicKey?: ArrayBuffer; apiKeyClientSecret?: string; toJSON() { return Object.assign(this as Except, { publicKey: Utils.fromBufferToByteString(this.publicKey), }); } static fromJSON(obj: DeepJsonify): AccountKeys { if (obj == null) { return null; } 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, (decObj: string) => Utils.fromByteStringToArray(decObj).buffer ), }, { publicKey: Utils.fromByteStringToArray(obj?.publicKey)?.buffer, } ); } static initRecordEncryptionPairsFromJSON(obj: any) { return EncryptionPair.fromJSON(obj, (decObj: any) => { if (obj == null) { return null; } const record: Record = {}; for (const id in decObj) { record[id] = SymmetricCryptoKey.fromJSON(decObj[id]); } return record; }); } } export class AccountProfile { apiKeyClientId?: string; authenticationStatus?: AuthenticationStatus; convertAccountToKeyConnector?: boolean; name?: string; 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 { if (obj == null) { return null; } 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; environmentUrls: EnvironmentUrls = new EnvironmentUrls(); equivalentDomains?: any; minimizeOnCopyToClipboard?: boolean; neverDomains?: { [id: string]: any }; passwordGenerationOptions?: any; usernameGenerationOptions?: any; generatorOptions?: any; pinProtected?: EncryptionPair = new EncryptionPair(); protectedPin?: string; settings?: AccountSettingsSettings; // TODO: Merge whatever is going on here into the AccountSettings model properly vaultTimeout?: number; vaultTimeoutAction?: string = "lock"; serverConfig?: ServerConfigData; static fromJSON(obj: Jsonify): AccountSettings { if (obj == null) { return null; } return Object.assign(new AccountSettings(), obj, { environmentUrls: EnvironmentUrls.fromJSON(obj?.environmentUrls), pinProtected: EncryptionPair.fromJSON( obj?.pinProtected, EncString.fromJSON ), serverConfig: ServerConfigData.fromJSON(obj?.serverConfig), }); } } export type AccountSettingsSettings = { equivalentDomains?: { [id: string]: any }; }; export class AccountTokens { accessToken?: string; refreshToken?: string; securityStamp?: string; static fromJSON(obj: Jsonify): AccountTokens { if (obj == null) { return null; } 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) { 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: Jsonify): Account { if (json == null) { return null; } return Object.assign(new Account({}), json, { keys: AccountKeys.fromJSON(json?.keys), profile: AccountProfile.fromJSON(json?.profile), settings: AccountSettings.fromJSON(json?.settings), tokens: AccountTokens.fromJSON(json?.tokens), }); } }