|
7 | 7 | * - Edge cases and invalid inputs |
8 | 8 | */ |
9 | 9 |
|
| 10 | +import { ALL_SOCKET_OPERATIONS } from '@sim/realtime-protocol/constants' |
10 | 11 | import { |
11 | 12 | expectPermissionAllowed, |
12 | 13 | expectPermissionDenied, |
@@ -163,7 +164,7 @@ describe('checkRolePermission', () => { |
163 | 164 | // Every operation reaching this gate is persisted, so a read-only member must |
164 | 165 | // hold none of them — including the position updates that used to be granted |
165 | 166 | // here on the mistaken premise that they were ephemeral cursor sync. |
166 | | - for (const operation of SOCKET_OPERATIONS) { |
| 167 | + for (const operation of ALL_SOCKET_OPERATIONS) { |
167 | 168 | const result = checkRolePermission('read', operation) |
168 | 169 | expect(result.allowed).toBe(false) |
169 | 170 | expect(result.reason).toContain('read') |
@@ -211,28 +212,42 @@ describe('checkRolePermission', () => { |
211 | 212 | }) |
212 | 213 |
|
213 | 214 | describe('permission hierarchy verification', () => { |
214 | | - it('should verify admin has same permissions as write', () => { |
215 | | - const adminOps = ROLE_ALLOWED_OPERATIONS.admin |
216 | | - const writeOps = ROLE_ALLOWED_OPERATIONS.write |
217 | | - |
218 | | - // Admin and write should have same operations |
219 | | - expect(adminOps).toEqual(writeOps) |
220 | | - }) |
221 | | - |
222 | | - it('should verify read is a subset of write permissions', () => { |
223 | | - const readOps = ROLE_ALLOWED_OPERATIONS.read |
224 | | - const writeOps = ROLE_ALLOWED_OPERATIONS.write |
225 | | - |
226 | | - for (const op of readOps) { |
227 | | - expect(writeOps).toContain(op) |
| 215 | + // These assert the PRODUCTION ACL over the protocol's complete operation list. |
| 216 | + // They used to compare the shared test fixture against itself, which certified |
| 217 | + // whatever the fixture said — including, for a while, the read-role grants that |
| 218 | + // let a read-only member persist block positions. |
| 219 | + |
| 220 | + it('grants admin everything write has, plus the admin-only operations', () => { |
| 221 | + for (const operation of ALL_SOCKET_OPERATIONS) { |
| 222 | + if (checkRolePermission('write', operation).allowed) { |
| 223 | + expect(checkRolePermission('admin', operation).allowed).toBe(true) |
| 224 | + } |
| 225 | + } |
| 226 | + // Strictly greater: at least one operation admin holds and write does not. |
| 227 | + const adminOnly = ALL_SOCKET_OPERATIONS.filter( |
| 228 | + (operation) => |
| 229 | + checkRolePermission('admin', operation).allowed && |
| 230 | + !checkRolePermission('write', operation).allowed |
| 231 | + ) |
| 232 | + expect(adminOnly.length).toBeGreaterThan(0) |
| 233 | + }) |
| 234 | + |
| 235 | + it('grants read nothing, so it is trivially a subset of write', () => { |
| 236 | + const readAllowed = ALL_SOCKET_OPERATIONS.filter( |
| 237 | + (operation) => checkRolePermission('read', operation).allowed |
| 238 | + ) |
| 239 | + expect(readAllowed).toEqual([]) |
| 240 | + }) |
| 241 | + |
| 242 | + it('keeps the shared fixture in step with the production ACL', () => { |
| 243 | + // The fixture is a convenience mirror; drift between it and the real table is |
| 244 | + // what made the stale read grants look intentional. |
| 245 | + for (const operation of ALL_SOCKET_OPERATIONS) { |
| 246 | + const fixtureAllows = ROLE_ALLOWED_OPERATIONS.read.includes( |
| 247 | + operation as (typeof ROLE_ALLOWED_OPERATIONS.read)[number] |
| 248 | + ) |
| 249 | + expect(fixtureAllows).toBe(checkRolePermission('read', operation).allowed) |
228 | 250 | } |
229 | | - }) |
230 | | - |
231 | | - it('should verify read has minimal permissions', () => { |
232 | | - const readOps = ROLE_ALLOWED_OPERATIONS.read |
233 | | - expect(readOps).toHaveLength(2) |
234 | | - expect(readOps).toContain('update-position') |
235 | | - expect(readOps).toContain('batch-update-positions') |
236 | 251 | }) |
237 | 252 | }) |
238 | 253 |
|
|
0 commit comments