Add very basic subscription server for Synapse module

This commit is contained in:
Travis Ralston 2020-01-25 20:34:15 -07:00
parent 4e4a6e0b56
commit fa6a6547ee
6 changed files with 152 additions and 1 deletions

View File

@ -82,3 +82,14 @@ protectedRooms:
# Note: the management room is *excluded* from this condition. Add it to the
# protected rooms to protect it.
protectAllJoinedRooms: false
# An optional server for the Synapse Mjolnir plugin to connect to. This is
# recommended if you're running the Synapse module in a worker environment,
# particularly if you're running a federation reader.
#
# It is not recommended to expose this to the wider internet. Connections
# are over TCP only.
banListServer:
enabled: false
bind: "0.0.0.0"
port: 24465

View File

@ -1,5 +1,5 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
Copyright 2019, 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.
@ -38,6 +38,11 @@ interface IConfig {
fasterMembershipChecks: boolean;
automaticallyRedactForReasons: string[]; // case-insensitive globs
protectAllJoinedRooms: boolean;
banListServer: {
enabled: boolean;
bind: string;
port: number;
};
/**
* Config options only set at runtime. Try to avoid using the objects

View File

@ -30,6 +30,7 @@ import BanList from "./models/BanList";
import { Mjolnir } from "./Mjolnir";
import { logMessage } from "./LogProxy";
import { MembershipEvent } from "matrix-bot-sdk/lib/models/events/MembershipEvent";
import {BanListServer} from "./server/BanListServer";
config.RUNTIME = {client: null};
@ -94,5 +95,11 @@ LogService.info("index", "Starting bot...");
await logMessage(LogLevel.INFO, "index", "Mjolnir is starting up. Use !mjolnir to query status.");
const bot = new Mjolnir(client, protectedRooms, banLists);
if (config.banListServer && config.banListServer.enabled) {
const server = new BanListServer(bot);
await server.start();
}
await bot.start();
})();

View File

@ -67,6 +67,10 @@ export default class BanList {
return this.rules.filter(r => r.kind === RULE_ROOM);
}
public get allRules(): ListRule[] {
return [...this.serverRules, ...this.userRules, ...this.roomRules];
}
public async updateList() {
this.rules = [];

View File

@ -0,0 +1,46 @@
/*
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 {Mjolnir} from "../Mjolnir";
import * as net from "net";
import {Socket} from "net";
import config from "../config";
import {LogService} from "matrix-bot-sdk";
import {Connection} from "./Connection";
export class BanListServer {
private connections: Connection[] = [];
constructor(private mjolnir: Mjolnir) {
}
public async start() {
LogService.info("BanListServer", `Starting server on ${config.banListServer.bind}:${config.banListServer.port}`);
const server = net.createServer(this.onConnect.bind(this));
server.listen(config.banListServer.port, config.banListServer.bind);
}
private onDisconnect(connection: Connection): void {
const index = this.connections.indexOf(connection);
if (index >= 0) this.connections.splice(index, 1);
}
private onConnect(socket: Socket) {
LogService.info("BanListServer", `New client connection from ${socket.address().toString()}`);
this.connections.push(new Connection(socket, this.mjolnir, this.onDisconnect.bind(this)));
}
}

78
src/server/Connection.ts Normal file
View File

@ -0,0 +1,78 @@
/*
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 {Socket} from "net";
import {Mjolnir} from "../Mjolnir";
import {LogService} from "matrix-bot-sdk";
import BanList from "../models/BanList";
const COMMAND_PREFIX = "|C,";
const COMMAND_SUFFIX = ",C|";
const SUBSCRIBE_PREFIX = `${COMMAND_PREFIX}SUBSCRIBE,`;
const RESET_PREFIX = `${COMMAND_PREFIX}RESET,`;
const RULE_PREFIX = `${COMMAND_PREFIX}RULE,`;
export class Connection {
private currentCommand = "";
private subscribedRooms: string[] = [];
constructor(private socket: Socket, private mjolnir: Mjolnir, private disconnectFn: (Socket) => void) {
socket.on("data", this.onData.bind(this));
socket.on("close", this.onClose.bind(this));
}
private onListUpdate(list: BanList) {
if (!this.subscribedRooms.includes(list.roomId)) {
return; // Ignore list update
}
this.socket.write(`${RESET_PREFIX}${list.roomId}${COMMAND_SUFFIX}`);
for (const rule of list.allRules) {
this.socket.write(`${RULE_PREFIX}${rule.kind},${rule.entity}${COMMAND_SUFFIX}`);
}
}
private onClose() {
this.disconnectFn(this);
}
private onData(b: Buffer) {
this.currentCommand += b.toString("ascii");
// Try and parse the command
const commandStart = this.currentCommand.indexOf(COMMAND_PREFIX);
if (commandStart < 0) return; // No command yet
let command = this.currentCommand.slice(commandStart);
let idx = command.indexOf(COMMAND_SUFFIX);
if (idx < 0) return; // incomplete command
command = command.substring(0, idx);
this.currentCommand = this.currentCommand.substring(commandStart + command.length);
LogService.info("Running " + command);
if (command.startsWith(SUBSCRIBE_PREFIX)) {
const roomId = command.substring(SUBSCRIBE_PREFIX.length, (command.length - COMMAND_SUFFIX.length - 1) + SUBSCRIBE_PREFIX.length);
const banList = this.mjolnir.lists.find(i => i.roomId === roomId);
if (!banList) {
LogService.warn("Connection", `Connection tried to subscribe to unknown ban list ${roomId}`);
} else {
this.subscribedRooms.push(roomId);
this.onListUpdate(banList);
}
} // else unknown command
}
}