mirror of
https://github.com/bitwarden/jslib
synced 2025-12-24 04:04:38 +00:00
[refactor] Split up the Account model
Until this point the account model has been very flat, holding many kinds of data. In order to be able to prune data at appropriate times, for example clearing keys at logout without clearing QoL settings like locale, the Account model has been divided into logical chunks.
This commit is contained in:
@@ -22,133 +22,154 @@ import { PolicyData } from '../data/policyData';
|
||||
import { ProviderData } from '../data/providerData';
|
||||
import { SendData } from '../data/sendData';
|
||||
|
||||
export class Account {
|
||||
userId: string;
|
||||
email: string;
|
||||
accessToken: string;
|
||||
decodedToken: any;
|
||||
apiKeyClientId: string;
|
||||
apiKeyClientSecret: string;
|
||||
alwaysShowDock: boolean;
|
||||
autoFillOnPageLoadDefault: boolean;
|
||||
encryptedCiphers: { [id: string]: CipherData };
|
||||
decryptedCiphers: CipherView[];
|
||||
cryptoMasterKey: SymmetricCryptoKey;
|
||||
cryptoMasterKeyBiometric: SymmetricCryptoKey;
|
||||
cryptoMasterKeyAuto: SymmetricCryptoKey;
|
||||
cryptoMasterKeyB64: string;
|
||||
encryptedCryptoSymmetricKey: string;
|
||||
decryptedCryptoSymmetricKey: SymmetricCryptoKey;
|
||||
defaultUriMatch: UriMatchType;
|
||||
disableAddLoginNotification: boolean;
|
||||
disableAutoTotpCopy: boolean;
|
||||
disableBadgeCounter: boolean;
|
||||
disableChangedPasswordNotification: boolean;
|
||||
disableContextMenuItem: boolean;
|
||||
disableGa: boolean;
|
||||
dontShowCardsCurrentTab: boolean;
|
||||
dontShowIdentitiesCurrentTab: boolean;
|
||||
emailVerified: boolean;
|
||||
enableAlwaysOnTop: boolean;
|
||||
enableAutoFillOnPageLoad: boolean;
|
||||
enableBrowserIntegration: boolean;
|
||||
enableBrowserIntegrationFingerprint: boolean;
|
||||
enableCloseToTray: boolean;
|
||||
enableMinimizeToTray: boolean;
|
||||
enableStartToTray: boolean;
|
||||
enableTray: boolean;
|
||||
decryptedOrganizationKeys: Map<string, SymmetricCryptoKey>;
|
||||
encryptedOrganizationKeys: any;
|
||||
decryptedProviderKeys: Map<string, SymmetricCryptoKey>;
|
||||
encryptedProviderKeys: any;
|
||||
entityId: string;
|
||||
entityType: string;
|
||||
environmentUrls: any;
|
||||
equivalentDomains: any;
|
||||
eventCollection: EventData[];
|
||||
encryptedFolders: { [id: string]: FolderData };
|
||||
decryptedFolders: FolderView[];
|
||||
forcePasswordReset: boolean;
|
||||
installedVersion: string;
|
||||
kdfIterations: number;
|
||||
kdfType: KdfType;
|
||||
keyHash: string;
|
||||
lastActive: number;
|
||||
lastSync: string;
|
||||
legacyEtmKey: SymmetricCryptoKey;
|
||||
localData: any;
|
||||
loginRedirect: any;
|
||||
mainWindowSize: number;
|
||||
minimizeOnCopyToClipboard: boolean;
|
||||
neverDomains: { [id: string]: any };
|
||||
openAtLogin: boolean;
|
||||
encryptedPasswordGenerationHistory: GeneratedPasswordHistory[];
|
||||
decryptedPasswordGenerationHistory: GeneratedPasswordHistory[];
|
||||
passwordGenerationOptions: any;
|
||||
encryptedPinProtected: string;
|
||||
decryptedPinProtected: EncString;
|
||||
protectedPin: string;
|
||||
encryptedPolicies: { [id: string]: PolicyData };
|
||||
decryptedPolicies: Policy[];
|
||||
providers: { [id: string]: ProviderData };
|
||||
publicKey: ArrayBuffer;
|
||||
refreshToken: string;
|
||||
rememberedEmail: string;
|
||||
securityStamp: string;
|
||||
encryptedSends: { [id: string]: SendData };
|
||||
decryptedSends: SendView[];
|
||||
settings: any;
|
||||
ssoCodeVerifier: string;
|
||||
ssoState: string;
|
||||
ssoOrganizationIdentifier: string;
|
||||
vaultTimeout: number;
|
||||
vaultTimeoutAction: string;
|
||||
clearClipboard: number;
|
||||
collapsedGroupings: Set<string>;
|
||||
encryptedCollections: { [id: string]: CollectionData };
|
||||
decryptedCollections: CollectionView[];
|
||||
encryptedPrivateKey: string;
|
||||
decryptedPrivateKey: ArrayBuffer;
|
||||
locale: string;
|
||||
organizations: { [id: string]: OrganizationData };
|
||||
everBeenUnlocked: boolean;
|
||||
enableGravitars: boolean;
|
||||
addEditCipherInfo: any;
|
||||
authenticationStatus: AuthenticationStatus;
|
||||
autoConfirmFingerPrints: boolean;
|
||||
disableAutoBiometricsPrompt: boolean;
|
||||
noAutoPromptBiometrics: boolean;
|
||||
biometricLocked: boolean;
|
||||
biometricUnlock: boolean;
|
||||
biometricText: string;
|
||||
enableBiometric: boolean;
|
||||
enableBiometrics: boolean;
|
||||
noAutoPromptBiometricsText: string;
|
||||
convertAccountToKeyConnector: boolean;
|
||||
usesKeyConnector: boolean;
|
||||
enableFullWidth: boolean;
|
||||
hasPremiumPersonally: boolean;
|
||||
export class EncryptionPair<TEncrypted, TDecrypted> {
|
||||
encrypted: TEncrypted;
|
||||
decrypted: TDecrypted;
|
||||
}
|
||||
|
||||
constructor(userId: string, userEmail: string,
|
||||
kdfType: KdfType, kdfIterations: number,
|
||||
clientId: string, clientSecret: string,
|
||||
accessToken: string, refreshToken: string,
|
||||
hasPremium: boolean) {
|
||||
this.userId = userId;
|
||||
this.email = userEmail;
|
||||
this.kdfType = kdfType;
|
||||
this.kdfIterations = kdfIterations;
|
||||
this.apiKeyClientId = clientId;
|
||||
this.apiKeyClientSecret = clientSecret;
|
||||
this.accessToken = accessToken;
|
||||
this.refreshToken = refreshToken;
|
||||
this.hasPremiumPersonally = hasPremium;
|
||||
this.vaultTimeoutAction = 'lock';
|
||||
this.vaultTimeout = 15;
|
||||
}
|
||||
export class DataEncryptionPair<TEncrypted, TDecrypted> {
|
||||
encrypted: { [id: string]: TEncrypted };
|
||||
decrypted: TDecrypted[];
|
||||
}
|
||||
|
||||
export class AccountData {
|
||||
ciphers?: DataEncryptionPair<CipherData, CipherView> = new DataEncryptionPair<CipherData, CipherView>();
|
||||
folders?: DataEncryptionPair<FolderData, FolderView> = new DataEncryptionPair<FolderData, FolderView>();
|
||||
localData?: any;
|
||||
sends?: DataEncryptionPair<SendData, SendView> = new DataEncryptionPair<SendData, SendView>();
|
||||
collections?: DataEncryptionPair<CollectionData, CollectionView> = new DataEncryptionPair<CollectionData, CollectionView>();
|
||||
kdfIterations?: number;
|
||||
kdfType?: KdfType;
|
||||
policies?: DataEncryptionPair<PolicyData, Policy> = new DataEncryptionPair<PolicyData, Policy>();
|
||||
passwordGenerationHistory?: EncryptionPair<GeneratedPasswordHistory[], GeneratedPasswordHistory[]> = new EncryptionPair<GeneratedPasswordHistory[], GeneratedPasswordHistory[]>();
|
||||
addEditCipherInfo?: any;
|
||||
collapsedGroupings?: Set<string>;
|
||||
eventCollection?: EventData[];
|
||||
organizations?: { [id: string]: OrganizationData };
|
||||
providers?: { [id: string]: ProviderData };
|
||||
}
|
||||
|
||||
export class AccountKeys {
|
||||
cryptoMasterKey?: SymmetricCryptoKey;
|
||||
cryptoMasterKeyAuto?: SymmetricCryptoKey;
|
||||
cryptoMasterKeyB64?: string;
|
||||
cryptoMasterKeyBiometric?: SymmetricCryptoKey;
|
||||
cryptoSymmetricKey?: EncryptionPair<string, SymmetricCryptoKey> = new EncryptionPair<string, SymmetricCryptoKey>();
|
||||
organizationKeys?: EncryptionPair<any, Map<string, SymmetricCryptoKey>> = new EncryptionPair<any, Map<string, SymmetricCryptoKey>>();
|
||||
privateKey?: EncryptionPair<string, ArrayBuffer> = new EncryptionPair<string, ArrayBuffer>();
|
||||
providerKeys?: EncryptionPair<any, Map<string, SymmetricCryptoKey>> = new EncryptionPair<any, Map<string, SymmetricCryptoKey>>();
|
||||
keyHash?: string;
|
||||
legacyEtmKey?: SymmetricCryptoKey;
|
||||
publicKey?: ArrayBuffer;
|
||||
}
|
||||
|
||||
export class AccountProfile {
|
||||
apiKeyClientId?: string;
|
||||
apiKeyClientSecret?: string;
|
||||
authenticationStatus?: AuthenticationStatus;
|
||||
convertAccountToKeyConnector?: boolean;
|
||||
email?: string;
|
||||
emailVerified?: boolean;
|
||||
entityId?: string;
|
||||
entityType?: string;
|
||||
everBeenUnlocked?: boolean;
|
||||
forcePasswordReset?: boolean;
|
||||
hasPremiumPersonally?: boolean;
|
||||
lastActive?: number;
|
||||
lastSync?: string;
|
||||
ssoCodeVerifier?: string;
|
||||
ssoOrganizationIdentifier?: string;
|
||||
ssoState?: string;
|
||||
userId?: string;
|
||||
usesKeyConnector?: boolean;
|
||||
}
|
||||
|
||||
export class AccountSettings {
|
||||
alwaysShowDock?: boolean;
|
||||
autoConfirmFingerPrints?: boolean;
|
||||
autoFillOnPageLoadDefault?: boolean;
|
||||
biometricLocked?: boolean;
|
||||
biometricText?: string;
|
||||
biometricUnlock?: boolean;
|
||||
clearClipboard?: number;
|
||||
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;
|
||||
enableBiometrics?: boolean;
|
||||
enableBrowserIntegration?: boolean;
|
||||
enableBrowserIntegrationFingerprint?: boolean;
|
||||
enableCloseToTray?: boolean;
|
||||
enableFullWidth?: boolean;
|
||||
enableGravitars?: boolean;
|
||||
enableMinimizeToTray?: boolean;
|
||||
enableStartToTray?: boolean;
|
||||
enableTray?: boolean;
|
||||
environmentUrls?: any;
|
||||
equivalentDomains?: any;
|
||||
locale?: string;
|
||||
minimizeOnCopyToClipboard?: boolean;
|
||||
neverDomains?: { [id: string]: any };
|
||||
noAutoPromptBiometrics?: boolean;
|
||||
noAutoPromptBiometricsText?: string;
|
||||
openAtLogin?: boolean;
|
||||
passwordGenerationOptions?: any;
|
||||
pinProtected?: EncryptionPair<string, EncString> = new EncryptionPair<string, EncString>();
|
||||
protectedPin?: string;
|
||||
settings?: any; // TODO: Merge whatever is going on here into the AccountSettings model properly
|
||||
vaultTimeout?: number;
|
||||
vaultTimeoutAction?: string;
|
||||
|
||||
get serverUrl(): string {
|
||||
return this.environmentUrls?.base ?? 'bitwarden.com';
|
||||
return this.settings.environmentUrls?.base ?? 'bitwarden.com';
|
||||
}
|
||||
}
|
||||
|
||||
export class AccountTokens {
|
||||
accessToken?: string;
|
||||
decodedToken?: any;
|
||||
refreshToken?: string;
|
||||
securityStamp?: string;
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ export class GlobalState {
|
||||
locale: string;
|
||||
openAtLogin: boolean;
|
||||
organizationInvitation: any;
|
||||
rememberEmail: boolean;
|
||||
rememberedEmail: string;
|
||||
theme: string;
|
||||
window: Map<string, any> = new Map<string, any>();
|
||||
@@ -15,4 +14,6 @@ export class GlobalState {
|
||||
biometricFingerprintValidated: boolean;
|
||||
vaultTimeout: number;
|
||||
vaultTimeoutAction: string;
|
||||
loginRedirect: any;
|
||||
mainWindowSize: number;
|
||||
}
|
||||
|
||||
@@ -353,11 +353,23 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
result.forcePasswordReset = tokenResponse.forcePasswordReset;
|
||||
|
||||
const accountInformation = await this.tokenService.decodeToken(tokenResponse.accessToken);
|
||||
await this.stateService.addAccount(new Account(
|
||||
accountInformation.sub, accountInformation.email,
|
||||
tokenResponse.kdf, tokenResponse.kdfIterations,
|
||||
clientId, clientSecret, tokenResponse.accessToken, tokenResponse.refreshToken,
|
||||
accountInformation.premium));
|
||||
await this.stateService.addAccount({
|
||||
profile: {
|
||||
userId: accountInformation.sub,
|
||||
email: accountInformation.email,
|
||||
apiKeyClientId: clientId,
|
||||
apiKeyClientSecret: clientSecret,
|
||||
hasPremiumPersonally: accountInformation.premium,
|
||||
},
|
||||
data: {
|
||||
kdfIterations: tokenResponse.kdfIterations,
|
||||
kdfType: tokenResponse.kdf,
|
||||
},
|
||||
tokens: {
|
||||
accessToken: tokenResponse.accessToken,
|
||||
refreshToken: tokenResponse.refreshToken,
|
||||
},
|
||||
});
|
||||
|
||||
if (tokenResponse.twoFactorToken != null) {
|
||||
await this.tokenService.setTwoFactorToken(tokenResponse.twoFactorToken, email);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user