1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

[PM-5264] Implement StateProvider in LoginEmailService (#7662)

* setup StateProvider in LoginService

* replace implementations

* replace implementation

* remove stateService

* change storage location for web to 'disk-local'

* implement migrate() method of Migrator

* add RememberedEmailMigrator to migrate.ts

* add rollback

* add tests

* replace implementation

* replace implementation

* add StateProvider to Desktop services

* rename LoginService to RememberEmailService

* update state definition

* rename file

* rename to storedEmail

* rename service to EmailService to avoid confusion

* add jsdocs

* refactor login.component.ts

* fix typos

* fix test

* rename to LoginEmailService

* update factory

* more renaming

* remove duplicate logic and rename method

* convert storedEmail to observable

* refactor to remove setStoredEmail() method

* move service to libs/auth/common

* address floating promises

* remove comment

* remove unnecessary deps in service registration
This commit is contained in:
rr-bw
2024-03-30 11:00:27 -07:00
committed by GitHub
parent 77cfa8a5ad
commit 2e51d96416
42 changed files with 396 additions and 240 deletions

View File

@@ -47,6 +47,7 @@ import { MoveDdgToStateProviderMigrator } from "./migrations/48-move-ddg-to-stat
import { AccountServerConfigMigrator } from "./migrations/49-move-account-server-configs";
import { AddKeyTypeToOrgKeysMigrator } from "./migrations/5-add-key-type-to-org-keys";
import { KeyConnectorMigrator } from "./migrations/50-move-key-connector-to-state-provider";
import { RememberedEmailMigrator } from "./migrations/51-move-remembered-email-to-state-providers";
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";
@@ -54,7 +55,7 @@ import { MoveBrowserSettingsToGlobal } from "./migrations/9-move-browser-setting
import { MinVersionMigrator } from "./migrations/min-version";
export const MIN_VERSION = 3;
export const CURRENT_VERSION = 50;
export const CURRENT_VERSION = 51;
export type MinVersion = typeof MIN_VERSION;
@@ -107,7 +108,8 @@ export function createMigrationBuilder() {
.with(MoveDesktopSettingsMigrator, 46, 47)
.with(MoveDdgToStateProviderMigrator, 47, 48)
.with(AccountServerConfigMigrator, 48, 49)
.with(KeyConnectorMigrator, 49, CURRENT_VERSION);
.with(KeyConnectorMigrator, 49, 50)
.with(RememberedEmailMigrator, 50, CURRENT_VERSION);
}
export async function currentVersion(

View File

@@ -0,0 +1,81 @@
import { MockProxy } from "jest-mock-extended";
import { MigrationHelper } from "../migration-helper";
import { mockMigrationHelper, runMigrator } from "../migration-helper.spec";
import { RememberedEmailMigrator } from "./51-move-remembered-email-to-state-providers";
function rollbackJSON() {
return {
global: {
extra: "data",
},
global_loginEmail_storedEmail: "user@example.com",
};
}
describe("RememberedEmailMigrator", () => {
const migrator = new RememberedEmailMigrator(50, 51);
describe("migrate", () => {
it("should migrate the rememberedEmail property from the legacy global object to a global StorageKey as 'global_loginEmail_storedEmail'", async () => {
const output = await runMigrator(migrator, {
global: {
rememberedEmail: "user@example.com",
extra: "data", // Represents a global property that should persist after migration
},
});
expect(output).toEqual({
global: {
extra: "data",
},
global_loginEmail_storedEmail: "user@example.com",
});
});
it("should remove the rememberedEmail property from the legacy global object", async () => {
const output = await runMigrator(migrator, {
global: {
rememberedEmail: "user@example.com",
},
});
expect(output.global).not.toHaveProperty("rememberedEmail");
});
});
describe("rollback", () => {
let helper: MockProxy<MigrationHelper>;
let sut: RememberedEmailMigrator;
const keyDefinitionLike = {
key: "storedEmail",
stateDefinition: {
name: "loginEmail",
},
};
beforeEach(() => {
helper = mockMigrationHelper(rollbackJSON(), 51);
sut = new RememberedEmailMigrator(50, 51);
});
it("should null out the storedEmail global StorageKey", async () => {
await sut.rollback(helper);
expect(helper.setToGlobal).toHaveBeenCalledTimes(1);
expect(helper.setToGlobal).toHaveBeenCalledWith(keyDefinitionLike, null);
});
it("should add the rememberedEmail property back to legacy global object", async () => {
await sut.rollback(helper);
expect(helper.set).toHaveBeenCalledTimes(1);
expect(helper.set).toHaveBeenCalledWith("global", {
rememberedEmail: "user@example.com",
extra: "data",
});
});
});
});

View File

@@ -0,0 +1,46 @@
import { KeyDefinitionLike, MigrationHelper, StateDefinitionLike } from "../migration-helper";
import { Migrator } from "../migrator";
type ExpectedGlobalState = { rememberedEmail?: string };
const LOGIN_EMAIL_STATE: StateDefinitionLike = { name: "loginEmail" };
const STORED_EMAIL: KeyDefinitionLike = {
key: "storedEmail",
stateDefinition: LOGIN_EMAIL_STATE,
};
export class RememberedEmailMigrator extends Migrator<50, 51> {
async migrate(helper: MigrationHelper): Promise<void> {
const legacyGlobal = await helper.get<ExpectedGlobalState>("global");
// Move global data
if (legacyGlobal?.rememberedEmail != null) {
await helper.setToGlobal(STORED_EMAIL, legacyGlobal.rememberedEmail);
}
// Delete legacy global data
delete legacyGlobal?.rememberedEmail;
await helper.set("global", legacyGlobal);
}
async rollback(helper: MigrationHelper): Promise<void> {
let legacyGlobal = await helper.get<ExpectedGlobalState>("global");
let updatedLegacyGlobal = false;
const globalStoredEmail = await helper.getFromGlobal<string>(STORED_EMAIL);
if (globalStoredEmail) {
if (!legacyGlobal) {
legacyGlobal = {};
}
updatedLegacyGlobal = true;
legacyGlobal.rememberedEmail = globalStoredEmail;
await helper.setToGlobal(STORED_EMAIL, null);
}
if (updatedLegacyGlobal) {
await helper.set("global", legacyGlobal);
}
}
}