Permissions (IAM): moderators and scoped grants #40

Merged
anas merged 1 commits from feature/permissions into main 2026-10-08 21:22:26 +00:00
Owner

Closes #29. Step 4 of the plan; the permission model that content moderation (#20) will use.

Roles: قاری (reader), موڈریٹر L2 (junior), سینئر موڈریٹر L1 (senior), ایڈمن. Changed with a dropdown on /admin.

Grants (admins give them to moderators on /admin/user/<id>, linked as "اجازتیں" next to each moderator):

Part Options
Scope all poets · one poet · one book/section (with everything inside it) · one work
Content poets, books, works, dictionary (dictionary grants are site-wide only)
Actions create (نیا), edit (ترمیم), delete (حذف), arrange (ترتیب)

The target is picked by pasting the poet, book or work's page link (e.g. /p266, /p266/ghazal, /p266/ghazal/sh7870; full URLs work too). Grants can be revoked; demoting a moderator to reader or admin clears their grants; grant and revoke are in the audit log.

One check: can(user, action, content, target) in api/src/permissions.ts. Admins can do everything, readers nothing; a moderator only what a grant covers (the work's poet, its book and every section above it are matched). Every moderation endpoint in #31/#32 will call it.

Tested

  • npm test: 23 pass. On real content: a grant on Ghalib's غزل section allows editing a ghazal in it but not a Ghalib work outside it, not a delete, not poets, not Iqbal; a poet grant on Iqbal allows creating books; dictionary grants refused unless site-wide; unknown page links refused; moderators cannot manage grants; revoke and demotion take effect.
  • On the running site: granting to a reader is refused; the L2 role, a book grant, a poet grant and a dictionary grant through the forms; the dictionary-on-a-poet error; the users list shows the role and the permissions link; audit entries in Urdu.

🤖 Generated with Claude Code

Closes #29. Step 4 of the plan; the permission model that content moderation (#20) will use. **Roles**: قاری (reader), موڈریٹر L2 (junior), سینئر موڈریٹر L1 (senior), ایڈمن. Changed with a dropdown on `/admin`. **Grants** (admins give them to moderators on `/admin/user/<id>`, linked as "اجازتیں" next to each moderator): | Part | Options | |---|---| | Scope | all poets · one poet · one book/section (with everything inside it) · one work | | Content | poets, books, works, dictionary (dictionary grants are site-wide only) | | Actions | create (نیا), edit (ترمیم), delete (حذف), arrange (ترتیب) | The target is picked by pasting the poet, book or work's page link (e.g. `/p266`, `/p266/ghazal`, `/p266/ghazal/sh7870`; full URLs work too). Grants can be revoked; demoting a moderator to reader or admin clears their grants; grant and revoke are in the audit log. **One check**: `can(user, action, content, target)` in `api/src/permissions.ts`. Admins can do everything, readers nothing; a moderator only what a grant covers (the work's poet, its book and every section above it are matched). Every moderation endpoint in #31/#32 will call it. **Tested** - `npm test`: 23 pass. On real content: a grant on Ghalib's غزل section allows editing a ghazal in it but not a Ghalib work outside it, not a delete, not poets, not Iqbal; a poet grant on Iqbal allows creating books; dictionary grants refused unless site-wide; unknown page links refused; moderators cannot manage grants; revoke and demotion take effect. - On the running site: granting to a reader is refused; the L2 role, a book grant, a poet grant and a dictionary grant through the forms; the dictionary-on-a-poet error; the users list shows the role and the permissions link; audit entries in Urdu. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
anas added 1 commit 2026-10-08 21:13:21 +00:00
- Roles: reader, mod-l2, mod-l1, admin. Grants: scope (all, poet, book/section with everything in
  it, one work) x content (poets, books, works, dictionary) x actions (create, edit, delete,
  arrange); dictionary grants are site-wide. can() in api/src/permissions.ts is the one check.
- Admin API and pages: role dropdown on /admin; /admin/user/:id lists a moderator's grants, adds
  them (target by poet id or page link) and revokes them; demoting a moderator clears their grants;
  grant and revoke go to the audit log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anas merged commit 264a42c996 into main 2026-10-08 21:22:26 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: anas/divan#40
No description provided.