Track OpenID automatic permissions by (widgetLocation, widgetUrl)

This commit is contained in:
Travis Ralston 2019-03-23 23:25:31 -06:00
parent 21d52a8311
commit 2dcb40f1be
5 changed files with 41 additions and 7 deletions

View file

@ -26,6 +26,7 @@ import Modal from "./Modal";
import MatrixClientPeg from "./MatrixClientPeg"; import MatrixClientPeg from "./MatrixClientPeg";
import SettingsStore from "./settings/SettingsStore"; import SettingsStore from "./settings/SettingsStore";
import WidgetOpenIDPermissionsDialog from "./components/views/dialogs/WidgetOpenIDPermissionsDialog"; import WidgetOpenIDPermissionsDialog from "./components/views/dialogs/WidgetOpenIDPermissionsDialog";
import WidgetUtils from "./utils/WidgetUtils";
if (!global.mxFromWidgetMessaging) { if (!global.mxFromWidgetMessaging) {
global.mxFromWidgetMessaging = new FromWidgetPostMessageApi(); global.mxFromWidgetMessaging = new FromWidgetPostMessageApi();
@ -39,9 +40,10 @@ if (!global.mxToWidgetMessaging) {
const OUTBOUND_API_NAME = 'toWidget'; const OUTBOUND_API_NAME = 'toWidget';
export default class WidgetMessaging { export default class WidgetMessaging {
constructor(widgetId, widgetUrl, target) { constructor(widgetId, widgetUrl, isUserWidget, target) {
this.widgetId = widgetId; this.widgetId = widgetId;
this.widgetUrl = widgetUrl; this.widgetUrl = widgetUrl;
this.isUserWidget = isUserWidget;
this.target = target; this.target = target;
this.fromWidget = global.mxFromWidgetMessaging; this.fromWidget = global.mxFromWidgetMessaging;
this.toWidget = global.mxToWidgetMessaging; this.toWidget = global.mxToWidgetMessaging;
@ -126,12 +128,14 @@ export default class WidgetMessaging {
async _onOpenIdRequest(ev, rawEv) { async _onOpenIdRequest(ev, rawEv) {
if (ev.widgetId !== this.widgetId) return; // not interesting if (ev.widgetId !== this.widgetId) return; // not interesting
const widgetSecurityKey = WidgetUtils.getWidgetSecurityKey(this.widgetId, this.widgetUrl, this.isUserWidget);
const settings = SettingsStore.getValue("widgetOpenIDPermissions"); 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"}); this.fromWidget.sendResponse(rawEv, {state: "blocked"});
return; return;
} }
if (settings.whitelist && settings.whitelist.includes(this.widgetId)) { if (settings.whitelist && settings.whitelist.includes(widgetSecurityKey)) {
const responseBody = {state: "allowed"}; const responseBody = {state: "allowed"};
const credentials = await MatrixClientPeg.get().getOpenIdToken(); const credentials = await MatrixClientPeg.get().getOpenIdToken();
Object.assign(responseBody, credentials); Object.assign(responseBody, credentials);
@ -147,6 +151,7 @@ export default class WidgetMessaging {
WidgetOpenIDPermissionsDialog, { WidgetOpenIDPermissionsDialog, {
widgetUrl: this.widgetUrl, widgetUrl: this.widgetUrl,
widgetId: this.widgetId, widgetId: this.widgetId,
isUserWidget: this.isUserWidget,
onFinished: async (confirm) => { onFinished: async (confirm) => {
const responseBody = {success: confirm}; const responseBody = {success: confirm};

View file

@ -20,12 +20,14 @@ import {_t} from "../../../languageHandler";
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import sdk from "../../../index"; import sdk from "../../../index";
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
import WidgetUtils from "../../../utils/WidgetUtils";
export default class WidgetOpenIDPermissionsDialog extends React.Component { export default class WidgetOpenIDPermissionsDialog extends React.Component {
static propTypes = { static propTypes = {
onFinished: PropTypes.func.isRequired, onFinished: PropTypes.func.isRequired,
widgetUrl: PropTypes.string.isRequired, widgetUrl: PropTypes.string.isRequired,
widgetId: PropTypes.string.isRequired, widgetId: PropTypes.string.isRequired,
isUserWidget: PropTypes.bool.isRequired,
}; };
constructor() { constructor() {
@ -52,7 +54,11 @@ export default class WidgetOpenIDPermissionsDialog extends React.Component {
if (!currentValues.whitelist) currentValues.whitelist = []; if (!currentValues.whitelist) currentValues.whitelist = [];
if (!currentValues.blacklist) currentValues.blacklist = []; 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); SettingsStore.setValue("widgetOpenIDPermissions", null, SettingLevel.DEVICE, currentValues);
} }

View file

@ -351,7 +351,7 @@ export default class AppTile extends React.Component {
_setupWidgetMessaging() { _setupWidgetMessaging() {
// FIXME: There's probably no reason to do this here: it should probably be done entirely // FIXME: There's probably no reason to do this here: it should probably be done entirely
// in ActiveWidgetStore. // 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); ActiveWidgetStore.setWidgetMessaging(this.props.id, widgetMessaging);
widgetMessaging.getCapabilities().then((requestedCapabilities) => { widgetMessaging.getCapabilities().then((requestedCapabilities) => {
console.log(`Widget ${this.props.id} requested capabilities: ` + requestedCapabilities); console.log(`Widget ${this.props.id} requested capabilities: ` + requestedCapabilities);

View file

@ -343,8 +343,8 @@ export const SETTINGS = {
"widgetOpenIDPermissions": { "widgetOpenIDPermissions": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: { default: {
whitelisted: [], whitelist: [],
blacklisted: [], blacklist: [],
}, },
}, },
"RoomList.orderByImportance": { "RoomList.orderByImportance": {

View file

@ -1,6 +1,7 @@
/* /*
Copyright 2017 Vector Creations Ltd Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd Copyright 2018 New Vector Ltd
Copyright 2019 Travis Ralston
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with 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 // before waitFor[Room/User]Widget rejects its promise
const WIDGET_WAIT_TIME = 20000; const WIDGET_WAIT_TIME = 20000;
import SettingsStore from "../settings/SettingsStore"; import SettingsStore from "../settings/SettingsStore";
import ActiveWidgetStore from "../stores/ActiveWidgetStore";
/** /**
* Encodes a URI according to a set of template variables. Variables will be * Encodes a URI according to a set of template variables. Variables will be
@ -396,4 +398,25 @@ export default class WidgetUtils {
return capWhitelist; 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}`);
}
} }