mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 01:36:06 -04:00
Update server ACLs when they change
This commit is contained in:
parent
23c6c20768
commit
d32ad18f3a
48
src/actions/ApplyAcl.ts
Normal file
48
src/actions/ApplyAcl.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2019 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 BanList from "../models/BanList";
|
||||
import { MatrixClient } from "matrix-bot-sdk";
|
||||
import { ServerAcl } from "../models/ServerAcl";
|
||||
import { RoomUpdateError } from "../models/RoomUpdateError";
|
||||
|
||||
/**
|
||||
* Applies the server ACLs represented by the ban lists to the provided rooms, returning the
|
||||
* room IDs that could not be updated and their error.
|
||||
* @param {BanList[]} lists The lists to construct ACLs from.
|
||||
* @param {string[]} roomIds The room IDs to apply the ACLs in.
|
||||
* @param {MatrixClient} client The Matrix client to apply the ACLs with.
|
||||
*/
|
||||
export async function applyServerAcls(lists: BanList[], roomIds: string[], client: MatrixClient): Promise<RoomUpdateError[]> {
|
||||
// Construct a server ACL first
|
||||
const acl = new ServerAcl().denyIpAddresses().allowServer("*");
|
||||
for (const list of lists) {
|
||||
for (const rule of list.serverRules) {
|
||||
acl.denyServer(rule.entity);
|
||||
}
|
||||
}
|
||||
|
||||
const errors: RoomUpdateError[] = [];
|
||||
for (const roomId of roomIds) {
|
||||
try {
|
||||
await client.sendStateEvent(roomId, "m.room.server_acl", "", acl.safeAclContent());
|
||||
} catch (e) {
|
||||
errors.push({roomId, errorMessage: e.message || (e.body ? e.body.error : '<no message>')});
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
39
src/index.ts
39
src/index.ts
@ -24,7 +24,9 @@ import {
|
||||
SimpleFsStorageProvider
|
||||
} from "matrix-bot-sdk";
|
||||
import config from "./config";
|
||||
import BanList, { ALL_RULE_TYPES } from "./BanList";
|
||||
import BanList, { ALL_RULE_TYPES } from "./models/BanList";
|
||||
import { applyServerAcls } from "./actions/ApplyAcl";
|
||||
import { RoomUpdateError } from "./models/RoomUpdateError";
|
||||
|
||||
LogService.setLogger(new RichConsoleLogger());
|
||||
|
||||
@ -44,8 +46,10 @@ client.on("room.event", async (roomId, event) => {
|
||||
for (const list of lists) {
|
||||
if (list.roomId !== roomId) continue;
|
||||
await list.updateList();
|
||||
// TODO: Re-apply ACLs as needed
|
||||
}
|
||||
|
||||
const errors = await applyServerAcls(lists, await client.getJoinedRooms(), client);
|
||||
return printActionResult(errors);
|
||||
} else if (event['type'] === "m.room.member") {
|
||||
// TODO: Check membership against ban lists
|
||||
}
|
||||
@ -106,8 +110,9 @@ async function printStatus(roomId: string) {
|
||||
html += "<b>Subscribed lists:</b><br><ul>";
|
||||
text += "Subscribed lists:\n";
|
||||
for (const list of lists) {
|
||||
html += `<li><a href="${list.roomRef}">${list.roomId}</a> (${list.rules.length} rules)</li>`;
|
||||
text += `${list.roomRef} (${list.rules.length} rules)\n`;
|
||||
const ruleInfo = `rules: ${list.serverRules.length} servers, ${list.userRules.length} users, ${list.roomRules.length} rooms`;
|
||||
html += `<li><a href="${list.roomRef}">${list.roomId}</a> (${ruleInfo})</li>`;
|
||||
text += `${list.roomRef} (${ruleInfo})\n`;
|
||||
}
|
||||
html += "</ul>";
|
||||
|
||||
@ -119,3 +124,29 @@ async function printStatus(roomId: string) {
|
||||
};
|
||||
return client.sendMessage(roomId, message);
|
||||
}
|
||||
|
||||
async function printActionResult(errors: RoomUpdateError[]) {
|
||||
let html = "";
|
||||
let text = "";
|
||||
|
||||
if (errors.length > 0) {
|
||||
html += `<font color="#ff0000"><b>${errors.length} errors updating protected rooms!</b></font><br /><ul>`;
|
||||
text += `${errors.length} errors updating protected rooms!\n`;
|
||||
for (const error of errors) {
|
||||
html += `<li><a href="https://matrix.to/#/${error.roomId}">${error.roomId}</a> - ${error.errorMessage}</li>`;
|
||||
text += `${error.roomId} - ${error.errorMessage}\n`;
|
||||
}
|
||||
html += "</ul>";
|
||||
} else {
|
||||
html += `<font color="#00ff00"><b>Updated all protected rooms with new rules successfully.</b></font>`;
|
||||
text += "Updated all protected rooms with new rules successfully";
|
||||
}
|
||||
|
||||
const message = {
|
||||
msgtype: "m.notice",
|
||||
body: text,
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: html,
|
||||
};
|
||||
return client.sendMessage(managementRoomId, message);
|
||||
}
|
||||
|
@ -27,11 +27,23 @@ export const SERVER_RULE_TYPES = [RULE_SERVER, "org.matrix.mjolnir.rule.server"]
|
||||
export const ALL_RULE_TYPES = [...USER_RULE_TYPES, ...ROOM_RULE_TYPES, ...SERVER_RULE_TYPES];
|
||||
|
||||
export default class BanList {
|
||||
public rules: ListRule[] = [];
|
||||
private rules: ListRule[] = [];
|
||||
|
||||
constructor(public readonly roomId: string, public readonly roomRef, private client: MatrixClient) {
|
||||
}
|
||||
|
||||
public get serverRules(): ListRule[] {
|
||||
return this.rules.filter(r => r.kind === RULE_SERVER);
|
||||
}
|
||||
|
||||
public get userRules(): ListRule[] {
|
||||
return this.rules.filter(r => r.kind === RULE_USER);
|
||||
}
|
||||
|
||||
public get roomRules(): ListRule[] {
|
||||
return this.rules.filter(r => r.kind === RULE_ROOM);
|
||||
}
|
||||
|
||||
public async updateList() {
|
||||
this.rules = [];
|
||||
|
||||
@ -41,6 +53,17 @@ export default class BanList {
|
||||
continue;
|
||||
}
|
||||
|
||||
let kind: string = null;
|
||||
if (USER_RULE_TYPES.includes(event['type'])) {
|
||||
kind = RULE_USER;
|
||||
} else if (ROOM_RULE_TYPES.includes(event['type'])) {
|
||||
kind = RULE_ROOM;
|
||||
} else if (SERVER_RULE_TYPES.includes(event['type'])) {
|
||||
kind = RULE_SERVER;
|
||||
} else {
|
||||
continue; // invalid/unknown
|
||||
}
|
||||
|
||||
// It's a rule - parse it
|
||||
const content = event['content'];
|
||||
if (!content) continue;
|
||||
@ -53,7 +76,7 @@ export default class BanList {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.rules.push(new ListRule(entity, recommendation, reason));
|
||||
this.rules.push(new ListRule(entity, recommendation, reason, kind));
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export class ListRule {
|
||||
|
||||
private glob: MatrixGlob;
|
||||
|
||||
constructor(public readonly entity: string, private action: string, public readonly reason: string) {
|
||||
constructor(public readonly entity: string, private action: string, public readonly reason: string, public readonly kind: string) {
|
||||
this.glob = new MatrixGlob(entity);
|
||||
}
|
||||
|
20
src/models/RoomUpdateError.ts
Normal file
20
src/models/RoomUpdateError.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 2019 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.
|
||||
*/
|
||||
|
||||
export interface RoomUpdateError {
|
||||
roomId: string;
|
||||
errorMessage: string;
|
||||
}
|
79
src/models/ServerAcl.ts
Normal file
79
src/models/ServerAcl.ts
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright 2019 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 { setToArray } from "../utils";
|
||||
|
||||
export interface ServerAclContent {
|
||||
allow: string[];
|
||||
deny: string[];
|
||||
allow_ip_literals: boolean;
|
||||
}
|
||||
|
||||
export class ServerAcl {
|
||||
private allowedServers: Set<string> = new Set<string>();
|
||||
private deniedServers: Set<string> = new Set<string>();
|
||||
private allowIps = false;
|
||||
|
||||
public allowIpAddresses(): ServerAcl {
|
||||
this.allowIps = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public denyIpAddresses(): ServerAcl {
|
||||
this.allowIps = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public allowServer(glob: string): ServerAcl {
|
||||
this.allowedServers.add(glob);
|
||||
return this;
|
||||
}
|
||||
|
||||
public setAllowedServers(globs: string[]): ServerAcl {
|
||||
this.allowedServers = new Set<string>(globs);
|
||||
return this;
|
||||
}
|
||||
|
||||
public denyServer(glob: string): ServerAcl {
|
||||
this.deniedServers.add(glob);
|
||||
return this;
|
||||
}
|
||||
|
||||
public setDeniedServers(globs: string[]): ServerAcl {
|
||||
this.deniedServers = new Set<string>(globs);
|
||||
return this;
|
||||
}
|
||||
|
||||
public literalAclContent(): ServerAclContent {
|
||||
return {
|
||||
allow: setToArray(this.allowedServers),
|
||||
deny: setToArray(this.deniedServers),
|
||||
allow_ip_literals: this.allowIps,
|
||||
};
|
||||
}
|
||||
|
||||
public safeAclContent(): ServerAclContent {
|
||||
const allowed = setToArray(this.allowedServers);
|
||||
if (!allowed || allowed.length === 0) {
|
||||
allowed.push("*"); // allow everything
|
||||
}
|
||||
return {
|
||||
allow: allowed,
|
||||
deny: setToArray(this.deniedServers),
|
||||
allow_ip_literals: this.allowIps,
|
||||
};
|
||||
}
|
||||
}
|
23
src/utils.ts
Normal file
23
src/utils.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
Copyright 2019 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.
|
||||
*/
|
||||
|
||||
export function setToArray<T>(set: Set<T>): T[] {
|
||||
const arr: T[] = [];
|
||||
for (const v of set) {
|
||||
arr.push(v);
|
||||
}
|
||||
return arr;
|
||||
}
|
Loading…
Reference in New Issue
Block a user