mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 01:36:06 -04:00
WIP: Redact events after all bans have been applied.
This commit is contained in:
parent
60741214f5
commit
c5b5026d4d
@ -36,6 +36,7 @@ import { IProtection } from "./protections/IProtection";
|
||||
import { PROTECTIONS } from "./protections/protections";
|
||||
import { AutomaticRedactionQueue } from "./queues/AutomaticRedactionQueue";
|
||||
import { Healthz } from "./health/healthz";
|
||||
import { EventRedactionQueue, RedactUserInRoom } from "./queues/EventRedactionQueue";
|
||||
|
||||
export const STATE_NOT_STARTED = "not_started";
|
||||
export const STATE_CHECKING_PERMISSIONS = "checking_permissions";
|
||||
@ -53,7 +54,8 @@ export class Mjolnir {
|
||||
private localpart: string;
|
||||
private currentState: string = STATE_NOT_STARTED;
|
||||
private protections: IProtection[] = [];
|
||||
private redactionQueue = new AutomaticRedactionQueue();
|
||||
private spamRedactionQueue = new AutomaticRedactionQueue();
|
||||
private eventRedactionQueue = new EventRedactionQueue();
|
||||
private automaticRedactionReasons: MatrixGlob[] = [];
|
||||
private protectedJoinedRoomIds: string[] = [];
|
||||
private explicitlyProtectedRoomIds: string[] = [];
|
||||
@ -139,7 +141,7 @@ export class Mjolnir {
|
||||
}
|
||||
|
||||
public get redactionHandler(): AutomaticRedactionQueue {
|
||||
return this.redactionQueue;
|
||||
return this.spamRedactionQueue;
|
||||
}
|
||||
|
||||
public get automaticRedactGlobs(): MatrixGlob[] {
|
||||
@ -493,8 +495,10 @@ export class Mjolnir {
|
||||
|
||||
const aclErrors = await applyServerAcls(this.banLists, Object.keys(this.protectedRooms), this);
|
||||
const banErrors = await applyUserBans(this.banLists, Object.keys(this.protectedRooms), this);
|
||||
const redactionErrors = await this.processRedactionQueue();
|
||||
hadErrors = hadErrors || await this.printActionResult(aclErrors, "Errors updating server ACLs:");
|
||||
hadErrors = hadErrors || await this.printActionResult(banErrors, "Errors updating member bans:");
|
||||
hadErrors = hadErrors || await this.printActionResult(redactionErrors, "Error updating redactions:");
|
||||
|
||||
if (!hadErrors && verbose) {
|
||||
const html = `<font color="#00cc00">Done updating rooms - no errors</font>`;
|
||||
@ -521,8 +525,10 @@ export class Mjolnir {
|
||||
|
||||
const aclErrors = await applyServerAcls(this.banLists, Object.keys(this.protectedRooms), this);
|
||||
const banErrors = await applyUserBans(this.banLists, Object.keys(this.protectedRooms), this);
|
||||
const redactionErrors = await this.processRedactionQueue();
|
||||
hadErrors = hadErrors || await this.printActionResult(aclErrors, "Errors updating server ACLs:");
|
||||
hadErrors = hadErrors || await this.printActionResult(banErrors, "Errors updating member bans:");
|
||||
hadErrors = hadErrors || await this.printActionResult(redactionErrors, "Error updating redactions:");
|
||||
|
||||
if (!hadErrors) {
|
||||
const html = `<font color="#00cc00"><b>Done updating rooms - no errors</b></font>`;
|
||||
@ -575,7 +581,7 @@ export class Mjolnir {
|
||||
|
||||
// Run the event handlers - we always run this after protections so that the protections
|
||||
// can flag the event for redaction.
|
||||
await this.redactionQueue.handleEvent(roomId, event, this.client);
|
||||
await this.spamRedactionQueue.handleEvent(roomId, event, this.client);
|
||||
|
||||
if (event['type'] === 'm.room.power_levels' && event['state_key'] === '') {
|
||||
// power levels were updated - recheck permissions
|
||||
@ -590,6 +596,8 @@ export class Mjolnir {
|
||||
} else if (event['type'] === "m.room.member") {
|
||||
// Only apply bans in the room we're looking at.
|
||||
const errors = await applyUserBans(this.banLists, [roomId], this);
|
||||
// do we need room scoped redaction here? yes...
|
||||
// I want to get an inital review before i check this bit.
|
||||
await this.printActionResult(errors);
|
||||
}
|
||||
}
|
||||
@ -658,4 +666,15 @@ export class Mjolnir {
|
||||
message: message /* If `undefined`, we'll use Synapse's default message. */
|
||||
});
|
||||
}
|
||||
|
||||
// This naming is horrible and clashes with the other redaction queue which isn't
|
||||
// really the same thing. The old one is more about an ongoing user who we haven't
|
||||
// banned, whereas this one is about redaction of users who aren't active.
|
||||
public queueRedactUserMessagesIn(userId: string, roomId: string) {
|
||||
this.eventRedactionQueue.add(new RedactUserInRoom(userId, roomId));
|
||||
}
|
||||
|
||||
public async processRedactionQueue() {
|
||||
return await this.eventRedactionQueue.process(this.client);
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import config from "../config";
|
||||
import { logMessage } from "../LogProxy";
|
||||
import { LogLevel } from "matrix-bot-sdk";
|
||||
import { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "../ErrorCache";
|
||||
import { redactUserMessagesIn } from "../utils";
|
||||
|
||||
/**
|
||||
* Applies the member bans represented by the ban lists to the provided rooms, returning the
|
||||
@ -69,7 +68,7 @@ export async function applyUserBans(lists: BanList[], roomIds: string[], mjolnir
|
||||
if (!config.noop) {
|
||||
await mjolnir.client.banUser(member.userId, roomId, userRule.reason);
|
||||
if (mjolnir.automaticRedactGlobs.find(g => g.test(userRule.reason.toLowerCase()))) {
|
||||
await redactUserMessagesIn(mjolnir.client, member.userId, [roomId]);
|
||||
mjolnir.queueRedactUserMessagesIn(member.userId, roomId);
|
||||
}
|
||||
} else {
|
||||
await logMessage(LogLevel.WARN, "ApplyBan", `Tried to ban ${member.userId} in ${roomId} but Mjolnir is running in no-op mode`, roomId);
|
||||
|
@ -13,7 +13,10 @@ 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.
|
||||
*/
|
||||
|
||||
//// NOTE: This is a queue of users whose events should be redacted
|
||||
////////// Not a queue of events to be redacted.
|
||||
////////// This is also unrelated to the AutomaticRedactionReasons.
|
||||
////////// It is as of writing only used by the flood/spam protections.
|
||||
import { extractRequestError, LogLevel, LogService, MatrixClient, Permalinks } from "matrix-bot-sdk";
|
||||
import { logMessage } from "../LogProxy";
|
||||
import config from "../config";
|
||||
|
97
src/queues/EventRedactionQueue.ts
Normal file
97
src/queues/EventRedactionQueue.ts
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
Copyright 2019-2021 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.
|
||||
*/
|
||||
//// NOTE: This is a queue for events so that other protections can happen first (bans and ACL)
|
||||
|
||||
import { LogLevel, MatrixClient } from "matrix-bot-sdk"
|
||||
import { ERROR_KIND_FATAL } from "../ErrorCache";
|
||||
import { logMessage } from "../LogProxy";
|
||||
import { RoomUpdateError } from "../models/RoomUpdateError";
|
||||
import { redactUserMessagesIn } from "../utils";
|
||||
|
||||
export interface QueuedRedaction {
|
||||
redact(client: MatrixClient): Promise<any>
|
||||
redactionEqual(redaction: QueuedRedaction): boolean
|
||||
report(e): RoomUpdateError
|
||||
}
|
||||
|
||||
export class RedactUserInRoom implements QueuedRedaction {
|
||||
userId: string;
|
||||
roomId: string;
|
||||
|
||||
constructor(userId: string, roomId: string) {
|
||||
this.userId = userId;
|
||||
this.roomId = roomId;
|
||||
}
|
||||
|
||||
public async redact(client: MatrixClient) {
|
||||
await logMessage(LogLevel.DEBUG, "Mjolnir", `Redacting events from ${this.userId} in room ${this.roomId}.`);
|
||||
await redactUserMessagesIn(client, this.userId, [this.roomId]);
|
||||
}
|
||||
|
||||
public redactionEqual(redaction: QueuedRedaction): boolean {
|
||||
if (redaction instanceof RedactUserInRoom) {
|
||||
return redaction.userId === this.userId && redaction.roomId === this.roomId;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public report(e): RoomUpdateError {
|
||||
const message = e.message || (e.body ? e.body.error : '<no message>');
|
||||
return {
|
||||
roomId: this.roomId,
|
||||
errorMessage: message,
|
||||
errorKind: ERROR_KIND_FATAL,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class EventRedactionQueue {
|
||||
private toRedact: Array<QueuedRedaction> = new Array<QueuedRedaction>();
|
||||
|
||||
public has(redaction: QueuedRedaction) {
|
||||
return this.toRedact.find(r => r.redactionEqual(redaction));
|
||||
}
|
||||
|
||||
public add(redaction: QueuedRedaction) {
|
||||
if (this.has(redaction)) {
|
||||
return;
|
||||
} else {
|
||||
this.toRedact.push(redaction);
|
||||
}
|
||||
}
|
||||
|
||||
public delete(redaction: QueuedRedaction) {
|
||||
this.toRedact = this.toRedact.filter(r => r.redactionEqual(redaction));
|
||||
}
|
||||
|
||||
public async process(client: MatrixClient): Promise<RoomUpdateError[]> {
|
||||
const errors: RoomUpdateError[]= [];
|
||||
// need to change this so it pops the array until empty
|
||||
// otherwise this will be cringe.
|
||||
for (const redaction of this.toRedact) {
|
||||
try {
|
||||
await redaction.redact(client);
|
||||
} catch (e) {
|
||||
errors.push(redaction.report(e));
|
||||
} finally {
|
||||
// FIXME: Need to figure out in which circumstances we want to retry.
|
||||
this.delete(redaction);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user