diff --git a/res/css/_components.scss b/res/css/_components.scss
index 4fb0eed4af..f29e30dcb4 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -70,6 +70,7 @@
@import "./views/dialogs/_ShareDialog.scss";
@import "./views/dialogs/_UnknownDeviceDialog.scss";
@import "./views/dialogs/_UserSettingsDialog.scss";
+@import "./views/dialogs/_WidgetOpenIDPermissionsDialog.scss";
@import "./views/dialogs/keybackup/_CreateKeyBackupDialog.scss";
@import "./views/dialogs/keybackup/_KeyBackupFailedDialog.scss";
@import "./views/dialogs/keybackup/_RestoreKeyBackupDialog.scss";
diff --git a/res/css/views/dialogs/_WidgetOpenIDPermissionsDialog.scss b/res/css/views/dialogs/_WidgetOpenIDPermissionsDialog.scss
new file mode 100644
index 0000000000..a419c105a9
--- /dev/null
+++ b/res/css/views/dialogs/_WidgetOpenIDPermissionsDialog.scss
@@ -0,0 +1,28 @@
+/*
+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.
+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.
+*/
+
+.mx_WidgetOpenIDPermissionsDialog .mx_SettingsFlag {
+ .mx_ToggleSwitch {
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: 8px;
+ }
+
+ .mx_SettingsFlag_label {
+ display: inline-block;
+ vertical-align: middle;
+ }
+}
diff --git a/src/WidgetMessaging.js b/src/WidgetMessaging.js
index 17ce9360b7..dba703ffb8 100644
--- a/src/WidgetMessaging.js
+++ b/src/WidgetMessaging.js
@@ -26,6 +26,8 @@ import Modal from "./Modal";
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
import {_t} from "./languageHandler";
import MatrixClientPeg from "./MatrixClientPeg";
+import SettingsStore from "./settings/SettingsStore";
+import WidgetOpenIDPermissionsDialog from "./components/views/dialogs/WidgetOpenIDPermissionsDialog";
if (!global.mxFromWidgetMessaging) {
global.mxFromWidgetMessaging = new FromWidgetPostMessageApi();
@@ -123,40 +125,46 @@ export default class WidgetMessaging {
this.fromWidget.removeListener("get_openid", this._openIdHandlerRef);
}
- _onOpenIdRequest(ev, rawEv) {
+ async _onOpenIdRequest(ev, rawEv) {
if (ev.widgetId !== this.widgetId) return; // not interesting
+ const settings = SettingsStore.getValue("widgetOpenIDPermissions");
+ if (settings.blacklist && settings.blacklist.includes(this.widgetId)) {
+ this.fromWidget.sendResponse(rawEv, {state: "blocked"});
+ return;
+ }
+ if (settings.whitelist && settings.whitelist.includes(this.widgetId)) {
+ const responseBody = {state: "allowed"};
+ const credentials = await MatrixClientPeg.get().getOpenIdToken();
+ Object.assign(responseBody, credentials);
+ this.fromWidget.sendResponse(rawEv, responseBody);
+ return;
+ }
+
// Confirm that we received the request
this.fromWidget.sendResponse(rawEv, {state: "request"});
- // TODO: Support blacklisting widgets
- // TODO: Support whitelisting widgets
-
// Actually ask for permission to send the user's data
- Modal.createTrackedDialog("OpenID widget permissions", '', QuestionDialog, {
- title: _t("A widget would like to verify your identity"),
- description: _t(
- "A widget located at %(widgetUrl)s would like to verify your identity. " +
- "By allowing this, the widget will be able to verify your user ID, but not " +
- "perform actions as you.", {
- widgetUrl: this.widgetUrl,
+ Modal.createTrackedDialog("OpenID widget permissions", '',
+ WidgetOpenIDPermissionsDialog, {
+ widgetUrl: this.widgetUrl,
+ widgetId: this.widgetId,
+
+ 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);
+ });
},
- ),
- button: _t("Allow"),
- 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);
- });
},
- });
+ );
}
}
diff --git a/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js b/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js
new file mode 100644
index 0000000000..bec71e49a3
--- /dev/null
+++ b/src/components/views/dialogs/WidgetOpenIDPermissionsDialog.js
@@ -0,0 +1,106 @@
+/*
+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.
+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 React from 'react';
+import PropTypes from 'prop-types';
+import {Tab, TabbedView} from "../../structures/TabbedView";
+import {_t, _td} from "../../../languageHandler";
+import GeneralUserSettingsTab from "../settings/tabs/user/GeneralUserSettingsTab";
+import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
+import LabsUserSettingsTab from "../settings/tabs/user/LabsUserSettingsTab";
+import SecurityUserSettingsTab from "../settings/tabs/user/SecurityUserSettingsTab";
+import NotificationUserSettingsTab from "../settings/tabs/user/NotificationUserSettingsTab";
+import PreferencesUserSettingsTab from "../settings/tabs/user/PreferencesUserSettingsTab";
+import VoiceUserSettingsTab from "../settings/tabs/user/VoiceUserSettingsTab";
+import HelpUserSettingsTab from "../settings/tabs/user/HelpUserSettingsTab";
+import FlairUserSettingsTab from "../settings/tabs/user/FlairUserSettingsTab";
+import sdk from "../../../index";
+import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
+
+export default class WidgetOpenIDPermissionsDialog extends React.Component {
+ static propTypes = {
+ onFinished: PropTypes.func.isRequired,
+ widgetUrl: PropTypes.string.isRequired,
+ widgetId: PropTypes.string.isRequired,
+ };
+
+ constructor() {
+ super();
+
+ this.state = {
+ rememberSelection: false,
+ };
+ }
+
+ _onAllow = () => {
+ this._onPermissionSelection(true);
+ };
+
+ _onDeny = () => {
+ this._onPermissionSelection(false);
+ };
+
+ _onPermissionSelection(allowed) {
+ if (this.state.rememberSelection) {
+ console.log(`Remembering ${this.props.widgetId} as allowed=${allowed} for OpenID`);
+
+ const currentValues = SettingsStore.getValue("widgetOpenIDPermissions");
+ if (!currentValues.whitelist) currentValues.whitelist = [];
+ if (!currentValues.blacklist) currentValues.blacklist = [];
+
+ (allowed ? currentValues.whitelist : currentValues.blacklist).push(this.props.widgetId);
+ SettingsStore.setValue("widgetOpenIDPermissions", null, SettingLevel.DEVICE, currentValues);
+ }
+
+ this.props.onFinished(allowed);
+ }
+
+ _onRememberSelectionChange = (newVal) => {
+ this.setState({rememberSelection: newVal});
+ };
+
+ render() {
+ const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
+ const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
+
+ return (
+
+ {_t(
+ "A widget located at %(widgetUrl)s would like to verify your identity. " +
+ "By allowing this, the widget will be able to verify your user ID, but not " +
+ "perform actions as you.", {
+ widgetUrl: this.props.widgetUrl,
+ },
+ )}
+