Skip to content

accessible_by permanently modifies a rule's conditions via normalize_conditions, causing can? to fail #876

Description

@rockorequin

The ConditionNormalizer in accessible_by permanently modifies a rule's conditions when you call accessible_by, meaning that a call to the can? method that worked before calling accessible_by may not work afterwards.

For example, say I have the models Client, Project, and Activity, where an activity belongs to a project and a project belongs to a client. In Ability, I can create a rule to say that a user can read any project or activity belonging to client with id 10:

can :read, [ Project, Activity ], client: { id: 10 }

This creates a rule with conditions: { client: { id: 10 } }.

Then if I run this code in the controller:

    @project = Project.first
    can? :read, @project
    @activities = Activity.accessible_by current_ability
    can? :read, @project

then the first call to can? runs without error. The call to Activity.accessible_by current_ability permanently changes the rule's conditions to { project: { client: { id: 10 } } } via a call to normalize_conditions, and the second identical call to can? then raises a NoMethodError saying "undefined method `project' for an instance of Project" because the new conditions make cancancan call @project.project.client instead of @project.client.

Is the rule illegal, ie should you only ever create a rule with an array of models that all have through reflections to the condition OR all have belongs_to reflections to the condition? Or should accessible_by not permanently change the conditions for the rule?

I'm using cancancan 3.6.1 and Rails 8.0.1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions