Skip to content

Commit

Permalink
[web] Introduce web migration to strip member permissions
Browse files Browse the repository at this point in the history
Summary:
Effectively the same as D12789, but for `web`.

---

Depends on D12789

Test Plan:
Before (thin, with member permissions):
{F2926333}

Before (thick, with member permissions):
{F2926335}

---

After (thin, WITHOUT member permissions):
{F2926336}

After (thick, still WITH member permissions):
{F2926371}

Reviewers: ashoat, tomek

Reviewed By: ashoat

Differential Revision: https://phab.comm.dev/D12790
  • Loading branch information
atulsmadhugiri committed Oct 8, 2024
1 parent 773baff commit 1aad44e
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 6 deletions.
12 changes: 9 additions & 3 deletions lib/utils/member-info-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
MemberInfoSansPermissions,
MemberInfoWithPermissions,
RawThreadInfo,
ThickRawThreadInfo,
ThinRawThreadInfo,
} from '../types/minimally-encoded-thread-permissions-types.js';
import type { RawThreadInfos } from '../types/thread-types.js';
Expand All @@ -30,7 +31,7 @@ function stripMemberPermissionsFromRawThreadInfo(
}

export type ThreadStoreWithMemberPermissions = {
+[id: string]: ThinRawThreadInfoWithPermissions,
+[id: string]: ThinRawThreadInfoWithPermissions | ThickRawThreadInfo,
};
// NOTE: Don't modify this function without understanding
// how it may affect existing client migrations.
Expand All @@ -40,8 +41,13 @@ function stripMemberPermissionsFromRawThreadInfos(
const strippedThreadStoreInfos: { [id: string]: RawThreadInfo } = {};

for (const threadID in threadStoreInfos) {
const rawThreadInfo: ThinRawThreadInfoWithPermissions =
threadStoreInfos[threadID];
const rawThreadInfo = threadStoreInfos[threadID];

if (rawThreadInfo.thick) {
strippedThreadStoreInfos[threadID] = rawThreadInfo;
continue;
}

const updatedRawThreadInfo: ThinRawThreadInfo =
stripMemberPermissionsFromRawThreadInfo(rawThreadInfo);
strippedThreadStoreInfos[threadID] = updatedRawThreadInfo;
Expand Down
4 changes: 4 additions & 0 deletions native/redux/persist.js
Original file line number Diff line number Diff line change
Expand Up @@ -1492,6 +1492,10 @@ const migrations: MigrationsManifest<NavInfo, AppState> = Object.freeze({
},
ops: {},
}): MigrationFunction<NavInfo, AppState>),
[84]: (async (state: AppState) => ({
state,
ops: {},
}): MigrationFunction<NavInfo, AppState>),
});

// NOTE: renaming this object, and especially the `version` property
Expand Down
80 changes: 77 additions & 3 deletions web/redux/persist.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow

import invariant from 'invariant';
import _keyBy from 'lodash/fp/keyBy.js';
import { getStoredState, purgeStoredState } from 'redux-persist';
import storage from 'redux-persist/es/storage/index.js';
import type { PersistConfig } from 'redux-persist/src/types.js';
Expand All @@ -16,7 +17,10 @@ import {
type ClientDBMessageStoreOperation,
type MessageStoreOperation,
} from 'lib/ops/message-store-ops.js';
import type { ClientDBThreadStoreOperation } from 'lib/ops/thread-store-ops.js';
import type {
ClientDBThreadStoreOperation,
ThreadStoreOperation,
} from 'lib/ops/thread-store-ops.js';
import { patchRawThreadInfoWithSpecialRole } from 'lib/permissions/special-roles.js';
import { createUpdateDBOpsForThreadStoreThreadInfos } from 'lib/shared/redux/client-db-utils.js';
import { deprecatedUpdateRolesAndPermissions } from 'lib/shared/redux/deprecated-update-roles-and-permissions.js';
Expand All @@ -30,11 +34,16 @@ import type { ClientDBMessageInfo } from 'lib/types/message-types.js';
import type { WebNavInfo } from 'lib/types/nav-types.js';
import { cookieTypes } from 'lib/types/session-types.js';
import { defaultConnectionInfo } from 'lib/types/socket-types.js';
import type { StoreOperations } from 'lib/types/store-ops-types.js';
import { defaultGlobalThemeInfo } from 'lib/types/theme-types.js';
import type { ClientDBThreadInfo } from 'lib/types/thread-types.js';
import type {
ClientDBThreadInfo,
RawThreadInfos,
} from 'lib/types/thread-types.js';
import { getConfig } from 'lib/utils/config.js';
import { parseCookies } from 'lib/utils/cookie-utils.js';
import { isDev } from 'lib/utils/dev-utils.js';
import { stripMemberPermissionsFromRawThreadInfos } from 'lib/utils/member-info-utils.js';
import {
generateIDSchemaMigrationOpsForDrafts,
convertDraftStoreToNewIDSchema,
Expand All @@ -43,7 +52,7 @@ import {
type MigrationFunction,
type MigrationsManifest,
} from 'lib/utils/migration-utils.js';
import { entries } from 'lib/utils/objects.js';
import { entries, values } from 'lib/utils/objects.js';
import {
convertClientDBThreadInfoToRawThreadInfo,
convertRawThreadInfoToClientDBThreadInfo,
Expand Down Expand Up @@ -644,6 +653,71 @@ const migrations: MigrationsManifest<WebNavInfo, AppState> = {
},
ops: {},
}): MigrationFunction<WebNavInfo, AppState>),
[84]: (async (state: AppState) => {
const sharedWorker = await getCommSharedWorker();
const isDatabaseSupported = await sharedWorker.isSupported();

if (!isDatabaseSupported) {
return {
state,
ops: {},
};
}

const stores = await sharedWorker.schedule({
type: workerRequestMessageTypes.GET_CLIENT_STORE,
});

const clientDBThreadInfos: ?$ReadOnlyArray<ClientDBThreadInfo> =
stores?.store?.threads;

if (
clientDBThreadInfos === null ||
clientDBThreadInfos === undefined ||
clientDBThreadInfos.length === 0
) {
return {
state,
ops: {},
};
}

// 1. Translate `ClientDBThreadInfo`s to `RawThreadInfo`s.
const rawThreadInfos = clientDBThreadInfos.map(
convertClientDBThreadInfoToRawThreadInfo,
);

// 2. Convert `RawThreadInfo`s to a map of `threadID` => `threadInfo`.
const keyedRawThreadInfos = _keyBy('id')(rawThreadInfos);

// This isn't actually accurate, but we force this cast here because the
// types for createUpdateDBOpsForThreadStoreThreadInfos assume they're
// converting from a client DB that contains RawThreadInfos. In fact, at
// this point the client DB contains ThinRawThreadInfoWithPermissions.
const stripMemberPermissions: RawThreadInfos => RawThreadInfos =
(stripMemberPermissionsFromRawThreadInfos: any);

// 3. Apply `stripMemberPermissions` to `ThreadInfo`s.
const updatedKeyedRawThreadInfos =
stripMemberPermissions(keyedRawThreadInfos);

// 4. Convert the updated `RawThreadInfos` back into an array.
const updatedKeyedRawThreadInfosArray = values(updatedKeyedRawThreadInfos);

// 5. Construct `ThreadStoreOperation`s.
const threadOperations: ThreadStoreOperation[] = [{ type: 'remove_all' }];
for (const rawThreadInfo of updatedKeyedRawThreadInfosArray) {
threadOperations.push({
type: 'replace',
payload: { id: rawThreadInfo.id, threadInfo: rawThreadInfo },
});
}

const operations: StoreOperations = {
threadStoreOperations: threadOperations,
};
return { state, ops: operations };
}: MigrationFunction<WebNavInfo, AppState>),
};

const persistConfig: PersistConfig = {
Expand Down

0 comments on commit 1aad44e

Please sign in to comment.