mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-26 12:25:20 +01:00
[bug] Migrate state even if there is not a user logged in (#615)
Currently the StateMigrationService depends on a userId key for running migrations, but if there is not an authenticated user saved to storage that userId is not present. These changes allow for migrating state data even without an active user. For account specific settings like clearClipboard we now temporarily store those values together in disk state until an account is authed that they can be added to. Temp account state is then cleared. Some notes: * In order for this to work we need GlobalState.stateVersion to have a default value of StateVersion.One instead of StateVersion.Latest. Defaulting it to latest was causing migrations to not run on some clients (like desktop) that try to access storage before migrations have been run but save a version as if migrations did run. * I also noticed we aren't clearing old state items from before migrating, and added a case for this to the migrator. * I extracted a few bits of reused code into private methods in the stateMigration service. Things like get/set from storage, default options, etc.
This commit is contained in:
parent
11e7133aef
commit
57351d29a2
@ -25,6 +25,6 @@ export class GlobalState {
|
||||
biometricText?: string;
|
||||
noAutoPromptBiometrics?: boolean;
|
||||
noAutoPromptBiometricsText?: string;
|
||||
stateVersion: StateVersion = StateVersion.Latest;
|
||||
stateVersion: StateVersion = StateVersion.One;
|
||||
environmentUrls: EnvironmentUrls = new EnvironmentUrls();
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ const keys = {
|
||||
global: "global",
|
||||
authenticatedAccounts: "authenticatedAccounts",
|
||||
activeUserId: "activeUserId",
|
||||
tempAccountSettings: "tempAccountSettings", // used to hold account specific settings (i.e clear clipboard) between initial migration and first account authentication
|
||||
};
|
||||
|
||||
const partialKeys = {
|
||||
@ -2213,6 +2214,9 @@ export class StateService<TAccount extends Account = Account>
|
||||
// EnvironmentUrls are set before authenticating and should override whatever is stored from last session
|
||||
storedAccount.settings.environmentUrls = account.settings.environmentUrls;
|
||||
account.settings = storedAccount.settings;
|
||||
} else if (await this.storageService.has(keys.tempAccountSettings)) {
|
||||
account.settings = await this.storageService.get<any>(keys.tempAccountSettings);
|
||||
await this.storageService.remove(keys.tempAccountSettings);
|
||||
}
|
||||
await this.storageService.save(
|
||||
account.profile.userId,
|
||||
|
@ -1,9 +1,7 @@
|
||||
import { StorageService } from "../abstractions/storage.service";
|
||||
|
||||
import { Account } from "../models/domain/account";
|
||||
import { GeneratedPasswordHistory } from "../models/domain/generatedPasswordHistory";
|
||||
import { GlobalState } from "../models/domain/globalState";
|
||||
import { State } from "../models/domain/state";
|
||||
import { StorageOptions } from "../models/domain/storageOptions";
|
||||
|
||||
import { CipherData } from "../models/data/cipherData";
|
||||
@ -18,11 +16,12 @@ import { SendData } from "../models/data/sendData";
|
||||
import { HtmlStorageLocation } from "../enums/htmlStorageLocation";
|
||||
import { KdfType } from "../enums/kdfType";
|
||||
import { StateVersion } from "../enums/stateVersion";
|
||||
|
||||
import { EnvironmentUrls } from "../models/domain/environmentUrls";
|
||||
|
||||
// Originally (before January 2022) storage was handled as a flat key/value pair store.
|
||||
// With the move to a typed object for state storage these keys should no longer be in use anywhere outside of this migration.
|
||||
const v1Keys = {
|
||||
const v1Keys: { [key: string]: string } = {
|
||||
accessToken: "accessToken",
|
||||
alwaysShowDock: "alwaysShowDock",
|
||||
autoConfirmFingerprints: "autoConfirmFingerprints",
|
||||
@ -100,7 +99,7 @@ const v1Keys = {
|
||||
rememberedEmail: "rememberedEmail",
|
||||
};
|
||||
|
||||
const v1KeyPrefixes = {
|
||||
const v1KeyPrefixes: { [key: string]: string } = {
|
||||
ciphers: "ciphers_",
|
||||
collections: "collections_",
|
||||
folders: "folders_",
|
||||
@ -117,6 +116,7 @@ const keys = {
|
||||
global: "global",
|
||||
authenticatedAccounts: "authenticatedAccounts",
|
||||
activeUserId: "activeUserId",
|
||||
tempAccountSettings: "tempAccountSettings", // used to hold account specific settings (i.e clear clipboard) between initial migration and first account authentication
|
||||
};
|
||||
|
||||
const partialKeys = {
|
||||
@ -132,17 +132,12 @@ export class StateMigrationService {
|
||||
) {}
|
||||
|
||||
async needsMigration(): Promise<boolean> {
|
||||
const currentStateVersion = (
|
||||
await this.storageService.get<GlobalState>(keys.global, {
|
||||
htmlStorageLocation: HtmlStorageLocation.Local,
|
||||
})
|
||||
)?.stateVersion;
|
||||
const currentStateVersion = await this.getCurrentStateVersion();
|
||||
return currentStateVersion == null || currentStateVersion < StateVersion.Latest;
|
||||
}
|
||||
|
||||
async migrate(): Promise<void> {
|
||||
let currentStateVersion =
|
||||
(await this.storageService.get<GlobalState>(keys.global))?.stateVersion ?? StateVersion.One;
|
||||
let currentStateVersion = await this.getCurrentStateVersion();
|
||||
while (currentStateVersion < StateVersion.Latest) {
|
||||
switch (currentStateVersion) {
|
||||
case StateVersion.One:
|
||||
@ -155,362 +150,244 @@ export class StateMigrationService {
|
||||
}
|
||||
|
||||
protected async migrateStateFrom1To2(): Promise<void> {
|
||||
const options: StorageOptions = { htmlStorageLocation: HtmlStorageLocation.Local };
|
||||
const userId = await this.storageService.get<string>("userId");
|
||||
const initialState: State<Account> =
|
||||
userId == null
|
||||
? {
|
||||
globals: new GlobalState(),
|
||||
accounts: {},
|
||||
activeUserId: null,
|
||||
authenticatedAccounts: [],
|
||||
const clearV1Keys = async (clearingUserId?: string) => {
|
||||
for (const key in v1Keys) {
|
||||
if (key == null) {
|
||||
continue;
|
||||
}
|
||||
await this.set(v1Keys[key], null);
|
||||
}
|
||||
if (clearingUserId != null) {
|
||||
for (const keyPrefix in v1KeyPrefixes) {
|
||||
if (keyPrefix == null) {
|
||||
continue;
|
||||
}
|
||||
: {
|
||||
authenticatedAccounts: [userId],
|
||||
activeUserId: userId,
|
||||
globals: {
|
||||
biometricAwaitingAcceptance: await this.storageService.get<boolean>(
|
||||
v1Keys.biometricAwaitingAcceptance,
|
||||
options
|
||||
),
|
||||
biometricFingerprintValidated: await this.storageService.get<boolean>(
|
||||
v1Keys.biometricFingerprintValidated,
|
||||
options
|
||||
),
|
||||
biometricText: await this.storageService.get<string>(v1Keys.biometricText, options),
|
||||
disableFavicon: await this.storageService.get<boolean>(
|
||||
v1Keys.disableFavicon,
|
||||
options
|
||||
),
|
||||
enableAlwaysOnTop: await this.storageService.get<boolean>(
|
||||
v1Keys.enableAlwaysOnTop,
|
||||
options
|
||||
),
|
||||
enableBiometrics: await this.storageService.get<boolean>(
|
||||
v1Keys.enableBiometric,
|
||||
options
|
||||
),
|
||||
environmentUrls: null,
|
||||
installedVersion: await this.storageService.get<string>(
|
||||
v1Keys.installedVersion,
|
||||
options
|
||||
),
|
||||
locale: await this.storageService.get<string>(v1Keys.locale, options),
|
||||
loginRedirect: null,
|
||||
mainWindowSize: null,
|
||||
noAutoPromptBiometrics: await this.storageService.get<boolean>(
|
||||
v1Keys.disableAutoBiometricsPrompt,
|
||||
options
|
||||
),
|
||||
noAutoPromptBiometricsText: await this.storageService.get<string>(
|
||||
v1Keys.noAutoPromptBiometricsText,
|
||||
options
|
||||
),
|
||||
openAtLogin: await this.storageService.get<boolean>(v1Keys.openAtLogin, options),
|
||||
organizationInvitation: await this.storageService.get<string>("", options),
|
||||
ssoCodeVerifier: await this.storageService.get<string>(
|
||||
v1Keys.ssoCodeVerifier,
|
||||
options
|
||||
),
|
||||
ssoOrganizationIdentifier: await this.storageService.get<string>(
|
||||
v1Keys.ssoIdentifier,
|
||||
options
|
||||
),
|
||||
ssoState: null,
|
||||
rememberedEmail: await this.storageService.get<string>(
|
||||
v1Keys.rememberedEmail,
|
||||
options
|
||||
),
|
||||
stateVersion: StateVersion.Two,
|
||||
theme: await this.storageService.get<string>(v1Keys.theme, options),
|
||||
twoFactorToken: await this.storageService.get<string>(
|
||||
v1KeyPrefixes.twoFactorToken + userId,
|
||||
options
|
||||
),
|
||||
vaultTimeout: await this.storageService.get<number>(v1Keys.vaultTimeout, options),
|
||||
vaultTimeoutAction: await this.storageService.get<string>(
|
||||
v1Keys.vaultTimeoutAction,
|
||||
options
|
||||
),
|
||||
window: null,
|
||||
},
|
||||
accounts: {
|
||||
[userId]: new Account({
|
||||
data: {
|
||||
addEditCipherInfo: null,
|
||||
ciphers: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<{ [id: string]: CipherData }>(
|
||||
v1KeyPrefixes.ciphers + userId,
|
||||
options
|
||||
),
|
||||
},
|
||||
collapsedGroupings: null,
|
||||
collections: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<{ [id: string]: CollectionData }>(
|
||||
v1KeyPrefixes.collections + userId,
|
||||
options
|
||||
),
|
||||
},
|
||||
eventCollection: await this.storageService.get<EventData[]>(
|
||||
v1Keys.eventCollection,
|
||||
options
|
||||
),
|
||||
folders: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<{ [id: string]: FolderData }>(
|
||||
v1KeyPrefixes.folders + userId,
|
||||
options
|
||||
),
|
||||
},
|
||||
localData: null,
|
||||
organizations: await this.storageService.get<{ [id: string]: OrganizationData }>(
|
||||
v1KeyPrefixes.organizations + userId
|
||||
),
|
||||
passwordGenerationHistory: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<GeneratedPasswordHistory[]>(
|
||||
"TODO",
|
||||
options
|
||||
), // TODO: Whats up here?
|
||||
},
|
||||
policies: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<{ [id: string]: PolicyData }>(
|
||||
v1KeyPrefixes.policies + userId,
|
||||
options
|
||||
),
|
||||
},
|
||||
providers: await this.storageService.get<{ [id: string]: ProviderData }>(
|
||||
v1KeyPrefixes.providers + userId
|
||||
),
|
||||
sends: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<{ [id: string]: SendData }>(
|
||||
v1KeyPrefixes.sends,
|
||||
options
|
||||
),
|
||||
},
|
||||
},
|
||||
keys: {
|
||||
apiKeyClientSecret: await this.storageService.get<string>(
|
||||
v1Keys.clientSecret,
|
||||
options
|
||||
),
|
||||
cryptoMasterKey: null,
|
||||
cryptoMasterKeyAuto: null,
|
||||
cryptoMasterKeyB64: null,
|
||||
cryptoMasterKeyBiometric: null,
|
||||
cryptoSymmetricKey: {
|
||||
encrypted: await this.storageService.get<string>(v1Keys.encKey, options),
|
||||
decrypted: null,
|
||||
},
|
||||
legacyEtmKey: null,
|
||||
organizationKeys: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<any>(
|
||||
v1Keys.encOrgKeys + userId,
|
||||
options
|
||||
),
|
||||
},
|
||||
privateKey: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<string>(v1Keys.encPrivate, options),
|
||||
},
|
||||
providerKeys: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<any>(
|
||||
v1Keys.encProviderKeys + userId,
|
||||
options
|
||||
),
|
||||
},
|
||||
publicKey: null,
|
||||
},
|
||||
profile: {
|
||||
apiKeyClientId: await this.storageService.get<string>(v1Keys.clientId, options),
|
||||
authenticationStatus: null,
|
||||
convertAccountToKeyConnector: await this.storageService.get<boolean>(
|
||||
v1Keys.convertAccountToKeyConnector,
|
||||
options
|
||||
),
|
||||
email: await this.storageService.get<string>(v1Keys.userEmail, options),
|
||||
emailVerified: await this.storageService.get<boolean>(
|
||||
v1Keys.emailVerified,
|
||||
options
|
||||
),
|
||||
entityId: null,
|
||||
entityType: null,
|
||||
everBeenUnlocked: null,
|
||||
forcePasswordReset: null,
|
||||
hasPremiumPersonally: null,
|
||||
kdfIterations: await this.storageService.get<number>(
|
||||
v1Keys.kdfIterations,
|
||||
options
|
||||
),
|
||||
kdfType: await this.storageService.get<KdfType>(v1Keys.kdf, options),
|
||||
keyHash: await this.storageService.get<string>(v1Keys.keyHash, options),
|
||||
lastActive: await this.storageService.get<number>(v1Keys.lastActive, options),
|
||||
lastSync: null,
|
||||
userId: userId,
|
||||
usesKeyConnector: null,
|
||||
},
|
||||
settings: {
|
||||
alwaysShowDock: await this.storageService.get<boolean>(
|
||||
v1Keys.alwaysShowDock,
|
||||
options
|
||||
),
|
||||
autoConfirmFingerPrints: await this.storageService.get<boolean>(
|
||||
v1Keys.autoConfirmFingerprints,
|
||||
options
|
||||
),
|
||||
autoFillOnPageLoadDefault: await this.storageService.get<boolean>(
|
||||
v1Keys.autoFillOnPageLoadDefault,
|
||||
options
|
||||
),
|
||||
biometricLocked: null,
|
||||
biometricUnlock: await this.storageService.get<boolean>(
|
||||
v1Keys.biometricUnlock,
|
||||
options
|
||||
),
|
||||
clearClipboard: await this.storageService.get<number>(
|
||||
v1Keys.clearClipboard,
|
||||
options
|
||||
),
|
||||
defaultUriMatch: await this.storageService.get<any>(
|
||||
v1Keys.defaultUriMatch,
|
||||
options
|
||||
),
|
||||
disableAddLoginNotification: await this.storageService.get<boolean>(
|
||||
v1Keys.disableAddLoginNotification,
|
||||
options
|
||||
),
|
||||
disableAutoBiometricsPrompt: await this.storageService.get<boolean>(
|
||||
v1Keys.disableAutoBiometricsPrompt,
|
||||
options
|
||||
),
|
||||
disableAutoTotpCopy: await this.storageService.get<boolean>(
|
||||
v1Keys.disableAutoTotpCopy,
|
||||
options
|
||||
),
|
||||
disableBadgeCounter: await this.storageService.get<boolean>(
|
||||
v1Keys.disableBadgeCounter,
|
||||
options
|
||||
),
|
||||
disableChangedPasswordNotification: await this.storageService.get<boolean>(
|
||||
v1Keys.disableChangedPasswordNotification,
|
||||
options
|
||||
),
|
||||
disableContextMenuItem: await this.storageService.get<boolean>(
|
||||
v1Keys.disableContextMenuItem,
|
||||
options
|
||||
),
|
||||
disableGa: await this.storageService.get<boolean>(v1Keys.disableGa, options),
|
||||
dontShowCardsCurrentTab: await this.storageService.get<boolean>(
|
||||
v1Keys.dontShowCardsCurrentTab,
|
||||
options
|
||||
),
|
||||
dontShowIdentitiesCurrentTab: await this.storageService.get<boolean>(
|
||||
v1Keys.dontShowIdentitiesCurrentTab,
|
||||
options
|
||||
),
|
||||
enableAlwaysOnTop: await this.storageService.get<boolean>(
|
||||
v1Keys.enableAlwaysOnTop,
|
||||
options
|
||||
),
|
||||
enableAutoFillOnPageLoad: await this.storageService.get<boolean>(
|
||||
v1Keys.enableAutoFillOnPageLoad,
|
||||
options
|
||||
),
|
||||
enableBiometric: await this.storageService.get<boolean>(
|
||||
v1Keys.enableBiometric,
|
||||
options
|
||||
),
|
||||
enableBrowserIntegration: await this.storageService.get<boolean>(
|
||||
v1Keys.enableBrowserIntegration,
|
||||
options
|
||||
),
|
||||
enableBrowserIntegrationFingerprint: await this.storageService.get<boolean>(
|
||||
v1Keys.enableBrowserIntegrationFingerprint,
|
||||
options
|
||||
),
|
||||
enableCloseToTray: await this.storageService.get<boolean>(
|
||||
v1Keys.enableCloseToTray,
|
||||
options
|
||||
),
|
||||
enableFullWidth: await this.storageService.get<boolean>(
|
||||
v1Keys.enableFullWidth,
|
||||
options
|
||||
),
|
||||
enableGravitars: await this.storageService.get<boolean>(
|
||||
v1Keys.enableGravatars,
|
||||
options
|
||||
),
|
||||
enableMinimizeToTray: await this.storageService.get<boolean>(
|
||||
v1Keys.enableMinimizeToTray,
|
||||
options
|
||||
),
|
||||
enableStartToTray: await this.storageService.get<boolean>(
|
||||
v1Keys.enableStartToTray,
|
||||
options
|
||||
),
|
||||
enableTray: await this.storageService.get<boolean>(v1Keys.enableTray, options),
|
||||
environmentUrls:
|
||||
(await this.storageService.get<EnvironmentUrls>(
|
||||
v1Keys.environmentUrls,
|
||||
options
|
||||
)) ?? new EnvironmentUrls(),
|
||||
equivalentDomains: await this.storageService.get<any>(
|
||||
v1Keys.equivalentDomains,
|
||||
options
|
||||
),
|
||||
minimizeOnCopyToClipboard: await this.storageService.get<boolean>(
|
||||
v1Keys.minimizeOnCopyToClipboard,
|
||||
options
|
||||
),
|
||||
neverDomains: await this.storageService.get<any>(v1Keys.neverDomains, options),
|
||||
openAtLogin: await this.storageService.get<boolean>(v1Keys.openAtLogin, options),
|
||||
passwordGenerationOptions: await this.storageService.get<any>(
|
||||
v1Keys.passwordGenerationOptions,
|
||||
options
|
||||
),
|
||||
pinProtected: {
|
||||
decrypted: null,
|
||||
encrypted: await this.storageService.get<string>(v1Keys.pinProtected, options),
|
||||
},
|
||||
protectedPin: await this.storageService.get<string>(v1Keys.protectedPin, options),
|
||||
settings: await this.storageService.get<any>(
|
||||
v1KeyPrefixes.settings + userId,
|
||||
options
|
||||
),
|
||||
vaultTimeout: await this.storageService.get<number>(v1Keys.vaultTimeout, options),
|
||||
vaultTimeoutAction: await this.storageService.get<string>(
|
||||
v1Keys.vaultTimeoutAction,
|
||||
options
|
||||
),
|
||||
},
|
||||
tokens: {
|
||||
accessToken: await this.storageService.get<string>(v1Keys.accessToken, options),
|
||||
decodedToken: null,
|
||||
refreshToken: await this.storageService.get<string>(v1Keys.refreshToken, options),
|
||||
securityStamp: null,
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
await this.set(v1KeyPrefixes[keyPrefix] + userId, null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
initialState.globals.environmentUrls =
|
||||
(await this.storageService.get<EnvironmentUrls>(v1Keys.environmentUrls, options)) ??
|
||||
new EnvironmentUrls();
|
||||
await this.storageService.save(keys.global, initialState.globals, options);
|
||||
await this.storageService.save(keys.activeUserId, initialState.activeUserId, options);
|
||||
if (initialState.activeUserId != null) {
|
||||
await this.storageService.save(
|
||||
initialState.activeUserId,
|
||||
initialState.accounts[initialState.activeUserId]
|
||||
);
|
||||
const globals: GlobalState = {
|
||||
stateVersion: StateVersion.Two,
|
||||
environmentUrls:
|
||||
(await this.get<EnvironmentUrls>(v1Keys.environmentUrls)) ?? new EnvironmentUrls(),
|
||||
locale: await this.get<string>(v1Keys.locale),
|
||||
loginRedirect: null,
|
||||
mainWindowSize: null,
|
||||
noAutoPromptBiometrics: await this.get<boolean>(v1Keys.disableAutoBiometricsPrompt),
|
||||
noAutoPromptBiometricsText: await this.get<string>(v1Keys.noAutoPromptBiometricsText),
|
||||
openAtLogin: await this.get<boolean>(v1Keys.openAtLogin),
|
||||
organizationInvitation: null,
|
||||
ssoCodeVerifier: await this.get<string>(v1Keys.ssoCodeVerifier),
|
||||
ssoOrganizationIdentifier: await this.get<string>(v1Keys.ssoIdentifier),
|
||||
ssoState: null,
|
||||
rememberedEmail: await this.get<string>(v1Keys.rememberedEmail),
|
||||
theme: await this.get<string>(v1Keys.theme),
|
||||
vaultTimeout: await this.get<number>(v1Keys.vaultTimeout),
|
||||
vaultTimeoutAction: await this.get<string>(v1Keys.vaultTimeoutAction),
|
||||
window: null,
|
||||
};
|
||||
|
||||
const userId = await this.get<string>(v1Keys.userId);
|
||||
|
||||
// (userId == null) = no logged in user (so no known userId) and we need to temporarily store account specific settings in state to migrate on first auth
|
||||
// (userId != null) = we have a currently authed user (so known userId) with encrypted data and other key settings we can move, no need to temporarily store account settings
|
||||
if (userId == null) {
|
||||
await this.set(keys.tempAccountSettings, {
|
||||
alwaysShowDock: await this.get<boolean>(v1Keys.alwaysShowDock),
|
||||
autoConfirmFingerPrints: await this.get<boolean>(v1Keys.autoConfirmFingerprints),
|
||||
autoFillOnPageLoadDefault: await this.get<boolean>(v1Keys.autoFillOnPageLoadDefault),
|
||||
biometricLocked: null,
|
||||
biometricUnlock: await this.get<boolean>(v1Keys.biometricUnlock),
|
||||
clearClipboard: await this.get<number>(v1Keys.clearClipboard),
|
||||
defaultUriMatch: await this.get<any>(v1Keys.defaultUriMatch),
|
||||
disableAddLoginNotification: await this.get<boolean>(v1Keys.disableAddLoginNotification),
|
||||
disableAutoBiometricsPrompt: await this.get<boolean>(v1Keys.disableAutoBiometricsPrompt),
|
||||
disableAutoTotpCopy: await this.get<boolean>(v1Keys.disableAutoTotpCopy),
|
||||
disableBadgeCounter: await this.get<boolean>(v1Keys.disableBadgeCounter),
|
||||
disableChangedPasswordNotification: await this.get<boolean>(
|
||||
v1Keys.disableChangedPasswordNotification
|
||||
),
|
||||
disableContextMenuItem: await this.get<boolean>(v1Keys.disableContextMenuItem),
|
||||
disableGa: await this.get<boolean>(v1Keys.disableGa),
|
||||
dontShowCardsCurrentTab: await this.get<boolean>(v1Keys.dontShowCardsCurrentTab),
|
||||
dontShowIdentitiesCurrentTab: await this.get<boolean>(v1Keys.dontShowIdentitiesCurrentTab),
|
||||
enableAlwaysOnTop: await this.get<boolean>(v1Keys.enableAlwaysOnTop),
|
||||
enableAutoFillOnPageLoad: await this.get<boolean>(v1Keys.enableAutoFillOnPageLoad),
|
||||
enableBiometric: await this.get<boolean>(v1Keys.enableBiometric),
|
||||
enableBrowserIntegration: await this.get<boolean>(v1Keys.enableBrowserIntegration),
|
||||
enableBrowserIntegrationFingerprint: await this.get<boolean>(
|
||||
v1Keys.enableBrowserIntegrationFingerprint
|
||||
),
|
||||
enableCloseToTray: await this.get<boolean>(v1Keys.enableCloseToTray),
|
||||
enableFullWidth: await this.get<boolean>(v1Keys.enableFullWidth),
|
||||
enableGravitars: await this.get<boolean>(v1Keys.enableGravatars),
|
||||
enableMinimizeToTray: await this.get<boolean>(v1Keys.enableMinimizeToTray),
|
||||
enableStartToTray: await this.get<boolean>(v1Keys.enableStartToTray),
|
||||
enableTray: await this.get<boolean>(v1Keys.enableTray),
|
||||
environmentUrls: globals.environmentUrls,
|
||||
equivalentDomains: await this.get<any>(v1Keys.equivalentDomains),
|
||||
minimizeOnCopyToClipboard: await this.get<boolean>(v1Keys.minimizeOnCopyToClipboard),
|
||||
neverDomains: await this.get<any>(v1Keys.neverDomains),
|
||||
openAtLogin: await this.get<boolean>(v1Keys.openAtLogin),
|
||||
passwordGenerationOptions: await this.get<any>(v1Keys.passwordGenerationOptions),
|
||||
pinProtected: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<string>(v1Keys.pinProtected),
|
||||
},
|
||||
protectedPin: await this.get<string>(v1Keys.protectedPin),
|
||||
settings: null,
|
||||
vaultTimeout: await this.get<number>(v1Keys.vaultTimeout),
|
||||
vaultTimeoutAction: await this.get<string>(v1Keys.vaultTimeoutAction),
|
||||
});
|
||||
await this.set(keys.global, globals);
|
||||
await this.set(keys.authenticatedAccounts, []);
|
||||
await this.set(keys.activeUserId, null);
|
||||
await clearV1Keys();
|
||||
return;
|
||||
}
|
||||
await this.storageService.save(keys.authenticatedAccounts, initialState.authenticatedAccounts);
|
||||
|
||||
globals.twoFactorToken = await this.get<string>(v1KeyPrefixes.twoFactorToken + userId);
|
||||
await this.set(keys.global, globals);
|
||||
await this.set(userId, {
|
||||
data: {
|
||||
addEditCipherInfo: null,
|
||||
ciphers: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<{ [id: string]: CipherData }>(v1KeyPrefixes.ciphers + userId),
|
||||
},
|
||||
collapsedGroupings: null,
|
||||
collections: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<{ [id: string]: CollectionData }>(
|
||||
v1KeyPrefixes.collections + userId
|
||||
),
|
||||
},
|
||||
eventCollection: await this.get<EventData[]>(v1Keys.eventCollection),
|
||||
folders: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<{ [id: string]: FolderData }>(v1KeyPrefixes.folders + userId),
|
||||
},
|
||||
localData: null,
|
||||
organizations: await this.get<{ [id: string]: OrganizationData }>(
|
||||
v1KeyPrefixes.organizations + userId
|
||||
),
|
||||
passwordGenerationHistory: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<GeneratedPasswordHistory[]>(v1Keys.history),
|
||||
},
|
||||
policies: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<{ [id: string]: PolicyData }>(v1KeyPrefixes.policies + userId),
|
||||
},
|
||||
providers: await this.get<{ [id: string]: ProviderData }>(v1KeyPrefixes.providers + userId),
|
||||
sends: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<{ [id: string]: SendData }>(v1KeyPrefixes.sends + userId),
|
||||
},
|
||||
},
|
||||
keys: {
|
||||
apiKeyClientSecret: await this.get<string>(v1Keys.clientSecret),
|
||||
cryptoMasterKey: null,
|
||||
cryptoMasterKeyAuto: null,
|
||||
cryptoMasterKeyB64: null,
|
||||
cryptoMasterKeyBiometric: null,
|
||||
cryptoSymmetricKey: {
|
||||
encrypted: await this.get<string>(v1Keys.encKey),
|
||||
decrypted: null,
|
||||
},
|
||||
legacyEtmKey: null,
|
||||
organizationKeys: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<any>(v1Keys.encOrgKeys + userId),
|
||||
},
|
||||
privateKey: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<string>(v1Keys.encPrivate),
|
||||
},
|
||||
providerKeys: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<any>(v1Keys.encProviderKeys + userId),
|
||||
},
|
||||
publicKey: null,
|
||||
},
|
||||
profile: {
|
||||
apiKeyClientId: await this.get<string>(v1Keys.clientId),
|
||||
authenticationStatus: null,
|
||||
convertAccountToKeyConnector: await this.get<boolean>(v1Keys.convertAccountToKeyConnector),
|
||||
email: await this.get<string>(v1Keys.userEmail),
|
||||
emailVerified: await this.get<boolean>(v1Keys.emailVerified),
|
||||
entityId: null,
|
||||
entityType: null,
|
||||
everBeenUnlocked: null,
|
||||
forcePasswordReset: null,
|
||||
hasPremiumPersonally: null,
|
||||
kdfIterations: await this.get<number>(v1Keys.kdfIterations),
|
||||
kdfType: await this.get<KdfType>(v1Keys.kdf),
|
||||
keyHash: await this.get<string>(v1Keys.keyHash),
|
||||
lastActive: await this.get<number>(v1Keys.lastActive),
|
||||
lastSync: null,
|
||||
userId: userId,
|
||||
usesKeyConnector: null,
|
||||
},
|
||||
settings: {
|
||||
alwaysShowDock: await this.get<boolean>(v1Keys.alwaysShowDock),
|
||||
autoConfirmFingerPrints: await this.get<boolean>(v1Keys.autoConfirmFingerprints),
|
||||
autoFillOnPageLoadDefault: await this.get<boolean>(v1Keys.autoFillOnPageLoadDefault),
|
||||
biometricLocked: null,
|
||||
biometricUnlock: await this.get<boolean>(v1Keys.biometricUnlock),
|
||||
clearClipboard: await this.get<number>(v1Keys.clearClipboard),
|
||||
defaultUriMatch: await this.get<any>(v1Keys.defaultUriMatch),
|
||||
disableAddLoginNotification: await this.get<boolean>(v1Keys.disableAddLoginNotification),
|
||||
disableAutoBiometricsPrompt: await this.get<boolean>(v1Keys.disableAutoBiometricsPrompt),
|
||||
disableAutoTotpCopy: await this.get<boolean>(v1Keys.disableAutoTotpCopy),
|
||||
disableBadgeCounter: await this.get<boolean>(v1Keys.disableBadgeCounter),
|
||||
disableChangedPasswordNotification: await this.get<boolean>(
|
||||
v1Keys.disableChangedPasswordNotification
|
||||
),
|
||||
disableContextMenuItem: await this.get<boolean>(v1Keys.disableContextMenuItem),
|
||||
disableGa: await this.get<boolean>(v1Keys.disableGa),
|
||||
dontShowCardsCurrentTab: await this.get<boolean>(v1Keys.dontShowCardsCurrentTab),
|
||||
dontShowIdentitiesCurrentTab: await this.get<boolean>(v1Keys.dontShowIdentitiesCurrentTab),
|
||||
enableAlwaysOnTop: await this.get<boolean>(v1Keys.enableAlwaysOnTop),
|
||||
enableAutoFillOnPageLoad: await this.get<boolean>(v1Keys.enableAutoFillOnPageLoad),
|
||||
enableBiometric: await this.get<boolean>(v1Keys.enableBiometric),
|
||||
enableBrowserIntegration: await this.get<boolean>(v1Keys.enableBrowserIntegration),
|
||||
enableBrowserIntegrationFingerprint: await this.get<boolean>(
|
||||
v1Keys.enableBrowserIntegrationFingerprint
|
||||
),
|
||||
enableCloseToTray: await this.get<boolean>(v1Keys.enableCloseToTray),
|
||||
enableFullWidth: await this.get<boolean>(v1Keys.enableFullWidth),
|
||||
enableGravitars: await this.get<boolean>(v1Keys.enableGravatars),
|
||||
enableMinimizeToTray: await this.get<boolean>(v1Keys.enableMinimizeToTray),
|
||||
enableStartToTray: await this.get<boolean>(v1Keys.enableStartToTray),
|
||||
enableTray: await this.get<boolean>(v1Keys.enableTray),
|
||||
environmentUrls: globals.environmentUrls,
|
||||
equivalentDomains: await this.get<any>(v1Keys.equivalentDomains),
|
||||
minimizeOnCopyToClipboard: await this.get<boolean>(v1Keys.minimizeOnCopyToClipboard),
|
||||
neverDomains: await this.get<any>(v1Keys.neverDomains),
|
||||
openAtLogin: await this.get<boolean>(v1Keys.openAtLogin),
|
||||
passwordGenerationOptions: await this.get<any>(v1Keys.passwordGenerationOptions),
|
||||
pinProtected: {
|
||||
decrypted: null,
|
||||
encrypted: await this.get<string>(v1Keys.pinProtected),
|
||||
},
|
||||
protectedPin: await this.get<string>(v1Keys.protectedPin),
|
||||
settings: await this.get<any>(v1KeyPrefixes.settings + userId),
|
||||
vaultTimeout: await this.get<number>(v1Keys.vaultTimeout),
|
||||
vaultTimeoutAction: await this.get<string>(v1Keys.vaultTimeoutAction),
|
||||
},
|
||||
tokens: {
|
||||
accessToken: await this.get<string>(v1Keys.accessToken),
|
||||
decodedToken: null,
|
||||
refreshToken: await this.get<string>(v1Keys.refreshToken),
|
||||
securityStamp: null,
|
||||
},
|
||||
});
|
||||
|
||||
await this.set(keys.authenticatedAccounts, [userId]);
|
||||
await this.set(keys.activeUserId, userId);
|
||||
await clearV1Keys(userId);
|
||||
|
||||
if (await this.secureStorageService.has(v1Keys.key, { keySuffix: "biometric" })) {
|
||||
await this.secureStorageService.save(
|
||||
@ -538,4 +415,27 @@ export class StateMigrationService {
|
||||
await this.secureStorageService.remove(v1Keys.key);
|
||||
}
|
||||
}
|
||||
|
||||
private get options(): StorageOptions {
|
||||
return { htmlStorageLocation: HtmlStorageLocation.Local };
|
||||
}
|
||||
|
||||
private get<T>(key: string): Promise<T> {
|
||||
return this.storageService.get<T>(key, this.options);
|
||||
}
|
||||
|
||||
private set(key: string, value: any): Promise<any> {
|
||||
if (value == null) {
|
||||
return this.storageService.remove(key, this.options);
|
||||
}
|
||||
return this.storageService.save(key, value, this.options);
|
||||
}
|
||||
|
||||
private async getGlobals(): Promise<GlobalState> {
|
||||
return await this.get<GlobalState>(keys.global);
|
||||
}
|
||||
|
||||
private async getCurrentStateVersion(): Promise<StateVersion> {
|
||||
return (await this.getGlobals())?.stateVersion;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user