mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
[PM-5499] auth request service migrations (#8597)
* move auth request storage to service * create migrations for auth requests * fix tests * fix browser * fix login strategy * update migration * use correct test descriptions in migration
This commit is contained in:
@@ -52,6 +52,7 @@ 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 { AuthRequestMigrator } from "./migrations/56-move-auth-requests";
|
||||
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";
|
||||
@@ -59,7 +60,7 @@ import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-setting
|
||||
import { MinVersionMigrator } from "./migrations/min-version";
|
||||
|
||||
export const MIN_VERSION = 3;
|
||||
export const CURRENT_VERSION = 55;
|
||||
export const CURRENT_VERSION = 56;
|
||||
|
||||
export type MinVersion = typeof MIN_VERSION;
|
||||
|
||||
@@ -117,7 +118,8 @@ export function createMigrationBuilder() {
|
||||
.with(DeleteInstalledVersion, 51, 52)
|
||||
.with(DeviceTrustCryptoServiceStateProviderMigrator, 52, 53)
|
||||
.with(SendMigrator, 53, 54)
|
||||
.with(MoveMasterKeyStateToProviderMigrator, 54, CURRENT_VERSION);
|
||||
.with(MoveMasterKeyStateToProviderMigrator, 54, 55)
|
||||
.with(AuthRequestMigrator, 55, CURRENT_VERSION);
|
||||
}
|
||||
|
||||
export async function currentVersion(
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import { MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { KeyDefinitionLike, MigrationHelper } from "../migration-helper";
|
||||
import { mockMigrationHelper } from "../migration-helper.spec";
|
||||
|
||||
import { AuthRequestMigrator } from "./56-move-auth-requests";
|
||||
|
||||
function exampleJSON() {
|
||||
return {
|
||||
global: {
|
||||
otherStuff: "otherStuff1",
|
||||
},
|
||||
authenticatedAccounts: ["FirstAccount", "SecondAccount"],
|
||||
FirstAccount: {
|
||||
settings: {
|
||||
otherStuff: "otherStuff2",
|
||||
approveLoginRequests: true,
|
||||
},
|
||||
otherStuff: "otherStuff3",
|
||||
adminAuthRequest: {
|
||||
id: "id1",
|
||||
privateKey: "privateKey1",
|
||||
},
|
||||
},
|
||||
SecondAccount: {
|
||||
settings: {
|
||||
otherStuff: "otherStuff4",
|
||||
},
|
||||
otherStuff: "otherStuff5",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function rollbackJSON() {
|
||||
return {
|
||||
user_FirstAccount_authRequestLocal_adminAuthRequest: {
|
||||
id: "id1",
|
||||
privateKey: "privateKey1",
|
||||
},
|
||||
user_FirstAccount_authRequestLocal_acceptAuthRequests: true,
|
||||
global: {
|
||||
otherStuff: "otherStuff1",
|
||||
},
|
||||
authenticatedAccounts: ["FirstAccount", "SecondAccount"],
|
||||
FirstAccount: {
|
||||
settings: {
|
||||
otherStuff: "otherStuff2",
|
||||
},
|
||||
otherStuff: "otherStuff3",
|
||||
},
|
||||
SecondAccount: {
|
||||
settings: {
|
||||
otherStuff: "otherStuff4",
|
||||
},
|
||||
otherStuff: "otherStuff5",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const ADMIN_AUTH_REQUEST_KEY: KeyDefinitionLike = {
|
||||
stateDefinition: {
|
||||
name: "authRequestLocal",
|
||||
},
|
||||
key: "adminAuthRequest",
|
||||
};
|
||||
|
||||
const ACCEPT_AUTH_REQUESTS_KEY: KeyDefinitionLike = {
|
||||
stateDefinition: {
|
||||
name: "authRequestLocal",
|
||||
},
|
||||
key: "acceptAuthRequests",
|
||||
};
|
||||
|
||||
describe("AuthRequestMigrator", () => {
|
||||
let helper: MockProxy<MigrationHelper>;
|
||||
let sut: AuthRequestMigrator;
|
||||
|
||||
describe("migrate", () => {
|
||||
beforeEach(() => {
|
||||
helper = mockMigrationHelper(exampleJSON(), 55);
|
||||
sut = new AuthRequestMigrator(55, 56);
|
||||
});
|
||||
|
||||
it("removes the existing adminAuthRequest and approveLoginRequests", async () => {
|
||||
await sut.migrate(helper);
|
||||
|
||||
expect(helper.set).toHaveBeenCalledWith("FirstAccount", {
|
||||
settings: {
|
||||
otherStuff: "otherStuff2",
|
||||
},
|
||||
otherStuff: "otherStuff3",
|
||||
});
|
||||
expect(helper.set).not.toHaveBeenCalledWith("SecondAccount");
|
||||
});
|
||||
|
||||
it("sets the adminAuthRequest and approveLoginRequests under the new key definitions", async () => {
|
||||
await sut.migrate(helper);
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith("FirstAccount", ADMIN_AUTH_REQUEST_KEY, {
|
||||
id: "id1",
|
||||
privateKey: "privateKey1",
|
||||
});
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith("FirstAccount", ACCEPT_AUTH_REQUESTS_KEY, true);
|
||||
expect(helper.setToUser).not.toHaveBeenCalledWith("SecondAccount");
|
||||
});
|
||||
});
|
||||
|
||||
describe("rollback", () => {
|
||||
beforeEach(() => {
|
||||
helper = mockMigrationHelper(rollbackJSON(), 56);
|
||||
sut = new AuthRequestMigrator(55, 56);
|
||||
});
|
||||
|
||||
it("nulls the new adminAuthRequest and acceptAuthRequests values", async () => {
|
||||
await sut.rollback(helper);
|
||||
|
||||
expect(helper.setToUser).toHaveBeenCalledWith("FirstAccount", ADMIN_AUTH_REQUEST_KEY, null);
|
||||
expect(helper.setToUser).toHaveBeenCalledWith("FirstAccount", ACCEPT_AUTH_REQUESTS_KEY, null);
|
||||
});
|
||||
|
||||
it("sets back the adminAuthRequest and approveLoginRequests under old account object", async () => {
|
||||
await sut.rollback(helper);
|
||||
|
||||
expect(helper.set).toHaveBeenCalledWith("FirstAccount", {
|
||||
adminAuthRequest: {
|
||||
id: "id1",
|
||||
privateKey: "privateKey1",
|
||||
},
|
||||
settings: {
|
||||
otherStuff: "otherStuff2",
|
||||
approveLoginRequests: true,
|
||||
},
|
||||
otherStuff: "otherStuff3",
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
import { KeyDefinitionLike, MigrationHelper } from "../migration-helper";
|
||||
import { Migrator } from "../migrator";
|
||||
|
||||
type AdminAuthRequestStorable = {
|
||||
id: string;
|
||||
privateKey: string;
|
||||
};
|
||||
|
||||
type ExpectedAccountType = {
|
||||
adminAuthRequest?: AdminAuthRequestStorable;
|
||||
settings?: {
|
||||
approveLoginRequests?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const ADMIN_AUTH_REQUEST_KEY: KeyDefinitionLike = {
|
||||
stateDefinition: {
|
||||
name: "authRequestLocal",
|
||||
},
|
||||
key: "adminAuthRequest",
|
||||
};
|
||||
|
||||
const ACCEPT_AUTH_REQUESTS_KEY: KeyDefinitionLike = {
|
||||
stateDefinition: {
|
||||
name: "authRequestLocal",
|
||||
},
|
||||
key: "acceptAuthRequests",
|
||||
};
|
||||
|
||||
export class AuthRequestMigrator extends Migrator<55, 56> {
|
||||
async migrate(helper: MigrationHelper): Promise<void> {
|
||||
const accounts = await helper.getAccounts<ExpectedAccountType>();
|
||||
|
||||
async function migrateAccount(userId: string, account: ExpectedAccountType): Promise<void> {
|
||||
let updatedAccount = false;
|
||||
|
||||
// Migrate admin auth request
|
||||
const existingAdminAuthRequest = account?.adminAuthRequest;
|
||||
|
||||
if (existingAdminAuthRequest != null) {
|
||||
await helper.setToUser(userId, ADMIN_AUTH_REQUEST_KEY, existingAdminAuthRequest);
|
||||
delete account.adminAuthRequest;
|
||||
updatedAccount = true;
|
||||
}
|
||||
|
||||
// Migrate approve login requests
|
||||
const existingApproveLoginRequests = account?.settings?.approveLoginRequests;
|
||||
|
||||
if (existingApproveLoginRequests != null) {
|
||||
await helper.setToUser(userId, ACCEPT_AUTH_REQUESTS_KEY, existingApproveLoginRequests);
|
||||
delete account.settings.approveLoginRequests;
|
||||
updatedAccount = true;
|
||||
}
|
||||
|
||||
if (updatedAccount) {
|
||||
// Save the migrated account
|
||||
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> {
|
||||
let updatedAccount = false;
|
||||
// Rollback admin auth request
|
||||
const migratedAdminAuthRequest: AdminAuthRequestStorable = await helper.getFromUser(
|
||||
userId,
|
||||
ADMIN_AUTH_REQUEST_KEY,
|
||||
);
|
||||
|
||||
if (migratedAdminAuthRequest != null) {
|
||||
account.adminAuthRequest = migratedAdminAuthRequest;
|
||||
updatedAccount = true;
|
||||
}
|
||||
|
||||
await helper.setToUser(userId, ADMIN_AUTH_REQUEST_KEY, null);
|
||||
|
||||
// Rollback approve login requests
|
||||
const migratedAcceptAuthRequest: boolean = await helper.getFromUser(
|
||||
userId,
|
||||
ACCEPT_AUTH_REQUESTS_KEY,
|
||||
);
|
||||
|
||||
if (migratedAcceptAuthRequest != null) {
|
||||
account.settings = Object.assign(account.settings ?? {}, {
|
||||
approveLoginRequests: migratedAcceptAuthRequest,
|
||||
});
|
||||
updatedAccount = true;
|
||||
}
|
||||
|
||||
await helper.setToUser(userId, ACCEPT_AUTH_REQUESTS_KEY, null);
|
||||
|
||||
if (updatedAccount) {
|
||||
await helper.set(userId, account);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([...accounts.map(({ userId, account }) => rollbackAccount(userId, account))]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user