Replace the concept of a Widget Security Key with an OIDC state
The security key naming/practice was misguided, so let's call it what it is (a settings key) and abstract away the complexity to a new store. Fixes https://github.com/vector-im/element-web/issues/15820 while we're here.
This commit is contained in:
parent
c91dc55bc1
commit
5da27aed94
5 changed files with 119 additions and 50 deletions
|
@ -246,7 +246,7 @@ export class StopGapWidget extends EventEmitter {
|
|||
public start(iframe: HTMLIFrameElement) {
|
||||
if (this.started) return;
|
||||
const allowedCapabilities = this.appTileProps.whitelistCapabilities || [];
|
||||
const driver = new StopGapWidgetDriver( allowedCapabilities, this.mockWidget, this.kind);
|
||||
const driver = new StopGapWidgetDriver(allowedCapabilities, this.mockWidget, this.kind, this.roomId);
|
||||
this.messaging = new ClientWidgetApi(this.mockWidget, iframe, driver);
|
||||
this.messaging.on("preparing", () => this.emit("preparing"));
|
||||
this.messaging.on("ready", () => this.emit("ready"));
|
||||
|
|
|
@ -30,13 +30,12 @@ import { iterableDiff, iterableUnion } from "../../utils/iterables";
|
|||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||
import ActiveRoomObserver from "../../ActiveRoomObserver";
|
||||
import Modal from "../../Modal";
|
||||
import WidgetUtils from "../../utils/WidgetUtils";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import WidgetOpenIDPermissionsDialog from "../../components/views/dialogs/WidgetOpenIDPermissionsDialog";
|
||||
import WidgetCapabilitiesPromptDialog, {
|
||||
getRememberedCapabilitiesForWidget,
|
||||
} from "../../components/views/dialogs/WidgetCapabilitiesPromptDialog";
|
||||
import { WidgetPermissionCustomisations } from "../../customisations/WidgetPermissions";
|
||||
import { OIDCState, WidgetPermissionStore } from "./WidgetPermissionStore";
|
||||
|
||||
// TODO: Purge this from the universe
|
||||
|
||||
|
@ -44,7 +43,12 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
private allowedCapabilities: Set<Capability>;
|
||||
|
||||
// TODO: Refactor widgetKind into the Widget class
|
||||
constructor(allowedCapabilities: Capability[], private forWidget: Widget, private forWidgetKind: WidgetKind) {
|
||||
constructor(
|
||||
allowedCapabilities: Capability[],
|
||||
private forWidget: Widget,
|
||||
private forWidgetKind: WidgetKind,
|
||||
private inRoomId?: string,
|
||||
) {
|
||||
super();
|
||||
|
||||
// Always allow screenshots to be taken because it's a client-induced flow. The widget can't
|
||||
|
@ -114,26 +118,27 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
public async askOpenID(observer: SimpleObservable<IOpenIDUpdate>) {
|
||||
const isUserWidget = this.forWidgetKind !== WidgetKind.Room; // modal and account widgets are "user" widgets
|
||||
const rawUrl = this.forWidget.templateUrl;
|
||||
const widgetSecurityKey = WidgetUtils.getWidgetSecurityKey(this.forWidget.id, rawUrl, isUserWidget);
|
||||
const oidcState = WidgetPermissionStore.instance.getOIDCState(
|
||||
this.forWidget, this.forWidgetKind, this.inRoomId,
|
||||
);
|
||||
|
||||
const getToken = (): Promise<IOpenIDCredentials> => {
|
||||
return MatrixClientPeg.get().getOpenIdToken();
|
||||
};
|
||||
|
||||
const settings = SettingsStore.getValue("widgetOpenIDPermissions");
|
||||
if (settings?.deny?.includes(widgetSecurityKey)) {
|
||||
if (oidcState === OIDCState.Denied) {
|
||||
return observer.update({state: OpenIDRequestState.Blocked});
|
||||
}
|
||||
if (settings?.allow?.includes(widgetSecurityKey)) {
|
||||
if (oidcState === OIDCState.Allowed) {
|
||||
return observer.update({state: OpenIDRequestState.Allowed, token: await getToken()});
|
||||
}
|
||||
|
||||
observer.update({state: OpenIDRequestState.PendingUserConfirmation});
|
||||
|
||||
Modal.createTrackedDialog("OpenID widget permissions", '', WidgetOpenIDPermissionsDialog, {
|
||||
widgetUrl: rawUrl,
|
||||
widgetId: this.forWidget.id,
|
||||
isUserWidget: isUserWidget,
|
||||
widget: this.forWidget,
|
||||
widgetKind: this.forWidgetKind,
|
||||
inRoomId: this.inRoomId,
|
||||
|
||||
onFinished: async (confirm) => {
|
||||
if (!confirm) {
|
||||
|
|
93
src/stores/widgets/WidgetPermissionStore.ts
Normal file
93
src/stores/widgets/WidgetPermissionStore.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 { AsyncStore } from "../AsyncStore";
|
||||
import { ActionPayload } from "../../dispatcher/payloads";
|
||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
|
||||
import { IWidget, Widget, WidgetKind } from "matrix-widget-api";
|
||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||
import WidgetUtils from "../../utils/WidgetUtils";
|
||||
import { SettingLevel } from "../../settings/SettingLevel";
|
||||
|
||||
export enum OIDCState {
|
||||
Allowed, // user has set the remembered value as allowed
|
||||
Denied, // user has set the remembered value as disallowed
|
||||
Unknown, // user has not set a remembered value
|
||||
}
|
||||
|
||||
export class WidgetPermissionStore {
|
||||
private static internalInstance: WidgetPermissionStore;
|
||||
|
||||
private constructor() {
|
||||
}
|
||||
|
||||
public static get instance(): WidgetPermissionStore {
|
||||
if (!WidgetPermissionStore.internalInstance) {
|
||||
WidgetPermissionStore.internalInstance = new WidgetPermissionStore();
|
||||
}
|
||||
return WidgetPermissionStore.internalInstance;
|
||||
}
|
||||
|
||||
// TODO (all functions here): Merge widgetKind with the widget definition
|
||||
|
||||
private packSettingKey(widget: Widget, kind: WidgetKind, roomId?: string): string {
|
||||
let location = roomId;
|
||||
if (kind !== WidgetKind.Room) {
|
||||
location = MatrixClientPeg.get().getUserId();
|
||||
}
|
||||
if (kind === WidgetKind.Modal) {
|
||||
location = '*MODAL*-' + location; // to guarantee differentiation from whatever spawned it
|
||||
}
|
||||
if (!location) {
|
||||
throw new Error("Failed to determine a location to check the widget's OIDC state with");
|
||||
}
|
||||
|
||||
return encodeURIComponent(`${location}::${widget.templateUrl}`);
|
||||
}
|
||||
|
||||
public getOIDCState(widget: Widget, kind: WidgetKind, roomId?: string): OIDCState {
|
||||
const settingsKey = this.packSettingKey(widget, kind, roomId);
|
||||
const settings = SettingsStore.getValue("widgetOpenIDPermissions");
|
||||
if (settings?.deny?.includes(settingsKey)) {
|
||||
return OIDCState.Denied;
|
||||
}
|
||||
if (settings?.allow?.includes(settingsKey)) {
|
||||
return OIDCState.Allowed;
|
||||
}
|
||||
return OIDCState.Unknown;
|
||||
}
|
||||
|
||||
public setOIDCState(widget: Widget, kind: WidgetKind, roomId: string, newState: OIDCState) {
|
||||
const settingsKey = this.packSettingKey(widget, kind, roomId);
|
||||
|
||||
const currentValues = SettingsStore.getValue("widgetOpenIDPermissions");
|
||||
if (!currentValues.allow) currentValues.allow = [];
|
||||
if (!currentValues.deny) currentValues.deny = [];
|
||||
|
||||
if (newState === OIDCState.Allowed) {
|
||||
currentValues.allow.push(settingsKey);
|
||||
} else if (newState === OIDCState.Denied) {
|
||||
currentValues.deny.push(settingsKey);
|
||||
} else {
|
||||
currentValues.allow = currentValues.allow.filter(c => c !== settingsKey);
|
||||
currentValues.deny = currentValues.deny.filter(c => c !== settingsKey);
|
||||
}
|
||||
|
||||
SettingsStore.setValue("widgetOpenIDPermissions", null, SettingLevel.DEVICE, currentValues);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue