Track OpenID automatic permissions by (widgetLocation, widgetUrl)

pull/21833/head
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 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};

View File

@ -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);
}

View File

@ -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);

View File

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

View File

@ -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}`);
}
}