Add a read-only role per app for calendar, feedback and tickets
A grant can now be `read` instead of `access`: requireAppAccess lets it through for GET/HEAD/OPTIONS and answers 403 to every other method, so new write routes are covered without being marked. `read` and `access` on the same app are rejected together; user management has no read role. /tickets/admin/me and /feedback/admin/me report `readOnly`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
9 files changed
+377
-10
No files matched your search
@@ -1,9 +1,12 @@
|
||||
import {describe, expect, it} from 'vitest';
|
||||
import {
|
||||
ACCESS_ROLE,
|
||||
READ_ROLE,
|
||||
appsOf,
|
||||
canWrite,
|
||||
isAppPermission,
|
||||
isAppRole,
|
||||
isReadOnly,
|
||||
toPermissions
|
||||
} from '../../src/models/admin/admin.schema.js';
|
||||
|
||||
@@ -59,6 +62,44 @@ describe('toPermissions', () => {
|
||||
it('reads an empty list as "no permissions", not as invalid', () => {
|
||||
expect(toPermissions([])).toEqual([]);
|
||||
});
|
||||
it('reads the read-only role', () => {
|
||||
expect(toPermissions([{app: 'tickets', role: READ_ROLE}])).toEqual([
|
||||
{app: 'tickets', role: READ_ROLE}
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects full and read-only access to the same app together', () => {
|
||||
expect(toPermissions(['tickets', {app: 'tickets', role: READ_ROLE}])).toBeNull();
|
||||
});
|
||||
|
||||
it('allows full access to one app next to read-only access to another', () => {
|
||||
expect(toPermissions(['tickets', {app: 'feedback', role: READ_ROLE}])).toEqual([
|
||||
{app: 'tickets', role: ACCESS_ROLE},
|
||||
{app: 'feedback', role: READ_ROLE}
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects read-only access to user management', () => {
|
||||
expect(toPermissions([{app: 'admin', role: READ_ROLE}])).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('canWrite / isReadOnly', () => {
|
||||
it('lets only the access role write', () => {
|
||||
const permissions = [
|
||||
{app: 'tickets' as const, role: ACCESS_ROLE},
|
||||
{app: 'feedback' as const, role: READ_ROLE}
|
||||
];
|
||||
expect(canWrite(permissions, 'tickets')).toBe(true);
|
||||
expect(canWrite(permissions, 'feedback')).toBe(false);
|
||||
expect(isReadOnly(permissions, 'tickets')).toBe(false);
|
||||
expect(isReadOnly(permissions, 'feedback')).toBe(true);
|
||||
});
|
||||
|
||||
it('is neither for an app without any grant', () => {
|
||||
expect(canWrite([], 'calendar')).toBe(false);
|
||||
expect(isReadOnly([], 'calendar')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAppRole', () => {
|
||||
|
||||
Reference in new issue
Block a user