import { Jsonify } from "type-fest"; import { EncryptedOrganizationKeyData } from "../../../admin-console/models/data/encrypted-organization-key.data"; import { OrganizationData } from "../../../admin-console/models/data/organization.data"; import { PolicyData } from "../../../admin-console/models/data/policy.data"; import { ProviderData } from "../../../admin-console/models/data/provider.data"; import { Policy } from "../../../admin-console/models/domain/policy"; import { AuthenticationStatus } from "../../../auth/enums/authentication-status"; import { AdminAuthRequestStorable } from "../../../auth/models/domain/admin-auth-req-storable"; import { EnvironmentUrls } from "../../../auth/models/domain/environment-urls"; import { ForceResetPasswordReason } from "../../../auth/models/domain/force-reset-password-reason"; import { KeyConnectorUserDecryptionOption } from "../../../auth/models/domain/user-decryption-options/key-connector-user-decryption-option"; import { TrustedDeviceUserDecryptionOption } from "../../../auth/models/domain/user-decryption-options/trusted-device-user-decryption-option"; import { UserDecryptionOptionsResponse } from "../../../auth/models/response/user-decryption-options/user-decryption-options.response"; import { KdfType, UriMatchType } from "../../../enums"; import { EventData } from "../../../models/data/event.data"; import { GeneratedPasswordHistory } from "../../../tools/generator/password"; import { SendData } from "../../../tools/send/models/data/send.data"; import { SendView } from "../../../tools/send/models/view/send.view"; import { DeepJsonify } from "../../../types/deep-jsonify"; import { CipherData } from "../../../vault/models/data/cipher.data"; import { CollectionData } from "../../../vault/models/data/collection.data"; import { FolderData } from "../../../vault/models/data/folder.data"; import { CipherView } from "../../../vault/models/view/cipher.view"; import { CollectionView } from "../../../vault/models/view/collection.view"; import { Utils } from "../../misc/utils"; import { ServerConfigData } from "../../models/data/server-config.data"; import { EncryptedString, EncString } from "./enc-string"; import { MasterKey, SymmetricCryptoKey, UserKey } 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: { encrypted?: Jsonify; decrypted?: string | 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 { userKey?: UserKey; masterKey?: MasterKey; masterKeyEncryptedUserKey?: string; deviceKey?: ReturnType; organizationKeys?: EncryptionPair< { [orgId: string]: EncryptedOrganizationKeyData }, Record > = new EncryptionPair< { [orgId: string]: EncryptedOrganizationKeyData }, Record >(); providerKeys?: EncryptionPair> = new EncryptionPair< any, Record >(); privateKey?: EncryptionPair = new EncryptionPair(); publicKey?: Uint8Array; apiKeyClientSecret?: string; /** @deprecated July 2023, left for migration purposes*/ cryptoMasterKey?: SymmetricCryptoKey; /** @deprecated July 2023, left for migration purposes*/ cryptoMasterKeyAuto?: string; /** @deprecated July 2023, left for migration purposes*/ cryptoMasterKeyBiometric?: string; /** @deprecated July 2023, left for migration purposes*/ cryptoSymmetricKey?: EncryptionPair = new EncryptionPair< string, SymmetricCryptoKey >(); toJSON() { return Utils.merge(this, { publicKey: Utils.fromBufferToByteString(this.publicKey), }); } static fromJSON(obj: DeepJsonify): AccountKeys { if (obj == null) { return null; } return Object.assign(new AccountKeys(), { userKey: SymmetricCryptoKey.fromJSON(obj?.userKey), masterKey: SymmetricCryptoKey.fromJSON(obj?.masterKey), deviceKey: obj?.deviceKey, 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) ), publicKey: Utils.fromByteStringToArray(obj?.publicKey), }); } 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; everHadUserKey?: boolean; everBeenUnlocked?: boolean; forcePasswordResetReason?: ForceResetPasswordReason; hasPremiumPersonally?: boolean; hasPremiumFromOrganization?: boolean; lastSync?: string; userId?: string; usesKeyConnector?: boolean; keyHash?: string; kdfIterations?: number; kdfMemory?: number; kdfParallelism?: 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; dismissedAutoFillOnPageLoadCallout?: 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; pinKeyEncryptedUserKey?: EncryptedString; pinKeyEncryptedUserKeyEphemeral?: EncryptedString; protectedPin?: string; settings?: AccountSettingsSettings; // TODO: Merge whatever is going on here into the AccountSettings model properly vaultTimeout?: number; vaultTimeoutAction?: string = "lock"; serverConfig?: ServerConfigData; approveLoginRequests?: boolean; avatarColor?: string; activateAutoFillOnPageLoadFromPolicy?: boolean; region?: string; smOnboardingTasks?: Record>; trustDeviceChoiceForDecryption?: boolean; /** @deprecated July 2023, left for migration purposes*/ pinProtected?: EncryptionPair = new EncryptionPair(); 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?: string[][]; }; 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 AccountDecryptionOptions { hasMasterPassword: boolean; trustedDeviceOption?: TrustedDeviceUserDecryptionOption; keyConnectorOption?: KeyConnectorUserDecryptionOption; constructor(init?: Partial) { if (init) { Object.assign(this, init); } } // TODO: these nice getters don't work because the Account object is not properly being deserialized out of // JSON (the Account static fromJSON method is not running) so these getters don't exist on the // account decryptions options object when pulled out of state. This is a bug that needs to be fixed later on // get hasTrustedDeviceOption(): boolean { // return this.trustedDeviceOption !== null && this.trustedDeviceOption !== undefined; // } // get hasKeyConnectorOption(): boolean { // return this.keyConnectorOption !== null && this.keyConnectorOption !== undefined; // } static fromResponse(response: UserDecryptionOptionsResponse): AccountDecryptionOptions { if (response == null) { return null; } const accountDecryptionOptions = new AccountDecryptionOptions(); accountDecryptionOptions.hasMasterPassword = response.hasMasterPassword; if (response.trustedDeviceOption) { accountDecryptionOptions.trustedDeviceOption = new TrustedDeviceUserDecryptionOption( response.trustedDeviceOption.hasAdminApproval, response.trustedDeviceOption.hasLoginApprovingDevice, response.trustedDeviceOption.hasManageResetPasswordPermission ); } if (response.keyConnectorOption) { accountDecryptionOptions.keyConnectorOption = new KeyConnectorUserDecryptionOption( response.keyConnectorOption.keyConnectorUrl ); } return accountDecryptionOptions; } static fromJSON(obj: Jsonify): AccountDecryptionOptions { if (obj == null) { return null; } const accountDecryptionOptions = Object.assign(new AccountDecryptionOptions(), obj); if (obj.trustedDeviceOption) { accountDecryptionOptions.trustedDeviceOption = new TrustedDeviceUserDecryptionOption( obj.trustedDeviceOption.hasAdminApproval, obj.trustedDeviceOption.hasLoginApprovingDevice, obj.trustedDeviceOption.hasManageResetPasswordPermission ); } if (obj.keyConnectorOption) { accountDecryptionOptions.keyConnectorOption = new KeyConnectorUserDecryptionOption( obj.keyConnectorOption.keyConnectorUrl ); } return accountDecryptionOptions; } } export class LoginState { ssoOrganizationIdentifier?: string; constructor(init?: Partial) { if (init) { Object.assign(this, init); } } static fromJSON(obj: Jsonify): LoginState { if (obj == null) { return null; } const loginState = Object.assign(new LoginState(), obj); return loginState; } } export class Account { data?: AccountData = new AccountData(); keys?: AccountKeys = new AccountKeys(); profile?: AccountProfile = new AccountProfile(); settings?: AccountSettings = new AccountSettings(); tokens?: AccountTokens = new AccountTokens(); decryptionOptions?: AccountDecryptionOptions = new AccountDecryptionOptions(); loginState?: LoginState = new LoginState(); adminAuthRequest?: Jsonify = null; 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, }, decryptionOptions: { ...new AccountDecryptionOptions(), ...init?.decryptionOptions, }, loginState: { ...new LoginState(), ...init?.loginState, }, adminAuthRequest: init?.adminAuthRequest, }); } 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), decryptionOptions: AccountDecryptionOptions.fromJSON(json?.decryptionOptions), loginState: LoginState.fromJSON(json?.loginState), adminAuthRequest: AdminAuthRequestStorable.fromJSON(json?.adminAuthRequest), }); } }