Add moderation permissions and durable player records

This commit is contained in:
2026-10-15 21:06:08 -04:00
parent c1758cce25
commit c91d49c298
17 changed files with 699 additions and 21 deletions
+19 -3
View File
@@ -256,7 +256,7 @@ describe('password storage and verification', () => {
describe('administrator user management', () => {
it('rejects the reserved system username for every role while allowing distinct usernames', async () => {
const admin = seedUser();
for (const role of ['admin', 'super_viewer', 'operator', 'viewer'] as const) {
for (const role of ['admin', 'super_viewer', 'operator', 'moderator', 'viewer'] as const) {
for (const username of ['Maxis', 'maxis', 'MAXIS', 'mAxIs']) {
await expect(
auth.createUser(admin.id, { username, role, password })
@@ -275,7 +275,7 @@ describe('administrator user management', () => {
it('creates each supported role with a forced initial password change and rejects case-insensitive duplicate names', async () => {
const admin = seedUser();
for (const role of ['admin', 'super_viewer', 'operator', 'viewer'] as const) {
for (const role of ['admin', 'super_viewer', 'operator', 'moderator', 'viewer'] as const) {
const user = await auth.createUser(admin.id, {
username: role + 'User',
displayName: ' ' + role + ' user ',
@@ -295,7 +295,7 @@ describe('administrator user management', () => {
await expect(
auth.createUser(admin.id, { username: 'OPERATORUSER', role: 'viewer', password })
).rejects.toMatchObject({ status: 409 });
expect(auth.listUsers()).toHaveLength(5);
expect(auth.listUsers()).toHaveLength(6);
expect(JSON.stringify(auth.listAuthAudit())).not.toContain(password);
});
@@ -304,6 +304,7 @@ describe('administrator user management', () => {
const actors = [
seedUser('superviewer', 'super_viewer'),
seedUser('operator', 'operator'),
seedUser('moderator', 'moderator'),
seedUser('viewer', 'viewer'),
seedUser('disabledadmin', 'admin', { disabled: true }),
seedUser('temporaryadmin', 'admin', { mustChangePassword: true })
@@ -333,6 +334,9 @@ describe('administrator user management', () => {
await expect(
auth.updateUser(first.id, first.id, { role: 'operator' })
).rejects.toMatchObject({ status: 409 });
await expect(
auth.updateUser(first.id, first.id, { role: 'moderator' })
).rejects.toMatchObject({ status: 409 });
await expect(auth.updateUser(first.id, first.id, { disabled: true })).rejects.toMatchObject(
{ status: 409 }
);
@@ -347,6 +351,18 @@ describe('administrator user management', () => {
expect(auth.getUser(second.id)?.role).toBe('admin');
});
it('revokes sessions when moderation access is granted or removed', async () => {
const admin = seedUser();
const target = seedUser('target', 'viewer');
const beforeGrant = auth.createSession(target.id, true);
await auth.updateUser(admin.id, target.id, { role: 'moderator' });
expect(auth.getUser(target.id)?.role).toBe('moderator');
expect(auth.readSession(beforeGrant.token)).toBeNull();
const beforeRemoval = auth.createSession(target.id, true);
await auth.updateUser(admin.id, target.id, { role: 'viewer' });
expect(auth.readSession(beforeRemoval.token)).toBeNull();
});
it.each([false, true])(
'revokes every target session on role, enabled-state and password changes (remembered=%s)',
async (remembered) => {