diff --git a/apps/rooms/src/rooms.app.ts b/apps/rooms/src/rooms.app.ts index 8fb09c5..ce2e705 100644 --- a/apps/rooms/src/rooms.app.ts +++ b/apps/rooms/src/rooms.app.ts @@ -1858,8 +1858,7 @@ const app = new Hono() Error: 'This room does not exist!', }) } - const isOwner = room.CreatorAccountId === accountId - if (!isOwner && !(await isStaff(c))) { + if (room.CreatorAccountId !== accountId) { return roomResult(c, { Success: false, ErrorId: 'Rooms.NotOwner', @@ -2098,7 +2097,8 @@ const app = new Hono() Error: 'This room does not exist!', }) } - if (room.CreatorAccountId !== accountId) { + const isOwner = room.CreatorAccountId === accountId + if (!isOwner && !(await isStaff(c))) { return roomResult(c, { Success: false, ErrorId: 'Rooms.NotOwner', diff --git a/apps/rooms/src/test/integration/api.test.ts b/apps/rooms/src/test/integration/api.test.ts index c7350d6..c3d6f4b 100644 --- a/apps/rooms/src/test/integration/api.test.ts +++ b/apps/rooms/src/test/integration/api.test.ts @@ -1634,6 +1634,45 @@ describe('rooms endpoints', () => { expect(interactions!.n).toBe(0) }) + it('DELETE /rooms/:id also lets staff through — the website’s takedown — but only there', async () => { + // Somebody else's room. A moderator token may delete it; the same token may NOT + // rename it, which is what a misapplied check once allowed. + await env.DB.prepare('INSERT INTO room (data) VALUES (?1)') + .bind( + JSON.stringify({ + RoomId: 9501, + Name: 'TakeMeDown', + CreatorAccountId: 1, + IsDorm: false, + Accessibility: 1, + ImageName: '', + SubRooms: [], + }) + ) + .run() + const staff = await bearer('999', ['gameClient', 'moderator']) + const roomExists = async () => + (await env.DB.prepare('SELECT 1 FROM room WHERE room_id = 9501').first()) !== null + + const rename = await SELF.fetch(`${ORIGIN}/rooms/9501/name`, { + method: 'PUT', + headers: { ...staff, 'content-type': 'application/x-www-form-urlencoded' }, + body: 'name=Renamed', + }) + expect(await bodyOf(rename)).toMatchObject({ Success: false, ErrorId: 'Rooms.NotOwner' }) + + // A plain player is still refused; the moderator gets through. + const del = async (headers: Record) => + bodyOf(await SELF.fetch(`${ORIGIN}/rooms/9501`, { method: 'DELETE', headers })) + expect(await del(await bearer('2', ['gameClient']))).toMatchObject({ + Success: false, + ErrorId: 'Rooms.NotOwner', + }) + expect(await roomExists()).toBe(true) + expect(await del(staff)).toMatchObject({ Success: true }) + expect(await roomExists()).toBe(false) + }) + it('PUT /rooms/:id/roles/:accountId grants a helper role outright, but never co-owner', async () => { type Role = { AccountId: number