load config yaml manually, remove more references to static config (#347)

This commit is contained in:
Jess Porter 2022-08-16 15:51:18 +01:00 committed by GitHub
parent 64c26e55f4
commit 4376679b99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2483 additions and 2603 deletions

View File

@ -18,10 +18,10 @@
"version": "sed -i '/# version automated/s/[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*/'$npm_package_version'/' synapse_antispam/setup.py && git add synapse_antispam/setup.py && cat synapse_antispam/setup.py"
},
"devDependencies": {
"@types/config": "0.0.41",
"@types/crypto-js": "^4.0.2",
"@types/html-to-text": "^8.0.1",
"@types/humanize-duration": "^3.27.1",
"@types/js-yaml": "^4.0.5",
"@types/jsdom": "^16.2.11",
"@types/mocha": "^9.0.0",
"@types/node": "^16.7.10",
@ -36,7 +36,6 @@
"typescript-formatter": "^7.2"
},
"dependencies": {
"config": "^3.3.6",
"express": "^4.17",
"html-to-text": "^8.0.0",
"humanize-duration": "^3.27.1",
@ -45,7 +44,8 @@
"jsdom": "^16.6.0",
"matrix-bot-sdk": "^0.5.19",
"parse-duration": "^1.0.2",
"shell-quote": "^1.7.3"
"shell-quote": "^1.7.3",
"yaml": "^2.1.1"
},
"engines": {
"node": ">=16.0.0"

View File

@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as config from "config";
import * as fs from "fs";
import { load } from "js-yaml";
import { MatrixClient } from "matrix-bot-sdk";
/**
@ -168,5 +169,9 @@ const defaultConfig: IConfig = {
},
};
const finalConfig = <IConfig>Object.assign({}, defaultConfig, config);
export default finalConfig;
export function read(): IConfig {
const content = fs.readFileSync(`./config/${process.env.NODE_ENV || 'default'}.yaml`, "utf8");
const parsed = load(content);
const config = {...defaultConfig, ...(parsed as object)} as IConfig;
return config;
}

View File

@ -16,27 +16,29 @@ limitations under the License.
import * as http from "http";
import { LogService } from "matrix-bot-sdk";
import { IConfig } from "../config";
// allowed to use the global configuration since this is only intended to be used by `src/index.ts`.
import config from '../config';
export class Healthz {
private static healthCode: number;
private healthCode: number;
public static set isHealthy(val: boolean) {
Healthz.healthCode = val ? config.health.healthz.healthyStatus : config.health.healthz.unhealthyStatus;
constructor(private config: IConfig) { }
public set isHealthy(val: boolean) {
this.healthCode = val ? this.config.health.healthz.healthyStatus : this.config.health.healthz.unhealthyStatus;
}
public static get isHealthy(): boolean {
return Healthz.healthCode === config.health.healthz.healthyStatus;
public get isHealthy(): boolean {
return this.healthCode === this.config.health.healthz.healthyStatus;
}
public static listen() {
public listen() {
const server = http.createServer((req, res) => {
res.writeHead(Healthz.healthCode);
res.end(`health code: ${Healthz.healthCode}`);
res.writeHead(this.healthCode);
res.end(`health code: ${this.healthCode}`);
});
server.listen(config.health.healthz.port, config.health.healthz.address, () => {
LogService.info("Healthz", `Listening for health requests on ${config.health.healthz.address}:${config.health.healthz.port}`);
server.listen(this.config.health.healthz.port, this.config.health.healthz.address, () => {
LogService.info("Healthz", `Listening for health requests on ${this.config.health.healthz.address}:${this.config.health.healthz.port}`);
});
}
}

View File

@ -23,24 +23,28 @@ import {
RichConsoleLogger,
SimpleFsStorageProvider
} from "matrix-bot-sdk";
import config from "./config";
import { read as configRead } from "./config";
import { Healthz } from "./health/healthz";
import { Mjolnir } from "./Mjolnir";
import { patchMatrixClient } from "./utils";
config.RUNTIME = {};
LogService.setLogger(new RichConsoleLogger());
LogService.setLevel(LogLevel.fromString(config.logLevel, LogLevel.DEBUG));
LogService.info("index", "Starting bot...");
Healthz.isHealthy = false; // start off unhealthy
if (config.health.healthz.enabled) {
Healthz.listen();
}
(async function () {
const config = configRead();
config.RUNTIME = {};
LogService.setLogger(new RichConsoleLogger());
LogService.setLevel(LogLevel.fromString(config.logLevel, LogLevel.DEBUG));
LogService.info("index", "Starting bot...");
const healthz = new Healthz(config);
healthz.isHealthy = false; // start off unhealthy
if (config.health.healthz.enabled) {
healthz.listen();
}
let bot: Mjolnir | null = null;
try {
const storagePath = path.isAbsolute(config.dataPath) ? config.dataPath : path.join(__dirname, '../', config.dataPath);
@ -63,7 +67,7 @@ if (config.health.healthz.enabled) {
}
try {
await bot.start();
Healthz.isHealthy = true;
healthz.isHealthy = true;
} catch (err) {
console.error(`Mjolnir failed to start: ${err}`);
throw err;

View File

@ -18,10 +18,11 @@ import * as expect from "expect";
import { Mjolnir } from "../../src/Mjolnir";
import { DEFAULT_LIST_EVENT_TYPE } from "../../src/commands/SetDefaultBanListCommand";
import { parseArguments } from "../../src/commands/UnbanBanCommand";
import config from "../../src/config";
import { read as configRead } from "../../src/config";
import { RULE_ROOM, RULE_SERVER, RULE_USER } from "../../src/models/ListRule";
function createTestMjolnir(defaultShortcode: string|null = null): Mjolnir {
const config = configRead();
const client = {
// Mock `MatrixClient.getAccountData` .
getAccountData: (eventType: string): Promise<any> => {

View File

@ -32,8 +32,8 @@ describe("Test: Reporting abuse", async () => {
});
// Create a few users and a room.
let goodUser = await newTestUser({ name: { contains: "reporting-abuse-good-user" }});
let badUser = await newTestUser({ name: { contains: "reporting-abuse-bad-user" }});
let goodUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "reporting-abuse-good-user" }});
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "reporting-abuse-bad-user" }});
let goodUserId = await goodUser.getUserId();
let badUserId = await badUser.getUserId();
@ -227,13 +227,13 @@ describe("Test: Reporting abuse", async () => {
});
// Create a moderator.
let moderatorUser = await newTestUser({ name: { contains: "reporting-abuse-moderator-user" }});
let moderatorUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "reporting-abuse-moderator-user" }});
matrixClient().inviteUser(await moderatorUser.getUserId(), this.mjolnir.managementRoomId);
await moderatorUser.joinRoom(this.mjolnir.managementRoomId);
// Create a few users and a room.
let goodUser = await newTestUser({ name: { contains: "reacting-abuse-good-user" }});
let badUser = await newTestUser({ name: { contains: "reacting-abuse-bad-user" }});
let goodUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "reacting-abuse-good-user" }});
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "reacting-abuse-bad-user" }});
let goodUserId = await goodUser.getUserId();
let badUserId = await badUser.getUserId();

View File

@ -30,7 +30,7 @@ describe("Test: Updating the PolicyList", function() {
it("Calculates what has changed correctly.", async function() {
this.timeout(10000);
const mjolnir: Mjolnir = this.mjolnir!
const moderator = await newTestUser({ name: { contains: "moderator" } });
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
const banListId = await mjolnir.client.createRoom({ invite: [await moderator.getUserId()] });
const banList = new PolicyList(banListId, banListId, mjolnir.client);
mjolnir.client.setUserPowerLevel(await moderator.getUserId(), banListId, 100);
@ -121,7 +121,7 @@ describe("Test: Updating the PolicyList", function() {
it("Will remove rules with old types when they are 'soft redacted' with a different but more recent event type.", async function() {
this.timeout(3000);
const mjolnir: Mjolnir = this.mjolnir!
const moderator = await newTestUser({ name: { contains: "moderator" }} );
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }} );
const banListId = await mjolnir.client.createRoom({ invite: [await moderator.getUserId()] });
const banList = new PolicyList(banListId, banListId, mjolnir.client);
mjolnir.client.setUserPowerLevel(await moderator.getUserId(), banListId, 100);
@ -142,7 +142,7 @@ describe("Test: Updating the PolicyList", function() {
})
it("A rule of the most recent type won't be deleted when an old rule is deleted for the same entity.", async function() {
const mjolnir: Mjolnir = this.mjolnir!
const moderator = await newTestUser({ name: { contains: "moderator" } });
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
const banListId = await mjolnir.client.createRoom({ invite: [await moderator.getUserId()] });
const banList = new PolicyList(banListId, banListId, mjolnir.client);
mjolnir.client.setUserPowerLevel(await moderator.getUserId(), banListId, 100);
@ -232,7 +232,7 @@ describe('Test: ACL updates will batch when rules are added in succession.', fun
it('Will batch ACL updates if we spam rules into a PolicyList', async function() {
const mjolnir: Mjolnir = this.mjolnir!
const serverName: string = new UserID(await mjolnir.client.getUserId()).domain
const moderator = await newTestUser({ name: { contains: "moderator" } });
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
moderator.joinRoom(this.mjolnir.client.managementRoomId);
const mjolnirId = await mjolnir.client.getUserId();
@ -300,7 +300,7 @@ describe('Test: unbaning entities via the PolicyList.', function() {
it('Will remove rules that have legacy types', async function() {
const mjolnir: Mjolnir = this.mjolnir!
const serverName: string = new UserID(await mjolnir.client.getUserId()).domain
const moderator = await newTestUser({ name: { contains: "moderator" } });
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
this.moderator = moderator;
await moderator.joinRoom(mjolnir.managementRoomId);
const mjolnirId = await mjolnir.client.getUserId();
@ -372,7 +372,7 @@ describe('Test: should apply bans to the most recently active rooms first', func
this.timeout(180000)
const mjolnir: Mjolnir = this.mjolnir!
const serverName: string = new UserID(await mjolnir.client.getUserId()).domain
const moderator = await newTestUser({ name: { contains: "moderator" } });
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
moderator.joinRoom(mjolnir.managementRoomId);
const mjolnirId = await mjolnir.client.getUserId();

View File

@ -1,6 +1,5 @@
import { HmacSHA1 } from "crypto-js";
import { getRequestFn, LogService, MatrixClient, MemoryStorageProvider, PantalaimonClient } from "matrix-bot-sdk";
import config from '../../src/config';
const REGISTRATION_ATTEMPTS = 10;
const REGISTRATION_RETRY_BASE_DELAY_MS = 100;
@ -16,8 +15,8 @@ const REGISTRATION_RETRY_BASE_DELAY_MS = 100;
* @param admin True to make the user an admin, false otherwise.
* @returns The response from synapse.
*/
export async function registerUser(username: string, displayname: string, password: string, admin: boolean): Promise<void> {
let registerUrl = `${config.homeserverUrl}/_synapse/admin/v1/register`
export async function registerUser(homeserver: string, username: string, displayname: string, password: string, admin: boolean): Promise<void> {
let registerUrl = `${homeserver}/_synapse/admin/v1/register`
const data: {nonce: string} = await new Promise((resolve, reject) => {
getRequestFn()({uri: registerUrl, method: "GET", timeout: 60000}, (error: any, response: any, resBody: any) => {
error ? reject(error) : resolve(JSON.parse(resBody))
@ -81,7 +80,7 @@ export type RegistrationOptions = {
*
* @returns A string that is both the username and password of a new user.
*/
async function registerNewTestUser(options: RegistrationOptions) {
async function registerNewTestUser(homeserver: string, options: RegistrationOptions) {
do {
let username;
if ("exact" in options.name) {
@ -90,7 +89,7 @@ async function registerNewTestUser(options: RegistrationOptions) {
username = `mjolnir-test-user-${options.name.contains}${Math.floor(Math.random() * 100000)}`
}
try {
await registerUser(username, username, username, Boolean(options.isAdmin));
await registerUser(homeserver, username, username, username, Boolean(options.isAdmin));
return username;
} catch (e) {
if (e?.body?.errcode === 'M_USER_IN_USE') {
@ -113,13 +112,13 @@ async function registerNewTestUser(options: RegistrationOptions) {
*
* @returns A new `MatrixClient` session for a unique test user.
*/
export async function newTestUser(options: RegistrationOptions): Promise<MatrixClient> {
const username = await registerNewTestUser(options);
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
export async function newTestUser(homeserver: string, options: RegistrationOptions): Promise<MatrixClient> {
const username = await registerNewTestUser(homeserver, options);
const pantalaimon = new PantalaimonClient(homeserver, new MemoryStorageProvider());
const client = await pantalaimon.createClientWithCredentials(username, username);
if (!options.isThrottled) {
let userId = await client.getUserId();
await overrideRatelimitForUser(userId);
await overrideRatelimitForUser(homeserver, userId);
}
return client;
}
@ -130,12 +129,12 @@ let _globalAdminUser: MatrixClient;
* Get a client that can perform synapse admin API actions.
* @returns A client logged in with an admin user.
*/
async function getGlobalAdminUser(): Promise<MatrixClient> {
async function getGlobalAdminUser(homeserver: string): Promise<MatrixClient> {
// Initialize global admin user if needed.
if (!_globalAdminUser) {
const USERNAME = "mjolnir-test-internal-admin-user";
try {
await registerUser(USERNAME, USERNAME, USERNAME, true);
await registerUser(homeserver, USERNAME, USERNAME, USERNAME, true);
} catch (e) {
if (e.isAxiosError && e?.response?.data?.errcode === 'M_USER_IN_USE') {
// Then we've already registered the user in a previous run and that is ok.
@ -143,7 +142,7 @@ async function getGlobalAdminUser(): Promise<MatrixClient> {
throw e;
}
}
_globalAdminUser = await new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider()).createClientWithCredentials(USERNAME, USERNAME);
_globalAdminUser = await new PantalaimonClient(homeserver, new MemoryStorageProvider()).createClientWithCredentials(USERNAME, USERNAME);
}
return _globalAdminUser;
}
@ -152,8 +151,8 @@ async function getGlobalAdminUser(): Promise<MatrixClient> {
* Disable ratelimiting for this user in Synapse.
* @param userId The user to disable ratelimiting for, has to include both the server part and local part.
*/
export async function overrideRatelimitForUser(userId: string) {
await (await getGlobalAdminUser()).doRequest("POST", `/_synapse/admin/v1/users/${userId}/override_ratelimit`, null, {
export async function overrideRatelimitForUser(homeserver: string, userId: string) {
await (await getGlobalAdminUser(homeserver)).doRequest("POST", `/_synapse/admin/v1/users/${userId}/override_ratelimit`, null, {
"messages_per_second": 0,
"burst_count": 0
});
@ -163,8 +162,8 @@ export async function overrideRatelimitForUser(userId: string) {
* Put back the default ratelimiting for this user in Synapse.
* @param userId The user to use default ratelimiting for, has to include both the server part and local part.
*/
export async function resetRatelimitForUser(userId: string) {
await (await getGlobalAdminUser()).doRequest("DELETE", `/_synapse/admin/v1/users/${userId}/override_ratelimit`, null);
export async function resetRatelimitForUser(homeserver: string, userId: string) {
await (await getGlobalAdminUser(homeserver)).doRequest("DELETE", `/_synapse/admin/v1/users/${userId}/override_ratelimit`, null);
}

View File

@ -1,6 +1,5 @@
import { strict as assert } from "assert";
import config from "../../../src/config";
import { newTestUser } from "../clientHelper";
import { PowerLevelAction } from "matrix-bot-sdk/lib/models/PowerLevelAction";
import { LogService } from "matrix-bot-sdk";
@ -16,20 +15,20 @@ describe("Test: The make admin command", function () {
it('Mjölnir make the bot self room administrator', async function () {
this.timeout(90000);
if (!config.admin?.enableMakeRoomAdminCommand) {
if (!this.config.admin?.enableMakeRoomAdminCommand) {
done();
}
const mjolnir = config.RUNTIME.client!;
const mjolnir = this.config.RUNTIME.client!;
const mjolnirUserId = await mjolnir.getUserId();
const moderator = await newTestUser({ name: { contains: "moderator" } });
const userA = await newTestUser({ name: { contains: "a" } });
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
const userA = await newTestUser(this.config.homeserverUrl, { name: { contains: "a" } });
const userAId = await userA.getUserId();
this.moderator = moderator;
this.userA = userA;
let powerLevels: any;
await moderator.joinRoom(config.managementRoom);
LogService.debug("makeadminTest", `Joining managementRoom: ${config.managementRoom}`);
await moderator.joinRoom(this.config.managementRoom);
LogService.debug("makeadminTest", `Joining managementRoom: ${this.config.managementRoom}`);
let targetRoom = await moderator.createRoom({ invite: [mjolnirUserId], preset: "public_chat" });
LogService.debug("makeadminTest", `moderator creating targetRoom: ${targetRoom}; and inviting ${mjolnirUserId}`);
await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text.', body: `!mjolnir rooms add ${targetRoom}` });
@ -57,14 +56,14 @@ describe("Test: The make admin command", function () {
it('Mjölnir make the tester room administrator', async function () {
this.timeout(90000);
if (!config.admin?.enableMakeRoomAdminCommand) {
if (!this.config.admin?.enableMakeRoomAdminCommand) {
done();
}
const mjolnir = config.RUNTIME.client!;
const moderator = await newTestUser({ name: { contains: "moderator" } });
const userA = await newTestUser({ name: { contains: "a" } });
const userB = await newTestUser({ name: { contains: "b" } });
const userC = await newTestUser({ name: { contains: "c" } });
const mjolnir = this.config.RUNTIME.client!;
const moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
const userA = await newTestUser(this.config.homeserverUrl, { name: { contains: "a" } });
const userB = await newTestUser(this.config.homeserverUrl, { name: { contains: "b" } });
const userC = await newTestUser(this.config.homeserverUrl, { name: { contains: "c" } });
const userBId = await userB.getUserId();
const userCId = await userC.getUserId();
this.moderator = moderator;

View File

@ -1,6 +1,5 @@
import { strict as assert } from "assert";
import config from "../../../src/config";
import { newTestUser } from "../clientHelper";
import { getMessagesByUserIn } from "../../../src/utils";
import { LogService } from "matrix-bot-sdk";
@ -13,19 +12,19 @@ import { getFirstReaction } from "./commandUtils";
it('Mjölnir redacts all of the events sent by a spammer when instructed to by giving their id and a room id.', async function() {
this.timeout(60000);
// Create a few users and a room.
let badUser = await newTestUser({ name: { contains: "spammer-needs-redacting" } });
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "spammer-needs-redacting" } });
let badUserId = await badUser.getUserId();
const mjolnir = config.RUNTIME.client!
const mjolnir = this.config.RUNTIME.client!
let mjolnirUserId = await mjolnir.getUserId();
let moderator = await newTestUser({ name: { contains: "moderator" } });
let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
this.moderator = moderator;
await moderator.joinRoom(config.managementRoom);
await moderator.joinRoom(this.config.managementRoom);
let targetRoom = await moderator.createRoom({ invite: [await badUser.getUserId(), mjolnirUserId]});
await moderator.setUserPowerLevel(mjolnirUserId, targetRoom, 100);
await badUser.joinRoom(targetRoom);
moderator.sendMessage(this.mjolnir.managementRoomId, {msgtype: 'm.text.', body: `!mjolnir rooms add ${targetRoom}`});
LogService.debug("redactionTest", `targetRoom: ${targetRoom}, managementRoom: ${config.managementRoom}`);
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}`})));
@ -58,13 +57,13 @@ import { getFirstReaction } from "./commandUtils";
it('Mjölnir redacts all of the events sent by a spammer when instructed to by giving their id in multiple rooms.', async function() {
this.timeout(60000);
// Create a few users and a room.
let badUser = await newTestUser({ name: { contains: "spammer-needs-redacting" } });
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "spammer-needs-redacting" } });
let badUserId = await badUser.getUserId();
const mjolnir = config.RUNTIME.client!
const mjolnir = this.config.RUNTIME.client!
let mjolnirUserId = await mjolnir.getUserId();
let moderator = await newTestUser({ name: { contains: "moderator" } });
let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
this.moderator = moderator;
await moderator.joinRoom(config.managementRoom);
await moderator.joinRoom(this.config.managementRoom);
let targetRooms: string[] = [];
for (let i = 0; i < 5; i++) {
let targetRoom = await moderator.createRoom({ invite: [await badUser.getUserId(), mjolnirUserId]});
@ -107,12 +106,12 @@ import { getFirstReaction } from "./commandUtils";
it("Redacts a single event when instructed to.", async function () {
this.timeout(60000);
// Create a few users and a room.
let badUser = await newTestUser({ name: { contains: "spammer-needs-redacting" } });
const mjolnir = config.RUNTIME.client!
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "spammer-needs-redacting" } });
const mjolnir = this.config.RUNTIME.client!
let mjolnirUserId = await mjolnir.getUserId();
let moderator = await newTestUser({ name: { contains: "moderator" } });
let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
this.moderator = moderator;
await moderator.joinRoom(config.managementRoom);
await moderator.joinRoom(this.config.managementRoom);
let targetRoom = await moderator.createRoom({ invite: [await badUser.getUserId(), mjolnirUserId]});
await moderator.setUserPowerLevel(mjolnirUserId, targetRoom, 100);
await badUser.joinRoom(targetRoom);

View File

@ -5,7 +5,7 @@ import { newTestUser } from "../clientHelper";
describe("Test: shutdown command", function() {
let client;
this.beforeEach(async function () {
client = await newTestUser({ name: { contains: "shutdown-command" }});
client = await newTestUser(this.config.homeserverUrl, { name: { contains: "shutdown-command" }});
await client.start();
})
this.afterEach(async function () {

View File

@ -28,7 +28,7 @@ describe("Test: DetectFederationLag protection", function() {
await this.mjolnir.enableProtection("DetectFederationLag");
// Setup a moderator.
this.moderator = await newTestUser({ name: { contains: "moderator" } });
this.moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
await this.moderator.joinRoom(this.mjolnir.managementRoomId);
const SETTINGS = {

View File

@ -1,4 +1,4 @@
import config from "../../src/config";
import { read as configRead } from "../../src/config";
import { makeMjolnir, teardownManagementRoom } from "./mjolnirSetupUtils";
// When Mjolnir starts (src/index.ts) it clobbers the config by resolving the management room
@ -12,7 +12,8 @@ export const mochaHooks = {
console.error("---- entering test", JSON.stringify(this.currentTest.title)); // Makes MatrixClient error logs a bit easier to parse.
console.log("mochaHooks.beforeEach");
// Sometimes it takes a little longer to register users.
this.timeout(10000)
this.timeout(10000);
const config = this.config = configRead();
this.managementRoomAlias = config.managementRoom;
this.mjolnir = await makeMjolnir(config);
config.RUNTIME.client = this.mjolnir.client;
@ -33,7 +34,7 @@ export const mochaHooks = {
this.mjolnir.client.setAccountData('org.matrix.mjolnir.watched_lists', { references: [] }),
]);
// remove alias from management room and leave it.
await teardownManagementRoom(this.mjolnir.client, this.mjolnir.managementRoomId, config.managementRoom);
await teardownManagementRoom(this.mjolnir.client, this.mjolnir.managementRoomId, this.managementRoomAlias);
console.error("---- completed test", JSON.stringify(this.currentTest.title), "\n\n"); // Makes MatrixClient error logs a bit easier to parse.
}
]

View File

@ -4,7 +4,7 @@ import { newTestUser, noticeListener } from "./clientHelper"
describe("Test: !help command", function() {
let client;
this.beforeEach(async function () {
client = await newTestUser({ name: { contains: "-" }});;
client = await newTestUser(this.config.homeserverUrl, { name: { contains: "-" }});;
await client.start();
})
this.afterEach(async function () {
@ -13,7 +13,7 @@ describe("Test: !help command", function() {
it('Mjolnir responded to !mjolnir help', async function() {
this.timeout(30000);
// send a messgage
await client.joinRoom(config.managementRoom);
await client.joinRoom(this.config.managementRoom);
// listener for getting the event reply
let reply = new Promise((resolve, reject) => {
client.on('room.message', noticeListener(this.mjolnir.managementRoomId, (event) => {

View File

@ -3,9 +3,10 @@
*/
import { makeMjolnir } from "./mjolnirSetupUtils";
import config from '../../src/config';
import { read as configRead } from '../../src/config';
(async () => {
const config = configRead();
let mjolnir = await makeMjolnir(config);
await mjolnir.start();
})();

View File

@ -50,7 +50,7 @@ export async function ensureAliasedRoomExists(client: MatrixClient, alias: strin
async function configureMjolnir(config: IConfig) {
try {
await registerUser(config.pantalaimon.username, config.pantalaimon.username, config.pantalaimon.password, true)
await registerUser(config.homeserverUrl, config.pantalaimon.username, config.pantalaimon.username, config.pantalaimon.password, true)
} catch (e) {
if (e?.body?.errcode === 'M_USER_IN_USE') {
console.log(`${config.pantalaimon.username} already registered, skipping`);
@ -79,7 +79,7 @@ export async function makeMjolnir(config: IConfig): Promise<Mjolnir> {
LogService.info("test/mjolnirSetupUtils", "Starting bot...");
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
const client = await pantalaimon.createClientWithCredentials(config.pantalaimon.username, config.pantalaimon.password);
await overrideRatelimitForUser(await client.getUserId());
await overrideRatelimitForUser(config.homeserverUrl, await client.getUserId());
patchMatrixClient();
await ensureAliasedRoomExists(client, config.managementRoom);
let mj = await Mjolnir.setupMjolnirFromConfig(client, config);

View File

@ -1,6 +1,7 @@
import { strict as assert } from "assert";
import { newTestUser } from "./clientHelper";
import { Mjolnir } from "../../src/Mjolnir";
import config from "../../src/config";
import { getRequestFn, LogService, MatrixClient } from "matrix-bot-sdk";
import { createBanList, getFirstReaction } from "./commands/commandUtils";
@ -8,9 +9,9 @@ import { createBanList, getFirstReaction } from "./commands/commandUtils";
/**
* Get a copy of the rules from the ruleserver.
*/
async function currentRules(): Promise<{ start: object, stop: object, since: string }> {
async function currentRules(mjolnir: Mjolnir): Promise<{ start: object, stop: object, since: string }> {
return await new Promise((resolve, reject) => getRequestFn()({
uri: `http://${config.web.address}:${config.web.port}/api/1/ruleserver/updates/`,
uri: `http://${mjolnir.config.web.address}:${mjolnir.config.web.port}/api/1/ruleserver/updates/`,
method: "GET"
}, (error, response, body) => {
if (error) {
@ -25,8 +26,8 @@ async function currentRules(): Promise<{ start: object, stop: object, since: str
* Wait for the rules to change as a result of the thunk. The returned promise will resolve when the rules being served have changed.
* @param thunk Should cause the rules the RuleServer is serving to change some way.
*/
async function waitForRuleChange(thunk): Promise<void> {
const initialRules = await currentRules();
async function waitForRuleChange(mjolnir: Mjolnir, thunk): Promise<void> {
const initialRules = await currentRules(mjolnir);
let rules = initialRules;
// We use JSON.stringify like this so that it is pretty printed in the log and human readable.
LogService.debug('policyConsumptionTest', `Rules before we wait for them to change: ${JSON.stringify(rules, null, 2)}`);
@ -35,7 +36,7 @@ async function waitForRuleChange(thunk): Promise<void> {
await new Promise<void>(resolve => {
setTimeout(resolve, 500);
})
rules = await currentRules();
rules = await currentRules(mjolnir);
};
// The problem is, we have no idea how long a consumer will take to process the changed rules.
// We know the pull peroid is 1 second though.
@ -47,9 +48,9 @@ async function waitForRuleChange(thunk): Promise<void> {
describe("Test: that policy lists are consumed by the associated synapse module", function () {
this.afterEach(async function () {
if(config.web.ruleServer.enabled) {
if(this.config.web.ruleServer.enabled) {
this.timeout(5000)
LogService.debug('policyConsumptionTest', `Rules at end of test ${JSON.stringify(await currentRules(), null, 2)}`);
LogService.debug('policyConsumptionTest', `Rules at end of test ${JSON.stringify(await currentRules(this.mjolnir), null, 2)}`);
const mjolnir = config.RUNTIME.client!;
// Clear any state associated with the account.
await mjolnir.setAccountData('org.matrix.mjolnir.watched_lists', {
@ -58,23 +59,23 @@ describe("Test: that policy lists are consumed by the associated synapse module"
}
})
this.beforeAll(async function() {
if (!config.web.ruleServer.enabled) {
if (!this.config.web.ruleServer.enabled) {
LogService.warn("policyConsumptionTest", "Skipping policy consumption test because the ruleServer is not enabled")
this.skip();
}
})
this.beforeEach(async function () {
this.timeout(1000);
const mjolnir = config.RUNTIME.client!;
const mjolnir = this.config.RUNTIME.client!;
})
it('blocks users in antispam when they are banned from sending messages and invites serverwide.', async function() {
this.timeout(20000);
// Create a few users and a room.
let badUser = await newTestUser({ name: { contains: "spammer" }});
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "spammer" }});
let badUserId = await badUser.getUserId();
const mjolnir = config.RUNTIME.client!
let mjolnirUserId = await mjolnir.getUserId();
let moderator = await newTestUser({ name: { contains: "moderator" }});
let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }});
this.moderator = moderator;
await moderator.joinRoom(this.mjolnir.managementRoomId);
let unprotectedRoom = await badUser.createRoom({ invite: [await moderator.getUserId()]});
@ -84,7 +85,7 @@ describe("Test: that policy lists are consumed by the associated synapse module"
const banList = await createBanList(this.mjolnir.managementRoomId, mjolnir, moderator);
await badUser.sendMessage(unprotectedRoom, {msgtype: 'm.text', body: 'Something bad and mean'});
await waitForRuleChange(async () => {
await waitForRuleChange(this.config.web.address, this.mjolnir.config.web.port, async () => {
await getFirstReaction(mjolnir, this.mjolnir.managementRoomId, '✅', async () => {
return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir ban ${banList} ${badUserId}` });
});
@ -95,7 +96,7 @@ describe("Test: that policy lists are consumed by the associated synapse module"
assert.ok(await moderator.sendMessage(unprotectedRoom, { msgtype: 'm.text', body: 'test'}), 'They should be able to send messages still too.');
// Test we can remove the rules.
await waitForRuleChange(async () => {
await waitForRuleChange(this.config.web.address, this.mjolnir.config.web.port, async () => {
await getFirstReaction(mjolnir, this.mjolnir.managementRoomId, '✅', async () => {
return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir unban ${banList} ${badUserId}` });
});
@ -105,15 +106,15 @@ describe("Test: that policy lists are consumed by the associated synapse module"
})
it('Test: Cannot send message to a room that is listed in a policy list and cannot invite a user to the room either', async function () {
this.timeout(20000);
let badUser = await newTestUser({ name: { contains: "spammer" }});
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "spammer" }});
const mjolnir = config.RUNTIME.client!
let moderator = await newTestUser({ name: { contains: "moderator" }});
let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }});
await moderator.joinRoom(this.mjolnir.managementRoomId);
const banList = await createBanList(this.mjolnir.managementRoomId, mjolnir, moderator);
let badRoom = await badUser.createRoom();
let unrelatedRoom = await badUser.createRoom();
await badUser.sendMessage(badRoom, {msgtype: 'm.text', body: "Very Bad Stuff in this room"});
await waitForRuleChange(async () => {
await waitForRuleChange(this.config.web.address, this.mjolnir.config.web.port, async () => {
await getFirstReaction(mjolnir, this.mjolnir.managementRoomId, '✅', async () => {
return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir ban ${banList} ${badRoom}` });
});
@ -123,7 +124,7 @@ describe("Test: that policy lists are consumed by the associated synapse module"
assert.ok(await badUser.sendMessage(unrelatedRoom, { msgtype: 'm.text.', body: 'hey'}), 'should be able to send messages to unrelated room');
assert.ok(await badUser.inviteUser(await moderator.getUserId(), unrelatedRoom), 'They should still be able to invite to other rooms though');
// Test we can remove these rules.
await waitForRuleChange(async () => {
await waitForRuleChange(this.config.web.address, this.mjolnir.config.web.port, async () => {
await getFirstReaction(mjolnir, this.mjolnir.managementRoomId, '✅', async () => {
return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir unban ${banList} ${badRoom}` });
});
@ -135,19 +136,19 @@ describe("Test: that policy lists are consumed by the associated synapse module"
it('Test: When a list becomes unwatched, the associated policies are stopped.', async function () {
this.timeout(20000);
const mjolnir = config.RUNTIME.client!
let moderator = await newTestUser({ name: { contains: "moderator" }});
let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }});
await moderator.joinRoom(this.mjolnir.managementRoomId);
const banList = await createBanList(this.mjolnir.managementRoomId, mjolnir, moderator);
let targetRoom = await moderator.createRoom();
await moderator.sendMessage(targetRoom, {msgtype: 'm.text', body: "Fluffy Foxes."});
await waitForRuleChange(async () => {
await waitForRuleChange(this.config.web.address, this.mjolnir.config.web.port, async () => {
await getFirstReaction(mjolnir, this.mjolnir.managementRoomId, '✅', async () => {
return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir ban ${banList} ${targetRoom}` });
});
});
await assert.rejects(moderator.sendMessage(targetRoom, { msgtype: 'm.text', body: 'test'}), 'should not be able to send messages to a room which is listed.');
await waitForRuleChange(async () => {
await waitForRuleChange(this.config.web.address, this.mjolnir.config.web.port, async () => {
await getFirstReaction(mjolnir, this.mjolnir.managementRoomId, '✅', async () => {
return await moderator.sendMessage(this.mjolnir.managementRoomId, { msgtype: 'm.text', body: `!mjolnir unwatch #${banList}:localhost:9999` });
});

View File

@ -1,6 +1,5 @@
import { strict as assert } from "assert";
import config from "../../src/config";
import { Mjolnir } from "../../src/Mjolnir";
import { IProtection } from "../../src/protections/IProtection";
import { PROTECTIONS } from "../../src/protections/protections";
@ -12,7 +11,7 @@ import { matrixClient, mjolnir } from "./mjolnirSetupUtils";
describe("Test: Protection settings", function() {
let client;
this.beforeEach(async function () {
client = await newTestUser({ name: { contains: "protection-settings" }});
client = await newTestUser(this.config.homeserverUrl, { name: { contains: "protection-settings" }});
await client.start();
})
this.afterEach(async function () {
@ -59,7 +58,7 @@ describe("Test: Protection settings", function() {
});
it("Mjolnir responds to !set correctly", async function() {
this.timeout(20000);
await client.joinRoom(config.managementRoom);
await client.joinRoom(this.config.managementRoom);
await this.mjolnir.registerProtection(new class implements IProtection {
name = "JY2TPN";
@ -84,7 +83,7 @@ describe("Test: Protection settings", function() {
});
it("Mjolnir adds a value to a list setting", async function() {
this.timeout(20000);
await client.joinRoom(config.managementRoom);
await client.joinRoom(this.config.managementRoom);
await this.mjolnir.registerProtection(new class implements IProtection {
name = "r33XyT";
@ -108,7 +107,7 @@ describe("Test: Protection settings", function() {
});
it("Mjolnir removes a value from a list setting", async function() {
this.timeout(20000);
await client.joinRoom(config.managementRoom);
await client.joinRoom(this.config.managementRoom);
await this.mjolnir.registerProtection(new class implements IProtection {
name = "oXzT0E";
@ -133,7 +132,7 @@ describe("Test: Protection settings", function() {
});
it("Mjolnir will change a protection setting in-place", async function() {
this.timeout(20000);
await client.joinRoom(config.managementRoom);
await client.joinRoom(this.config.managementRoom);
await this.mjolnir.registerProtection(new class implements IProtection {
name = "d0sNrt";

View File

@ -5,7 +5,7 @@ import { newTestUser } from "./clientHelper";
describe("Test: Report polling", function() {
let client;
this.beforeEach(async function () {
client = await newTestUser({ name: { contains: "protection-settings" }});
client = await newTestUser(this.config.homeserverUrl, { name: { contains: "protection-settings" }});
})
it("Mjolnir correctly retrieves a report from synapse", async function() {
this.timeout(40000);

View File

@ -257,7 +257,7 @@ describe("Test: Testing RoomMemberManager", function() {
const start = new Date(Date.now() - 10_000);
// Setup a moderator.
this.moderator = await newTestUser({ name: { contains: "moderator" } });
this.moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
await this.mjolnir.client.inviteUser(await this.moderator.getUserId(), this.mjolnir.managementRoomId)
await this.moderator.joinRoom(this.mjolnir.managementRoomId);
@ -265,7 +265,7 @@ describe("Test: Testing RoomMemberManager", function() {
this.users = [];
const SAMPLE_SIZE = 10;
for (let i = 0; i < SAMPLE_SIZE; ++i) {
this.users.push(await newTestUser({ name: { contains: `user_${i}_room_member_test` } }));
this.users.push(await newTestUser(this.config.homeserverUrl, { name: { contains: `user_${i}_room_member_test` } }));
}
const userIds = [];
for (let client of this.users) {
@ -380,7 +380,7 @@ describe("Test: Testing RoomMemberManager", function() {
const start = new Date(Date.now() - 10_000);
// Setup a moderator.
this.moderator = await newTestUser({ name: { contains: "moderator" } });
this.moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" } });
await this.moderator.joinRoom(this.mjolnir.managementRoomId);
// Create a few users.
@ -388,8 +388,8 @@ describe("Test: Testing RoomMemberManager", function() {
this.badUsers = [];
const SAMPLE_SIZE = 10;
for (let i = 0; i < SAMPLE_SIZE; ++i) {
this.goodUsers.push(await newTestUser({ name: { contains: `good_user_${i}_room_member_test` } }));
this.badUsers.push(await newTestUser({ name: { contains: `bad_user_${i}_room_member_test` } }));
this.goodUsers.push(await newTestUser(this.config.homeserverUrl, { name: { contains: `good_user_${i}_room_member_test` } }));
this.badUsers.push(await newTestUser(this.config.homeserverUrl, { name: { contains: `bad_user_${i}_room_member_test` } }));
}
const goodUserIds: string[] = [];
const badUserIds: string[] = [];

View File

@ -1,6 +1,5 @@
import { strict as assert } from "assert";
import config from "../../src/config";
import { Mjolnir } from "../../src/Mjolnir";
import { IProtection } from "../../src/protections/IProtection";
import { newTestUser, noticeListener } from "./clientHelper";
@ -11,8 +10,8 @@ describe("Test: standard consequences", function() {
let badUser;
let goodUser;
this.beforeEach(async function () {
badUser = await newTestUser({ name: { contains: "standard-consequences" }});
goodUser = await newTestUser({ name: { contains: "standard-consequences" }});
badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "standard-consequences" }});
goodUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "standard-consequences" }});
await badUser.start();
await goodUser.start();
})

View File

@ -4,7 +4,7 @@ import { getMessagesByUserIn } from "../../src/utils";
describe("Test: throttled users can function with Mjolnir.", function () {
it('throttled users survive being throttled by synapse', async function() {
let throttledUser = await newTestUser({ name: { contains: "throttled" }, isThrottled: true });
let throttledUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "throttled" }, isThrottled: true });
let throttledUserId = await throttledUser.getUserId();
let targetRoom = await throttledUser.createRoom();
// send enough messages to hit the rate limit.

View File

@ -10,9 +10,9 @@ describe("Test: timeline pagination", function () {
it('does not paginate across the entire room history while backfilling.', async function() {
this.timeout(60000);
// Create a few users and a room.
let badUser = await newTestUser({ name: { contains: "spammer" }});
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "spammer" }});
let badUserId = await badUser.getUserId();
let moderator = await newTestUser({ name: { contains: "moderator" }});
let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }});
let targetRoom = await moderator.createRoom({ invite: [await badUser.getUserId()]});
await badUser.joinRoom(targetRoom);
@ -39,9 +39,9 @@ describe("Test: timeline pagination", function () {
})
it('does not call the callback with an empty array when there are no relevant events', async function() {
this.timeout(60000);
let badUser = await newTestUser({ name: { contains: "spammer" }});
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "spammer" }});
let badUserId = await badUser.getUserId();
let moderator = await newTestUser({ name: { contains: "moderator" }});
let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }});
let targetRoom = await moderator.createRoom();
// send some irrelevant messages
await Promise.all([...Array(200).keys()].map((i) => moderator.sendMessage(targetRoom, {msgtype: 'm.text.', body: `Irrelevant Message #${i}`})));
@ -54,9 +54,9 @@ describe("Test: timeline pagination", function () {
})
it("The limit provided is respected", async function() {
this.timeout(60000);
let badUser = await newTestUser({ name: { contains: "spammer" }});
let badUser = await newTestUser(this.config.homeserverUrl, { name: { contains: "spammer" }});
let badUserId = await badUser.getUserId();
let moderator = await newTestUser({ name: { contains: "moderator" }});
let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }});
let targetRoom = await moderator.createRoom({ invite: [await badUser.getUserId()]});
await badUser.joinRoom(targetRoom);
// send some bad person messages
@ -83,7 +83,7 @@ describe("Test: timeline pagination", function () {
});
it("Gives the events to the callback ordered by youngest first (even more important when the limit is reached halfway through a chunk).", async function() {
this.timeout(60000);
let moderator = await newTestUser({ name: { contains: "moderator" }});
let moderator = await newTestUser(this.config.homeserverUrl, { name: { contains: "moderator" }});
let moderatorId = await moderator.getUserId();
let targetRoom = await moderator.createRoom();
for (let i = 0; i < 20; i++) {

View File

@ -8,11 +8,13 @@ describe("Test: utils", function() {
it("replaceRoomIdsWithPills correctly turns a room ID in to a pill", async function() {
this.timeout(20000);
const managementRoomAlias = this.config.managementRoom;
await this.mjolnir.client.sendStateEvent(
this.mjolnir.managementRoomId,
"m.room.canonical_alias",
"",
{ alias: config.managementRoom }
{ alias: managementRoomAlias }
);
const out = await replaceRoomIdsWithPills(
@ -24,7 +26,7 @@ describe("Test: utils", function() {
const ourHomeserver = new UserID(await this.mjolnir.client.getUserId()).domain;
assert.equal(
out.formatted_body,
`it's fun here in <a href="https://matrix.to/#/${config.managementRoom}?via=${ourHomeserver}">${config.managementRoom}</a>`
`it's fun here in <a href="https://matrix.to/#/${managementRoomAlias}?via=${ourHomeserver}">${managementRoomAlias}</a>`
);
});
});

4794
yarn.lock

File diff suppressed because it is too large Load Diff