${tex}`)
+ }
+ });
+ return phtml.html();
}
// ensure removal of escape backslashes in non-Markdown messages
if (md.indexOf("\\") > -1) {
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 5760cf9ca1..ec82211789 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -46,6 +46,13 @@
"Alternatively, you can try to use the public server at
turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Alternatively, you can try to use the public server at
turn.matrix.org, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.",
"Try using turn.matrix.org": "Try using turn.matrix.org",
"OK": "OK",
+ "Unable to access microphone": "Unable to access microphone",
+ "Call failed because no microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "Call failed because no microphone could not be accessed. Check that a microphone is plugged in and set up correctly.",
+ "Unable to access webcam / microphone": "Unable to access webcam / microphone",
+ "Call failed because no webcam or microphone could not be accessed. Check that:": "Call failed because no webcam or microphone could not be accessed. Check that:",
+ "A microphone and webcam are plugged in and set up correctly": "A microphone and webcam are plugged in and set up correctly",
+ "Permission is granted to use the webcam": "Permission is granted to use the webcam",
+ "No other application is using the webcam": "No other application is using the webcam",
"Unable to capture screen": "Unable to capture screen",
"Existing Call": "Existing Call",
"You are already in a call.": "You are already in a call.",
@@ -755,6 +762,7 @@
"%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s",
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
"Change notification settings": "Change notification settings",
+ "Render LaTeX maths in messages": "Render LaTeX maths in messages",
"Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.": "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.",
"New spinner design": "New spinner design",
"Message Pinning": "Message Pinning",
@@ -954,9 +962,9 @@
"Changing password will currently reset any end-to-end encryption keys on all sessions, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.": "Changing password will currently reset any end-to-end encryption keys on all sessions, making encrypted chat history unreadable, unless you first export your room keys and re-import them afterwards. In future this will be improved.",
"Export E2E room keys": "Export E2E room keys",
"Do you want to set an email address?": "Do you want to set an email address?",
- "Current password": "Current password",
- "New Password": "New Password",
"Confirm password": "Confirm password",
+ "Passwords don't match": "Passwords don't match",
+ "Current password": "Current password",
"Change Password": "Change Password",
"Your homeserver does not support cross-signing.": "Your homeserver does not support cross-signing.",
"Cross-signing is ready for use.": "Cross-signing is ready for use.",
@@ -1128,6 +1136,8 @@
"Message layout": "Message layout",
"Compact": "Compact",
"Modern": "Modern",
+ "Hide advanced": "Hide advanced",
+ "Show advanced": "Show advanced",
"Set the name of a font installed on your system & %(brand)s will attempt to use it.": "Set the name of a font installed on your system & %(brand)s will attempt to use it.",
"Customise your appearance": "Customise your appearance",
"Appearance Settings only affect this %(brand)s session.": "Appearance Settings only affect this %(brand)s session.",
@@ -1986,8 +1996,6 @@
"Name": "Name",
"Topic (optional)": "Topic (optional)",
"Make this room public": "Make this room public",
- "Hide advanced": "Hide advanced",
- "Show advanced": "Show advanced",
"Block anyone not part of %(serverName)s from ever joining this room.": "Block anyone not part of %(serverName)s from ever joining this room.",
"Create Room": "Create Room",
"Sign out": "Sign out",
@@ -2313,7 +2321,6 @@
"Sign in": "Sign in",
"Use an email address to recover your account": "Use an email address to recover your account",
"Enter email address (required on this homeserver)": "Enter email address (required on this homeserver)",
- "Passwords don't match": "Passwords don't match",
"Other users can invite you to rooms using your contact details": "Other users can invite you to rooms using your contact details",
"Enter phone number (required on this homeserver)": "Enter phone number (required on this homeserver)",
"Use lowercase letters, numbers, dashes and underscores only": "Use lowercase letters, numbers, dashes and underscores only",
@@ -2457,6 +2464,8 @@
"Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s",
"Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other",
"Failed to find the general chat for this community": "Failed to find the general chat for this community",
+ "Got an account?
Sign in": "Got an account?
Sign in",
+ "New here?
Create an account": "New here?
Create an account",
"Notification settings": "Notification settings",
"Security & privacy": "Security & privacy",
"All settings": "All settings",
@@ -2475,6 +2484,11 @@
"A new password must be entered.": "A new password must be entered.",
"New passwords must match each other.": "New passwords must match each other.",
"Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.": "Changing your password will reset any end-to-end encryption keys on all of your sessions, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another session before resetting your password.",
+ "Your Matrix account on %(serverName)s": "Your Matrix account on %(serverName)s",
+ "Your Matrix account on
": "Your Matrix account on
",
+ "No identity server is configured: add one in server settings to reset your password.": "No identity server is configured: add one in server settings to reset your password.",
+ "Sign in instead": "Sign in instead",
+ "New Password": "New Password",
"A verification email will be sent to your inbox to confirm setting your new password.": "A verification email will be sent to your inbox to confirm setting your new password.",
"Send Reset Email": "Send Reset Email",
"Sign in instead": "Sign in instead",
diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts
index cc6fd29fe3..31e133be72 100644
--- a/src/settings/Settings.ts
+++ b/src/settings/Settings.ts
@@ -117,6 +117,12 @@ export interface ISetting {
}
export const SETTINGS: {[setting: string]: ISetting} = {
+ "feature_latex_maths": {
+ isFeature: true,
+ displayName: _td("Render LaTeX maths in messages"),
+ supportedLevels: LEVELS_FEATURE,
+ default: false,
+ },
"feature_communities_v2_prototypes": {
isFeature: true,
displayName: _td(
diff --git a/src/stores/ModalWidgetStore.ts b/src/stores/ModalWidgetStore.ts
index 0485afd106..c0b64d76fe 100644
--- a/src/stores/ModalWidgetStore.ts
+++ b/src/stores/ModalWidgetStore.ts
@@ -64,7 +64,7 @@ export class ModalWidgetStore extends AsyncStoreWithClient
{
this.openSourceWidgetId = null;
this.modalInstance = null;
},
- });
+ }, null, /* priority = */ false, /* static = */ true);
};
public closeModalWidget = (sourceWidget: Widget, data?: IModalWidgetReturnData) => {
diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts
index 0f3138fe9e..b2fe630760 100644
--- a/src/stores/room-list/RoomListStore.ts
+++ b/src/stores/room-list/RoomListStore.ts
@@ -34,6 +34,7 @@ import { MarkedExecution } from "../../utils/MarkedExecution";
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
import { NameFilterCondition } from "./filters/NameFilterCondition";
import { RoomNotificationStateStore } from "../notifications/RoomNotificationStateStore";
+import { VisibilityProvider } from "./filters/VisibilityProvider";
interface IState {
tagsEnabled?: boolean;
@@ -401,6 +402,10 @@ export class RoomListStoreClass extends AsyncStoreWithClient {
}
private async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise {
+ if (!VisibilityProvider.instance.isRoomVisible(room)) {
+ return; // don't do anything on rooms that aren't visible
+ }
+
const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause);
if (shouldUpdate) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
@@ -544,7 +549,8 @@ export class RoomListStoreClass extends AsyncStoreWithClient {
public async regenerateAllLists({trigger = true}) {
console.warn("Regenerating all room lists");
- const rooms = this.matrixClient.getVisibleRooms();
+ const rooms = this.matrixClient.getVisibleRooms()
+ .filter(r => VisibilityProvider.instance.isRoomVisible(r));
const customTags = new Set();
if (this.state.tagsEnabled) {
for (const room of rooms) {
diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts
index 439141edb4..25059aabe7 100644
--- a/src/stores/room-list/algorithms/Algorithm.ts
+++ b/src/stores/room-list/algorithms/Algorithm.ts
@@ -34,6 +34,7 @@ import { EffectiveMembership, getEffectiveMembership, splitRoomsByMembership } f
import { OrderingAlgorithm } from "./list-ordering/OrderingAlgorithm";
import { getListAlgorithmInstance } from "./list-ordering";
import SettingsStore from "../../../settings/SettingsStore";
+import { VisibilityProvider } from "../filters/VisibilityProvider";
/**
* Fired when the Algorithm has determined a list has been updated.
@@ -188,6 +189,10 @@ export class Algorithm extends EventEmitter {
// Note throughout: We need async so we can wait for handleRoomUpdate() to do its thing,
// otherwise we risk duplicating rooms.
+ if (val && !VisibilityProvider.instance.isRoomVisible(val)) {
+ val = null; // the room isn't visible - lie to the rest of this function
+ }
+
// Set the last sticky room to indicate that we're in a change. The code throughout the
// class can safely handle a null room, so this should be safe to do as a backup.
this._lastStickyRoom = this._stickyRoom || {};
diff --git a/src/stores/room-list/filters/VisibilityProvider.ts b/src/stores/room-list/filters/VisibilityProvider.ts
new file mode 100644
index 0000000000..553dd33ce0
--- /dev/null
+++ b/src/stores/room-list/filters/VisibilityProvider.ts
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 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 {Room} from "matrix-js-sdk/src/models/room";
+import { RoomListCustomisations } from "../../../customisations/RoomList";
+
+export class VisibilityProvider {
+ private static internalInstance: VisibilityProvider;
+
+ private constructor() {
+ }
+
+ public static get instance(): VisibilityProvider {
+ if (!VisibilityProvider.internalInstance) {
+ VisibilityProvider.internalInstance = new VisibilityProvider();
+ }
+ return VisibilityProvider.internalInstance;
+ }
+
+ public isRoomVisible(room: Room): boolean {
+ /* eslint-disable prefer-const */
+ let isVisible = true; // Returned at the end of this function
+ let forced = false; // When true, this function won't bother calling the customisation points
+ /* eslint-enable prefer-const */
+
+ // ------
+ // TODO: The `if` statements to control visibility of custom room types
+ // would go here. The remainder of this function assumes that the statements
+ // will be here.
+ //
+ // When removing this comment block, please remove the lint disable lines in the area.
+ // ------
+
+ const isVisibleFn = RoomListCustomisations.isRoomVisible;
+ if (!forced && isVisibleFn) {
+ isVisible = isVisibleFn(room);
+ }
+
+ return isVisible;
+ }
+}
diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts
index e8c0ea141e..cc2934aec1 100644
--- a/src/stores/widgets/StopGapWidget.ts
+++ b/src/stores/widgets/StopGapWidget.ts
@@ -17,8 +17,6 @@
import { Room } from "matrix-js-sdk/src/models/room";
import {
ClientWidgetApi,
- IGetOpenIDActionRequest,
- IGetOpenIDActionResponseData,
IStickerActionRequest,
IStickyActionRequest,
ITemplateParams,
@@ -27,10 +25,8 @@ import {
IWidgetApiRequestEmptyData,
IWidgetData,
MatrixCapabilities,
- OpenIDRequestState,
runTemplate,
Widget,
- WidgetApiToWidgetAction,
WidgetApiFromWidgetAction,
IModalWidgetOpenRequest,
IWidgetApiErrorResponseData,
@@ -50,8 +46,6 @@ import ActiveWidgetStore from "../ActiveWidgetStore";
import { objectShallowClone } from "../../utils/objects";
import defaultDispatcher from "../../dispatcher/dispatcher";
import { ElementWidgetActions, IViewRoomApiRequest } from "./ElementWidgetActions";
-import Modal from "../../Modal";
-import WidgetOpenIDPermissionsDialog from "../../components/views/dialogs/WidgetOpenIDPermissionsDialog";
import {ModalWidgetStore} from "../ModalWidgetStore";
import ThemeWatcher from "../../settings/watchers/ThemeWatcher";
import {getCustomTheme} from "../../theme";
@@ -74,7 +68,7 @@ interface IAppTileProps {
}
// TODO: Don't use this because it's wrong
-class ElementWidget extends Widget {
+export class ElementWidget extends Widget {
constructor(private rawDefinition: IWidget) {
super(rawDefinition);
}
@@ -235,55 +229,6 @@ export class StopGapWidget extends EventEmitter {
return this.messaging.widget.id;
}
- private onOpenIdReq = async (ev: CustomEvent) => {
- ev.preventDefault();
-
- const rawUrl = this.appTileProps.app.url;
- const widgetSecurityKey = WidgetUtils.getWidgetSecurityKey(this.widgetId, rawUrl, this.appTileProps.userWidget);
-
- const settings = SettingsStore.getValue("widgetOpenIDPermissions");
- if (settings.deny && settings.deny.includes(widgetSecurityKey)) {
- this.messaging.transport.reply(ev.detail, {
- state: OpenIDRequestState.Blocked,
- });
- return;
- }
- if (settings.allow && settings.allow.includes(widgetSecurityKey)) {
- const credentials = await MatrixClientPeg.get().getOpenIdToken();
- this.messaging.transport.reply(ev.detail, {
- state: OpenIDRequestState.Allowed,
- ...credentials,
- });
- return;
- }
-
- // Confirm that we received the request
- this.messaging.transport.reply(ev.detail, {
- state: OpenIDRequestState.PendingUserConfirmation,
- });
-
- // Actually ask for permission to send the user's data
- Modal.createTrackedDialog("OpenID widget permissions", '', WidgetOpenIDPermissionsDialog, {
- widgetUrl: rawUrl,
- widgetId: this.widgetId,
- isUserWidget: this.appTileProps.userWidget,
-
- onFinished: async (confirm) => {
- const responseBody: IGetOpenIDActionResponseData = {
- state: confirm ? OpenIDRequestState.Allowed : OpenIDRequestState.Blocked,
- original_request_id: ev.detail.requestId, // eslint-disable-line camelcase
- };
- if (confirm) {
- const credentials = await MatrixClientPeg.get().getOpenIdToken();
- Object.assign(responseBody, credentials);
- }
- this.messaging.transport.send(WidgetApiToWidgetAction.OpenIDCredentials, responseBody).catch(error => {
- console.error("Failed to send OpenID credentials: ", error);
- });
- },
- });
- };
-
private onOpenModal = async (ev: CustomEvent) => {
ev.preventDefault();
if (ModalWidgetStore.instance.canOpenModalWidget()) {
@@ -301,11 +246,10 @@ export class StopGapWidget extends EventEmitter {
public start(iframe: HTMLIFrameElement) {
if (this.started) return;
const allowedCapabilities = this.appTileProps.whitelistCapabilities || [];
- const driver = new StopGapWidgetDriver( allowedCapabilities, this.mockWidget, this.kind);
+ const driver = new StopGapWidgetDriver(allowedCapabilities, this.mockWidget, this.kind, this.roomId);
this.messaging = new ClientWidgetApi(this.mockWidget, iframe, driver);
this.messaging.on("preparing", () => this.emit("preparing"));
this.messaging.on("ready", () => this.emit("ready"));
- this.messaging.on(`action:${WidgetApiFromWidgetAction.GetOpenIDCredentials}`, this.onOpenIdReq);
this.messaging.on(`action:${WidgetApiFromWidgetAction.OpenModalWidget}`, this.onOpenModal);
WidgetMessagingStore.instance.storeMessaging(this.mockWidget, this.messaging);
diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts
index 722c7d8f49..60988040d3 100644
--- a/src/stores/widgets/StopGapWidgetDriver.ts
+++ b/src/stores/widgets/StopGapWidgetDriver.ts
@@ -16,19 +16,30 @@
import {
Capability,
+ EventDirection,
+ IOpenIDCredentials,
+ IOpenIDUpdate,
ISendEventDetails,
MatrixCapabilities,
+ OpenIDRequestState,
+ SimpleObservable,
Widget,
WidgetDriver,
+ WidgetEventCapability,
WidgetKind,
} from "matrix-widget-api";
import { iterableDiff, iterableUnion } from "../../utils/iterables";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import ActiveRoomObserver from "../../ActiveRoomObserver";
import Modal from "../../Modal";
+import WidgetOpenIDPermissionsDialog from "../../components/views/dialogs/WidgetOpenIDPermissionsDialog";
import WidgetCapabilitiesPromptDialog, {
getRememberedCapabilitiesForWidget,
} from "../../components/views/dialogs/WidgetCapabilitiesPromptDialog";
+import { WidgetPermissionCustomisations } from "../../customisations/WidgetPermissions";
+import { OIDCState, WidgetPermissionStore } from "./WidgetPermissionStore";
+import { WidgetType } from "../../widgets/WidgetType";
+import { EventType } from "matrix-js-sdk/src/@types/event";
// TODO: Purge this from the universe
@@ -36,13 +47,27 @@ export class StopGapWidgetDriver extends WidgetDriver {
private allowedCapabilities: Set;
// TODO: Refactor widgetKind into the Widget class
- constructor(allowedCapabilities: Capability[], private forWidget: Widget, private forWidgetKind: WidgetKind) {
+ constructor(
+ allowedCapabilities: Capability[],
+ private forWidget: Widget,
+ private forWidgetKind: WidgetKind,
+ private inRoomId?: string,
+ ) {
super();
// Always allow screenshots to be taken because it's a client-induced flow. The widget can't
// spew screenshots at us and can't request screenshots of us, so it's up to us to provide the
// button if the widget says it supports screenshots.
this.allowedCapabilities = new Set([...allowedCapabilities, MatrixCapabilities.Screenshots]);
+
+ // Grant the permissions that are specific to given widget types
+ if (WidgetType.JITSI.matches(this.forWidget.type) && forWidgetKind === WidgetKind.Room) {
+ this.allowedCapabilities.add(MatrixCapabilities.AlwaysOnScreen);
+ } else if (WidgetType.STICKERPICKER.matches(this.forWidget.type) && forWidgetKind === WidgetKind.Account) {
+ const stickerSendingCap = WidgetEventCapability.forRoomEvent(EventDirection.Send, EventType.Sticker).raw;
+ this.allowedCapabilities.add(MatrixCapabilities.StickerSending); // legacy as far as MSC2762 is concerned
+ this.allowedCapabilities.add(stickerSendingCap);
+ }
}
public async validateCapabilities(requested: Set): Promise> {
@@ -52,7 +77,19 @@ export class StopGapWidgetDriver extends WidgetDriver {
const diff = iterableDiff(requested, this.allowedCapabilities);
const missing = new Set(diff.removed); // "removed" is "in A (requested) but not in B (allowed)"
const allowedSoFar = new Set(this.allowedCapabilities);
- getRememberedCapabilitiesForWidget(this.forWidget).forEach(cap => allowedSoFar.add(cap));
+ getRememberedCapabilitiesForWidget(this.forWidget).forEach(cap => {
+ allowedSoFar.add(cap);
+ missing.delete(cap);
+ });
+ if (WidgetPermissionCustomisations.preapproveCapabilities) {
+ const approved = await WidgetPermissionCustomisations.preapproveCapabilities(this.forWidget, requested);
+ if (approved) {
+ approved.forEach(cap => {
+ allowedSoFar.add(cap);
+ missing.delete(cap);
+ });
+ }
+ }
// TODO: Do something when the widget requests new capabilities not yet asked for
if (missing.size > 0) {
try {
@@ -79,7 +116,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
if (!client || !roomId) throw new Error("Not in a room or not attached to a client");
- let r: {event_id: string} = null; // eslint-disable-line camelcase
+ let r: { event_id: string } = null; // eslint-disable-line camelcase
if (stateKey !== null) {
// state event
r = await client.sendStateEvent(roomId, eventType, content, stateKey);
@@ -90,4 +127,37 @@ export class StopGapWidgetDriver extends WidgetDriver {
return {roomId, eventId: r.event_id};
}
+
+ public async askOpenID(observer: SimpleObservable) {
+ const oidcState = WidgetPermissionStore.instance.getOIDCState(
+ this.forWidget, this.forWidgetKind, this.inRoomId,
+ );
+
+ const getToken = (): Promise => {
+ return MatrixClientPeg.get().getOpenIdToken();
+ };
+
+ if (oidcState === OIDCState.Denied) {
+ return observer.update({state: OpenIDRequestState.Blocked});
+ }
+ if (oidcState === OIDCState.Allowed) {
+ return observer.update({state: OpenIDRequestState.Allowed, token: await getToken()});
+ }
+
+ observer.update({state: OpenIDRequestState.PendingUserConfirmation});
+
+ Modal.createTrackedDialog("OpenID widget permissions", '', WidgetOpenIDPermissionsDialog, {
+ widget: this.forWidget,
+ widgetKind: this.forWidgetKind,
+ inRoomId: this.inRoomId,
+
+ onFinished: async (confirm) => {
+ if (!confirm) {
+ return observer.update({state: OpenIDRequestState.Blocked});
+ }
+
+ return observer.update({state: OpenIDRequestState.Allowed, token: await getToken()});
+ },
+ });
+ }
}
diff --git a/src/stores/widgets/WidgetPermissionStore.ts b/src/stores/widgets/WidgetPermissionStore.ts
new file mode 100644
index 0000000000..41e8bc6652
--- /dev/null
+++ b/src/stores/widgets/WidgetPermissionStore.ts
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2020 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 SettingsStore from "../../settings/SettingsStore";
+import { Widget, WidgetKind } from "matrix-widget-api";
+import { MatrixClientPeg } from "../../MatrixClientPeg";
+import { SettingLevel } from "../../settings/SettingLevel";
+
+export enum OIDCState {
+ Allowed, // user has set the remembered value as allowed
+ Denied, // user has set the remembered value as disallowed
+ Unknown, // user has not set a remembered value
+}
+
+export class WidgetPermissionStore {
+ private static internalInstance: WidgetPermissionStore;
+
+ private constructor() {
+ }
+
+ public static get instance(): WidgetPermissionStore {
+ if (!WidgetPermissionStore.internalInstance) {
+ WidgetPermissionStore.internalInstance = new WidgetPermissionStore();
+ }
+ return WidgetPermissionStore.internalInstance;
+ }
+
+ // TODO (all functions here): Merge widgetKind with the widget definition
+
+ private packSettingKey(widget: Widget, kind: WidgetKind, roomId?: string): string {
+ let location = roomId;
+ if (kind !== WidgetKind.Room) {
+ location = MatrixClientPeg.get().getUserId();
+ }
+ if (kind === WidgetKind.Modal) {
+ location = '*MODAL*-' + location; // to guarantee differentiation from whatever spawned it
+ }
+ if (!location) {
+ throw new Error("Failed to determine a location to check the widget's OIDC state with");
+ }
+
+ return encodeURIComponent(`${location}::${widget.templateUrl}`);
+ }
+
+ public getOIDCState(widget: Widget, kind: WidgetKind, roomId?: string): OIDCState {
+ const settingsKey = this.packSettingKey(widget, kind, roomId);
+ const settings = SettingsStore.getValue("widgetOpenIDPermissions");
+ if (settings?.deny?.includes(settingsKey)) {
+ return OIDCState.Denied;
+ }
+ if (settings?.allow?.includes(settingsKey)) {
+ return OIDCState.Allowed;
+ }
+ return OIDCState.Unknown;
+ }
+
+ public setOIDCState(widget: Widget, kind: WidgetKind, roomId: string, newState: OIDCState) {
+ const settingsKey = this.packSettingKey(widget, kind, roomId);
+
+ const currentValues = SettingsStore.getValue("widgetOpenIDPermissions");
+ if (!currentValues.allow) currentValues.allow = [];
+ if (!currentValues.deny) currentValues.deny = [];
+
+ if (newState === OIDCState.Allowed) {
+ currentValues.allow.push(settingsKey);
+ } else if (newState === OIDCState.Denied) {
+ currentValues.deny.push(settingsKey);
+ } else {
+ currentValues.allow = currentValues.allow.filter(c => c !== settingsKey);
+ currentValues.deny = currentValues.deny.filter(c => c !== settingsKey);
+ }
+
+ SettingsStore.setValue("widgetOpenIDPermissions", null, SettingLevel.DEVICE, currentValues);
+ }
+}
diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts
index 526c2d5ce7..986c68342c 100644
--- a/src/utils/WidgetUtils.ts
+++ b/src/utils/WidgetUtils.ts
@@ -22,7 +22,6 @@ import SdkConfig from "../SdkConfig";
import dis from '../dispatcher/dispatcher';
import WidgetEchoStore from '../stores/WidgetEchoStore';
import SettingsStore from "../settings/SettingsStore";
-import ActiveWidgetStore from "../stores/ActiveWidgetStore";
import {IntegrationManagers} from "../integrations/IntegrationManagers";
import {Room} from "matrix-js-sdk/src/models/room";
import {WidgetType} from "../widgets/WidgetType";
@@ -457,27 +456,6 @@ export default class WidgetUtils {
return capWhitelist;
}
- static getWidgetSecurityKey(widgetId: string, widgetUrl: string, isUserWidget: boolean): string {
- let widgetLocation = ActiveWidgetStore.getRoomId(widgetId);
-
- if (isUserWidget) {
- const userWidget = WidgetUtils.getUserWidgetsArray()
- .find((w) => w.id === widgetId && w.content && w.content.url === widgetUrl);
-
- if (!userWidget) {
- throw new Error("No matching user widget to form security key");
- }
-
- widgetLocation = userWidget.sender;
- }
-
- if (!widgetLocation) {
- throw new Error("Failed to locate where the widget resides");
- }
-
- return encodeURIComponent(`${widgetLocation}::${widgetUrl}`);
- }
-
static getLocalJitsiWrapperUrl(opts: {forLocalRender?: boolean, auth?: string} = {}) {
// NB. we can't just encodeURIComponent all of these because the $ signs need to be there
const queryStringParts = [
diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js
index 07cd51edbd..bf55e9c430 100644
--- a/test/components/views/messages/TextualBody-test.js
+++ b/test/components/views/messages/TextualBody-test.js
@@ -36,6 +36,7 @@ describe("", () => {
MatrixClientPeg.matrixClient = {
getRoom: () => mkStubRoom("room_id"),
getAccountData: () => undefined,
+ isGuest: () => false,
};
const ev = mkEvent({
@@ -59,6 +60,7 @@ describe("", () => {
MatrixClientPeg.matrixClient = {
getRoom: () => mkStubRoom("room_id"),
getAccountData: () => undefined,
+ isGuest: () => false,
};
const ev = mkEvent({
@@ -83,6 +85,7 @@ describe("", () => {
MatrixClientPeg.matrixClient = {
getRoom: () => mkStubRoom("room_id"),
getAccountData: () => undefined,
+ isGuest: () => false,
};
});
@@ -135,6 +138,7 @@ describe("", () => {
getHomeserverUrl: () => "https://my_server/",
on: () => undefined,
removeListener: () => undefined,
+ isGuest: () => false,
};
});
diff --git a/yarn.lock b/yarn.lock
index def240fdf2..c06494d319 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6206,6 +6206,13 @@ jsx-ast-utils@^2.4.1:
array-includes "^3.1.1"
object.assign "^4.1.0"
+katex@^0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/katex/-/katex-0.12.0.tgz#2fb1c665dbd2b043edcf8a1f5c555f46beaa0cb9"
+ integrity sha512-y+8btoc/CK70XqcHqjxiGWBOeIL8upbS0peTPXTvgrh21n1RiWWcIpSWM+4uXq+IAgNh9YYQWdc7LVDPDAEEAg==
+ dependencies:
+ commander "^2.19.0"
+
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -6532,10 +6539,10 @@ matrix-react-test-utils@^0.2.2:
resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.2.2.tgz#c87144d3b910c7edc544a6699d13c7c2bf02f853"
integrity sha512-49+7gfV6smvBIVbeloql+37IeWMTD+fiywalwCqk8Dnz53zAFjKSltB3rmWHso1uecLtQEcPtCijfhzcLXAxTQ==
-matrix-widget-api@^0.1.0-beta.9:
- version "0.1.0-beta.9"
- resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.9.tgz#83952132c1610e013acb3e695f923f971ddd5637"
- integrity sha512-nXo4iaquSya6hYLXccX8o1K960ckSQ0YXIubRDha+YmB+L09F5a7bUPS5JN2tYANOMzyfFAzWVuFwjHv4+K+rg==
+matrix-widget-api@^0.1.0-beta.10:
+ version "0.1.0-beta.10"
+ resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.10.tgz#2e4d658d90ff3152c5567089b4ddd21fb44ec1dd"
+ integrity sha512-yX2UURjM1zVp7snPiOFcH9+FDBdHfAdt5HEAyDUHGJ7w/F2zOtcK/y0dMlZ1+XhxY7Wv0IBZH0US8X/ioJRX1A==
dependencies:
events "^3.2.0"