From e8ac35b4aacf4265d210956150eb9f799faea99d Mon Sep 17 00:00:00 2001 From: Mikhail Aheichyk Date: Wed, 25 Jan 2023 17:19:57 +0300 Subject: [PATCH 1/2] RuntimeModule is extended with WidgetPermissions to allow widget permissions preapproval from the module. Signed-off-by: Mikhail Aheichyk mikhail.aheichyk@nordeck.net --- package.json | 3 ++- src/RuntimeModule.ts | 62 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8c411e2..4c3ab2e 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "typescript": "^4.6.3" }, "dependencies": { - "@babel/runtime": "^7.17.9" + "@babel/runtime": "^7.17.9", + "matrix-widget-api": "^1.1.1" } } diff --git a/src/RuntimeModule.ts b/src/RuntimeModule.ts index 704561f..cdf7ea6 100644 --- a/src/RuntimeModule.ts +++ b/src/RuntimeModule.ts @@ -15,6 +15,7 @@ limitations under the License. */ import { EventEmitter } from "events"; +import { Capability, Widget } from "matrix-widget-api"; import { ModuleApi } from "./ModuleApi"; import { PlainSubstitution } from "./types/translations"; @@ -41,4 +42,65 @@ export abstract class RuntimeModule extends EventEmitter { protected t(s: string, variables?: Record): string { return this.moduleApi.translateString(s, variables); } + + /** + * Gets widget permissions behaviour to preapprove permissions. + * @returns instance of IWidgetPermissions with custom behaviour or undefined for default + */ + public getWidgetPermissions(): WidgetPermissions | undefined { + return undefined; + } +} + +/** + * Represents widget permissions behaviour to preapprove permissions that can be customized by module. + */ +export interface WidgetPermissions { + /** + * Approves the widget embedding. + * This will be used to embed certain widgets without prompting the user. + * @param {Widget} widget The widget to approve embedding for. + * @returns {boolean} true if embedding is preapproved, false otherwise + */ + isEmbeddingPreapproved(widget: Widget): boolean; + + /** + * Approves the widget for identity token. + * This will be used to give certain widgets an identity token without having to + * prompt the user to approve it. + * @param {Widget} widget The widget to approve identity request for. + * @returns {boolean} Resolves to true if identity request is preapproved, false otherwise + */ + isIdentityRequestPreapproved(widget: Widget): Promise; + + /** + * Approves the widget for capabilities that it requested, if any can be + * approved. Typically this will be used to give certain widgets capabilities + * without having to prompt the user to approve them. This cannot reject + * capabilities that Element will be automatically granting, such as the + * ability for Jitsi widgets to stay on screen - those will be approved + * regardless. + * @param {Widget} widget The widget to approve capabilities for. + * @param {Set} requestedCapabilities The capabilities the widget requested. + * @returns {Set} Resolves to the capabilities that are approved for use + * by the widget. If none are approved, this should return an empty Set. + */ + preapproveCapabilities(widget: Widget, requestedCapabilities: Set): Promise>; +} + +/** + * Defines a default widget permissions behaviour. Can be extended to customize. + */ +export class DefaultWidgetPermissions implements WidgetPermissions { + isEmbeddingPreapproved(widget: Widget): boolean { + return false; + } + + async isIdentityRequestPreapproved(widget: Widget): Promise { + return false; + } + + async preapproveCapabilities(widget: Widget, requestedCapabilities: Set): Promise> { + return new Set(); + } } From c2522bbaaa5a47d168391f96cd613c4eeffe7c5f Mon Sep 17 00:00:00 2001 From: Mikhail Aheichyk Date: Wed, 1 Feb 2023 10:37:33 +0300 Subject: [PATCH 2/2] Test for WidgetPermissions Signed-off-by: Mikhail Aheichyk --- test/RuntimeModule.test.ts | 62 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 test/RuntimeModule.test.ts diff --git a/test/RuntimeModule.test.ts b/test/RuntimeModule.test.ts new file mode 100644 index 0000000..e98e0bf --- /dev/null +++ b/test/RuntimeModule.test.ts @@ -0,0 +1,62 @@ +/* +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 {Capability, Widget, MatrixWidgetType} from "matrix-widget-api"; + +import {DefaultWidgetPermissions} from "../src/RuntimeModule"; + +describe("RuntimeModule", () => { + describe("WidgetPermissions", () => { + const mockWidget = new Widget({ + id: "widget-id", + creatorUserId: "@user-id", + type: MatrixWidgetType.Custom, + url: "https://example.com", + }); + + const defaultWidgetPermissions = new DefaultWidgetPermissions(); + + const customWidgetPermissions = new class extends DefaultWidgetPermissions { + isEmbeddingPreapproved(widget: Widget): boolean { + return true; + } + + isIdentityRequestPreapproved(widget: Widget): Promise { + return Promise.resolve(true); + } + + preapproveCapabilities(widget: Widget, requestedCapabilities: Set): Promise> { + return Promise.resolve(requestedCapabilities); + } + }(); + + it("default permissions", async () => { + expect(defaultWidgetPermissions.isEmbeddingPreapproved(mockWidget)).toBe(false); + await expect(defaultWidgetPermissions.isIdentityRequestPreapproved(mockWidget)).resolves.toBe(false); + await expect( + defaultWidgetPermissions.preapproveCapabilities(mockWidget, new Set(["org.matrix.msc2931.navigate"])), + ).resolves.toEqual(new Set()); + }); + + it("custom permissions", async () => { + expect(customWidgetPermissions.isEmbeddingPreapproved(mockWidget)).toBe(true); + await expect(customWidgetPermissions.isIdentityRequestPreapproved(mockWidget)).resolves.toBe(true); + await expect( + customWidgetPermissions.preapproveCapabilities(mockWidget, new Set(["org.matrix.msc2931.navigate"])), + ).resolves.toEqual(new Set(["org.matrix.msc2931.navigate"])); + }); + }); +});