Widget permissions customizations using module api (#10121)

* Using module api to customize widget permissions

Signed-off-by: Mikhail Aheichyk <mikhail.aheichyk@nordeck.net>

* Revert type export and use ComponentProps instead.

Signed-off-by: Mikhail Aheichyk <mikhail.aheichyk@nordeck.net>

---------

Signed-off-by: Mikhail Aheichyk <mikhail.aheichyk@nordeck.net>
Co-authored-by: Mikhail Aheichyk <mikhail.aheichyk@nordeck.net>
This commit is contained in:
maheichyk 2023-02-23 17:53:44 +03:00 committed by GitHub
parent 9a0e537916
commit 7b77f76486
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 241 additions and 31 deletions

View file

@ -133,6 +133,7 @@ describe("Stickers", () => {
type: "m.stickerpicker", type: "m.stickerpicker",
name: STICKER_PICKER_WIDGET_NAME, name: STICKER_PICKER_WIDGET_NAME,
url: stickerPickerUrl, url: stickerPickerUrl,
creatorUserId: "@userId",
}, },
id: STICKER_PICKER_WIDGET_ID, id: STICKER_PICKER_WIDGET_ID,
}, },

View file

@ -58,7 +58,7 @@
"@babel/runtime": "^7.12.5", "@babel/runtime": "^7.12.5",
"@matrix-org/analytics-events": "^0.4.0", "@matrix-org/analytics-events": "^0.4.0",
"@matrix-org/matrix-wysiwyg": "^1.1.1", "@matrix-org/matrix-wysiwyg": "^1.1.1",
"@matrix-org/react-sdk-module-api": "^0.0.3", "@matrix-org/react-sdk-module-api": "^0.0.4",
"@sentry/browser": "^7.0.0", "@sentry/browser": "^7.0.0",
"@sentry/tracing": "^7.0.0", "@sentry/tracing": "^7.0.0",
"@testing-library/react-hooks": "^8.0.1", "@testing-library/react-hooks": "^8.0.1",

View file

@ -17,6 +17,7 @@ limitations under the License.
import React, { useContext } from "react"; import React, { useContext } from "react";
import { MatrixCapabilities } from "matrix-widget-api"; import { MatrixCapabilities } from "matrix-widget-api";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { ApprovalOpts, WidgetLifecycle } from "@matrix-org/react-sdk-module-api/lib/lifecycles/WidgetLifecycle";
import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from "./IconizedContextMenu"; import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from "./IconizedContextMenu";
import { ChevronFace } from "../../structures/ContextMenu"; import { ChevronFace } from "../../structures/ContextMenu";
@ -34,6 +35,8 @@ import { WidgetType } from "../../../widgets/WidgetType";
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore"; import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
import { getConfigLivestreamUrl, startJitsiAudioLivestream } from "../../../Livestream"; import { getConfigLivestreamUrl, startJitsiAudioLivestream } from "../../../Livestream";
import { ModuleRunner } from "../../../modules/ModuleRunner";
import { ElementWidget } from "../../../stores/widgets/StopGapWidget";
interface IProps extends React.ComponentProps<typeof IconizedContextMenu> { interface IProps extends React.ComponentProps<typeof IconizedContextMenu> {
app: IApp; app: IApp;
@ -45,7 +48,7 @@ interface IProps extends React.ComponentProps<typeof IconizedContextMenu> {
onEditClick?(): void; onEditClick?(): void;
} }
const WidgetContextMenu: React.FC<IProps> = ({ export const WidgetContextMenu: React.FC<IProps> = ({
onFinished, onFinished,
app, app,
userWidget, userWidget,
@ -158,24 +161,31 @@ const WidgetContextMenu: React.FC<IProps> = ({
const isLocalWidget = WidgetType.JITSI.matches(app.type); const isLocalWidget = WidgetType.JITSI.matches(app.type);
let revokeButton; let revokeButton;
if (!userWidget && !isLocalWidget && isAllowedWidget) { if (!userWidget && !isLocalWidget && isAllowedWidget) {
const onRevokeClick = (): void => { const opts: ApprovalOpts = { approved: undefined };
logger.info("Revoking permission for widget to load: " + app.eventId); ModuleRunner.instance.invoke(WidgetLifecycle.PreLoadRequest, opts, new ElementWidget(app));
const current = SettingsStore.getValue("allowedWidgets", roomId);
if (app.eventId !== undefined) current[app.eventId] = false;
const level = SettingsStore.firstSupportedLevel("allowedWidgets");
SettingsStore.setValue("allowedWidgets", roomId, level, current).catch((err) => {
logger.error(err);
// We don't really need to do anything about this - the user will just hit the button again.
});
onFinished();
};
revokeButton = <IconizedContextMenuOption onClick={onRevokeClick} label={_t("Revoke permissions")} />; if (!opts.approved) {
const onRevokeClick = (): void => {
logger.info("Revoking permission for widget to load: " + app.eventId);
const current = SettingsStore.getValue("allowedWidgets", roomId);
if (app.eventId !== undefined) current[app.eventId] = false;
const level = SettingsStore.firstSupportedLevel("allowedWidgets");
if (!level) throw new Error("level must be defined");
SettingsStore.setValue("allowedWidgets", roomId ?? null, level, current).catch((err) => {
logger.error(err);
// We don't really need to do anything about this - the user will just hit the button again.
});
onFinished();
};
revokeButton = <IconizedContextMenuOption onClick={onRevokeClick} label={_t("Revoke permissions")} />;
}
} }
let moveLeftButton; let moveLeftButton;
if (showUnpin && widgetIndex > 0) { if (showUnpin && widgetIndex > 0) {
const onClick = (): void => { const onClick = (): void => {
if (!room) throw new Error("room must be defined");
WidgetLayoutStore.instance.moveWithinContainer(room, Container.Top, app, -1); WidgetLayoutStore.instance.moveWithinContainer(room, Container.Top, app, -1);
onFinished(); onFinished();
}; };
@ -207,5 +217,3 @@ const WidgetContextMenu: React.FC<IProps> = ({
</IconizedContextMenu> </IconizedContextMenu>
); );
}; };
export default WidgetContextMenu;

View file

@ -23,6 +23,7 @@ import classNames from "classnames";
import { MatrixCapabilities } from "matrix-widget-api"; import { MatrixCapabilities } from "matrix-widget-api";
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room"; import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { ApprovalOpts, WidgetLifecycle } from "@matrix-org/react-sdk-module-api/lib/lifecycles/WidgetLifecycle";
import AccessibleButton from "./AccessibleButton"; import AccessibleButton from "./AccessibleButton";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
@ -36,7 +37,7 @@ import { aboveLeftOf, ContextMenuButton } from "../../structures/ContextMenu";
import PersistedElement, { getPersistKey } from "./PersistedElement"; import PersistedElement, { getPersistKey } from "./PersistedElement";
import { WidgetType } from "../../../widgets/WidgetType"; import { WidgetType } from "../../../widgets/WidgetType";
import { ElementWidget, StopGapWidget } from "../../../stores/widgets/StopGapWidget"; import { ElementWidget, StopGapWidget } from "../../../stores/widgets/StopGapWidget";
import WidgetContextMenu from "../context_menus/WidgetContextMenu"; import { WidgetContextMenu } from "../context_menus/WidgetContextMenu";
import WidgetAvatar from "../avatars/WidgetAvatar"; import WidgetAvatar from "../avatars/WidgetAvatar";
import LegacyCallHandler from "../../../LegacyCallHandler"; import LegacyCallHandler from "../../../LegacyCallHandler";
import { IApp } from "../../../stores/WidgetStore"; import { IApp } from "../../../stores/WidgetStore";
@ -50,6 +51,7 @@ import { Action } from "../../../dispatcher/actions";
import { ElementWidgetCapabilities } from "../../../stores/widgets/ElementWidgetCapabilities"; import { ElementWidgetCapabilities } from "../../../stores/widgets/ElementWidgetCapabilities";
import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore"; import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore";
import { SdkContextClass } from "../../../contexts/SDKContext"; import { SdkContextClass } from "../../../contexts/SDKContext";
import { ModuleRunner } from "../../../modules/ModuleRunner";
interface IProps { interface IProps {
app: IApp; app: IApp;
@ -162,6 +164,9 @@ export default class AppTile extends React.Component<IProps, IState> {
private hasPermissionToLoad = (props: IProps): boolean => { private hasPermissionToLoad = (props: IProps): boolean => {
if (this.usingLocalWidget()) return true; if (this.usingLocalWidget()) return true;
if (!props.room) return true; // user widgets always have permissions if (!props.room) return true; // user widgets always have permissions
const opts: ApprovalOpts = { approved: undefined };
ModuleRunner.instance.invoke(WidgetLifecycle.PreLoadRequest, opts, new ElementWidget(this.props.app));
if (opts.approved) return true;
const currentlyAllowedWidgets = SettingsStore.getValue("allowedWidgets", props.room.roomId); const currentlyAllowedWidgets = SettingsStore.getValue("allowedWidgets", props.room.roomId);
const allowed = props.app.eventId !== undefined && (currentlyAllowedWidgets[props.app.eventId] ?? false); const allowed = props.app.eventId !== undefined && (currentlyAllowedWidgets[props.app.eventId] ?? false);

View file

@ -40,7 +40,7 @@ import { E2EStatus } from "../../../utils/ShieldUtils";
import RoomContext from "../../../contexts/RoomContext"; import RoomContext from "../../../contexts/RoomContext";
import { UIComponent, UIFeature } from "../../../settings/UIFeature"; import { UIComponent, UIFeature } from "../../../settings/UIFeature";
import { ChevronFace, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu"; import { ChevronFace, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu";
import WidgetContextMenu from "../context_menus/WidgetContextMenu"; import { WidgetContextMenu } from "../context_menus/WidgetContextMenu";
import { useRoomMemberCount } from "../../../hooks/useRoomMembers"; import { useRoomMemberCount } from "../../../hooks/useRoomMembers";
import { useFeatureEnabled } from "../../../hooks/useSettings"; import { useFeatureEnabled } from "../../../hooks/useSettings";
import { usePinnedEvents } from "./PinnedMessagesCard"; import { usePinnedEvents } from "./PinnedMessagesCard";

View file

@ -24,7 +24,7 @@ import AppTile from "../elements/AppTile";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import { useWidgets } from "./RoomSummaryCard"; import { useWidgets } from "./RoomSummaryCard";
import { ChevronFace, ContextMenuButton, useContextMenu } from "../../structures/ContextMenu"; import { ChevronFace, ContextMenuButton, useContextMenu } from "../../structures/ContextMenu";
import WidgetContextMenu from "../context_menus/WidgetContextMenu"; import { WidgetContextMenu } from "../context_menus/WidgetContextMenu";
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore"; import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
import UIStore from "../../../stores/UIStore"; import UIStore from "../../../stores/UIStore";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore"; import RightPanelStore from "../../../stores/right-panel/RightPanelStore";

View file

@ -39,6 +39,11 @@ import { Room } from "matrix-js-sdk/src/models/room";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread"; import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
import { Direction } from "matrix-js-sdk/src/matrix"; import { Direction } from "matrix-js-sdk/src/matrix";
import {
ApprovalOpts,
CapabilitiesOpts,
WidgetLifecycle,
} from "@matrix-org/react-sdk-module-api/lib/lifecycles/WidgetLifecycle";
import SdkConfig, { DEFAULTS } from "../../SdkConfig"; import SdkConfig, { DEFAULTS } from "../../SdkConfig";
import { iterableDiff, iterableIntersection } from "../../utils/iterables"; import { iterableDiff, iterableIntersection } from "../../utils/iterables";
@ -55,6 +60,7 @@ import dis from "../../dispatcher/dispatcher";
import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities"; import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
import { navigateToPermalink } from "../../utils/permalinks/navigator"; import { navigateToPermalink } from "../../utils/permalinks/navigator";
import { SdkContextClass } from "../../contexts/SDKContext"; import { SdkContextClass } from "../../contexts/SDKContext";
import { ModuleRunner } from "../../modules/ModuleRunner";
// TODO: Purge this from the universe // TODO: Purge this from the universe
@ -171,15 +177,22 @@ export class StopGapWidgetDriver extends WidgetDriver {
allowedSoFar.add(cap); allowedSoFar.add(cap);
missing.delete(cap); missing.delete(cap);
}); });
let approved: Set<string> | undefined;
if (WidgetPermissionCustomisations.preapproveCapabilities) { if (WidgetPermissionCustomisations.preapproveCapabilities) {
const approved = await WidgetPermissionCustomisations.preapproveCapabilities(this.forWidget, requested); approved = await WidgetPermissionCustomisations.preapproveCapabilities(this.forWidget, requested);
if (approved) { } else {
approved.forEach((cap) => { const opts: CapabilitiesOpts = { approvedCapabilities: undefined };
allowedSoFar.add(cap); ModuleRunner.instance.invoke(WidgetLifecycle.CapabilitiesRequest, opts, this.forWidget, requested);
missing.delete(cap); approved = opts.approvedCapabilities;
});
}
} }
if (approved) {
approved.forEach((cap) => {
allowedSoFar.add(cap);
missing.delete(cap);
});
}
// TODO: Do something when the widget requests new capabilities not yet asked for // TODO: Do something when the widget requests new capabilities not yet asked for
let rememberApproved = false; let rememberApproved = false;
if (missing.size > 0) { if (missing.size > 0) {
@ -366,6 +379,15 @@ export class StopGapWidgetDriver extends WidgetDriver {
} }
public async askOpenID(observer: SimpleObservable<IOpenIDUpdate>): Promise<void> { public async askOpenID(observer: SimpleObservable<IOpenIDUpdate>): Promise<void> {
const opts: ApprovalOpts = { approved: undefined };
ModuleRunner.instance.invoke(WidgetLifecycle.IdentityRequest, opts, this.forWidget);
if (opts.approved) {
return observer.update({
state: OpenIDRequestState.Allowed,
token: await MatrixClientPeg.get().getOpenIdToken(),
});
}
const oidcState = SdkContextClass.instance.widgetPermissionStore.getOIDCState( const oidcState = SdkContextClass.instance.widgetPermissionStore.getOIDCState(
this.forWidget, this.forWidget,
this.forWidgetKind, this.forWidgetKind,

View file

@ -0,0 +1,98 @@
/*
Copyright 2023 Mikhail Aheichyk
Copyright 2023 Nordeck IT + Consulting GmbH.
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 React, { ComponentProps } from "react";
import { screen, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { MatrixWidgetType } from "matrix-widget-api";
import {
ApprovalOpts,
WidgetInfo,
WidgetLifecycle,
} from "@matrix-org/react-sdk-module-api/lib/lifecycles/WidgetLifecycle";
import { WidgetContextMenu } from "../../../../src/components/views/context_menus/WidgetContextMenu";
import { IApp } from "../../../../src/stores/WidgetStore";
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
import WidgetUtils from "../../../../src/utils/WidgetUtils";
import { ModuleRunner } from "../../../../src/modules/ModuleRunner";
import SettingsStore from "../../../../src/settings/SettingsStore";
describe("<WidgetContextMenu />", () => {
const widgetId = "w1";
const eventId = "e1";
const roomId = "r1";
const userId = "@user-id:server";
const app: IApp = {
id: widgetId,
eventId,
roomId,
type: MatrixWidgetType.Custom,
url: "https://example.com",
name: "Example 1",
creatorUserId: userId,
avatar_url: undefined,
};
const mockClient = {
getUserId: jest.fn().mockReturnValue(userId),
} as unknown as MatrixClient;
let onFinished: () => void;
beforeEach(() => {
onFinished = jest.fn();
jest.spyOn(WidgetUtils, "canUserModifyWidgets").mockReturnValue(true);
});
afterEach(() => {
jest.restoreAllMocks();
});
function getComponent(props: Partial<ComponentProps<typeof WidgetContextMenu>> = {}): JSX.Element {
return (
<MatrixClientContext.Provider value={mockClient}>
<WidgetContextMenu app={app} onFinished={onFinished} {...props} />
</MatrixClientContext.Provider>
);
}
it("renders revoke button", async () => {
const { rerender } = render(getComponent());
const revokeButton = screen.getByLabelText("Revoke permissions");
expect(revokeButton).toBeInTheDocument();
jest.spyOn(ModuleRunner.instance, "invoke").mockImplementation((lifecycleEvent, opts, widgetInfo) => {
if (lifecycleEvent === WidgetLifecycle.PreLoadRequest && (widgetInfo as WidgetInfo).id === widgetId) {
(opts as ApprovalOpts).approved = true;
}
});
rerender(getComponent());
expect(revokeButton).not.toBeInTheDocument();
});
it("revokes permissions", async () => {
render(getComponent());
await userEvent.click(screen.getByLabelText("Revoke permissions"));
expect(onFinished).toHaveBeenCalled();
expect(SettingsStore.getValue("allowedWidgets", roomId)[eventId]).toBe(false);
});
});

View file

@ -23,6 +23,11 @@ import { act, render, RenderResult } from "@testing-library/react";
import userEvent from "@testing-library/user-event"; import userEvent from "@testing-library/user-event";
import { MatrixClient } from "matrix-js-sdk/src/matrix"; import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { SpiedFunction } from "jest-mock"; import { SpiedFunction } from "jest-mock";
import {
ApprovalOpts,
WidgetInfo,
WidgetLifecycle,
} from "@matrix-org/react-sdk-module-api/lib/lifecycles/WidgetLifecycle";
import RightPanel from "../../../../src/components/structures/RightPanel"; import RightPanel from "../../../../src/components/structures/RightPanel";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg"; import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
@ -44,6 +49,7 @@ import AppsDrawer from "../../../../src/components/views/rooms/AppsDrawer";
import { ElementWidgetCapabilities } from "../../../../src/stores/widgets/ElementWidgetCapabilities"; import { ElementWidgetCapabilities } from "../../../../src/stores/widgets/ElementWidgetCapabilities";
import { ElementWidget } from "../../../../src/stores/widgets/StopGapWidget"; import { ElementWidget } from "../../../../src/stores/widgets/StopGapWidget";
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore"; import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
import { ModuleRunner } from "../../../../src/modules/ModuleRunner";
describe("AppTile", () => { describe("AppTile", () => {
let cli: MatrixClient; let cli: MatrixClient;
@ -380,4 +386,21 @@ describe("AppTile", () => {
}); });
}); });
}); });
it("for a pinned widget permission load", () => {
jest.spyOn(ModuleRunner.instance, "invoke").mockImplementation((lifecycleEvent, opts, widgetInfo) => {
if (lifecycleEvent === WidgetLifecycle.PreLoadRequest && (widgetInfo as WidgetInfo).id === app1.id) {
(opts as ApprovalOpts).approved = true;
}
});
// userId and creatorUserId are different
const renderResult = render(
<MatrixClientContext.Provider value={cli}>
<AppTile key={app1.id} app={app1} room={r1} userId="@user1" creatorUserId="@userAnother" />
</MatrixClientContext.Provider>,
);
expect(renderResult.queryByRole("button", { name: "Continue" })).not.toBeInTheDocument();
});
}); });

View file

@ -18,12 +18,27 @@ import { mocked, MockedObject } from "jest-mock";
import { MatrixClient, ClientEvent, ITurnServer as IClientTurnServer } from "matrix-js-sdk/src/client"; import { MatrixClient, ClientEvent, ITurnServer as IClientTurnServer } from "matrix-js-sdk/src/client";
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo"; import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
import { Direction, EventType, MatrixEvent, MsgType, RelationType } from "matrix-js-sdk/src/matrix"; import { Direction, EventType, MatrixEvent, MsgType, RelationType } from "matrix-js-sdk/src/matrix";
import { Widget, MatrixWidgetType, WidgetKind, WidgetDriver, ITurnServer } from "matrix-widget-api"; import {
Widget,
MatrixWidgetType,
WidgetKind,
WidgetDriver,
ITurnServer,
SimpleObservable,
OpenIDRequestState,
IOpenIDUpdate,
} from "matrix-widget-api";
import {
ApprovalOpts,
CapabilitiesOpts,
WidgetLifecycle,
} from "@matrix-org/react-sdk-module-api/lib/lifecycles/WidgetLifecycle";
import { SdkContextClass } from "../../../src/contexts/SDKContext"; import { SdkContextClass } from "../../../src/contexts/SDKContext";
import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; import { MatrixClientPeg } from "../../../src/MatrixClientPeg";
import { StopGapWidgetDriver } from "../../../src/stores/widgets/StopGapWidgetDriver"; import { StopGapWidgetDriver } from "../../../src/stores/widgets/StopGapWidgetDriver";
import { stubClient } from "../../test-utils"; import { stubClient } from "../../test-utils";
import { ModuleRunner } from "../../../src/modules/ModuleRunner";
import dis from "../../../src/dispatcher/dispatcher"; import dis from "../../../src/dispatcher/dispatcher";
describe("StopGapWidgetDriver", () => { describe("StopGapWidgetDriver", () => {
@ -101,6 +116,44 @@ describe("StopGapWidgetDriver", () => {
expect(approvedCapabilities).toEqual(requestedCapabilities); expect(approvedCapabilities).toEqual(requestedCapabilities);
}); });
it("approves capabilities via module api", async () => {
const driver = mkDefaultDriver();
const requestedCapabilities = new Set(["org.matrix.msc2931.navigate", "org.matrix.msc2762.timeline:*"]);
jest.spyOn(ModuleRunner.instance, "invoke").mockImplementation(
(lifecycleEvent, opts, widgetInfo, requested) => {
if (lifecycleEvent === WidgetLifecycle.CapabilitiesRequest) {
(opts as CapabilitiesOpts).approvedCapabilities = requested;
}
},
);
const approvedCapabilities = await driver.validateCapabilities(requestedCapabilities);
expect(approvedCapabilities).toEqual(requestedCapabilities);
});
it("approves identity via module api", async () => {
const driver = mkDefaultDriver();
jest.spyOn(ModuleRunner.instance, "invoke").mockImplementation((lifecycleEvent, opts, widgetInfo) => {
if (lifecycleEvent === WidgetLifecycle.IdentityRequest) {
(opts as ApprovalOpts).approved = true;
}
});
const listener = jest.fn();
const observer = new SimpleObservable<IOpenIDUpdate>();
observer.onUpdate(listener);
await driver.askOpenID(observer);
const openIdUpdate: IOpenIDUpdate = {
state: OpenIDRequestState.Allowed,
token: await client.getOpenIdToken(),
};
expect(listener).toBeCalledWith(openIdUpdate);
});
describe("sendToDevice", () => { describe("sendToDevice", () => {
const contentMap = { const contentMap = {
"@alice:example.org": { "@alice:example.org": {

View file

@ -1598,10 +1598,10 @@
version "3.2.14" version "3.2.14"
resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz#acd96c00a881d0f462e1f97a56c73742c8dbc984" resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz#acd96c00a881d0f462e1f97a56c73742c8dbc984"
"@matrix-org/react-sdk-module-api@^0.0.3": "@matrix-org/react-sdk-module-api@^0.0.4":
version "0.0.3" version "0.0.4"
resolved "https://registry.yarnpkg.com/@matrix-org/react-sdk-module-api/-/react-sdk-module-api-0.0.3.tgz#a7ac1b18a72d18d08290b81fa33b0d8d00a77d2b" resolved "https://registry.yarnpkg.com/@matrix-org/react-sdk-module-api/-/react-sdk-module-api-0.0.4.tgz#da71fc2e4c8143e87b5c2bc067ccbc0c146816fe"
integrity sha512-jQmLhVIanuX0g7Jx1OIqlzs0kp72PfSpv3umi55qVPYcAPQmO252AUs0vncatK8O4e013vohdnNhly19a/kmLQ== integrity sha512-4gcgef3Ne9+Ae0bAErK1Swo9FxTZBDEogX/Iu2kcLWWROOKMjmeWL2PkM83ylsxZ32YY6a6ndRqV/SwRmDeJxg==
dependencies: dependencies:
"@babel/runtime" "^7.17.9" "@babel/runtime" "^7.17.9"