From e15041bd53538a325b4d18a21c0e98b24f77938b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 2 Nov 2020 15:17:05 -0700 Subject: [PATCH] Add a custom widget API action for viewing a different room --- .../views/dialogs/ModalWidgetDialog.tsx | 2 +- src/stores/widgets/ElementWidgetActions.ts | 9 +++++ .../widgets/ElementWidgetCapabilities.ts | 19 ++++++++++ src/stores/widgets/StopGapWidget.ts | 37 +++++++++++++++++-- src/stores/widgets/StopGapWidgetDriver.ts | 29 +++++++++++++-- 5 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 src/stores/widgets/ElementWidgetCapabilities.ts diff --git a/src/components/views/dialogs/ModalWidgetDialog.tsx b/src/components/views/dialogs/ModalWidgetDialog.tsx index 6ce3230a7a..16cf89c340 100644 --- a/src/components/views/dialogs/ModalWidgetDialog.tsx +++ b/src/components/views/dialogs/ModalWidgetDialog.tsx @@ -61,7 +61,7 @@ export default class ModalWidgetDialog extends React.PureComponent this.emit("preparing")); this.messaging.on("ready", () => this.emit("ready")); @@ -298,6 +300,35 @@ export class StopGapWidget extends EventEmitter { ActiveWidgetStore.setRoomId(this.mockWidget.id, this.appTileProps.room.roomId); } + // Always attach a handler for ViewRoom, but permission check it internally + this.messaging.on(`action:${ElementWidgetActions.ViewRoom}`, (ev: CustomEvent) => { + ev.preventDefault(); // stop the widget API from auto-rejecting this + + // Check up front if this is even a valid request + const targetRoomId = (ev.detail.data || {}).room_id; + if (!targetRoomId) { + return this.messaging.transport.reply(ev.detail, { + error: {message: "Invalid room ID."}, + }); + } + + // Check the widget's permission + if (!this.messaging.hasCapability(ElementWidgetCapabilities.CanChangeViewedRoom)) { + return this.messaging.transport.reply(ev.detail, { + error: {message: "This widget does not have permission for this action (denied)."}, + }); + } + + // at this point we can change rooms, so do that + defaultDispatcher.dispatch({ + action: 'view_room', + room_id: targetRoomId, + }); + + // acknowledge so the widget doesn't freak out + this.messaging.transport.reply(ev.detail, {}); + }); + if (WidgetType.JITSI.matches(this.mockWidget.type)) { this.messaging.on("action:set_always_on_screen", (ev: CustomEvent) => { diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index b54e4a5f7d..9b455ac481 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -14,17 +14,40 @@ * limitations under the License. */ -import { Capability, WidgetDriver } from "matrix-widget-api"; +import { Capability, WidgetDriver, WidgetType } from "matrix-widget-api"; import { iterableUnion } from "../../utils/iterables"; +import { MatrixClientPeg } from "../../MatrixClientPeg"; +import { arrayFastClone } from "../../utils/arrays"; +import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities"; // TODO: Purge this from the universe export class StopGapWidgetDriver extends WidgetDriver { - constructor(private allowedCapabilities: Capability[]) { + constructor(private allowedCapabilities: Capability[], private forType: WidgetType) { super(); } public async validateCapabilities(requested: Set): Promise> { - return new Set(iterableUnion(requested, this.allowedCapabilities)); + // TODO: All of this should be a capabilities prompt. + // See https://github.com/vector-im/element-web/issues/13111 + + // Note: None of this well-known widget permissions stuff is documented intentionally. We + // do not want to encourage people relying on this, but need to be able to support it at + // the moment. + // + // If you're a widget developer and seeing this message, please ask the Element team if + // it is safe for you to use this permissions system before trying to use it - it might + // not be here in the future. + + const wkPerms = (MatrixClientPeg.get().getClientWellKnown() || {})['io.element.widget_permissions']; + const allowedCaps = arrayFastClone(this.allowedCapabilities); + if (wkPerms) { + if (Array.isArray(wkPerms["view_room_action"])) { + if (wkPerms["view_room_action"].includes(this.forType)) { + allowedCaps.push(ElementWidgetCapabilities.CanChangeViewedRoom); + } + } + } + return new Set(iterableUnion(requested, allowedCaps)); } }