Part of #59.
Part of the broken-access-control remediation epic. Depends on Phase 1. This is the only write-side identity-trust finding — highest integrity risk.
Findings addressed
Work
- Derive
tutor_id from the authenticated JWT (current_user → DB user); ignore/validate any body-supplied tutor_id.
- Validate
student_id and target_id against a TutorStudentAssignment (src/models/tutor_student.py) before mutating Summary/PracticeAssignment or committing the Override.
- Apply the same relationship check to the GET history route.
Definition of Done
Affected files
src/api/handlers/overrides.py, src/models/tutor_student.py
Part of #59.
Part of the broken-access-control remediation epic. Depends on Phase 1. This is the only write-side identity-trust finding — highest integrity risk.
Findings addressed
overrides.py:36(POST /overrides/) —tutor_id,student_id, andtarget_idare taken from the request body. The handler only checks that some user withtutor_idexists; the authenticated caller (current_user) is never compared to it. Result: any tutor can attribute anOverrideaudit record to another tutor and overwrite arbitrarySummary.next_steps/narrativeorPracticeAssignment.ai_question_text/ai_answer_textfor any student.overrides.py:147(GET /overrides/{student_id}) — returns any student's override history with no tutor↔student relationship check.Work
tutor_idfrom the authenticated JWT (current_user→ DB user); ignore/validate any body-suppliedtutor_id.student_idandtarget_idagainst aTutorStudentAssignment(src/models/tutor_student.py) before mutatingSummary/PracticeAssignmentor committing theOverride.Definition of Done
override.tutor_idalways equals the authenticated caller.403) when the caller has no assignment to the target student.403for unrelated tutors; owner/admin works.Affected files
src/api/handlers/overrides.py,src/models/tutor_student.py