feat(exam): /api/exam template CRUD router (as-user RLS, E1 fix)
S4-5: new routers/exam/ package mounted at /api/exam (R5.1/E5, not under
/database/). Template CRUD with hybrid persistence (R5.2):
- POST/GET/GET{id}/PUT{id}/DELETE{id} /templates + PATCH /questions/{qid}
- Calls Supabase AS THE USER via SupabaseAnonClient.for_user (E1 fix), so the
RLS in 72-exam-marker.sql is enforced; no service-role for user-facing ops.
- Institute resolved/validated via the user_institute_ids() SECURITY DEFINER
RPC (institute_memberships is deny-all as-user per E4); client-supplied
institute_id is validated, never trusted (R5.5).
- Ownership pre-checked before writes (E2); out-of-scope ids read back as 404
under RLS (IDOR-safe). Soft-delete archives, never hard-deletes.
- PUT full-replace preserves client UUIDs as Neo4j join keys (spec §2).
- eb_exams.exam_code denormalised via a documented service-role catalogue
lookup (eb_exams is shared reference data, deny-all as-user per E4).
Unit tests cover auth, CRUD, ownership/IDOR, institute validation, soft-delete.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6ce6272a1e
commit
f52c3267ca
@@ -0,0 +1,9 @@
|
||||
"""Exam-marker API package (/api/exam/).
|
||||
|
||||
A clean top-level router group (R5.1/E5), deliberately NOT nested under /database/. Every
|
||||
endpoint authenticates the JWT and calls Supabase as-the-user so the RLS in
|
||||
volumes/db/cc/72-exam-marker.sql is enforced (spec E1/E2 fixes).
|
||||
"""
|
||||
from routers.exam.templates import router
|
||||
|
||||
__all__ = ["router"]
|
||||
Reference in New Issue
Block a user