Skip to content
This repository has been archived by the owner on Jun 17, 2022. It is now read-only.

Commit

Permalink
Clear stale everBeenUnlocked value from onDisk storage (#682)
Browse files Browse the repository at this point in the history
* Add StateVersion.Four to remove old everBeenUnlocked key

* Save new state properly

* Add unit tests

* Fix linting
  • Loading branch information
eliykat committed Feb 14, 2022
1 parent bcbb52e commit 609baec
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 1 deletion.
3 changes: 2 additions & 1 deletion common/src/enums/stateVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export enum StateVersion {
One = 1, // Original flat key/value pair store
Two = 2, // Move to a typed State object
Three = 3, // Fix migration of users' premium status
Latest = Three,
Four = 4, // Fix 'Never Lock' option by removing stale data
Latest = Four,
}
20 changes: 20 additions & 0 deletions common/src/services/stateMigration.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ export class StateMigrationService<
case StateVersion.Two:
await this.migrateStateFrom2To3();
break;
case StateVersion.Three:
await this.migrateStateFrom3To4();
break;
}

currentStateVersion += 1;
Expand Down Expand Up @@ -474,6 +477,23 @@ export class StateMigrationService<
await this.set(keys.global, globals);
}

protected async migrateStateFrom3To4(): Promise<void> {
const authenticatedUserIds = await this.get<string[]>(keys.authenticatedAccounts);
await Promise.all(
authenticatedUserIds.map(async (userId) => {
const account = await this.get<TAccount>(userId);
if (account?.profile?.everBeenUnlocked != null) {
delete account.profile.everBeenUnlocked;
return this.set(userId, account);
}
})
);

const globals = await this.getGlobals();
globals.stateVersion = StateVersion.Four;
await this.set(keys.global, globals);
}

protected get options(): StorageOptions {
return { htmlStorageLocation: HtmlStorageLocation.Local };
}
Expand Down
88 changes: 88 additions & 0 deletions spec/common/services/stateMigration.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Arg, Substitute, SubstituteOf } from "@fluffy-spoon/substitute";

import { StorageService } from "jslib-common/abstractions/storage.service";

import { StateMigrationService } from "jslib-common/services/stateMigration.service";

import { StateFactory } from "jslib-common/factories/stateFactory";

import { Account } from "jslib-common/models/domain/account";
import { GlobalState } from "jslib-common/models/domain/globalState";

import { StateVersion } from "jslib-common/enums/stateVersion";

const userId = "USER_ID";

describe("State Migration Service", () => {
let storageService: SubstituteOf<StorageService>;
let secureStorageService: SubstituteOf<StorageService>;
let stateFactory: SubstituteOf<StateFactory>;

let stateMigrationService: StateMigrationService;

beforeEach(() => {
storageService = Substitute.for<StorageService>();
secureStorageService = Substitute.for<StorageService>();
stateFactory = Substitute.for<StateFactory>();

stateMigrationService = new StateMigrationService(
storageService,
secureStorageService,
stateFactory
);
});

describe("StateVersion 3 to 4 migration", async () => {
beforeEach(() => {
const globalVersion3: Partial<GlobalState> = {
stateVersion: StateVersion.Three,
};

storageService.get("global", Arg.any()).resolves(globalVersion3);
storageService.get("authenticatedAccounts", Arg.any()).resolves([userId]);
});

it("clears everBeenUnlocked", async () => {
const accountVersion3: Account = {
profile: {
apiKeyClientId: null,
convertAccountToKeyConnector: null,
email: "EMAIL",
emailVerified: true,
everBeenUnlocked: true,
hasPremiumPersonally: false,
kdfIterations: 100000,
kdfType: 0,
keyHash: "KEY_HASH",
lastSync: "LAST_SYNC",
userId: userId,
usesKeyConnector: false,
forcePasswordReset: false,
},
};

const expectedAccountVersion4: Account = {
profile: {
...accountVersion3.profile,
},
};
delete expectedAccountVersion4.profile.everBeenUnlocked;

storageService.get(userId, Arg.any()).resolves(accountVersion3);

await stateMigrationService.migrate();

storageService.received(1).save(userId, expectedAccountVersion4, Arg.any());
});

it("updates StateVersion number", async () => {
await stateMigrationService.migrate();

storageService.received(1).save(
"global",
Arg.is((globals: GlobalState) => globals.stateVersion === StateVersion.Four),
Arg.any()
);
});
});
});

0 comments on commit 609baec

Please sign in to comment.