0

I currently have Devise and Pundit working great in a closed system that requires a user be logged in to see anything.

The new goal: How can I use Pundit to require an AdminUser be logged in to access the Admin namespace?

I have a separate AdminUser model, separate Admin controllers, Policies and Namespace:

routes.rb

namespace :admin do
  root to: 'home#index'
  resources :users
end

devise_for :admin_users, skip: [:sessions]
as :admin_user do
  get 'admin/signin', to: 'admin/devise/sessions#new', as: :new_admin_user_session
  post 'admin/signin', to: 'admin/devise/sessions#create', as: :admin_user_session
  delete 'admin/signout', to: 'admin/devise/sessions#destroy', as: :destroy_admin_user_session
end

controllers/admin/admin_controller.rb

class Admin::AdminController < ApplicationController

end

controllers/admin/home_controller.rb

class Admin::HomeController < Admin::AdminController

  def index
    authorize [:admin, :home]
  end

end

policies/admin/admin_policy.rb (Closed system, currently looks for User instead of AdminUser)

class Admin::AdminPolicy
  attr_reader :user, :record

  def initialize(user, record)
    # Must be logged in
    raise Pundit::NotAuthorizedError, "You must be logged in to perform this action" unless user
    @user = user
    @record = record
  end

  def index?
    false
  end

  def show?
    false
  end

  def create?
    false
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      raise Pundit::NotAuthorizedError, "You must be logged in to perform this action" unless user
      @user = user
      @scope = scope
    end

    def resolve
      scope.all
    end
  end
end

policies/admin/home_policy.rb (Example sub-policy of the Admin namespace)

class Admin::HomePolicy < Admin::AdminPolicy

  def index?
    user.present?
  end

end

Both of these policies are setup in the same way as my User policies, and therefore are not looking for an AdminUser. How can I make these work for my AdminUser model?

Kobius
  • 674
  • 7
  • 28

1 Answers1

2

I think you're on the right track. You've already created a namespace for your admin controllers which inherits from the Admin::AdminController. Pundit injects a helper method into your ApplicationController called pundit_user which by default simply returns current_user.

While you could consider not using a separate model for an admin and instead use a proper authorization setup, that might not be convenient for every situation. I'd just like to put it out there for you to think about.

The easiest way I can think of to solve this problem is by simply overriding the helper method in your Admin::AdminController, like so:

class Admin::AdminController < ApplicationController
  def pundit_user
    current_admin_user
  end
end

Mind that this does assume the AdminUser model is setup somewhere along the line of this convention

Jaap Haagmans
  • 6,232
  • 1
  • 25
  • 30
  • Thank you! I spotted that snippet on the Github page and assumed it needed to be in the AdminPolicy. I've spent hours trying countless methods to get it to work, and it just needed to be in the controller. D'oh! Thanks Jaap! – Kobius Aug 28 '18 at 21:51