From 5fad7c666f0fa301a180ec1a72794b1fd6088a5e Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Fri, 11 Feb 2022 13:40:13 +1000 Subject: [PATCH] Add StateVersion.Three to fix premium migration (#666) * Add StateVersion.Three to fix premium migration --- common/src/enums/stateVersion.ts | 3 +- common/src/services/stateMigration.service.ts | 26 +++++++++++++++ common/src/services/token.service.ts | 32 ++++++++++++------- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/common/src/enums/stateVersion.ts b/common/src/enums/stateVersion.ts index aa45edbbdd..f966c32324 100644 --- a/common/src/enums/stateVersion.ts +++ b/common/src/enums/stateVersion.ts @@ -1,5 +1,6 @@ export enum StateVersion { One = 1, // Original flat key/value pair store Two = 2, // Move to a typed State object - Latest = Two, + Three = 3, // Fix migration of users' premium status + Latest = Three, } diff --git a/common/src/services/stateMigration.service.ts b/common/src/services/stateMigration.service.ts index 7d5e2bc295..cc40583cf3 100644 --- a/common/src/services/stateMigration.service.ts +++ b/common/src/services/stateMigration.service.ts @@ -24,6 +24,8 @@ import { GlobalStateFactory } from "../factories/globalStateFactory"; import { StateFactory } from "../factories/stateFactory"; import { Account, AccountSettings } from "../models/domain/account"; +import { TokenService } from "./token.service"; + // 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: { [key: string]: string } = { @@ -153,6 +155,9 @@ export class StateMigrationService< case StateVersion.One: await this.migrateStateFrom1To2(); break; + case StateVersion.Two: + await this.migrateStateFrom2To3(); + break; } currentStateVersion += 1; @@ -448,6 +453,27 @@ export class StateMigrationService< } } + protected async migrateStateFrom2To3(): Promise { + const authenticatedUserIds = await this.get(keys.authenticatedAccounts); + await Promise.all( + authenticatedUserIds.map(async (userId) => { + const account = await this.get(userId); + if ( + account?.profile?.hasPremiumPersonally === null && + account.tokens?.accessToken != null + ) { + const decodedToken = await TokenService.decodeToken(account.tokens.accessToken); + account.profile.hasPremiumPersonally = decodedToken.premium; + await this.set(userId, account); + } + }) + ); + + const globals = await this.getGlobals(); + globals.stateVersion = StateVersion.Three; + await this.set(keys.global, globals); + } + protected get options(): StorageOptions { return { htmlStorageLocation: HtmlStorageLocation.Local }; } diff --git a/common/src/services/token.service.ts b/common/src/services/token.service.ts index f1e328b073..ad39aed57e 100644 --- a/common/src/services/token.service.ts +++ b/common/src/services/token.service.ts @@ -6,6 +6,25 @@ import { Utils } from "../misc/utils"; import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; export class TokenService implements TokenServiceAbstraction { + static decodeToken(token: string): Promise { + if (token == null) { + throw new Error("Token not provided."); + } + + const parts = token.split("."); + if (parts.length !== 3) { + throw new Error("JWT must have 3 parts"); + } + + const decoded = Utils.fromUrlB64ToUtf8(parts[1]); + if (decoded == null) { + throw new Error("Cannot decode the token"); + } + + const decodedToken = JSON.parse(decoded); + return decodedToken; + } + constructor(private stateService: StateService) {} async setTokens( @@ -87,18 +106,7 @@ export class TokenService implements TokenServiceAbstraction { throw new Error("Token not found."); } - const parts = token.split("."); - if (parts.length !== 3) { - throw new Error("JWT must have 3 parts"); - } - - const decoded = Utils.fromUrlB64ToUtf8(parts[1]); - if (decoded == null) { - throw new Error("Cannot decode the token"); - } - - const decodedToken = JSON.parse(decoded); - return decodedToken; + return TokenService.decodeToken(token); } async getTokenExpirationDate(): Promise {