Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 9 commits into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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