Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 4 - Pundit with Rolify - permitting a group of roles

I am trying to make an app with Rails 4.

I have defined a series of roles with Rolify gem.

Now, I want to use pundit to allow users with a role to do certain things. Where more than one type of role can do a thing, I have defined a group of roles.

In my application_policy, I have defined private methods which set out the groups of roles that I want to use in the pundit permissions.

My application policy instantiates user and record. I then define record as the name of the relevant model (the same name as the policy for that model).

I have:

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    false
  end

  def show?
    scope.where(:id => record.id).exists?
  end

  def create?
    false
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  def scope
    Pundit.policy_scope!(user, record.class)
  end

  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      scope
    end
  end

  private


    def cf_legal
      [ :Admin, :CF_Legal, :CF_Policy_Manager ]
    end

    def cf_content
      [ :Admin, :CF_Author, :CF_Editor ]
    end


end

Then in my content policy, I want to say:

def record
   content
end

def create
    user.has_role? :cf_content
end

When I save this and try it, I can't see the thing that I am supposed to see (as a user with the role Author.

Can anyone see how to do this?

like image 707
Mel Avatar asked Feb 21 '16 03:02

Mel


2 Answers

tl;dr: Use query methods on your Policy class.

First, the model should have it's own Policy class that (optionally) extends your ApplicationPolicy class. Let's say your model is called Post. Then you could do something like:

class PostPolicy < ApplicationPolicy

   attr_reader :user, :post

   def initialize(user,post)
     @user = user
     @post = post
   end

   def create?
     cf_content.all? { |role| user.has_role?(role) }
   end

   private

   def cf_content
    [ :Admin, :Author, :Editor ]
   end
end


class PostsController
  def create
    @post = Post.new(params[:post])
    authorize @post, :create?
    # @post.save and redirect, etc.
  end
end

And the authorize call will invoke the create? query method and check if the user has the roles in cf_content.

You may not even need to add the second argument, create?, as the pundit documentation says:

The authorize method automatically infers that Post will have a matching PostPolicy class, and instantiates this class, handing in the current user and the given record. It then infers from the action name, that it should call update? on this instance of the policy.

Or in your case, create? instead of update?.

like image 180
Josh Deeden Avatar answered Oct 18 '22 05:10

Josh Deeden


You can check for multiple roles like this :

user.admin? or user.author?

If you want to check for role from an array you can also check with : cf_content.include?(user.role) OR cf_content.include?(user.role.title) whichever suits the scenairio.

You can also check for instance bound roles with -> user.applied_roles and see if the roles it returns includes your expected role.

like image 36
Muhammad Yawar Ali Avatar answered Oct 18 '22 05:10

Muhammad Yawar Ali