From 074a16bfaf3cd254070f32b73cf76188739e2fa9 Mon Sep 17 00:00:00 2001 From: gnuxie Date: Fri, 21 Oct 2022 17:21:06 +0100 Subject: [PATCH] limited access control still needs hooking up when mjolnir restarts, and when the access control changes --- src/appservice/AccessControl.ts | 57 +++++++++++++++++++++++++++++++++ src/appservice/AppService.ts | 14 +++++--- 2 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 src/appservice/AccessControl.ts diff --git a/src/appservice/AccessControl.ts b/src/appservice/AccessControl.ts new file mode 100644 index 00000000..806bf1c8 --- /dev/null +++ b/src/appservice/AccessControl.ts @@ -0,0 +1,57 @@ +/* +Copyright 2022 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 { Bridge } from "matrix-appservice-bridge"; +import AccessControlUnit, { EntityAccess } from "../models/AccessControlUnit"; +import PolicyList from "../models/PolicyList"; +import { Permalinks } from "matrix-bot-sdk"; + +// We need to refactor AccessControlUnit so you can have +// previousAccess and currentAccess listener for changes. +// wait that only works for literals not globs... +// i guess when the rule change is a glob we have to scan everything. +export class AccessControl { + + private constructor( + private readonly accessControlList: PolicyList, + private readonly accessControlUnit: AccessControlUnit + ) { + } + + public static async setupAccessControl( + accessControlListId: string, + bridge: Bridge, + ): Promise { + const accessControlList = new PolicyList( + accessControlListId, + Permalinks.forRoom(accessControlListId), + bridge.getBot().getClient() + ); + const accessControlUnit = new AccessControlUnit([accessControlList]); + await accessControlList.updateList(); + return new AccessControl(accessControlList, accessControlUnit); + } + + public handleEvent(roomId: string, event: any) { + if (roomId === this.accessControlList.roomId) { + this.accessControlList.updateForEvent(event); + } + } + + public getUserAccess(mxid: string): EntityAccess { + return this.accessControlUnit.getAccessForUser(mxid, "CHECK_SERVER"); + } +} diff --git a/src/appservice/AppService.ts b/src/appservice/AppService.ts index df74dc36..a7940ee4 100644 --- a/src/appservice/AppService.ts +++ b/src/appservice/AppService.ts @@ -22,7 +22,8 @@ import { MjolnirManager } from ".//MjolnirManager"; import { DataStore, PgDataStore } from ".//datastore"; import { Api } from "./Api"; import { IConfig } from "./config/config"; -import { Mjolnir } from "../Mjolnir"; +import { AccessControl } from "./AccessControl"; +import { Access } from "../models/AccessControlUnit"; export class MjolnirAppService { @@ -31,7 +32,7 @@ export class MjolnirAppService { private readonly bridge: Bridge, private readonly dataStore: DataStore, private readonly mjolnirManager: MjolnirManager, - //private readonly accessControl: AccessControl, + private readonly accessControl: AccessControl, ) { new Api(config.homeserver.url, this).start(config.webAPI.port); } @@ -51,11 +52,13 @@ export class MjolnirAppService { suppressEcho: false, }); const mjolnirManager = new MjolnirManager(); + const accessControlListId = await bridge.getBot().getClient().resolveRoom(config.accessControlList); const appService = new MjolnirAppService( config, bridge, dataStore, - mjolnirManager + mjolnirManager, + await AccessControl.setupAccessControl(accessControlListId, bridge) ); bridge.opts.controller = { onUserQuery: appService.onUserQuery.bind(appService), @@ -88,7 +91,10 @@ export class MjolnirAppService { } public async provisionNewMjolnir(requestingUserId: string): Promise<[string, string]> { - // FIXME: we need to restrict who can do it (special list? ban remote users?) + const access = this.accessControl.getUserAccess(requestingUserId); + if (access.outcome !== Access.Allowed) { + throw new Error(`${requestingUserId} tried to provision a mjolnir when they do not have access ${access.outcome} ${access.rule?.reason ?? 'no reason specified'}`); + } const provisionedMjolnirs = await this.dataStore.lookupByOwner(requestingUserId); if (provisionedMjolnirs.length === 0) { const mjolnirLocalPart = `mjolnir_${randomUUID()}`;