Reusable guards that answer “can this user access this offering or team?” layered on enrollment data. They complement lower-level helpers in authorizationHelpers.ts (role checks, assertInstructorOrAdmin).
Prefer these guards whenever multiple routes enforce the same enrollment story under /course-offerings or /teams.
| File | Responsibility |
|---|---|
courseOfferingGuards.ts |
Course offering existence + enrollment-based visibility |
teamAccess.ts |
Team ↔ offering linkage + readability rules |
-
assertCourseOfferingExists(offeringId)— verifies the row exists (NotFoundotherwise). -
assertOfferingAccessForUser({ userId, offeringId }, policy)— enrollment gate with selectable policy strings such asanyEnrollment,instructorOnly,teachingStaff. Also grants admins when appropriate (isAdminonUser, depending on helper usage).
Controllers for nested resources (teams, enrollment under an offering, Spark mounts) compose these assertions before Prisma-heavy work.
-
assertTeamBelongsToOffering(teamId, offeringId)— structural check that the team'sCourseOffering.idmatches the URL:offeringId. -
assertTeamReadableByUser({ userId, teamId[, offeringId] })— coherent read authorization forGETstyle operations (handles admin / teaching staff / membership / viewer semantics per implementation).
Teams documentation lists when aggregate /teams/:teamId vs nested /course-offerings/:offeringId/teams endpoints call which helper.
Used heavily by enrollment and teams controllers. projects controllers often call authorizationHelpers directly (deploy/stop paths also respect offering locks and admin/staff nuances).
Routing conventions:
- Enrollment boundary = course offerings — roster, nested teams listing, Spark, and bulk project tagging typically live under
/course-offerings/:offeringId/.... - Aggregate team routes —
GET|PUT|DELETE /teams/:teamIdand member routes operate by team id; useteamAccessso linkage to an offering stays consistent with nested routes (teams).
- Parse
offeringIdwith Zod +paramCoercions. - Call
await assertOfferingAccessForUser(..., 'anyEnrollment')(pick correct policy label from source). - Query Prisma/service.
- Enforce
assertOfferingAccessForUserfor instructor/teaching scopes. - Enforce
assertTeamBelongsToOffering(teamId, offeringId)when both IDs appear together.
When you need assertInstructorOrAdmin-style shortcuts, reuse helpers from utils/authorizationHelpers.ts rather than duplicating enrollment queries.
Prefer adding assert* async functions returning Promise<void> throwing AppError subclasses for consistency (utils**).
| File |
|---|
test/authorization/courseOfferingGuards.test.ts |
test/authorization/teamAccess.test.ts |
test/utils/authorizationHelpers.test.ts |