Skip to content

Refactor RBAC permissions logic to centralize and test without external gems#369

Open
cycomachead wants to merge 1 commit intomainfrom
cycomachead/73-refactor-rbac-permissions/1
Open

Refactor RBAC permissions logic to centralize and test without external gems#369
cycomachead wants to merge 1 commit intomainfrom
cycomachead/73-refactor-rbac-permissions/1

Conversation

@cycomachead
Copy link
Copy Markdown
Contributor

Centralize RBAC Permissions with CoursePolicy and Authorization Concern

This PR replaces decentralized @role string comparisons scattered across controllers and views with a dedicated CoursePolicy class and Authorization controller concern, removing reliance on external gems while implementing modern RBAC practices.

General Info

Changes

Previously, authorization was handled ad-hoc throughout controllers and views using @role == 'instructor' string comparisons. This approach was fragile, untested, and didn't correctly handle all role types (e.g., leadta was not recognized as a staff role in several places).

New files:

  • app/policies/course_policy.rb — Centralized RBAC policy class defining four role tiers:
    • site_admin: User.admin? flag, can do everything
    • course_admin: teacher or leadta enrollment, can manage everything in a course
    • staff (TA): regular TA, can view everything, approve/deny requests, sync assignments/enrollments
    • student: can only manage their own extensions
  • app/controllers/concerns/authorization.rb — Controller concern providing current_policy, authorize!, and deny_access! helpers with consistent redirect/JSON error behavior

Key behavioral changes:

Action Before After
Course/form settings Any "instructor" (teacher or TA) Course admins only (teacher/leadta)
Toggle assignments Any "instructor" Course admins only
Approve/deny requests Any "instructor" All staff (teacher/leadta/ta)
Cancel/delete requests Any user Staff only (students blocked)
Sync assignments/enrollments No role check Staff only
View enrollments Any "instructor" All staff
leadta role Not handled (returned nil in some paths) Treated as staff + course_admin
Auth failure redirect courses_path course_path(@course) when course is known

The ensure_instructor_role method in ApplicationController now delegates to authorize! :staff? rather than checking @role == 'instructor'. The user_role method on Course now uses UserToCourse.staff_roles to correctly include leadta in the staff check.

Testing

Added 58 comprehensive unit tests in spec/policies/course_policy_spec.rb covering every permission method for all role combinations (site_admin, teacher, leadta, ta, student, unenrolled).

Updated existing controller specs to use real database enrollments instead of allow_any_instance_of stubs, making tests more accurate and resilient. Added new controller-level tests for:

  • Regular TAs being denied access to course/form settings
  • Students being denied access to enrollments, sync endpoints, and assignment toggling
  • Lead TAs being granted course admin privileges (toggle extended circumstances)

All 448 existing tests continue to pass.

Documentation

No external documentation required. The CoursePolicy class is thoroughly documented inline with role definitions and usage examples.

Checklist

  • Name of branch corresponds to story

Superconductor Ticket Implementation | App Preview | Guided Review

…oncern

Replaces decentralized `@role` string comparisons with a dedicated
CoursePolicy class and controller Authorization concern. This refactor
removes reliance on external gems while implementing modern RBAC
practices.

Key changes:
- Created CoursePolicy to define four role tiers: site_admin,
  course_admin (teacher/leadta), staff (TA), and student.
- Restricted course/form settings and assignment toggling to course
  admins only (previously open to all TAs).
- Prevented students from canceling or deleting requests.
- Added 58 comprehensive tests for all role/permission combinations.
- Removed legacy `@role == 'instructor'` checks across controllers
  and views.

Co-authored-by: Claude Code <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant