2017-11-30 02:11:03 +08:00
|
|
|
/*
|
2017-11-30 18:20:29 +08:00
|
|
|
Copyright 2017 New Vector Ltd
|
2019-03-13 14:34:34 +08:00
|
|
|
Copyright 2019 Travis Ralston
|
2017-11-30 02:11:03 +08:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2017-12-01 22:44:14 +08:00
|
|
|
/*
|
2018-02-23 23:11:28 +08:00
|
|
|
* See - https://docs.google.com/document/d/1uPF7XWY_dXTKVKV7jZQ2KmsI19wn9-kFRgQ1tFQP7wQ/edit?usp=sharing for
|
|
|
|
* spec. details / documentation.
|
2017-12-01 22:44:14 +08:00
|
|
|
*/
|
|
|
|
|
2018-03-28 19:22:06 +08:00
|
|
|
import FromWidgetPostMessageApi from './FromWidgetPostMessageApi';
|
|
|
|
import ToWidgetPostMessageApi from './ToWidgetPostMessageApi';
|
2019-03-13 14:34:34 +08:00
|
|
|
import Modal from "./Modal";
|
2019-12-21 05:13:46 +08:00
|
|
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
2019-03-16 11:33:31 +08:00
|
|
|
import SettingsStore from "./settings/SettingsStore";
|
|
|
|
import WidgetOpenIDPermissionsDialog from "./components/views/dialogs/WidgetOpenIDPermissionsDialog";
|
2019-03-24 13:25:31 +08:00
|
|
|
import WidgetUtils from "./utils/WidgetUtils";
|
2020-03-24 23:55:54 +08:00
|
|
|
import {KnownWidgetActions} from "./widgets/WidgetApi";
|
2017-12-05 08:08:17 +08:00
|
|
|
|
2018-03-28 19:22:06 +08:00
|
|
|
if (!global.mxFromWidgetMessaging) {
|
|
|
|
global.mxFromWidgetMessaging = new FromWidgetPostMessageApi();
|
|
|
|
global.mxFromWidgetMessaging.start();
|
2017-12-05 01:54:00 +08:00
|
|
|
}
|
2018-03-28 19:22:06 +08:00
|
|
|
if (!global.mxToWidgetMessaging) {
|
|
|
|
global.mxToWidgetMessaging = new ToWidgetPostMessageApi();
|
|
|
|
global.mxToWidgetMessaging.start();
|
2017-12-05 01:54:00 +08:00
|
|
|
}
|
2017-11-30 19:30:30 +08:00
|
|
|
|
2018-03-28 19:22:06 +08:00
|
|
|
const OUTBOUND_API_NAME = 'toWidget';
|
2018-01-18 21:16:06 +08:00
|
|
|
|
2018-03-28 19:22:06 +08:00
|
|
|
export default class WidgetMessaging {
|
2019-03-24 13:25:31 +08:00
|
|
|
constructor(widgetId, widgetUrl, isUserWidget, target) {
|
2018-03-28 19:22:06 +08:00
|
|
|
this.widgetId = widgetId;
|
|
|
|
this.widgetUrl = widgetUrl;
|
2019-03-24 13:25:31 +08:00
|
|
|
this.isUserWidget = isUserWidget;
|
2018-03-28 19:22:06 +08:00
|
|
|
this.target = target;
|
|
|
|
this.fromWidget = global.mxFromWidgetMessaging;
|
|
|
|
this.toWidget = global.mxToWidgetMessaging;
|
2019-03-24 12:50:26 +08:00
|
|
|
this._onOpenIdRequest = this._onOpenIdRequest.bind(this);
|
2018-03-28 19:22:06 +08:00
|
|
|
this.start();
|
2017-12-02 00:17:18 +08:00
|
|
|
}
|
|
|
|
|
2018-03-28 19:22:06 +08:00
|
|
|
messageToWidget(action) {
|
2018-05-13 03:49:43 +08:00
|
|
|
action.widgetId = this.widgetId; // Required to be sent for all outbound requests
|
|
|
|
|
2018-03-28 19:22:06 +08:00
|
|
|
return this.toWidget.exec(action, this.target).then((data) => {
|
|
|
|
// Check for errors and reject if found
|
2017-12-15 23:24:22 +08:00
|
|
|
if (data.response === undefined) { // null is valid
|
|
|
|
throw new Error("Missing 'response' field");
|
|
|
|
}
|
|
|
|
if (data.response && data.response.error) {
|
|
|
|
const err = data.response.error;
|
|
|
|
const msg = String(err.message ? err.message : "An error was returned");
|
|
|
|
if (err._error) {
|
|
|
|
console.error(err._error);
|
|
|
|
}
|
|
|
|
// Potential XSS attack if 'msg' is not appropriately sanitized,
|
|
|
|
// as it is untrusted input by our parent window (which we assume is Riot).
|
|
|
|
// We can't aggressively sanitize [A-z0-9] since it might be a translation.
|
|
|
|
throw new Error(msg);
|
|
|
|
}
|
2018-03-28 19:22:06 +08:00
|
|
|
// Return the response field for the request
|
2017-12-15 23:24:22 +08:00
|
|
|
return data.response;
|
|
|
|
});
|
2017-12-02 00:17:18 +08:00
|
|
|
}
|
2017-12-15 23:24:22 +08:00
|
|
|
|
2020-03-24 23:55:54 +08:00
|
|
|
/**
|
|
|
|
* Tells the widget that the client is ready to handle further widget requests.
|
2020-03-25 00:05:57 +08:00
|
|
|
* @returns {Promise<*>} Resolves after the widget has acknowledged the ready message.
|
2020-03-24 23:55:54 +08:00
|
|
|
*/
|
|
|
|
flagReadyToContinue() {
|
|
|
|
return this.messageToWidget({
|
|
|
|
api: OUTBOUND_API_NAME,
|
|
|
|
action: KnownWidgetActions.ClientReady,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-12-16 00:39:04 +08:00
|
|
|
/**
|
|
|
|
* Request a screenshot from a widget
|
2018-02-26 06:21:30 +08:00
|
|
|
* @return {Promise} To be resolved with screenshot data when it has been generated
|
2017-12-16 00:39:04 +08:00
|
|
|
*/
|
|
|
|
getScreenshot() {
|
2020-04-02 03:59:48 +08:00
|
|
|
console.log('Requesting screenshot for', this.widgetId);
|
2018-03-28 19:22:06 +08:00
|
|
|
return this.messageToWidget({
|
2018-02-23 23:11:28 +08:00
|
|
|
api: OUTBOUND_API_NAME,
|
2017-12-16 05:36:02 +08:00
|
|
|
action: "screenshot",
|
2018-03-28 19:22:06 +08:00
|
|
|
})
|
|
|
|
.catch((error) => new Error("Failed to get screenshot: " + error.message))
|
|
|
|
.then((response) => response.screenshot);
|
2017-12-16 00:39:04 +08:00
|
|
|
}
|
2017-12-16 17:16:24 +08:00
|
|
|
|
2018-02-26 06:21:30 +08:00
|
|
|
/**
|
|
|
|
* Request capabilities required by the widget
|
|
|
|
* @return {Promise} To be resolved with an array of requested widget capabilities
|
|
|
|
*/
|
2017-12-16 17:16:24 +08:00
|
|
|
getCapabilities() {
|
2020-04-02 03:59:48 +08:00
|
|
|
console.log('Requesting capabilities for', this.widgetId);
|
2018-03-28 19:22:06 +08:00
|
|
|
return this.messageToWidget({
|
2018-02-23 23:11:28 +08:00
|
|
|
api: OUTBOUND_API_NAME,
|
2017-12-16 17:16:24 +08:00
|
|
|
action: "capabilities",
|
2018-03-28 19:22:06 +08:00
|
|
|
}).then((response) => {
|
2020-04-02 03:59:48 +08:00
|
|
|
console.log('Got capabilities for', this.widgetId, response.capabilities);
|
2018-03-28 19:22:06 +08:00
|
|
|
return response.capabilities;
|
|
|
|
});
|
2017-12-16 17:16:24 +08:00
|
|
|
}
|
2017-12-01 22:56:27 +08:00
|
|
|
|
2018-05-11 23:22:54 +08:00
|
|
|
sendVisibility(visible) {
|
|
|
|
return this.messageToWidget({
|
|
|
|
api: OUTBOUND_API_NAME,
|
|
|
|
action: "visibility",
|
|
|
|
visible,
|
|
|
|
})
|
|
|
|
.catch((error) => {
|
|
|
|
console.error("Failed to send visibility: ", error);
|
|
|
|
});
|
|
|
|
}
|
2018-03-28 19:22:06 +08:00
|
|
|
|
|
|
|
start() {
|
|
|
|
this.fromWidget.addEndpoint(this.widgetId, this.widgetUrl);
|
2019-03-24 12:50:26 +08:00
|
|
|
this.fromWidget.addListener("get_openid", this._onOpenIdRequest);
|
2018-03-28 19:22:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
stop() {
|
|
|
|
this.fromWidget.removeEndpoint(this.widgetId, this.widgetUrl);
|
2019-03-24 12:50:26 +08:00
|
|
|
this.fromWidget.removeListener("get_openid", this._onOpenIdRequest);
|
2019-03-13 14:34:34 +08:00
|
|
|
}
|
|
|
|
|
2019-03-16 11:33:31 +08:00
|
|
|
async _onOpenIdRequest(ev, rawEv) {
|
2019-03-13 14:34:34 +08:00
|
|
|
if (ev.widgetId !== this.widgetId) return; // not interesting
|
|
|
|
|
2019-03-24 13:25:31 +08:00
|
|
|
const widgetSecurityKey = WidgetUtils.getWidgetSecurityKey(this.widgetId, this.widgetUrl, this.isUserWidget);
|
|
|
|
|
2019-03-16 11:33:31 +08:00
|
|
|
const settings = SettingsStore.getValue("widgetOpenIDPermissions");
|
2019-03-26 11:14:21 +08:00
|
|
|
if (settings.deny && settings.deny.includes(widgetSecurityKey)) {
|
2019-03-16 11:33:31 +08:00
|
|
|
this.fromWidget.sendResponse(rawEv, {state: "blocked"});
|
|
|
|
return;
|
|
|
|
}
|
2019-03-26 11:14:21 +08:00
|
|
|
if (settings.allow && settings.allow.includes(widgetSecurityKey)) {
|
2019-03-16 11:33:31 +08:00
|
|
|
const responseBody = {state: "allowed"};
|
|
|
|
const credentials = await MatrixClientPeg.get().getOpenIdToken();
|
|
|
|
Object.assign(responseBody, credentials);
|
|
|
|
this.fromWidget.sendResponse(rawEv, responseBody);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-13 14:34:34 +08:00
|
|
|
// Confirm that we received the request
|
|
|
|
this.fromWidget.sendResponse(rawEv, {state: "request"});
|
|
|
|
|
|
|
|
// Actually ask for permission to send the user's data
|
2019-03-16 11:33:31 +08:00
|
|
|
Modal.createTrackedDialog("OpenID widget permissions", '',
|
|
|
|
WidgetOpenIDPermissionsDialog, {
|
|
|
|
widgetUrl: this.widgetUrl,
|
|
|
|
widgetId: this.widgetId,
|
2019-03-24 13:25:31 +08:00
|
|
|
isUserWidget: this.isUserWidget,
|
2019-03-16 11:33:31 +08:00
|
|
|
|
|
|
|
onFinished: async (confirm) => {
|
|
|
|
const responseBody = {success: confirm};
|
|
|
|
if (confirm) {
|
|
|
|
const credentials = await MatrixClientPeg.get().getOpenIdToken();
|
|
|
|
Object.assign(responseBody, credentials);
|
|
|
|
}
|
|
|
|
this.messageToWidget({
|
|
|
|
api: OUTBOUND_API_NAME,
|
|
|
|
action: "openid_credentials",
|
|
|
|
data: responseBody,
|
|
|
|
}).catch((error) => {
|
|
|
|
console.error("Failed to send OpenID credentials: ", error);
|
|
|
|
});
|
2019-03-13 14:34:34 +08:00
|
|
|
},
|
|
|
|
},
|
2019-03-16 11:33:31 +08:00
|
|
|
);
|
2017-11-30 19:30:30 +08:00
|
|
|
}
|
|
|
|
}
|