Search code examples
ruby-on-railsrubycancancancancan

cancancan get all dynamic permissions


I'm writing API where I need to expose all available permissions with ids and other info to an api user.

I have the following structure

class User < ApplicationRecord
  has_many :permissions, dependent: :destroy
end

class Permission < ApplicationRecord
  belongs_to :user, dependent: :destroy
end

and I have ability


class Ability
  include CanCan::Ability

  def initialize(user)
    user_permissions(user)
    dynamic_permissions(user)
  end

  def user_permissions(user)
    can :manage, :dashboard if user.admin?
  end

  def dynamic_permissions(user)
    user.permissions.each do |permission|
    #...
      next branch_permissions(permission) if permission.subject_class == 'branch'
    end
  end

  def branch_permissions(permission)
    can permission.action.to_sym, Branch, id: permission.allowed_ids
  end

and in controller I want to achieve all permissions => can and cannot possible for that user. I have checked docs already and found #permissions. But that doesn't work for dynamic permissions that I have for branch

So I expect the following to be done in cancancan way, providing pseudo-code

  Ability.new(User.find(params[:id]).dynamic_permissions =>
  # {:can=>{:manage=>{"dashboard"=>[]}, :read=>{"Branch"=>['id1', 'id2']}, :index=>{"Branch"=>['id1', 'id2']}, :show=>{"Branch"=>['id1', 'id2']}}, :cannot=>{}}

Current #permissions cancancan method returns only static permissions that are hardcoded to ability file missing all dynamic evaluation.

{:can=>{:manage=>{"dashboard"=>[]}, :read=>{"Branch"=>[]}, :index=>{"Branch"=>[]}, :show=>{"Branch"=>[]}}, :cannot=>{}}

I know I can modify and write custom method that will fetch all of that, but I'm wondering what is accepted approach achieving this.


Solution

  • ok, it was actually simple. Reading sources helped.

      def branch_permissions(permission)
        can permission.action.to_sym, Branch, id: permission.allowed_ids
      end
    

    this code should be changed to

      def branch_permissions(permission)
        can permission.action.to_sym, Branch, id: permission.allowed_ids.map(&:to_sym)
      end
    

    This method parse_attributes_from_extra_args will treat symbols as attributes for internal state, and then in permissions will show this as I expected. You can serialize those symbols to string then

    #  Ability.new(User.find(params[:id]).permissions
    => {:can=>{:manage=>{"dashboard"=>[]}, :read=>{"Branch"=>[:id1, :id2]}, :index=>{"Branch"=>['id1', 'id2']}, :show=>{"Branch"=>[:id1', :id2]}}, :cannot=>{}}