diff --git a/src/Mjolnir.ts b/src/Mjolnir.ts index 91525f23..71264791 100644 --- a/src/Mjolnir.ts +++ b/src/Mjolnir.ts @@ -27,7 +27,7 @@ import { TextualMessageEventContent } from "matrix-bot-sdk"; -import { ALL_RULE_TYPES as ALL_BAN_LIST_RULE_TYPES, RULE_ROOM, RULE_SERVER, RULE_USER } from "./models/ListRule"; +import { ALL_RULE_TYPES as ALL_BAN_LIST_RULE_TYPES, EntityType, RULE_ROOM, RULE_SERVER, RULE_USER } from "./models/PolicyRule"; import { applyServerAcls } from "./actions/ApplyAcl"; import { RoomUpdateError } from "./models/RoomUpdateError"; import { COMMAND_PREFIX, handleCommand } from "./commands/CommandHandler"; @@ -50,7 +50,7 @@ import RuleServer from "./models/RuleServer"; import { RoomMemberManager } from "./RoomMembers"; import { ProtectedRoomActivityTracker } from "./queues/ProtectedRoomActivityTracker"; import { ThrottlingQueue } from "./queues/ThrottlingQueue"; -import PolicyList, { ListRuleChange } from "./models/PolicyList"; +import PolicyList, { PolicyRuleChange } from "./models/PolicyList"; const levelToFn = { [LogLevel.DEBUG.toString()]: LogService.debug, @@ -355,7 +355,7 @@ export class Mjolnir { this.currentState = STATE_SYNCING; if (config.syncOnStartup) { await this.logMessage(LogLevel.INFO, "Mjolnir@startup", "Syncing lists..."); - await this.syncLists(config.verboseLogging); + await this.syncPolicyLists(config.verboseLogging); await this.registerProtections(); } @@ -434,7 +434,7 @@ export class Mjolnir { const rooms = (additionalProtectedRooms?.rooms ?? []); rooms.push(roomId); await this.client.setAccountData(PROTECTED_ROOMS_EVENT_TYPE, { rooms: rooms }); - await this.syncLists(config.verboseLogging); + await this.syncPolicyLists(config.verboseLogging); } public async removeProtectedRoom(roomId: string) { @@ -482,7 +482,7 @@ export class Mjolnir { this.applyUnprotectedRooms(); if (withSync) { - await this.syncLists(config.verboseLogging); + await this.syncPolicyLists(config.verboseLogging); } } @@ -882,7 +882,7 @@ export class Mjolnir { * Sync all the rooms with all the watched lists, banning and applying any changed ACLS. * @param verbose Whether to report any errors to the management room. */ - public async syncLists(verbose = true) { + public async syncPolicyLists(verbose = true) { for (const list of this.policyLists) { const changes = await list.updateList(); await this.printBanlistChanges(changes, list, true); @@ -1052,7 +1052,7 @@ export class Mjolnir { * @param ignoreSelf Whether to exclude changes that have been made by Mjolnir. * @returns true if the message was sent, false if it wasn't (because there there were no changes to report). */ - private async printBanlistChanges(changes: ListRuleChange[], list: PolicyList, ignoreSelf = false): Promise { + private async printBanlistChanges(changes: PolicyRuleChange[], list: PolicyList, ignoreSelf = false): Promise { if (ignoreSelf) { const sender = await this.client.getUserId(); changes = changes.filter(change => change.sender !== sender); @@ -1194,4 +1194,25 @@ export class Mjolnir { await protection.handleReport(this, roomId, reporterId, event, reason); } } + + /** + * Return the composite opinion for an entity. + * + * In the current implementation, we return the first opinion found in the list + * of policies. Future versions will support additional mechanisms for composing + * opinions. + * + * @param entity + * @param type + * @returns The opinion or null if no list defines an opinion on this entity. + */ + public opinionForEntity(entity: string, type: EntityType): number | null { + for (let policyList of this.policyLists) { + let opinion = policyList.opinionForEntity(entity, type); + if (opinion !== null) { + return opinion; + } + } + return null; + } } diff --git a/src/actions/ApplyAcl.ts b/src/actions/ApplyAcl.ts index 44767ce0..84192548 100644 --- a/src/actions/ApplyAcl.ts +++ b/src/actions/ApplyAcl.ts @@ -21,6 +21,7 @@ import { Mjolnir } from "../Mjolnir"; import config from "../config"; import { LogLevel, UserID } from "matrix-bot-sdk"; import { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "../ErrorCache"; +import { Recommendation } from "../models/PolicyRule"; /** * Applies the server ACLs represented by the ban lists to the provided rooms, returning the @@ -36,7 +37,7 @@ export async function applyServerAcls(lists: PolicyList[], roomIds: string[], mj // Construct a server ACL first const acl = new ServerAcl(serverName).denyIpAddresses().allowServer("*"); for (const list of lists) { - for (const rule of list.serverRules) { + for (const rule of list.getServerRules(Recommendation.Ban)) { acl.denyServer(rule.entity); } } diff --git a/src/actions/ApplyBan.ts b/src/actions/ApplyBan.ts index c76d651a..fa7226e7 100644 --- a/src/actions/ApplyBan.ts +++ b/src/actions/ApplyBan.ts @@ -20,6 +20,7 @@ import { Mjolnir } from "../Mjolnir"; import config from "../config"; import { LogLevel } from "matrix-bot-sdk"; import { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "../ErrorCache"; +import { Recommendation } from "../models/PolicyRule"; /** * Applies the member bans represented by the ban lists to the provided rooms, returning the @@ -57,7 +58,7 @@ export async function applyUserBans(lists: PolicyList[], roomIds: string[], mjol let banned = false; for (const list of lists) { - for (const userRule of list.userRules) { + for (const userRule of list.getUserRules(Recommendation.Ban)) { if (userRule.isMatch(member.userId)) { // User needs to be banned diff --git a/src/commands/DumpRulesCommand.ts b/src/commands/DumpRulesCommand.ts index ca92e791..5f79ca66 100644 --- a/src/commands/DumpRulesCommand.ts +++ b/src/commands/DumpRulesCommand.ts @@ -16,7 +16,7 @@ limitations under the License. import { RichReply } from "matrix-bot-sdk"; import { Mjolnir } from "../Mjolnir"; -import { EntityType } from "../models/ListRule"; +import { EntityType } from "../models/PolicyRule"; import { htmlEscape } from "../utils"; /** @@ -33,7 +33,7 @@ export async function execRulesMatchingCommand(roomId: string, event: any, mjoln let html = ""; let text = ""; for (const list of mjolnir.lists) { - const matches = list.rulesMatchingEntity(entity) + const matches = list.rulesMatchingEntity(entity, "*"); if (matches.length === 0) { continue; @@ -90,19 +90,19 @@ export async function execDumpRulesCommand(roomId: string, event: any, mjolnir: html += `${list.roomId}${shortcodeInfo}: