Pundit: Best way to use policy_scope with custom query data

Created on 2 Jul 2014  路  3Comments  路  Source: varvet/pundit

Currently, I'm trying to use policy_scope in index (with pagination) and search controller action, but I've found no way to pass additional arguments to pundit scope. My current workaround is manually creating policy scope and resolve them like this

#user_controllers
@users = UserPolicy::Scope.new(current_admin, User.all(page: params['page'], per_page: params['per_page'], sort_by: params['sort_by'], order: params['order'])).resolve
#user_policy
class Scope < Struct.new(:admin, :scope)
  def resolve
    if admin.has_permission('index', 'User')
      scope
    end
  end
end

User is not an ActiveRecord class, I'm using Her as the ORM to my REST API.
Is there a better way instead of manually initiate scope like this?

Most helpful comment

Instead of passing additional arguments to the pundit scope you can chain your query to the scope that's returned (typically an ActiveRecord::Relation). That way, the scope in the UserPolicy is only tasked with determining what users that the current_user is _authorized_ to see.

All your other concerns, like pagination, are then handled in the controller. Please see below for an example.

Users Controller

# users_controller
def index
  @users = policy_scope(User).where(archived: false).paginate(@page_options)
end

User Policy

class UserPolicy < ApplicationPolicy
  class Scope < Struct.new(:user, :scope)
    def resolve
      if user.is_admin_user?
        scope.all
      else
        scope.where(account_id: user.account_id)
      end
    end
  end
end

Hope this helps.

All 3 comments

Instead of passing additional arguments to the pundit scope you can chain your query to the scope that's returned (typically an ActiveRecord::Relation). That way, the scope in the UserPolicy is only tasked with determining what users that the current_user is _authorized_ to see.

All your other concerns, like pagination, are then handled in the controller. Please see below for an example.

Users Controller

# users_controller
def index
  @users = policy_scope(User).where(archived: false).paginate(@page_options)
end

User Policy

class UserPolicy < ApplicationPolicy
  class Scope < Struct.new(:user, :scope)
    def resolve
      if user.is_admin_user?
        scope.all
      else
        scope.where(account_id: user.account_id)
      end
    end
  end
end

Hope this helps.

Thanks, @johnotander!

Here's a nice way to semantically limit existing scopes.

scope.all.merge(user.companies)
Was this page helpful?
0 / 5 - 0 ratings