Wire up the widget permission prompt to the cross-platform setting

This doesn't have any backwards compatibility with anyone who has already clicked "Allow". We kinda want everyone to read the new prompt, so what better way to do it than effectively revoke all widget permissions?

Part of https://github.com/vector-im/riot-web/issues/11262
pull/21833/head
Travis Ralston 2019-11-18 17:56:33 -07:00
parent fea8737632
commit b185eed462
4 changed files with 41 additions and 21 deletions

View File

@ -34,7 +34,7 @@ import dis from '../../../dispatcher';
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
import classNames from 'classnames';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import SettingsStore from "../../../settings/SettingsStore";
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
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
*/
_getNewState(newProps) {
const widgetPermissionId = [newProps.room.roomId, encodeURIComponent(newProps.url)].join('_');
const hasPermissionToLoad = localStorage.getItem(widgetPermissionId);
// This is a function to make the impact of calling SettingsStore slightly less
const hasPermissionToLoad = () => {
const currentlyAllowedWidgets = SettingsStore.getValue("allowedWidgets", newProps.room.roomId);
return !!currentlyAllowedWidgets[newProps.eventId];
};
const PersistedElement = sdk.getComponent("elements.PersistedElement");
return {
@ -78,10 +81,9 @@ export default class AppTile extends React.Component {
// True while the iframe content is loading
loading: this.props.waitForIframeLoad && !PersistedElement.isMounted(this._persistKey),
widgetUrl: this._addWurlParams(newProps.url),
widgetPermissionId: widgetPermissionId,
// 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
hasPermissionToLoad: hasPermissionToLoad === 'true' || newProps.userId === newProps.creatorUserId,
hasPermissionToLoad: newProps.userId === newProps.creatorUserId || hasPermissionToLoad(),
error: null,
deleting: false,
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() {
console.warn('Granting permission to load widget - ', this.state.widgetUrl);
localStorage.setItem(this.state.widgetPermissionId, true);
this.setState({hasPermissionToLoad: true});
// Now that we have permission, fetch the IM token
this.setScalarToken();
const roomId = this.props.room.roomId;
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});
// Fetch a token for the integration manager, now that we're allowed to
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() {
console.warn('Revoking permission to load widget - ', this.state.widgetUrl);
localStorage.removeItem(this.state.widgetPermissionId);
this.setState({hasPermissionToLoad: false});
const roomId = this.props.room.roomId;
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});
// Force the widget to be non-persistent
ActiveWidgetStore.destroyPersistentWidget(this.props.id);
const PersistedElement = sdk.getComponent("elements.PersistedElement");
PersistedElement.destroyElement(this._persistKey);
// Force the widget to be non-persistent (able to be deleted/forgotten)
ActiveWidgetStore.destroyPersistentWidget(this.props.id);
const PersistedElement = sdk.getComponent("elements.PersistedElement");
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() {
@ -720,6 +736,7 @@ AppTile.displayName ='AppTile';
AppTile.propTypes = {
id: PropTypes.string.isRequired,
eventId: PropTypes.string, // required for room widgets
url: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
room: PropTypes.object.isRequired,

View File

@ -67,13 +67,14 @@ module.exports = createReactClass({
return ev.getStateKey() === ActiveWidgetStore.getPersistentWidgetId();
});
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 AppTile = sdk.getComponent('elements.AppTile');
return <AppTile
key={app.id}
id={app.id}
eventId={app.eventId}
url={app.url}
name={app.name}
type={app.type}

View File

@ -107,7 +107,7 @@ module.exports = createReactClass({
this.props.room.roomId, WidgetUtils.getRoomWidgets(this.props.room),
);
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 +159,7 @@ module.exports = createReactClass({
return (<AppTile
key={app.id}
id={app.id}
eventId={app.eventId}
url={app.url}
name={app.name}
type={app.type}

View File

@ -400,7 +400,7 @@ export default class WidgetUtils {
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 user = MatrixClientPeg.get().getUser(myUserId);
const params = {
@ -419,6 +419,7 @@ export default class WidgetUtils {
app.creatorUserId = senderUserId;
app.id = appId;
app.eventId = eventId;
app.name = app.name || app.type;
if (app.data) {