diff --git a/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.tsx b/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.tsx index 68c2991ed8..2d2d638af9 100644 --- a/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.tsx +++ b/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.tsx @@ -21,10 +21,11 @@ import { logger } from "matrix-js-sdk/src/logger"; import { _t } from "../../../languageHandler"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; -import { OIDCState, WidgetPermissionStore } from "../../../stores/widgets/WidgetPermissionStore"; +import { OIDCState } from "../../../stores/widgets/WidgetPermissionStore"; import { IDialogProps } from "./IDialogProps"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; +import { SdkContextClass } from '../../../contexts/SDKContext'; interface IProps extends IDialogProps { widget: Widget; @@ -57,7 +58,7 @@ export default class WidgetOpenIDPermissionsDialog extends React.PureComponent(undefined); @@ -51,6 +52,7 @@ export class SdkContextClass { public client?: MatrixClient; // All protected fields to make it easier to derive test stores + protected _WidgetPermissionStore?: WidgetPermissionStore; protected _RightPanelStore?: RightPanelStore; protected _RoomNotificationStateStore?: RoomNotificationStateStore; protected _RoomViewStore?: RoomViewStore; @@ -102,6 +104,12 @@ export class SdkContextClass { } return this._WidgetLayoutStore; } + public get widgetPermissionStore(): WidgetPermissionStore { + if (!this._WidgetPermissionStore) { + this._WidgetPermissionStore = new WidgetPermissionStore(this); + } + return this._WidgetPermissionStore; + } public get widgetStore(): WidgetStore { if (!this._WidgetStore) { this._WidgetStore = WidgetStore.instance; diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index ba01a10926..ff2619ad59 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -47,7 +47,7 @@ import Modal from "../../Modal"; import WidgetOpenIDPermissionsDialog from "../../components/views/dialogs/WidgetOpenIDPermissionsDialog"; import WidgetCapabilitiesPromptDialog from "../../components/views/dialogs/WidgetCapabilitiesPromptDialog"; import { WidgetPermissionCustomisations } from "../../customisations/WidgetPermissions"; -import { OIDCState, WidgetPermissionStore } from "./WidgetPermissionStore"; +import { OIDCState } from "./WidgetPermissionStore"; import { WidgetType } from "../../widgets/WidgetType"; import { CHAT_EFFECTS } from "../../effects"; import { containsEmoji } from "../../effects/utils"; @@ -350,7 +350,7 @@ export class StopGapWidgetDriver extends WidgetDriver { } public async askOpenID(observer: SimpleObservable) { - const oidcState = WidgetPermissionStore.instance.getOIDCState( + const oidcState = SdkContextClass.instance.widgetPermissionStore.getOIDCState( this.forWidget, this.forWidgetKind, this.inRoomId, ); diff --git a/src/stores/widgets/WidgetPermissionStore.ts b/src/stores/widgets/WidgetPermissionStore.ts index 246492333c..fca018ca5c 100644 --- a/src/stores/widgets/WidgetPermissionStore.ts +++ b/src/stores/widgets/WidgetPermissionStore.ts @@ -17,8 +17,8 @@ import { Widget, WidgetKind } from "matrix-widget-api"; import SettingsStore from "../../settings/SettingsStore"; -import { MatrixClientPeg } from "../../MatrixClientPeg"; import { SettingLevel } from "../../settings/SettingLevel"; +import { SdkContextClass } from "../../contexts/SDKContext"; export enum OIDCState { Allowed, // user has set the remembered value as allowed @@ -27,16 +27,7 @@ export enum OIDCState { } export class WidgetPermissionStore { - private static internalInstance: WidgetPermissionStore; - - private constructor() { - } - - public static get instance(): WidgetPermissionStore { - if (!WidgetPermissionStore.internalInstance) { - WidgetPermissionStore.internalInstance = new WidgetPermissionStore(); - } - return WidgetPermissionStore.internalInstance; + public constructor(private readonly context: SdkContextClass) { } // TODO (all functions here): Merge widgetKind with the widget definition @@ -44,7 +35,7 @@ export class WidgetPermissionStore { private packSettingKey(widget: Widget, kind: WidgetKind, roomId?: string): string { let location = roomId; if (kind !== WidgetKind.Room) { - location = MatrixClientPeg.get().getUserId(); + location = this.context.client?.getUserId(); } if (kind === WidgetKind.Modal) { location = '*MODAL*-' + location; // to guarantee differentiation from whatever spawned it @@ -71,7 +62,10 @@ export class WidgetPermissionStore { public setOIDCState(widget: Widget, kind: WidgetKind, roomId: string, newState: OIDCState) { const settingsKey = this.packSettingKey(widget, kind, roomId); - const currentValues = SettingsStore.getValue("widgetOpenIDPermissions"); + let currentValues = SettingsStore.getValue("widgetOpenIDPermissions"); + if (!currentValues) { + currentValues = {}; + } if (!currentValues.allow) currentValues.allow = []; if (!currentValues.deny) currentValues.deny = []; diff --git a/test/TestSdkContext.ts b/test/TestSdkContext.ts index 137b71f9a3..4ce9100a94 100644 --- a/test/TestSdkContext.ts +++ b/test/TestSdkContext.ts @@ -22,6 +22,7 @@ import RightPanelStore from "../src/stores/right-panel/RightPanelStore"; import { RoomViewStore } from "../src/stores/RoomViewStore"; import { SpaceStoreClass } from "../src/stores/spaces/SpaceStore"; import { WidgetLayoutStore } from "../src/stores/widgets/WidgetLayoutStore"; +import { WidgetPermissionStore } from "../src/stores/widgets/WidgetPermissionStore"; import WidgetStore from "../src/stores/WidgetStore"; /** @@ -32,6 +33,7 @@ export class TestSdkContext extends SdkContextClass { public _RightPanelStore?: RightPanelStore; public _RoomNotificationStateStore?: RoomNotificationStateStore; public _RoomViewStore?: RoomViewStore; + public _WidgetPermissionStore?: WidgetPermissionStore; public _WidgetLayoutStore?: WidgetLayoutStore; public _WidgetStore?: WidgetStore; public _PosthogAnalytics?: PosthogAnalytics; diff --git a/test/stores/widgets/WidgetPermissionStore-test.ts b/test/stores/widgets/WidgetPermissionStore-test.ts new file mode 100644 index 0000000000..3ebb7fc9f5 --- /dev/null +++ b/test/stores/widgets/WidgetPermissionStore-test.ts @@ -0,0 +1,107 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { mocked } from "jest-mock"; +import { MatrixClient } from "matrix-js-sdk/src/matrix"; +import { Widget, WidgetKind } from "matrix-widget-api"; + +import { OIDCState, WidgetPermissionStore } from "../../../src/stores/widgets/WidgetPermissionStore"; +import SettingsStore from "../../../src/settings/SettingsStore"; +import { TestSdkContext } from "../../TestSdkContext"; +import { SettingLevel } from "../../../src/settings/SettingLevel"; +import { SdkContextClass } from "../../../src/contexts/SDKContext"; +import { stubClient } from "../../test-utils"; + +jest.mock("../../../src/settings/SettingsStore"); + +describe("WidgetPermissionStore", () => { + let widgetPermissionStore: WidgetPermissionStore; + let mockClient: MatrixClient; + const userId = "@alice:localhost"; + const roomId = "!room:localhost"; + const w = new Widget({ + id: "wid", + creatorUserId: userId, + type: "m.custom", + url: "https://invalid.address.here", + }); + let settings = {}; // key value store + + beforeEach(() => { + settings = {}; // clear settings + mocked(SettingsStore.getValue).mockImplementation((setting: string) => { + return settings[setting]; + }); + mocked(SettingsStore.setValue).mockImplementation((settingName: string, + roomId: string | null, + level: SettingLevel, + value: any, + ): Promise => { + // the store doesn't use any specific level or room ID (room IDs are packed into keys in `value`) + settings[settingName] = value; + return Promise.resolve(); + }); + mockClient = stubClient(); + const context = new TestSdkContext(); + context.client = mockClient; + widgetPermissionStore = new WidgetPermissionStore(context); + }); + + it("should persist OIDCState.Allowed for a widget", () => { + widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Allowed); + // check it remembered the value + expect( + widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null), + ).toEqual(OIDCState.Allowed); + }); + + it("should persist OIDCState.Denied for a widget", () => { + widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Denied); + // check it remembered the value + expect( + widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null), + ).toEqual(OIDCState.Denied); + }); + + it("should update OIDCState for a widget", () => { + widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Allowed); + widgetPermissionStore.setOIDCState(w, WidgetKind.Account, null, OIDCState.Denied); + // check it remembered the latest value + expect( + widgetPermissionStore.getOIDCState(w, WidgetKind.Account, null), + ).toEqual(OIDCState.Denied); + }); + + it("should scope the location for a widget when setting OIDC state", () => { + // allow this widget for this room + widgetPermissionStore.setOIDCState(w, WidgetKind.Room, roomId, OIDCState.Allowed); + // check it remembered the value + expect( + widgetPermissionStore.getOIDCState(w, WidgetKind.Room, roomId), + ).toEqual(OIDCState.Allowed); + // check this is not the case for the entire account + expect( + widgetPermissionStore.getOIDCState(w, WidgetKind.Account, roomId), + ).toEqual(OIDCState.Unknown); + }); + it("is created once in SdkContextClass", () => { + const context = new SdkContextClass(); + const store = context.widgetPermissionStore; + expect(store).toBeDefined(); + const store2 = context.widgetPermissionStore; + expect(store2).toStrictEqual(store); + }); +});