A look at PolicyList.update (#454)

This started out as just a way to find out why mjolnir was syncing with lists several times for each update to a policy list.

The main changes are

- Verbosity was irrelevant to the sync command but for some reason was an option.
  Unfortunately all this did was suppress whether to tell you when it had finished, meaning it wouldn't
  when verbose logging was disabled. Historically this was probably a parameter that got passed through
  to applyServerAcl/applyUserBans, which can be horribly verbose, but they access the config directly.

- Stop emitting `'PolicyList.update'` when there are no changes.
- Include a revision ID for the `'PolicyList.update'`method and event.
- Use the revision ID in the `ProtectedRoomsSet` so that we don't unnecessarily resynchronize all rooms when the `'PolicyList.update'` event is received. Though not when the `sync` command is used. Since this is supposed to `sync` in the case when there is a state reset or otherwise or the user has changed some room settings.
- insert an await lock around the `PolicyList.update` method to avoid a race condition where a call can be started and finished within the extent of an existing call (via another task, this can happen if the server is slow with handling one request). `PolicyList.udpate` now has a helper that is synchronous to be called directly after requesting the room state. The reason for this is to enforce that no one `await`s while updating the policy list's cache of rules. Which is important because it is one of the biggest methods that I tolerate and visually checking for `await` is impossible.
- The revision ID uses a ULID, but this is unnecessary and could have just been a "dumb counter".

closes https://github.com/matrix-org/mjolnir/issues/447
This commit is contained in:
Gnuxie 2022-12-08 16:09:55 +00:00 committed by GitHub
parent 1d3da94f38
commit 433ff7eadd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 147 additions and 69 deletions

View File

@ -59,6 +59,7 @@
"parse-duration": "^1.0.2",
"pg": "^8.8.0",
"shell-quote": "^1.7.3",
"ulidx": "^0.3.0",
"yaml": "^2.1.1"
},
"engines": {

View File

@ -302,7 +302,7 @@ export class Mjolnir {
if (this.config.verifyPermissionsOnStartup) {
await this.managementRoomOutput.logMessage(LogLevel.INFO, "Mjolnir@startup", "Checking permissions...");
await this.protectedRoomsTracker.verifyPermissions(this.config.verboseLogging);
await this.protectedRoomsTracker.verifyPermissions();
}
// Start the bot.
@ -311,7 +311,7 @@ export class Mjolnir {
this.currentState = STATE_SYNCING;
if (this.config.syncOnStartup) {
await this.managementRoomOutput.logMessage(LogLevel.INFO, "Mjolnir@startup", "Syncing lists...");
await this.protectedRoomsTracker.syncLists(this.config.verboseLogging);
await this.protectedRoomsTracker.syncLists();
}
this.currentState = STATE_RUNNING;
@ -426,7 +426,7 @@ export class Mjolnir {
}
if (withSync) {
await this.protectedRoomsTracker.syncLists(this.config.verboseLogging);
await this.protectedRoomsTracker.syncLists();
}
}

View File

@ -21,7 +21,7 @@ import ManagementRoomOutput from "./ManagementRoomOutput";
import { MatrixSendClient } from "./MatrixEmitter";
import AccessControlUnit, { Access } from "./models/AccessControlUnit";
import { RULE_ROOM, RULE_SERVER, RULE_USER } from "./models/ListRule";
import PolicyList, { ListRuleChange } from "./models/PolicyList";
import PolicyList, { ListRuleChange, Revision } from "./models/PolicyList";
import { RoomUpdateError } from "./models/RoomUpdateError";
import { ProtectionManager } from "./protections/ProtectionManager";
import { EventRedactionQueue, RedactUserInRoom } from "./queues/EventRedactionQueue";
@ -92,7 +92,12 @@ export class ProtectedRoomsSet {
* Intended to be `this.syncWithUpdatedPolicyList` so we can add it in `this.watchList` and remove it in `this.unwatchList`.
* Otherwise we would risk being informed about lists we no longer watch.
*/
private readonly listUpdateListener: (list: PolicyList, changes: ListRuleChange[]) => void;
private readonly listUpdateListener: (list: PolicyList, changes: ListRuleChange[], revision: Revision) => void;
/**
* The revision of a each watched list that we have applied to protected rooms.
*/
private readonly listRevisions = new Map<PolicyList, /** The last revision we used to sync protected rooms. */ Revision>();
constructor(
private readonly client: MatrixSendClient,
@ -210,15 +215,9 @@ export class ProtectedRoomsSet {
}
/**
* 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.
* Synchronize all the protected rooms with all of the policies described in the watched policy lists.
*/
public async syncLists(verbose = true) {
for (const list of this.policyLists) {
const changes = await list.updateList();
await this.printBanlistChanges(changes, list);
}
private async syncRoomsWithPolicies() {
let hadErrors = false;
const [aclErrors, banErrors] = await Promise.all([
this.applyServerAcls(this.policyLists, this.protectedRoomsByActivity()),
@ -229,7 +228,7 @@ export class ProtectedRoomsSet {
hadErrors = hadErrors || await this.printActionResult(banErrors, "Errors updating member bans:");
hadErrors = hadErrors || await this.printActionResult(redactionErrors, "Error updating redactions:");
if (!hadErrors && verbose) {
if (!hadErrors) {
const html = `<font color="#00cc00">Done updating rooms - no errors</font>`;
const text = "Done updating rooms - no errors";
await this.client.sendMessage(this.managementRoomId, {
@ -241,6 +240,22 @@ export class ProtectedRoomsSet {
}
}
/**
* Update each watched list and then synchronize all the protected rooms with all the policies described in the watched lists,
* banning and applying any changed ACLS via `syncRoomsWithPolicies`.
*/
public async syncLists() {
for (const list of this.policyLists) {
const { revision } = await list.updateList();
const previousRevision = this.listRevisions.get(list);
if (previousRevision === undefined || revision.supersedes(previousRevision)) {
this.listRevisions.set(list, revision);
// we rely on `this.listUpdateListener` to print the changes to the list.
}
}
await this.syncRoomsWithPolicies();
}
public addProtectedRoom(roomId: string): void {
if (this.protectedRooms.has(roomId)) {
// we need to protect ourselves form syncing all the lists unnecessarily
@ -263,28 +278,15 @@ export class ProtectedRoomsSet {
* @param policyList The `PolicyList` which we will check for changes and apply them to all protected rooms.
* @returns When all of the protected rooms have been updated.
*/
private async syncWithUpdatedPolicyList(policyList: PolicyList, changes: ListRuleChange[]): Promise<void> {
let hadErrors = false;
const [aclErrors, banErrors] = await Promise.all([
this.applyServerAcls(this.policyLists, this.protectedRoomsByActivity()),
this.applyUserBans(this.protectedRoomsByActivity())
]);
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>`;
const text = "Done updating rooms - no errors";
await this.client.sendMessage(this.managementRoomId, {
msgtype: "m.notice",
body: text,
format: "org.matrix.custom.html",
formatted_body: html,
});
private async syncWithUpdatedPolicyList(policyList: PolicyList, changes: ListRuleChange[], revision: Revision): Promise<void> {
// avoid resyncing the rooms if we have already done so for the latest revision of this list.
const previousRevision = this.listRevisions.get(policyList);
if (previousRevision === undefined || revision.supersedes(previousRevision)) {
this.listRevisions.set(policyList, revision);
await this.syncRoomsWithPolicies();
}
// This can fail if the change is very large and it is much less important than applying bans, so do it last.
// We always print changes because we make this listener responsible for doing it.
await this.printBanlistChanges(changes, policyList);
}

View File

@ -18,5 +18,5 @@ import { Mjolnir } from "../Mjolnir";
// !mjolnir sync
export async function execSyncCommand(roomId: string, event: any, mjolnir: Mjolnir) {
return mjolnir.protectedRoomsTracker.syncLists(mjolnir.config.verboseLogging);
return mjolnir.protectedRoomsTracker.syncLists();
}

View File

@ -155,7 +155,7 @@ export async function execUnbanCommand(roomId: string, event: any, mjolnir: Mjol
if (unbannedSomeone) {
await mjolnir.managementRoomOutput.logMessage(LogLevel.DEBUG, "UnbanBanCommand", `Syncing lists to ensure no users were accidentally unbanned`);
await mjolnir.protectedRoomsTracker.syncLists(mjolnir.config.verboseLogging);
await mjolnir.protectedRoomsTracker.syncLists();
}
};

View File

@ -18,6 +18,8 @@ import { extractRequestError, LogService, RoomCreateOptions, UserID } from "matr
import { EventEmitter } from "events";
import { ALL_RULE_TYPES, EntityType, ListRule, Recommendation, ROOM_RULE_TYPES, RULE_ROOM, RULE_SERVER, RULE_USER, SERVER_RULE_TYPES, USER_RULE_TYPES } from "./ListRule";
import { MatrixSendClient } from "../MatrixEmitter";
import AwaitLock from "await-lock";
import { monotonicFactory } from "ulidx";
export const SHORTCODE_EVENT_TYPE = "org.matrix.mjolnir.shortcode";
@ -54,8 +56,8 @@ export interface ListRuleChange {
declare interface PolicyList {
// PolicyList.update is emitted when the PolicyList has pulled new rules from Matrix and informs listeners of any changes.
on(event: 'PolicyList.update', listener: (list: PolicyList, changes: ListRuleChange[]) => void): this
emit(event: 'PolicyList.update', list: PolicyList, changes: ListRuleChange[]): boolean
on(event: 'PolicyList.update', listener: (list: PolicyList, changes: ListRuleChange[], revision: Revision) => void): this
emit(event: 'PolicyList.update', list: PolicyList, changes: ListRuleChange[], revision: Revision): boolean
}
/**
@ -96,6 +98,18 @@ class PolicyList extends EventEmitter {
*/
private static readonly EVENT_RULE_ANNOTATION_KEY = 'org.matrix.mjolnir.annotation.rule';
/**
* An ID that represents the current version of the list state.
* Each time we use `updateList` we create a new revision to represent the change of state.
* Listeners can then use the revision to work out whether they have already applied
* the latest revision.
*/
private revisionId = new Revision();
/**
* A lock to protect `updateList` from a situation where one call to `getRoomState` can start and end before another.
*/
private readonly updateListLock = new AwaitLock();
/**
* Construct a PolicyList, does not synchronize with the room.
* @param roomId The id of the policy room, i.e. a room containing MSC2313 policies.
@ -346,10 +360,24 @@ class PolicyList extends EventEmitter {
* and updating the model to reflect the room.
* @returns A description of any rules that were added, modified or removed from the list as a result of this update.
*/
public async updateList(): Promise<ListRuleChange[]> {
let changes: ListRuleChange[] = [];
public async updateList(): Promise<ReturnType<PolicyList["updateListWithState"]>> {
await this.updateListLock.acquireAsync();
try {
const state = await this.client.getRoomState(this.roomId);
return this.updateListWithState(state);
} finally {
this.updateListLock.release();
}
}
/**
* Same as `updateList` but without async to make sure that no one uses await within the body.
* The reason no one should use await is to avoid a horrible race should `updateList` be called more than once.
* @param state Room state to update the list with, provided by `updateList`
* @returns Any changes that have been made to the PolicyList.
*/
private updateListWithState(state: any): { revision: Revision, changes: ListRuleChange[] } {
const changes: ListRuleChange[] = [];
for (const event of state) {
if (event['state_key'] === '' && event['type'] === SHORTCODE_EVENT_TYPE) {
this.shortcode = (event['content'] || {})['shortcode'] || null;
@ -445,7 +473,10 @@ class PolicyList extends EventEmitter {
changes.push({ rule, changeType, event, sender: event.sender, ...previousState ? { previousState } : {} });
}
}
this.emit('PolicyList.update', this, changes);
if (changes.length > 0) {
this.revisionId = new Revision();
this.emit('PolicyList.update', this, changes, this.revisionId);
}
if (this.batchedEvents.keys.length !== 0) {
// The only reason why this isn't a TypeError is because we need to know about this when it happens, because it means
// we're probably doing something wrong, on the other hand, if someone messes with a server implementation and
@ -453,7 +484,7 @@ class PolicyList extends EventEmitter {
// we don't want Mjolnir to stop working properly. Though, I am not confident a burried warning is going to alert us.
LogService.warn("PolicyList", "The policy list is being informed about events that it cannot find in the room state, this is really bad and you should seek help.");
}
return changes;
return { revision: this.revisionId, changes };
}
/**
@ -533,3 +564,35 @@ class UpdateBatcher {
this.checkBatch(eventId);
}
}
/**
* Represents a specific version of the state contained in `PolicyList`.
* These are unique and can be compared with `supersedes`.
* We use a ULID to work out whether a revision supersedes another.
*/
export class Revision {
/**
* Ensures that ULIDs are monotonic.
*/
private static makeULID = monotonicFactory();
/**
* Is only public for the comparison method,
* I feel like I'm missing something here and it is possible without
*/
public readonly ulid = Revision.makeULID();
constructor() {
// nothing to do.
}
/**
* Check whether this revision supersedes another revision.
* @param revision The revision we want to check this supersedes.
* @returns True if this Revision supersedes the other revision.
*/
public supersedes(revision: Revision): boolean {
return this.ulid > revision.ulid;
}
}

View File

@ -1,7 +1,7 @@
import { strict as assert } from "assert";
import { newTestUser } from "./clientHelper";
import { LogService, MatrixClient, Permalinks, UserID } from "matrix-bot-sdk";
import PolicyList, { ChangeType, ListRuleChange } from "../../src/models/PolicyList";
import PolicyList, { ChangeType } from "../../src/models/PolicyList";
import { ServerAcl } from "../../src/models/ServerAcl";
import { getFirstReaction } from "./commands/commandUtils";
import { getMessagesByUserIn } from "../../src/utils";
@ -55,7 +55,7 @@ describe("Test: Updating the PolicyList", function() {
// Test adding a new rule
await createPolicyRule(mjolnir.client, banListId, RULE_USER, '@added:localhost:9999', '');
let changes: ListRuleChange[] = await banList.updateList();
let { changes } = await banList.updateList();
assert.equal(changes.length, 1, 'There should only be one change');
assert.equal(changes[0].changeType, ChangeType.Added);
assert.equal(changes[0].sender, await mjolnir.client.getUserId());
@ -66,13 +66,13 @@ describe("Test: Updating the PolicyList", function() {
let originalEventId = await createPolicyRule(mjolnir.client, banListId, RULE_USER, '@modified:localhost:9999', '');
await banList.updateList();
let modifyingEventId = await createPolicyRule(mjolnir.client, banListId, RULE_USER, '@modified:localhost:9999', 'modified reason');
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Modified);
assert.equal(changes[0].previousState['event_id'], originalEventId, 'There should be a previous state event for a modified rule');
assert.equal(changes[0].event['event_id'], modifyingEventId);
let modifyingAgainEventId = await createPolicyRule(mjolnir.client, banListId, RULE_USER, '@modified:localhost:9999', 'modified again');
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Modified);
assert.equal(changes[0].previousState['event_id'], modifyingEventId, 'There should be a previous state event for a modified rule');
@ -84,7 +84,7 @@ describe("Test: Updating the PolicyList", function() {
await banList.updateList();
assert.equal(banList.userRules.filter(r => r.entity === '@redacted:localhost:9999').length, 1);
await mjolnir.client.redactEvent(banListId, redactThis);
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Removed);
assert.equal(changes[0].event['event_id'], redactThis, 'Should show the new version of the event with redacted content');
@ -99,7 +99,7 @@ describe("Test: Updating the PolicyList", function() {
await banList.updateList();
assert.equal(banList.userRules.filter(r => r.entity === softRedactedEntity).length, 1);
await mjolnir.client.sendStateEvent(banListId, RULE_USER, `rule:${softRedactedEntity}`, {});
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Removed);
assert.equal(Object.keys(changes[0].event['content']).length, 0, 'Should show the new version of the event with redacted content');
@ -109,25 +109,25 @@ describe("Test: Updating the PolicyList", function() {
// Now test a double soft redaction just to make sure stuff doesn't explode
await mjolnir.client.sendStateEvent(banListId, RULE_USER, `rule:${softRedactedEntity}`, {});
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 0, "It shouldn't detect a double soft redaction as a change, it should be seen as adding an invalid rule.");
assert.equal(banList.userRules.filter(r => r.entity === softRedactedEntity).length, 0, 'The rule should have been removed');
// Test that different (old) rule types will be modelled as the latest event type.
originalEventId = await createPolicyRule(mjolnir.client, banListId, 'org.matrix.mjolnir.rule.user', '@old:localhost:9999', '');
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Added);
assert.equal(banList.userRules.filter(r => r.entity === '@old:localhost:9999').length, 1);
modifyingEventId = await createPolicyRule(mjolnir.client, banListId, 'm.room.rule.user', '@old:localhost:9999', 'modified reason');
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Modified);
assert.equal(changes[0].event['event_id'], modifyingEventId);
assert.equal(changes[0].previousState['event_id'], originalEventId, 'There should be a previous state event for a modified rule');
assert.equal(banList.userRules.filter(r => r.entity === '@old:localhost:9999').length, 1);
modifyingAgainEventId = await createPolicyRule(mjolnir.client, banListId, RULE_USER, '@old:localhost:9999', 'changes again');
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Modified);
assert.equal(changes[0].event['event_id'], modifyingAgainEventId);
@ -144,12 +144,12 @@ describe("Test: Updating the PolicyList", function() {
const entity = '@old:localhost:9999';
let originalEventId = await createPolicyRule(mjolnir.client, banListId, 'm.room.rule.user', entity, '');
let changes = await banList.updateList();
let { changes } = await banList.updateList();
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Added);
assert.equal(banList.userRules.filter(rule => rule.entity === entity).length, 1, 'There should be a rule stored that we just added...')
let softRedactingEventId = await mjolnir.client.sendStateEvent(banListId, RULE_USER, `rule:${entity}`, {});
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Removed);
assert.equal(changes[0].event['event_id'], softRedactingEventId);
@ -165,12 +165,12 @@ describe("Test: Updating the PolicyList", function() {
const entity = '@old:localhost:9999';
let originalEventId = await createPolicyRule(mjolnir.client, banListId, 'm.room.rule.user', entity, '');
let changes = await banList.updateList();
let { changes } = await banList.updateList();
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Added);
assert.equal(banList.userRules.filter(rule => rule.entity === entity).length, 1, 'There should be a rule stored that we just added...')
let updatedEventId = await createPolicyRule(mjolnir.client, banListId, RULE_USER, entity, '');
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
// If in the future you change this and it fails, it's really subjective whether this constitutes a modification, since the only thing that has changed
// is the rule type. The actual content is identical.
assert.equal(changes.length, 1);
@ -181,13 +181,13 @@ describe("Test: Updating the PolicyList", function() {
// Now we delete the old version of the rule without consequence.
await mjolnir.client.sendStateEvent(banListId, 'm.room.rule.user', `rule:${entity}`, {});
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 0);
assert.equal(banList.userRules.filter(rule => rule.entity === entity).length, 1, 'The rule should still be active.');
// And we can still delete the new version of the rule.
let softRedactingEventId = await mjolnir.client.sendStateEvent(banListId, RULE_USER, `rule:${entity}`, {});
changes = await banList.updateList();
changes = (await banList.updateList()).changes;
assert.equal(changes.length, 1);
assert.equal(changes[0].changeType, ChangeType.Removed);
assert.equal(changes[0].event['event_id'], softRedactingEventId);
@ -201,7 +201,7 @@ describe("Test: Updating the PolicyList", function() {
for (let i = 0; i < ALL_RULE_TYPES.length; i++) {
await createPolicyRule(mjolnir.client, banListId, ALL_RULE_TYPES[i], `*${i}*`, '');
}
let changes: ListRuleChange[] = await banList.updateList();
let { changes } = await banList.updateList();
assert.equal(changes.length, ALL_RULE_TYPES.length);
assert.equal(banList.allRules.length, ALL_RULE_TYPES.length);
})
@ -218,7 +218,7 @@ describe('Test: We will not be able to ban ourselves via ACL.', function() {
await createPolicyRule(mjolnir.client, banListId, RULE_SERVER, 'evil.com', '');
await createPolicyRule(mjolnir.client, banListId, RULE_SERVER, '*', '');
// We should still intern the matching rules rule.
let changes: ListRuleChange[] = await banList.updateList();
let { changes } = await banList.updateList();
assert.equal(banList.serverRules.length, 3);
// But when we construct an ACL, we should be safe.
const acl = new ServerAcl(serverName)
@ -252,7 +252,7 @@ describe('Test: ACL updates will batch when rules are added in succession.', fun
}
// If a previous test hasn't cleaned up properly, these rooms will be populated by bogus ACLs at this point.
await mjolnir.protectedRoomsTracker.syncLists(mjolnir.config.verboseLogging);
await mjolnir.protectedRoomsTracker.syncLists();
await Promise.all(protectedRooms.map(async room => {
// We're going to need timeline pagination I'm afraid.
const roomAcl = await mjolnir.client.getRoomStateEvent(room, "m.room.server_acl", "");
@ -274,7 +274,7 @@ describe('Test: ACL updates will batch when rules are added in succession.', fun
}
// We do this because it should force us to wait until all the ACL events have been applied.
// Even if that does mean the last few events will not go through batching...
await mjolnir.protectedRoomsTracker.syncLists(mjolnir.config.verboseLogging);
await mjolnir.protectedRoomsTracker.syncLists();
// At this point we check that the state within Mjolnir is internally consistent, this is just because debugging the following
// is a pita.
@ -317,7 +317,7 @@ describe('Test: unbaning entities via the PolicyList.', function() {
await mjolnir.addProtectedRoom(protectedRoom);
// If a previous test hasn't cleaned up properly, these rooms will be populated by bogus ACLs at this point.
await mjolnir.protectedRoomsTracker.syncLists(mjolnir.config.verboseLogging);
await mjolnir.protectedRoomsTracker.syncLists();
// If this is not present, then it means the room isn't being protected, which is really bad.
const roomAcl = await mjolnir.client.getRoomStateEvent(protectedRoom, "m.room.server_acl", "");
assert.equal(roomAcl?.deny?.length ?? 0, 0, 'There should be no entries in the deny ACL.');
@ -364,7 +364,7 @@ describe('Test: unbaning entities via the PolicyList.', function() {
}
// Wait for mjolnir to sync protected rooms to update ACL.
await mjolnir.protectedRoomsTracker.syncLists(mjolnir.config.verboseLogging);
await mjolnir.protectedRoomsTracker.syncLists();
// Confirm that the server is unbanned.
await banList.updateList();
assert.equal(banList.allRules.length, 0);
@ -393,7 +393,7 @@ describe('Test: should apply bans to the most recently active rooms first', func
}
// If a previous test hasn't cleaned up properly, these rooms will be populated by bogus ACLs at this point.
await mjolnir.protectedRoomsTracker.syncLists(mjolnir.config.verboseLogging);
await mjolnir.protectedRoomsTracker.syncLists();
await Promise.all(protectedRooms.map(async room => {
const roomAcl = await mjolnir.client.getRoomStateEvent(room, "m.room.server_acl", "").catch(e => e.statusCode === 404 ? { deny: [] } : Promise.reject(e));
assert.equal(roomAcl?.deny?.length ?? 0, 0, 'There should be no entries in the deny ACL.');
@ -404,7 +404,7 @@ describe('Test: should apply bans to the most recently active rooms first', func
await mjolnir.client.joinRoom(banListId);
await mjolnir.watchList(Permalinks.forRoom(banListId));
await mjolnir.protectedRoomsTracker.syncLists(mjolnir.config.verboseLogging);
await mjolnir.protectedRoomsTracker.syncLists();
// shuffle protected rooms https://stackoverflow.com/a/12646864, we do this so we can create activity "randomly" in them.
for (let i = protectedRooms.length - 1; i > 0; i--) {
@ -441,7 +441,7 @@ describe('Test: should apply bans to the most recently active rooms first', func
})
// Wait until all the ACL events have been applied.
await mjolnir.protectedRoomsTracker.syncLists(mjolnir.config.verboseLogging);
await mjolnir.protectedRoomsTracker.syncLists();
for (let i = 0; i < protectedRooms.length; i++) {
assert.equal(aclRooms[i], protectedRooms[i], "The ACL should have been applied to the active rooms first.");
@ -524,7 +524,7 @@ describe('Test: AccessControlUnit interaction with policy lists.', function() {
await removePolicyRule(mjolnir.client, policyListId, RULE_SERVER, 'bad.example.com');
await removePolicyRule(mjolnir.client, policyListId, RULE_SERVER, '*.ddns.example.com');
await removePolicyRule(mjolnir.client, policyListId, RULE_USER, "@spam:matrix.org");
const changes = await policyList.updateList()
const { changes } = await policyList.updateList()
await mjolnir.protectedRoomsTracker.syncLists();
assert.equal(changes.length, 5, "The rules should have correctly been removed");

View File

@ -2146,6 +2146,11 @@ kuler@^2.0.0:
resolved "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz"
integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==
layerr@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/layerr/-/layerr-0.1.2.tgz#16c8e7fb042d3595ab15492bdad088f31d7afd15"
integrity sha512-ob5kTd9H3S4GOG2nVXyQhOu9O8nBgP555XxWPkJI0tR0JeRilfyTp8WtPdIJHLXBmHMSdEq5+KMxiYABeScsIQ==
levn@^0.4.1:
version "0.4.1"
resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz"
@ -3574,6 +3579,13 @@ typescript@^4.8.4:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6"
integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==
ulidx@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/ulidx/-/ulidx-0.3.0.tgz#19899adb2d8c57d4f6d598ab63da9164634a5251"
integrity sha512-Qvpa2xAzS6fBUpiqHSHWvn6XiSLCAPyNDDz035vsEWmUoXRqC4c9JySLIfdBuK0N1xGBxng6GHDOZgyNQfxAHg==
dependencies:
layerr "^0.1.2"
underscore@~1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604"