Files
upstream-jitsi-meet/tests/prosody/mod_muc_resource_validate_spec.js
upstream-mirror 1d2bfcac0c
Some checks failed
Prosody Plugin Tests / test (push) Failing after 10m11s
Close stale issues and PRs / stale (push) Failing after 1s
snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
2026-09-15 15:56:32 +05:00

200 lines
7.1 KiB
JavaScript

import assert from 'assert';
import { getContainer } from './helpers/container.js';
import { createTestContext } from './helpers/test_context.js';
import { isAvailablePresence } from './helpers/xmpp_utils.js';
const MUC = 'conference.localhost';
const PROSODY_CFG = '/etc/prosody/cfg/conference.cfg.lua';
let _roomCounter = 0;
const room = () => `validate-${++_roomCounter}@${MUC}`;
/**
* Toggle anonymous_strict on mod_muc_resource_validate by editing the Prosody
* config inside the container and reloading Prosody (SIGHUP via prosodyctl reload).
* mod_muc_resource_validate re-reads config on the config-reloaded event.
*
* The default in the test config is anonymous_strict = true. Call
* setStrictMode(false) to temporarily disable strict checking, then restore
* with setStrictMode(true) in a finally block.
*
* @param {boolean} enabled
*/
async function setStrictMode(enabled) {
const container = getContainer();
const from = enabled ? 'anonymous_strict = false' : 'anonymous_strict = true';
const to = enabled ? 'anonymous_strict = true' : 'anonymous_strict = false';
await container.exec([ 'sed', '-i', `s/${from}/${to}/`, PROSODY_CFG ]);
await container.exec([ 'prosodyctl', 'reload' ]);
// prosodyctl reload sends SIGHUP and returns immediately; give Prosody a moment
// to process the config-reloaded event before the next test action.
await new Promise(resolve => setTimeout(resolve, 500));
}
describe('mod_muc_resource_validate', () => {
let ctx;
beforeEach(() => {
ctx = createTestContext();
});
afterEach(() => ctx.cleanup());
// ── Basic pattern validation ──────────────────────────────────────────────
//
// These tests verify the resource format rules independently of the
// anonymous_strict UUID-prefix constraint, so they temporarily disable
// strict mode to allow arbitrary (but format-valid) nicks.
it('allows a valid alphanumeric resource', async () => {
const r = room();
await ctx.connectFocus(r);
await setStrictMode(false);
try {
const c = await ctx.connect();
const presence = await c.joinRoom(r, 'ValidNick123');
assert.ok(isAvailablePresence(presence),
'valid alphanumeric resource must be allowed');
} finally {
await setStrictMode(true);
}
});
it('allows a resource with underscore after the first character', async () => {
const r = room();
await ctx.connectFocus(r);
await setStrictMode(false);
try {
const c = await ctx.connect();
const presence = await c.joinRoom(r, 'abc_123');
assert.ok(isAvailablePresence(presence),
'resource with internal underscore must be allowed');
} finally {
await setStrictMode(true);
}
});
// Format-invalid resources are rejected by the format check before the
// UUID-prefix check runs, so these tests work regardless of strict mode.
it('rejects a resource starting with an underscore', async () => {
const r = room();
await ctx.connectFocus(r);
const c = await ctx.connect();
const presence = await c.joinRoom(r, '_invalid');
assert.equal(presence.attrs.type, 'error',
'resource starting with underscore must be rejected');
assert.ok(
presence.getChild('error')?.getChild('not-allowed'),
'error stanza must contain <not-allowed/>'
);
});
it('rejects a resource containing a hyphen', async () => {
const r = room();
await ctx.connectFocus(r);
const c = await ctx.connect();
const presence = await c.joinRoom(r, 'invalid-nick');
assert.equal(presence.attrs.type, 'error',
'resource with hyphen must be rejected');
assert.ok(
presence.getChild('error')?.getChild('not-allowed'),
'error stanza must contain <not-allowed/>'
);
});
// ── Anonymous strict mode ─────────────────────────────────────────────────
//
// anonymous_strict = true is the default in the test config, so no toggle
// is needed here.
it('allows resource matching UUID prefix', async () => {
const r = room();
await ctx.connectFocus(r);
const c = await ctx.connect();
// joinRoom defaults to the UUID prefix when no nick is supplied.
const presence = await c.joinRoom(r);
assert.ok(isAvailablePresence(presence),
'resource matching UUID prefix must be allowed');
});
it('rejects resource not matching UUID prefix', async () => {
const r = room();
await ctx.connectFocus(r);
const c = await ctx.connect();
const presence = await c.joinRoom(r, 'wrongnick');
assert.equal(presence.attrs.type, 'error',
'resource not matching UUID prefix must be rejected');
assert.ok(
presence.getChild('error')?.getChild('not-allowed'),
'error stanza must contain <not-allowed/>'
);
});
// ── Nick (resource) changes ───────────────────────────────────────────────
//
// The MUC resource is the stable per-session identity and must not change
// after join. Display-name changes travel in the presence <nick/> child and
// keep the same resource, so they must still be allowed.
it('rejects a nick change to a different resource', async () => {
const r = room();
await ctx.connectFocus(r);
const c = await ctx.connect();
const joined = await c.joinRoom(r);
assert.ok(isAvailablePresence(joined), 'initial join must succeed');
// Send a presence to a different MUC resource, i.e. a nick change.
await c.sendPresence(`${r}/rotatednick`);
const presence = await c.waitForPresenceFrom(`${r}/rotatednick`, { type: 'error' });
assert.equal(presence.attrs.type, 'error', 'nick change must be rejected');
assert.ok(
presence.getChild('error')?.getChild('not-allowed'),
'error stanza must contain <not-allowed/>'
);
});
it('allows a presence update that keeps the same resource', async () => {
const r = room();
await ctx.connectFocus(r);
const c = await ctx.connect();
const joined = await c.joinRoom(r);
assert.ok(isAvailablePresence(joined), 'initial join must succeed');
// A presence update to the same MUC resource (e.g. a display-name change)
// is not a nick change and must be allowed through.
await c.sendPresence(`${r}/${c.nick}`);
const presence = await c.waitForPresenceFrom(`${r}/${c.nick}`);
assert.notEqual(presence.attrs.type, 'error',
'presence update keeping the same resource must be allowed');
});
});