Merge pull request #3630 from matrix-org/travis/widget-permission

Wire up the widget permission prompt to the cross-platform setting
This commit is contained in:
Travis Ralston 2019-11-20 10:37:51 -07:00 committed by GitHub
commit 1c1b8cf6b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 21 deletions

View file

@ -34,7 +34,7 @@ import dis from '../../../dispatcher';
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore'; import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
import classNames from 'classnames'; import classNames from 'classnames';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:']; const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
const ENABLE_REACT_PERF = false; const ENABLE_REACT_PERF = false;
@ -69,8 +69,11 @@ export default class AppTile extends React.Component {
* @return {Object} Updated component state to be set with setState * @return {Object} Updated component state to be set with setState
*/ */
_getNewState(newProps) { _getNewState(newProps) {
const widgetPermissionId = [newProps.room.roomId, encodeURIComponent(newProps.url)].join('_'); // This is a function to make the impact of calling SettingsStore slightly less
const hasPermissionToLoad = localStorage.getItem(widgetPermissionId); const hasPermissionToLoad = () => {
const currentlyAllowedWidgets = SettingsStore.getValue("allowedWidgets", newProps.room.roomId);
return !!currentlyAllowedWidgets[newProps.eventId];
};
const PersistedElement = sdk.getComponent("elements.PersistedElement"); const PersistedElement = sdk.getComponent("elements.PersistedElement");
return { return {
@ -78,10 +81,9 @@ export default class AppTile extends React.Component {
// True while the iframe content is loading // True while the iframe content is loading
loading: this.props.waitForIframeLoad && !PersistedElement.isMounted(this._persistKey), loading: this.props.waitForIframeLoad && !PersistedElement.isMounted(this._persistKey),
widgetUrl: this._addWurlParams(newProps.url), widgetUrl: this._addWurlParams(newProps.url),
widgetPermissionId: widgetPermissionId,
// Assume that widget has permission to load if we are the user who // Assume that widget has permission to load if we are the user who
// added it to the room, or if explicitly granted by the user // added it to the room, or if explicitly granted by the user
hasPermissionToLoad: hasPermissionToLoad === 'true' || newProps.userId === newProps.creatorUserId, hasPermissionToLoad: newProps.userId === newProps.creatorUserId || hasPermissionToLoad(),
error: null, error: null,
deleting: false, deleting: false,
widgetPageTitle: newProps.widgetPageTitle, widgetPageTitle: newProps.widgetPageTitle,
@ -446,24 +448,38 @@ export default class AppTile extends React.Component {
}); });
} }
/* TODO -- Store permission in account data so that it is persisted across multiple devices */
_grantWidgetPermission() { _grantWidgetPermission() {
console.warn('Granting permission to load widget - ', this.state.widgetUrl); const roomId = this.props.room.roomId;
localStorage.setItem(this.state.widgetPermissionId, true); console.info("Granting permission for widget to load: " + this.props.eventId);
const current = SettingsStore.getValue("allowedWidgets", roomId);
current[this.props.eventId] = true;
SettingsStore.setValue("allowedWidgets", roomId, SettingLevel.ROOM_ACCOUNT, current).then(() => {
this.setState({hasPermissionToLoad: true}); this.setState({hasPermissionToLoad: true});
// Now that we have permission, fetch the IM token
// Fetch a token for the integration manager, now that we're allowed to
this.setScalarToken(); this.setScalarToken();
}).catch(err => {
console.error(err);
// We don't really need to do anything about this - the user will just hit the button again.
});
} }
_revokeWidgetPermission() { _revokeWidgetPermission() {
console.warn('Revoking permission to load widget - ', this.state.widgetUrl); const roomId = this.props.room.roomId;
localStorage.removeItem(this.state.widgetPermissionId); console.info("Revoking permission for widget to load: " + this.props.eventId);
const current = SettingsStore.getValue("allowedWidgets", roomId);
current[this.props.eventId] = false;
SettingsStore.setValue("allowedWidgets", roomId, SettingLevel.ROOM_ACCOUNT, current).then(() => {
this.setState({hasPermissionToLoad: false}); this.setState({hasPermissionToLoad: false});
// Force the widget to be non-persistent // Force the widget to be non-persistent (able to be deleted/forgotten)
ActiveWidgetStore.destroyPersistentWidget(this.props.id); ActiveWidgetStore.destroyPersistentWidget(this.props.id);
const PersistedElement = sdk.getComponent("elements.PersistedElement"); const PersistedElement = sdk.getComponent("elements.PersistedElement");
PersistedElement.destroyElement(this._persistKey); PersistedElement.destroyElement(this._persistKey);
}).catch(err => {
console.error(err);
// We don't really need to do anything about this - the user will just hit the button again.
});
} }
formatAppTileName() { formatAppTileName() {
@ -720,6 +736,7 @@ AppTile.displayName ='AppTile';
AppTile.propTypes = { AppTile.propTypes = {
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
eventId: PropTypes.string, // required for room widgets
url: PropTypes.string.isRequired, url: PropTypes.string.isRequired,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
room: PropTypes.object.isRequired, room: PropTypes.object.isRequired,

View file

@ -67,13 +67,15 @@ module.exports = createReactClass({
return ev.getStateKey() === ActiveWidgetStore.getPersistentWidgetId(); return ev.getStateKey() === ActiveWidgetStore.getPersistentWidgetId();
}); });
const app = WidgetUtils.makeAppConfig( const app = WidgetUtils.makeAppConfig(
appEvent.getStateKey(), appEvent.getContent(), appEvent.getSender(), persistentWidgetInRoomId, appEvent.getStateKey(), appEvent.getContent(), appEvent.getSender(),
persistentWidgetInRoomId, appEvent.getId(),
); );
const capWhitelist = WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, persistentWidgetInRoomId); const capWhitelist = WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, persistentWidgetInRoomId);
const AppTile = sdk.getComponent('elements.AppTile'); const AppTile = sdk.getComponent('elements.AppTile');
return <AppTile return <AppTile
key={app.id} key={app.id}
id={app.id} id={app.id}
eventId={app.eventId}
url={app.url} url={app.url}
name={app.name} name={app.name}
type={app.type} type={app.type}

View file

@ -107,7 +107,9 @@ module.exports = createReactClass({
this.props.room.roomId, WidgetUtils.getRoomWidgets(this.props.room), this.props.room.roomId, WidgetUtils.getRoomWidgets(this.props.room),
); );
return widgets.map((ev) => { return widgets.map((ev) => {
return WidgetUtils.makeAppConfig(ev.getStateKey(), ev.getContent(), ev.getSender()); return WidgetUtils.makeAppConfig(
ev.getStateKey(), ev.getContent(), ev.getSender(), ev.getRoomId(), ev.getId(),
);
}); });
}, },
@ -159,6 +161,7 @@ module.exports = createReactClass({
return (<AppTile return (<AppTile
key={app.id} key={app.id}
id={app.id} id={app.id}
eventId={app.eventId}
url={app.url} url={app.url}
name={app.name} name={app.name}
type={app.type} type={app.type}

View file

@ -400,7 +400,7 @@ export default class WidgetUtils {
return client.setAccountData('m.widgets', userWidgets); return client.setAccountData('m.widgets', userWidgets);
} }
static makeAppConfig(appId, app, senderUserId, roomId) { static makeAppConfig(appId, app, senderUserId, roomId, eventId) {
const myUserId = MatrixClientPeg.get().credentials.userId; const myUserId = MatrixClientPeg.get().credentials.userId;
const user = MatrixClientPeg.get().getUser(myUserId); const user = MatrixClientPeg.get().getUser(myUserId);
const params = { const params = {
@ -419,6 +419,7 @@ export default class WidgetUtils {
app.creatorUserId = senderUserId; app.creatorUserId = senderUserId;
app.id = appId; app.id = appId;
app.eventId = eventId;
app.name = app.name || app.type; app.name = app.name || app.type;
if (app.data) { if (app.data) {