Skip to content

Commit

Permalink
load config yaml manually, remove more references to static config (#347
Browse files Browse the repository at this point in the history
)
  • Loading branch information
jesopo authored Aug 16, 2022
1 parent 64c26e5 commit 4376679
Show file tree
Hide file tree
Showing 25 changed files with 2,949 additions and 3,069 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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"
Expand Down
11 changes: 8 additions & 3 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

/**
Expand Down Expand Up @@ -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;
}
24 changes: 13 additions & 11 deletions src/health/healthz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);
});
}
}
26 changes: 15 additions & 11 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
(async function () {
const config = configRead();

LogService.info("index", "Starting bot...");
config.RUNTIME = {};

Healthz.isHealthy = false; // start off unhealthy
if (config.health.healthz.enabled) {
Healthz.listen();
}
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();
}

(async function () {
let bot: Mjolnir | null = null;
try {
const storagePath = path.isAbsolute(config.dataPath) ? config.dataPath : path.join(__dirname, '../', config.dataPath);
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion test/commands/UnbanBanCommandTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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> => {
Expand Down
10 changes: 5 additions & 5 deletions test/integration/abuseReportTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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();

Expand Down
12 changes: 6 additions & 6 deletions test/integration/banListTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();

Expand Down
31 changes: 15 additions & 16 deletions test/integration/clientHelper.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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))
Expand Down Expand Up @@ -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) {
Expand All @@ -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') {
Expand All @@ -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;
}
Expand All @@ -130,20 +129,20 @@ 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.
} else {
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;
}
Expand All @@ -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
});
Expand All @@ -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);
}


Expand Down
Loading

0 comments on commit 4376679

Please sign in to comment.