Skip to content

Refactor permissions logic with cancancan gem and define Abilities for user roles#368

Draft
cycomachead wants to merge 1 commit intomainfrom
cycomachead/72-refactor-permissions-with-cancancan/1
Draft

Refactor permissions logic with cancancan gem and define Abilities for user roles#368
cycomachead wants to merge 1 commit intomainfrom
cycomachead/72-refactor-permissions-with-cancancan/1

Conversation

@cycomachead
Copy link
Copy Markdown
Contributor

General Info

Changes

This PR refactors the permissions system to use the CanCanCan gem, replacing ad-hoc role checks scattered across controllers with a centralized Ability model.

Why

Previously, authorization was handled inconsistently — some controllers checked @role == 'instructor', others used params[:role] (an insecure pattern that allowed clients to self-report their role), and some had no checks at all. This made it difficult to reason about what each role could do and easy to miss edge cases.

What Changed

Permissions Model (app/models/ability.rb)

A new Ability class defines all permissions in one place:

  • Site admins: Full access to everything
  • Course admins (teachers + lead TAs): Full management of their specific courses, including settings, requests, assignments, and enrollments
  • Regular TAs: Can view everything within a course, approve/deny requests, toggle assignments, and sync assignments/enrollments — but cannot update CourseSettings, FormSetting, or toggle extended circumstances status
  • Students: Can only read their course/assignments and manage their own extension requests
  • Any staff member (in any course): Can create/import new courses
  • All logged-in users: Can view the course import page

Removed enrollments (removed: true) are excluded from all permission grants.

Bug Fix

Course#user_role was not mapping leadta to the instructor role — it only checked for teacher and ta. This has been fixed to use UserToCourse.staff_roles.

Controller Updates

  • ApplicationController: Added a global rescue_from CanCan::AccessDenied handler (supports HTML and JSON responses). Simplified ensure_instructor_role to delegate to CanCanCan.
  • CoursesController: Added authorize! calls for show, new, edit, create, sync_assignments, sync_enrollments, enrollments, and delete. Fixed index to include leadta in the staff courses listing.
  • RequestsController: Added authorize! for all sensitive actions; removed the old check_instructor_permission before-action.
  • CourseSettingsController / FormSettingsController: Replaced ensure_instructor_role with model-specific authorization helpers.
  • AssignmentsController: Replaced manual role check (which used params[:role]) with authorize! :toggle_enabled, @assignment.
  • UserToCoursesController: Replaced ensure_course_admin with authorize! :toggle_allow_extended_requests, @enrollment.

Testing

  • Added 72 new Ability spec tests covering all roles (admin, teacher, lead TA, TA, student, unauthenticated, multi-role, and removed enrollment scenarios).
  • Updated existing controller specs to use real database enrollments instead of mocked roles or params[:role], making tests more accurate and secure.
  • Added :as_ta and :as_leadta factory traits for UserToCourse.
  • All existing tests pass with the refactored authorization.

Documentation

No additional documentation required. The Ability model itself serves as the canonical reference for what each role can do.

Checklist

  • Name of branch corresponds to story

Superconductor Ticket Implementation | App Preview | Guided Review

Refactor permissions logic to use the CanCanCan gem, replacing manual
role checks with a centralized Ability model. This defines granular
permissions for site admins, course admins (teachers/lead TAs),
regular TAs, and students.

- Site admins: full system access.
- Course admins: full management of their specific courses.
- TAs: can view data and manage requests but cannot change settings.
- Students: can only manage their own requests.
- Staff: can create/import new courses.

Includes a global access-denied handler and comprehensive test coverage
for all roles and edge cases.

Co-authored-by: Claude Code <noreply@anthropic.com>
@cycomachead cycomachead marked this pull request as draft April 14, 2026 01:54
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