Files
Draupnir/apps/draupnir/test/integration/commands/redactCommandTest.ts
T
Catalan Lover 781d55db8b
Tests / Build & Lint (push) Failing after 2m41s
Tests / Unit tests (push) Successful in 2m58s
Tests / Integration tests (push) Failing after 21s
Tests / Application Service Integration tests (push) Failing after 11s
GHCR - Development Branches / ghcr-publish (push) Failing after 12m54s
Docker Hub - Develop / docker-latest (push) Failing after 13m51s
Zero Touch Provisioning Support Stage 1 (#1070)
Starts us down the path of fixing https://github.com/the-draupnir-project/Draupnir/issues/1023

This PR gets us Zero Touch Provisioning support for AS mode and Bot mode. If your env can take usr and pswd and turn it into access tokens or however you want to do that then well we dont need to do PSWD Auth at all.

If you dont PSWD auth is useful for this and is probably what i will resort to in mdad for Zero Touch Provisioning.

Draft as this PR is very much not even mx-tested because mx-tester decided to say nope cat.

* Zero Touch Provisioning Support

* Update Account data keys to Gnuxie Suggested Values

Co-authored-by: Gnuxie <50846879+Gnuxie@users.noreply.github.com>

* Refine ZTD

Co-authored-by: Gnuxie <Gnuxie@users.noreply.github.com>

* Refine Config validation

* Run Prettier

* Fix Test Linting

* Refine ZTD Branch by removing leftover config values.

* Remove fallback management room value as it breaks ZTP

* Fix config validation having Truthy problems

* Fix dangling import.

* Fix config validation error.

* Revert "Fix config validation error."

This reverts commit c313dcbb52.

* Remove fake cast on config.initialManager

* note only available in develop while we fix shit

* Stop tests from accessing config.managementRoom directly.

---------

Co-authored-by: Gnuxie <50846879+Gnuxie@users.noreply.github.com>
Co-authored-by: Gnuxie <Gnuxie@users.noreply.github.com>
Co-authored-by: gnuxie <Gnuxie@protonmail.com>
2026-05-05 11:51:50 +01:00

300 lines
9.9 KiB
TypeScript

// Copyright 2022 - 2024 Gnuxie <Gnuxie@protonmail.com>
// Copyright 2021 The Matrix.org Foundation C.I.C.
//
// SPDX-License-Identifier: Apache-2.0
//
// SPDX-FileAttributionText: <text>
// This modified file incorporates work from mjolnir
// https://github.com/matrix-org/mjolnir
// </text>
import { strict as assert } from "assert";
import { newTestUser } from "../clientHelper";
import { getMessagesByUserIn } from "../../../src/utils";
import { LogService } from "@vector-im/matrix-bot-sdk";
import { getFirstReaction } from "./commandUtils";
import {
draupnirClient,
draupnirSafeEmitter,
DraupnirTestContext,
} from "../mjolnirSetupUtils";
import { MatrixClient } from "@vector-im/matrix-bot-sdk";
import { RoomEvent } from "matrix-protection-suite";
import { extractRawRoomEvent } from "matrix-protection-suite-for-matrix-bot-sdk";
interface RedactionTestContext extends DraupnirTestContext {
moderator?: MatrixClient;
}
describe("Test: The redaction command", function () {
// If a test has a timeout while awaitng on a promise then we never get given control back.
afterEach(function (this: RedactionTestContext) {
this.moderator?.stop();
});
it(
"Draupnir redacts all of the events sent by a spammer when instructed to by giving their id and a room id.",
async function (this: RedactionTestContext) {
this.timeout(60000);
// Create a few users and a room.
const badUser = await newTestUser(this.config.homeserverUrl, {
name: { contains: "spammer-needs-redacting" },
});
const badUserId = await badUser.getUserId();
const draupnirMatrixClient = draupnirClient();
const draupnir = this.draupnir;
if (draupnirMatrixClient === null || draupnir === undefined) {
throw new TypeError(`Test isn't setup correctly`);
}
const mjolnirUserId = await draupnirMatrixClient.getUserId();
const moderator = await newTestUser(this.config.homeserverUrl, {
name: { contains: "moderator" },
});
this.moderator = moderator;
await moderator.joinRoom(draupnir.managementRoomID);
const targetRoom = await moderator.createRoom({
invite: [await badUser.getUserId(), mjolnirUserId],
});
await moderator.setUserPowerLevel(mjolnirUserId, targetRoom, 100);
await badUser.joinRoom(targetRoom);
await getFirstReaction(
draupnirSafeEmitter(),
draupnir.managementRoomID,
"✅",
async () => {
return moderator.sendMessage(draupnir.managementRoomID, {
msgtype: "m.text",
body: `!draupnir rooms add ${targetRoom}`,
});
}
);
LogService.debug(
"redactionTest",
`targetRoom: ${targetRoom}, managementRoom: ${this.config.managementRoom}`
);
// Sandwich irrelevant messages in bad messages.
await badUser.sendMessage(targetRoom, {
msgtype: "m.text",
body: "Very Bad Stuff",
});
await Promise.all(
[...Array(50).keys()].map((i) =>
moderator.sendMessage(targetRoom, {
msgtype: "m.text",
body: `Irrelevant Message #${i}`,
})
)
);
for (let i = 0; i < 5; i++) {
await badUser.sendMessage(targetRoom, {
msgtype: "m.text",
body: "Very Bad Stuff",
});
}
await Promise.all(
[...Array(50).keys()].map((i) =>
moderator.sendMessage(targetRoom, {
msgtype: "m.text",
body: `Irrelevant Message #${i}`,
})
)
);
await badUser.sendMessage(targetRoom, {
msgtype: "m.text",
body: "Very Bad Stuff",
});
await getFirstReaction(
draupnirSafeEmitter(),
draupnir.managementRoomID,
"✅",
async () => {
return await moderator.sendMessage(draupnir.managementRoomID, {
msgtype: "m.text",
body: `!draupnir redact ${badUserId} --room ${targetRoom}`,
});
}
);
await getMessagesByUserIn(
moderator,
badUserId,
targetRoom,
1000,
function (events) {
events.map((e) => {
if (e.type === "m.room.member") {
assert.equal(
Object.keys(e.content).length,
1,
"Only membership should be left on the membership even when it has been redacted."
);
} else if (Object.keys(e.content).length !== 0) {
throw new Error(
`This event should have been redacted: ${JSON.stringify(e, null, 2)}`
);
}
});
}
);
} as unknown as Mocha.AsyncFunc
);
it(
"Draupnir redacts all of the events sent by a spammer when instructed to by giving their id in multiple rooms.",
async function (this: RedactionTestContext) {
this.timeout(60000);
// Create a few users and a room.
const badUser = await newTestUser(this.config.homeserverUrl, {
name: { contains: "spammer-needs-redacting" },
});
const badUserId = await badUser.getUserId();
const draupnir = this.draupnir;
if (draupnir === undefined) {
throw new TypeError(`Test isn't setup correctly`);
}
const mjolnirUserId = await draupnir.client.getUserId();
const moderator = await newTestUser(this.config.homeserverUrl, {
name: { contains: "moderator" },
});
this.moderator = moderator;
await moderator.joinRoom(draupnir.managementRoomID);
const targetRooms: string[] = [];
for (let i = 0; i < 5; i++) {
const targetRoom = await moderator.createRoom({
invite: [await badUser.getUserId(), mjolnirUserId],
});
await moderator.setUserPowerLevel(mjolnirUserId, targetRoom, 100);
await badUser.joinRoom(targetRoom);
await moderator.sendMessage(draupnir.managementRoomID, {
msgtype: "m.text",
body: `!draupnir rooms add ${targetRoom}`,
});
targetRooms.push(targetRoom);
// Sandwich irrelevant messages in bad messages.
await badUser.sendMessage(targetRoom, {
msgtype: "m.text",
body: "Very Bad Stuff",
});
await Promise.all(
[...Array(50).keys()].map((j) =>
moderator.sendMessage(targetRoom, {
msgtype: "m.text",
body: `Irrelevant Message #${j}`,
})
)
);
for (let j = 0; j < 5; j++) {
await badUser.sendMessage(targetRoom, {
msgtype: "m.text",
body: "Very Bad Stuff",
});
}
await Promise.all(
[...Array(50).keys()].map((j) =>
moderator.sendMessage(targetRoom, {
msgtype: "m.text",
body: `Irrelevant Message #${j}`,
})
)
);
await badUser.sendMessage(targetRoom, {
msgtype: "m.text",
body: "Very Bad Stuff",
});
}
await getFirstReaction(
draupnirSafeEmitter(),
draupnir.managementRoomID,
"✅",
async () => {
return await moderator.sendMessage(draupnir.managementRoomID, {
msgtype: "m.text",
body: `!draupnir redact ${badUserId}`,
});
}
);
await Promise.all(
targetRooms.map(async (targetRoom) => {
await getMessagesByUserIn(
moderator,
badUserId,
targetRoom,
1000,
function (events) {
events.map((e) => {
if (e.type === "m.room.member") {
assert.equal(
Object.keys(e.content).length,
1,
"Only membership should be left on the membership even when it has been redacted."
);
} else if (Object.keys(e.content).length !== 0) {
throw new Error(
`This event should have been redacted: ${JSON.stringify(e, null, 2)}`
);
}
});
}
);
})
);
} as unknown as Mocha.AsyncFunc
);
it("Redacts a single event when instructed to.", async function (
this: RedactionTestContext
) {
this.timeout(60000);
// Create a few users and a room.
const badUser = await newTestUser(this.config.homeserverUrl, {
name: { contains: "spammer-needs-redacting" },
});
const draupnir = this.draupnir;
if (draupnir === undefined) {
throw new TypeError(`Test isn't setup correctly`);
}
const moderator = await newTestUser(this.config.homeserverUrl, {
name: { contains: "moderator" },
});
this.moderator = moderator;
await moderator.joinRoom(draupnir.managementRoomID);
const targetRoom = await moderator.createRoom({
invite: [await badUser.getUserId(), draupnir.clientUserID],
});
await moderator.setUserPowerLevel(draupnir.clientUserID, targetRoom, 100);
await badUser.joinRoom(targetRoom);
await moderator.sendMessage(draupnir.managementRoomID, {
msgtype: "m.text",
body: `!draupnir rooms add ${targetRoom}`,
});
const eventToRedact = await badUser.sendMessage(targetRoom, {
msgtype: "m.text",
body: "Very Bad Stuff",
});
await getFirstReaction(
draupnirSafeEmitter(),
draupnir.managementRoomID,
"✅",
async () => {
return await moderator.sendMessage(draupnir.managementRoomID, {
msgtype: "m.text",
body: `!draupnir redact https://matrix.to/#/${encodeURIComponent(targetRoom)}/${encodeURIComponent(eventToRedact)}`,
});
}
);
const redactedEvent = extractRawRoomEvent(
await moderator.getEvent(targetRoom, eventToRedact)
) as RoomEvent;
assert.equal(
Object.keys(redactedEvent.content).length,
0,
"This event should have been redacted"
);
} as unknown as Mocha.AsyncFunc);
});