Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Model.accessible_through #721

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

xjunior
Copy link

@xjunior xjunior commented Jul 16, 2021

Provides a scope within the model to find instances of the model accessible by the given ability within the given action/subject permission pair.

I.E.:

Given the scenario below

class Department < ActiveRecord::Base
end

class User < ActiveRecord::Base
  belongs_to :department
end

class Ability
  include CanCan::Ability

  def initialize(user)
    can :contact, User, { department: { id: user.department_id } }
    can :contact, User, { department: { id: user.managing_department_ids } } if user.manager?
  end
end

The following would give you a list of departments that the given ability can contact their users:

> user = User.new(department_id: 13, manager: false)
> ability = Ability.new(user)
> Department.accessible_through(ability, :contact, User).to_sql
=> SELECT * FROM departments WHERE id = 13
#
> user = User.new(department_id: 13, managing_department_ids: [2, 3, 4], manager: true)
> ability = Ability.new(user)
> Department.accessible_through(ability, :contact, User).to_sql
=> SELECT * FROM departments WHERE ((id = 13) OR (id IN (2, 3, 4)))

Sometimes the name of the relation doesn't match the model:

class User < ActiveRecord::Base
  has_many :managing_users, class_name: "User", foreign_key: :managed_by_id
end

class Ability
  include CanCan::Ability

  def initialize(user)
    can :contact, User, { managing_users: { id: user.department_id } }
  end
end

When that happens, you can override it with relation. This would give you a list of departments that the given ability can contact their users:

> user = User.new(department_id: 13, manager: false)
> ability = Ability.new(user)
> Department.accessible_through(ability, :contact, User, relation: :managing_users).to_sql
=> SELECT * FROM departments WHERE id = 13
>
> user = User.new(department_id: 13, managing_department_ids: [2, 3, 4], manager: true)
> ability = Ability.new(user)
> Department.accessible_through(ability, :contact, User, relation: :managing_users).to_sql
=> SELECT * FROM departments WHERE ((id = 13) OR (id IN (2, 3, 4)))

Copy link

@garettarrowood garettarrowood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! This would be helpful.

@coorasse
Copy link
Member

coorasse commented Jul 6, 2022

I am not exactly sure that I understand the use case. It feels to me that you should be able to do that already with Department.accessible_by(:contact, current_ability)

@xjunior
Copy link
Author

xjunior commented Jul 6, 2022

@coorasse not really, because the rules are defined on User, not on Department.

    can :contact, User, { department: { id: user.department_id } }
    can :contact, User, { department: { id: user.managing_department_ids } } if user.manager?

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.

3 participants