mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
* create mp and kdf service * update mp service interface to not rely on active user * rename observable methods * update crypto service with new MP service * add master password service to login strategies - make fake service for easier testing - fix crypto service tests * update auth service and finish strategies * auth request refactors * more service refactors and constructor updates * setMasterKey refactors * remove master key methods from crypto service * remove master key and hash from state service * missed fixes * create migrations and fix references * fix master key imports * default force set password reason to none * add password reset reason observable factory to service * remove kdf changes and migrate only disk data * update migration number * fix sync service deps * use disk for force set password state * fix desktop migration * fix sso test * fix tests * fix more tests * fix even more tests * fix even more tests * fix cli * remove kdf service abstraction * add missing deps for browser * fix merge conflicts * clear reset password reason on lock or logout * fix tests * fix other tests * add jsdocs to abstraction * use state provider in crypto service * inverse master password service factory * add clearOn to master password service * add parameter validation to master password service * add component level userId * add missed userId * migrate key hash * fix login strategy service * delete crypto master key from account * migrate master key encrypted user key * rename key hash to master key hash * use mp service for getMasterKeyEncryptedUserKey * fix tests * fix user key decryption logic * add clear methods to mp service * fix circular dep and encryption issue * fix test * remove extra account service call * use EncString in state provider * fix tests * return to using encrypted string for serialization
This commit is contained in:
@@ -51,6 +51,7 @@ import { RememberedEmailMigrator } from "./migrations/51-move-remembered-email-t
|
||||
import { DeleteInstalledVersion } from "./migrations/52-delete-installed-version";
|
||||
import { DeviceTrustCryptoServiceStateProviderMigrator } from "./migrations/53-migrate-device-trust-crypto-svc-to-state-providers";
|
||||
import { SendMigrator } from "./migrations/54-move-encrypted-sends";
|
||||
import { MoveMasterKeyStateToProviderMigrator } from "./migrations/55-move-master-key-state-to-provider";
|
||||
import { RemoveLegacyEtmKeyMigrator } from "./migrations/6-remove-legacy-etm-key";
|
||||
import { MoveBiometricAutoPromptToAccount } from "./migrations/7-move-biometric-auto-prompt-to-account";
|
||||
import { MoveStateVersionMigrator } from "./migrations/8-move-state-version";
|
||||
@@ -58,7 +59,7 @@ import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-setting
|
||||
import { MinVersionMigrator } from "./migrations/min-version";
|
||||
|
||||
export const MIN_VERSION = 3;
|
||||
export const CURRENT_VERSION = 54;
|
||||
export const CURRENT_VERSION = 55;
|
||||
|
||||
export type MinVersion = typeof MIN_VERSION;
|
||||
|
||||
@@ -115,7 +116,8 @@ export function createMigrationBuilder() {
|
||||
.with(RememberedEmailMigrator, 50, 51)
|
||||
.with(DeleteInstalledVersion, 51, 52)
|
||||
.with(DeviceTrustCryptoServiceStateProviderMigrator, 52, 53)
|
||||
.with(SendMigrator, 53, 54);
|
||||
.with(SendMigrator, 53, 54)
|
||||
.with(MoveMasterKeyStateToProviderMigrator, 54, CURRENT_VERSION);
|
||||
}
|
||||
|
||||
export async function currentVersion(
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
import { any, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { MigrationHelper } from "../migration-helper";
|
||||
import { mockMigrationHelper } from "../migration-helper.spec";
|
||||
|
||||
import {
|
||||
FORCE_SET_PASSWORD_REASON_DEFINITION,
|
||||
MASTER_KEY_ENCRYPTED_USER_KEY_DEFINITION,
|
||||
MASTER_KEY_HASH_DEFINITION,
|
||||
MoveMasterKeyStateToProviderMigrator,
|
||||
} from "./55-move-master-key-state-to-provider";
|
||||
|
||||
function preMigrationState() {
|
||||
return {
|
||||
global: {
|
||||
otherStuff: "otherStuff1",
|
||||
},
|
||||
authenticatedAccounts: ["FirstAccount", "SecondAccount", "ThirdAccount"],
|
||||
// prettier-ignore
|
||||
"FirstAccount": {
|
||||
profile: {
|
||||
forceSetPasswordReason: "FirstAccount_forceSetPasswordReason",
|
||||
keyHash: "FirstAccount_keyHash",
|
||||
otherStuff: "overStuff2",
|
||||
},
|
||||
keys: {
|
||||
masterKeyEncryptedUserKey: "FirstAccount_masterKeyEncryptedUserKey",
|
||||
},
|
||||
otherStuff: "otherStuff3",
|
||||
},
|
||||
// prettier-ignore
|
||||
"SecondAccount": {
|
||||
profile: {
|
||||
forceSetPasswordReason: "SecondAccount_forceSetPasswordReason",
|
||||
keyHash: "SecondAccount_keyHash",
|
||||
otherStuff: "otherStuff4",
|
||||
},
|
||||
keys: {
|
||||
masterKeyEncryptedUserKey: "SecondAccount_masterKeyEncryptedUserKey",
|
||||
},
|
||||
otherStuff: "otherStuff5",
|
||||
},
|
||||
// prettier-ignore
|
||||
"ThirdAccount": {
|
||||
profile: {
|
||||
otherStuff: "otherStuff6",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function postMigrationState() {
|
||||
return {
|
||||
user_FirstAccount_masterPassword_forceSetPasswordReason: "FirstAccount_forceSetPasswordReason",
|
||||
user_FirstAccount_masterPassword_masterKeyHash: "FirstAccount_keyHash",
|
||||
user_FirstAccount_masterPassword_masterKeyEncryptedUserKey:
|
||||
"FirstAccount_masterKeyEncryptedUserKey",
|
||||
user_SecondAccount_masterPassword_forceSetPasswordReason:
|
||||
"SecondAccount_forceSetPasswordReason",
|
||||
user_SecondAccount_masterPassword_masterKeyHash: "SecondAccount_keyHash",
|
||||
user_SecondAccount_masterPassword_masterKeyEncryptedUserKey:
|
||||
"SecondAccount_masterKeyEncryptedUserKey",
|
||||
global: {
|
||||
otherStuff: "otherStuff1",
|
||||
},
|
||||
authenticatedAccounts: ["FirstAccount", "SecondAccount"],
|
||||
// prettier-ignore
|
||||
"FirstAccount": {
|
||||
profile: {
|
||||
otherStuff: "overStuff2",
|
||||
},
|
||||
otherStuff: "otherStuff3",
|
||||
},
|
||||
// prettier-ignore
|
||||
"SecondAccount": {
|
||||
profile: {
|
||||
otherStuff: "otherStuff4",
|
||||
},
|
||||
otherStuff: "otherStuff5",
|
||||
},
|
||||
// prettier-ignore
|
||||
"ThirdAccount": {
|
||||
profile: {
|
||||
otherStuff: "otherStuff6",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("MoveForceSetPasswordReasonToStateProviderMigrator", () => {
|
||||
let helper: MockProxy<MigrationHelper>;
|
||||
let sut: MoveMasterKeyStateToProviderMigrator;
|
||||
|
||||
describe("migrate", () => {
|
||||
beforeEach(() => {
|
||||
helper = mockMigrationHelper(preMigrationState(), 54);
|
||||
sut = new MoveMasterKeyStateToProviderMigrator(54, 55);
|
||||
});
|
||||
|
||||
it("should remove properties from existing accounts", async () => {
|
||||
await sut.migrate(helper);
|
||||
expect(helper.set).toHaveBeenCalledWith("FirstAccount", {
|
||||
profile: {
|
||||
otherStuff: "overStuff2",
|
||||
},
|
||||
keys: {},
|
||||
otherStuff: "otherStuff3",
|
||||
});
|
||||
expect(helper.set).toHaveBeenCalledWith("SecondAccount", {
|
||||
profile: {
|
||||
otherStuff: "otherStuff4",
|
||||
},
|
||||
keys: {},
|
||||
otherStuff: "otherStuff5",
|
||||
});
|
||||
});
|
||||
|
||||
it("should set properties for each account", async () => {
|
||||
await sut.migrate(helper);
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith(
|
||||
"FirstAccount",
|
||||
FORCE_SET_PASSWORD_REASON_DEFINITION,
|
||||
"FirstAccount_forceSetPasswordReason",
|
||||
);
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith(
|
||||
"FirstAccount",
|
||||
MASTER_KEY_HASH_DEFINITION,
|
||||
"FirstAccount_keyHash",
|
||||
);
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith(
|
||||
"FirstAccount",
|
||||
MASTER_KEY_ENCRYPTED_USER_KEY_DEFINITION,
|
||||
"FirstAccount_masterKeyEncryptedUserKey",
|
||||
);
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith(
|
||||
"SecondAccount",
|
||||
FORCE_SET_PASSWORD_REASON_DEFINITION,
|
||||
"SecondAccount_forceSetPasswordReason",
|
||||
);
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith(
|
||||
"SecondAccount",
|
||||
MASTER_KEY_HASH_DEFINITION,
|
||||
"SecondAccount_keyHash",
|
||||
);
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith(
|
||||
"SecondAccount",
|
||||
MASTER_KEY_ENCRYPTED_USER_KEY_DEFINITION,
|
||||
"SecondAccount_masterKeyEncryptedUserKey",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("rollback", () => {
|
||||
beforeEach(() => {
|
||||
helper = mockMigrationHelper(postMigrationState(), 55);
|
||||
sut = new MoveMasterKeyStateToProviderMigrator(54, 55);
|
||||
});
|
||||
|
||||
it.each(["FirstAccount", "SecondAccount"])("should null out new values", async (userId) => {
|
||||
await sut.rollback(helper);
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith(
|
||||
userId,
|
||||
FORCE_SET_PASSWORD_REASON_DEFINITION,
|
||||
null,
|
||||
);
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith(userId, MASTER_KEY_HASH_DEFINITION, null);
|
||||
});
|
||||
|
||||
it("should add explicit value back to accounts", async () => {
|
||||
await sut.rollback(helper);
|
||||
|
||||
expect(helper.set).toHaveBeenCalledWith("FirstAccount", {
|
||||
profile: {
|
||||
forceSetPasswordReason: "FirstAccount_forceSetPasswordReason",
|
||||
keyHash: "FirstAccount_keyHash",
|
||||
otherStuff: "overStuff2",
|
||||
},
|
||||
keys: {
|
||||
masterKeyEncryptedUserKey: "FirstAccount_masterKeyEncryptedUserKey",
|
||||
},
|
||||
otherStuff: "otherStuff3",
|
||||
});
|
||||
expect(helper.set).toHaveBeenCalledWith("SecondAccount", {
|
||||
profile: {
|
||||
forceSetPasswordReason: "SecondAccount_forceSetPasswordReason",
|
||||
keyHash: "SecondAccount_keyHash",
|
||||
otherStuff: "otherStuff4",
|
||||
},
|
||||
keys: {
|
||||
masterKeyEncryptedUserKey: "SecondAccount_masterKeyEncryptedUserKey",
|
||||
},
|
||||
otherStuff: "otherStuff5",
|
||||
});
|
||||
});
|
||||
|
||||
it("should not try to restore values to missing accounts", async () => {
|
||||
await sut.rollback(helper);
|
||||
|
||||
expect(helper.set).not.toHaveBeenCalledWith("ThirdAccount", any());
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,111 @@
|
||||
import { KeyDefinitionLike, MigrationHelper } from "../migration-helper";
|
||||
import { Migrator } from "../migrator";
|
||||
|
||||
type ExpectedAccountType = {
|
||||
keys?: {
|
||||
masterKeyEncryptedUserKey?: string;
|
||||
};
|
||||
profile?: {
|
||||
forceSetPasswordReason?: number;
|
||||
keyHash?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const FORCE_SET_PASSWORD_REASON_DEFINITION: KeyDefinitionLike = {
|
||||
key: "forceSetPasswordReason",
|
||||
stateDefinition: {
|
||||
name: "masterPassword",
|
||||
},
|
||||
};
|
||||
|
||||
export const MASTER_KEY_HASH_DEFINITION: KeyDefinitionLike = {
|
||||
key: "masterKeyHash",
|
||||
stateDefinition: {
|
||||
name: "masterPassword",
|
||||
},
|
||||
};
|
||||
|
||||
export const MASTER_KEY_ENCRYPTED_USER_KEY_DEFINITION: KeyDefinitionLike = {
|
||||
key: "masterKeyEncryptedUserKey",
|
||||
stateDefinition: {
|
||||
name: "masterPassword",
|
||||
},
|
||||
};
|
||||
|
||||
export class MoveMasterKeyStateToProviderMigrator extends Migrator<54, 55> {
|
||||
async migrate(helper: MigrationHelper): Promise<void> {
|
||||
const accounts = await helper.getAccounts<ExpectedAccountType>();
|
||||
async function migrateAccount(userId: string, account: ExpectedAccountType): Promise<void> {
|
||||
const forceSetPasswordReason = account?.profile?.forceSetPasswordReason;
|
||||
if (forceSetPasswordReason != null) {
|
||||
await helper.setToUser(
|
||||
userId,
|
||||
FORCE_SET_PASSWORD_REASON_DEFINITION,
|
||||
forceSetPasswordReason,
|
||||
);
|
||||
|
||||
delete account.profile.forceSetPasswordReason;
|
||||
await helper.set(userId, account);
|
||||
}
|
||||
|
||||
const masterKeyHash = account?.profile?.keyHash;
|
||||
if (masterKeyHash != null) {
|
||||
await helper.setToUser(userId, MASTER_KEY_HASH_DEFINITION, masterKeyHash);
|
||||
|
||||
delete account.profile.keyHash;
|
||||
await helper.set(userId, account);
|
||||
}
|
||||
|
||||
const masterKeyEncryptedUserKey = account?.keys?.masterKeyEncryptedUserKey;
|
||||
if (masterKeyEncryptedUserKey != null) {
|
||||
await helper.setToUser(
|
||||
userId,
|
||||
MASTER_KEY_ENCRYPTED_USER_KEY_DEFINITION,
|
||||
masterKeyEncryptedUserKey,
|
||||
);
|
||||
|
||||
delete account.keys.masterKeyEncryptedUserKey;
|
||||
await helper.set(userId, account);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([...accounts.map(({ userId, account }) => migrateAccount(userId, account))]);
|
||||
}
|
||||
async rollback(helper: MigrationHelper): Promise<void> {
|
||||
const accounts = await helper.getAccounts<ExpectedAccountType>();
|
||||
async function rollbackAccount(userId: string, account: ExpectedAccountType): Promise<void> {
|
||||
const forceSetPasswordReason = await helper.getFromUser(
|
||||
userId,
|
||||
FORCE_SET_PASSWORD_REASON_DEFINITION,
|
||||
);
|
||||
const masterKeyHash = await helper.getFromUser(userId, MASTER_KEY_HASH_DEFINITION);
|
||||
const masterKeyEncryptedUserKey = await helper.getFromUser(
|
||||
userId,
|
||||
MASTER_KEY_ENCRYPTED_USER_KEY_DEFINITION,
|
||||
);
|
||||
if (account != null) {
|
||||
if (forceSetPasswordReason != null) {
|
||||
account.profile = Object.assign(account.profile ?? {}, {
|
||||
forceSetPasswordReason,
|
||||
});
|
||||
}
|
||||
if (masterKeyHash != null) {
|
||||
account.profile = Object.assign(account.profile ?? {}, {
|
||||
keyHash: masterKeyHash,
|
||||
});
|
||||
}
|
||||
if (masterKeyEncryptedUserKey != null) {
|
||||
account.keys = Object.assign(account.keys ?? {}, {
|
||||
masterKeyEncryptedUserKey,
|
||||
});
|
||||
}
|
||||
await helper.set(userId, account);
|
||||
}
|
||||
|
||||
await helper.setToUser(userId, FORCE_SET_PASSWORD_REASON_DEFINITION, null);
|
||||
await helper.setToUser(userId, MASTER_KEY_HASH_DEFINITION, null);
|
||||
}
|
||||
|
||||
await Promise.all([...accounts.map(({ userId, account }) => rollbackAccount(userId, account))]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user