mirror of
https://github.com/the-draupnir-project/Draupnir.git
synced 2026-08-28 05:14:04 +00:00
Plumb in SynapseHTTPAntispam to RoomTakedownProtection room discovery.
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
// SPDX-FileCopyrightText: 2025 Gnuxie <Gnuxie@protonmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import {
|
||||
ConstantPeriodItemBatch,
|
||||
isError,
|
||||
Logger,
|
||||
SHA256RoomHashStore,
|
||||
StandardBatcher,
|
||||
} from "matrix-protection-suite";
|
||||
import { CheckEventForSpamRequestBody } from "../../webapis/SynapseHTTPAntispam/CheckEventForSpamEndpoint";
|
||||
import { SynapseHttpAntispam } from "../../webapis/SynapseHTTPAntispam/SynapseHttpAntispam";
|
||||
import { StringRoomID } from "@the-draupnir-project/matrix-basic-types";
|
||||
import { UserMayInviteRequestBody } from "../../webapis/SynapseHTTPAntispam/UserMayInviteEndpoint";
|
||||
import { UserMayJoinRoomRequestBody } from "../../webapis/SynapseHTTPAntispam/UserMayJoinRoomEndpoint";
|
||||
|
||||
const log = new Logger("SynapseHTTPAntispamRoomDiscovery");
|
||||
|
||||
export interface RoomDiscovery {
|
||||
unregisterListeners(): void;
|
||||
}
|
||||
|
||||
export class SynapseHTTPAntispamRoomDiscovery implements RoomDiscovery {
|
||||
private readonly discoveredRooms = new Set<StringRoomID>();
|
||||
private readonly batcher = new StandardBatcher(
|
||||
() =>
|
||||
new ConstantPeriodItemBatch<StringRoomID, void>(
|
||||
this.forwardDiscoveredBatch,
|
||||
{ waitPeriodMS: 2500 }
|
||||
)
|
||||
);
|
||||
constructor(
|
||||
private readonly synapseHTTPAntispam: SynapseHttpAntispam,
|
||||
private readonly hashStore: SHA256RoomHashStore
|
||||
) {
|
||||
synapseHTTPAntispam.checkEventForSpamHandles.registerNonBlockingHandle(
|
||||
this.handleCheckEventForSpam
|
||||
);
|
||||
synapseHTTPAntispam.userMayInviteHandles.registerNonBlockingHandle(
|
||||
this.handleUserMayInvite
|
||||
);
|
||||
synapseHTTPAntispam.userMayJoinRoomHandles.registerNonBlockingHandle(
|
||||
this.handleUserMayJoin
|
||||
);
|
||||
}
|
||||
|
||||
private readonly forwardDiscoveredBatch = async function (
|
||||
this: SynapseHTTPAntispamRoomDiscovery,
|
||||
rawEntries: [StringRoomID][]
|
||||
): Promise<void> {
|
||||
const entries = rawEntries.flat();
|
||||
const storeResult = await this.hashStore.storeUndiscoveredRooms(entries);
|
||||
if (isError(storeResult)) {
|
||||
log.error(
|
||||
"Unxpected error while trying to store undiscovered rooms",
|
||||
storeResult.error
|
||||
);
|
||||
return;
|
||||
}
|
||||
for (const roomID of entries) {
|
||||
this.discoveredRooms.add(roomID);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
private readonly handleCheckEventForSpam = function (
|
||||
this: SynapseHTTPAntispamRoomDiscovery,
|
||||
{ event }: CheckEventForSpamRequestBody
|
||||
): void {
|
||||
if (event.type === "m.room.member") {
|
||||
if (!this.discoveredRooms.has(event.room_id)) {
|
||||
this.batcher.add(event.room_id);
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
private readonly handleUserMayInvite = function (
|
||||
this: SynapseHTTPAntispamRoomDiscovery,
|
||||
{ room_id }: UserMayInviteRequestBody
|
||||
): void {
|
||||
if (!this.discoveredRooms.has(room_id)) {
|
||||
this.batcher.add(room_id);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
private readonly handleUserMayJoin = function (
|
||||
this: SynapseHTTPAntispamRoomDiscovery,
|
||||
{ room }: UserMayJoinRoomRequestBody
|
||||
): void {
|
||||
if (!this.discoveredRooms.has(room)) {
|
||||
this.batcher.add(room);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
unregisterListeners(): void {
|
||||
this.synapseHTTPAntispam.checkEventForSpamHandles.unregisterHandle(
|
||||
this.handleCheckEventForSpam
|
||||
);
|
||||
this.synapseHTTPAntispam.userMayInviteHandles.unregisterHandle(
|
||||
this.handleUserMayInvite
|
||||
);
|
||||
this.synapseHTTPAntispam.userMayJoinRoomHandles.unregisterHandle(
|
||||
this.handleUserMayJoin
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
AbstractProtection,
|
||||
ActionResult,
|
||||
describeProtection,
|
||||
Logger,
|
||||
Ok,
|
||||
PolicyListRevision,
|
||||
PolicyRuleChange,
|
||||
@@ -21,12 +22,13 @@ import { Draupnir } from "../../Draupnir";
|
||||
import { StandardRoomTakedown } from "./RoomTakedown";
|
||||
import { RoomAuditLog } from "./RoomAuditLog";
|
||||
import { SynapseAdminRoomTakedownCapability } from "../../capabilities/SynapseAdminRoomTakedown/SynapseAdminRoomTakedown";
|
||||
import { ResultError } from "@gnuxie/typescript-result";
|
||||
import {
|
||||
RoomDiscovery,
|
||||
SynapseHTTPAntispamRoomDiscovery,
|
||||
} from "./RoomDiscovery";
|
||||
|
||||
// FIXME: We still haven't figured out how to poll for new rooms via the
|
||||
// Synapse admin API.
|
||||
|
||||
// FIXME: We need to add the stores to draupnir somehow.
|
||||
// probably from the toplevel.
|
||||
const log = new Logger("RoomTakedownProtection");
|
||||
|
||||
type RoomTakedownProtectionCapabilities = {
|
||||
roomTakedownCapability: RoomTakedownCapability;
|
||||
@@ -48,7 +50,8 @@ export class RoomTakedownProtection
|
||||
capabilities: RoomTakedownProtectionCapabilities,
|
||||
protectedRoomsSet: ProtectedRoomsSet,
|
||||
hashStore: SHA256RoomHashStore,
|
||||
auditLog: RoomAuditLog
|
||||
auditLog: RoomAuditLog,
|
||||
private readonly roomDiscovery: RoomDiscovery | undefined
|
||||
) {
|
||||
super(description, capabilities, protectedRoomsSet, {});
|
||||
this.roomTakedown = new StandardRoomTakedown(
|
||||
@@ -69,6 +72,10 @@ export class RoomTakedownProtection
|
||||
): Promise<ActionResult<void>> {
|
||||
return this.roomTakedown.handlePolicyChange(revision, changes);
|
||||
}
|
||||
|
||||
handleProtectionDisable(): void {
|
||||
this.roomDiscovery?.unregisterListeners();
|
||||
}
|
||||
}
|
||||
|
||||
describeProtection<RoomTakedownProtectionCapabilities, Draupnir>({
|
||||
@@ -81,13 +88,35 @@ describeProtection<RoomTakedownProtectionCapabilities, Draupnir>({
|
||||
roomTakedownCapability: SynapseAdminRoomTakedownCapability.name,
|
||||
},
|
||||
factory(description, protectedRoomsSet, draupnir, capabilitySet, _settings) {
|
||||
if (
|
||||
draupnir.stores.hashStore === undefined ||
|
||||
draupnir.stores.roomAuditLog === undefined
|
||||
) {
|
||||
return ResultError.Result(
|
||||
"This protection requires a hash store and audit log to be available to draupnir, and they are not in your configuration."
|
||||
);
|
||||
}
|
||||
const roomDiscovery = (() => {
|
||||
if (draupnir.synapseHTTPAntispam !== undefined) {
|
||||
return new SynapseHTTPAntispamRoomDiscovery(
|
||||
draupnir.synapseHTTPAntispam,
|
||||
draupnir.stores.hashStore
|
||||
);
|
||||
} else {
|
||||
log.warn(
|
||||
"synapseHTTPAntispam is not configured for this draupnir, and will not be used for room discovery"
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
return Ok(
|
||||
new RoomTakedownProtection(
|
||||
description,
|
||||
capabilitySet,
|
||||
protectedRoomsSet,
|
||||
draupnir.hashStore,
|
||||
draupnir.auditLog
|
||||
draupnir.stores.hashStore,
|
||||
draupnir.stores.roomAuditLog,
|
||||
roomDiscovery
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user