diff --git a/src/WidgetMessaging.js b/src/WidgetMessaging.js index bbecdfa086..8d419ba6eb 100644 --- a/src/WidgetMessaging.js +++ b/src/WidgetMessaging.js @@ -26,6 +26,7 @@ import Modal from "./Modal"; import MatrixClientPeg from "./MatrixClientPeg"; import SettingsStore from "./settings/SettingsStore"; import WidgetOpenIDPermissionsDialog from "./components/views/dialogs/WidgetOpenIDPermissionsDialog"; +import WidgetUtils from "./utils/WidgetUtils"; if (!global.mxFromWidgetMessaging) { global.mxFromWidgetMessaging = new FromWidgetPostMessageApi(); @@ -39,9 +40,10 @@ if (!global.mxToWidgetMessaging) { const OUTBOUND_API_NAME = 'toWidget'; export default class WidgetMessaging { - constructor(widgetId, widgetUrl, target) { + constructor(widgetId, widgetUrl, isUserWidget, target) { this.widgetId = widgetId; this.widgetUrl = widgetUrl; + this.isUserWidget = isUserWidget; this.target = target; this.fromWidget = global.mxFromWidgetMessaging; this.toWidget = global.mxToWidgetMessaging; @@ -126,12 +128,14 @@ export default class WidgetMessaging { async _onOpenIdRequest(ev, rawEv) { if (ev.widgetId !== this.widgetId) return; // not interesting + const widgetSecurityKey = WidgetUtils.getWidgetSecurityKey(this.widgetId, this.widgetUrl, this.isUserWidget); + const settings = SettingsStore.getValue("widgetOpenIDPermissions"); - if (settings.blacklist && settings.blacklist.includes(this.widgetId)) { + if (settings.blacklist && settings.blacklist.includes(widgetSecurityKey)) { this.fromWidget.sendResponse(rawEv, {state: "blocked"}); return; } - if (settings.whitelist && settings.whitelist.includes(this.widgetId)) { + if (settings.whitelist && settings.whitelist.includes(widgetSecurityKey)) { const responseBody = {state: "allowed"}; const credentials = await MatrixClientPeg.get().getOpenIdToken(); Object.assign(responseBody, credentials); @@ -147,6 +151,7 @@ export default class WidgetMessaging { WidgetOpenIDPermissionsDialog, { widgetUrl: this.widgetUrl, widgetId: this.widgetId, + isUserWidget: this.isUserWidget, onFinished: async (confirm) => { const responseBody = {success: confirm}; diff --git a/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js b/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js index e6e97a3305..5f341261f8 100644 --- a/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js +++ b/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js @@ -20,12 +20,14 @@ import {_t} from "../../../languageHandler"; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import sdk from "../../../index"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; +import WidgetUtils from "../../../utils/WidgetUtils"; export default class WidgetOpenIDPermissionsDialog extends React.Component { static propTypes = { onFinished: PropTypes.func.isRequired, widgetUrl: PropTypes.string.isRequired, widgetId: PropTypes.string.isRequired, + isUserWidget: PropTypes.bool.isRequired, }; constructor() { @@ -52,7 +54,11 @@ export default class WidgetOpenIDPermissionsDialog extends React.Component { if (!currentValues.whitelist) currentValues.whitelist = []; if (!currentValues.blacklist) currentValues.blacklist = []; - (allowed ? currentValues.whitelist : currentValues.blacklist).push(this.props.widgetId); + const securityKey = WidgetUtils.getWidgetSecurityKey( + this.props.widgetId, + this.props.widgetUrl, + this.props.isUserWidget); + (allowed ? currentValues.whitelist : currentValues.blacklist).push(securityKey); SettingsStore.setValue("widgetOpenIDPermissions", null, SettingLevel.DEVICE, currentValues); } diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 8ed408ffbe..955c2d5480 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -351,7 +351,7 @@ export default class AppTile extends React.Component { _setupWidgetMessaging() { // FIXME: There's probably no reason to do this here: it should probably be done entirely // in ActiveWidgetStore. - const widgetMessaging = new WidgetMessaging(this.props.id, this.props.url, this.refs.appFrame.contentWindow); + const widgetMessaging = new WidgetMessaging(this.props.id, this.props.url, this.props.userWidget, this.refs.appFrame.contentWindow); ActiveWidgetStore.setWidgetMessaging(this.props.id, widgetMessaging); widgetMessaging.getCapabilities().then((requestedCapabilities) => { console.log(`Widget ${this.props.id} requested capabilities: ` + requestedCapabilities); diff --git a/src/settings/Settings.js b/src/settings/Settings.js index fcf70b4df7..765b2f85c6 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -343,8 +343,8 @@ export const SETTINGS = { "widgetOpenIDPermissions": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, default: { - whitelisted: [], - blacklisted: [], + whitelist: [], + blacklist: [], }, }, "RoomList.orderByImportance": { diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index b5a2ae31fb..41a241c905 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -1,6 +1,7 @@ /* Copyright 2017 Vector Creations Ltd Copyright 2018 New Vector Ltd +Copyright 2019 Travis Ralston Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25,6 +26,7 @@ import WidgetEchoStore from '../stores/WidgetEchoStore'; // before waitFor[Room/User]Widget rejects its promise const WIDGET_WAIT_TIME = 20000; import SettingsStore from "../settings/SettingsStore"; +import ActiveWidgetStore from "../stores/ActiveWidgetStore"; /** * Encodes a URI according to a set of template variables. Variables will be @@ -396,4 +398,25 @@ export default class WidgetUtils { return capWhitelist; } + + static getWidgetSecurityKey(widgetId, widgetUrl, isUserWidget) { + 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}`); + } }