4
votes

I have an existing Rails app with Devise authenticating the User model and Pundit authenticating against an Enrollment model which links User to my Company model. Both User and Company are in the public schema of the apartment gem. I don't suspect apartment is part of the issue but I figured I would mention it.

I added Active Admin with the AdminUser class - I want to keep my admin user separate from the app users.

If I try to access /admin or /admin/dashboard I get:

Pundit::PolicyScopingNotPerformedError at /admin/users
Pundit::PolicyScopingNotPerformedError

If I try my models like /admin/users Pundit seems to ignore the active_admin policies and goes to the main app policies. In my case the app throws an exception because it's expecting an Enrollment vs the AdminUser.

If I disable:

##/config/initializers/active_admin.rb
  config.authorization_adapter = ActiveAdmin::PunditAdapter

##/controllers/application_controller
  after_action :verify_authorized, except: [:landing, :dashboard], unless: :devise_controller?
  after_action :verify_policy_scoped, only: [:index]

It all works but then I lose Pundit etc in my main app.

Here is a gist of my code:

https://gist.github.com/jasper502/4b2f1b8b6f21a26c64a5

Here are the related posts that could find on this issue:

https://gorails.com/forum/using-pundit-with-activeadmin

How to get Active Admin to work with Pundit after login

I was looking to disable Pundit all together over in this post (Can you disable Pundit with Devise and Active Admin?) but it would be nice to just make this work.

UPDATE

I have work around but I still don't know if this should work out of the box and I have some weird issue causing all of this. Gist updated.

I ended up using:

https://viget.com/extend/8-insanely-useful-activeadmin-customizations

and a bit of:

Documentation for conditional before_action/before_filter

and a bit of the answer below. I shoehorned in a filter to force AA to call authorize on the resources and collections inside AA. Next would be to add the policy scopes but my brain hurts too much now.

I also had to add another filter to bypass authentication on the Dashboard as it's headless. Seems to work so far.

UPDATE 2

Hmmm... I think I spoke too soon. This all works only if I am logged in as a regular User - I if I log out it all falls apart again.

2

2 Answers

6
votes

@dan-tappin I imagine you already found a similar solution based on your comments but here is what I ended up adding to each of my AA model registrations:

#app/admin/user.rb
ActiveAdmin.register User do
  controller do
    before_filter :authorize_index, only: :index
    def authorize_index
      policy_scope(User)
    end

    before_filter :authorize_show_edit_destroy, only: [:show, :edit, :destroy]
    def authorize_show_edit_destroy
      authorize resource
    end
  end
end

Basically this utilizes the ability to execute in the controller scope using normal rails before_filter syntax to limit the execution with :only. Then because the before_filter happens after the inherrited_resources filters we have access to the "resource" and we can authorize against it like you would normally against any model instance. See: https://github.com/activeadmin/activeadmin/issues/1108#issuecomment-14711733

The reason the policy scope is needed in the first place is because a normal pundit installation requires the following in the application_controller.rb

#app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  include Pundit
  protect_from_forgery with: :exception

  #before_action :authenticate_is_admin!

  after_action :verify_authorized, except: [:index, :dashboard], unless: :devise_controller?
  after_action :verify_policy_scoped, only: :index, unless: :devise_controller?

  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  private
  def authenticate_admin!
    redirect_to new_user_session_path unless current_user.admin?
  end

  private
  def pundit_user
    current_user
  end

  private
  def user_not_authorized
    flash[:error] = "You are not authorized to perform this action."
    redirect_to(request.referrer || new_user_session_path)
  end
end

It expects a call to policy scope the model for all index actions. Dashboard controller renders the index action by default thus necessitating this before_filter hack.

0
votes

Can you just skip the scoping in your Active Admin controller with skip_policy_scope?

See the documentation:

If you're using verify_authorized in your controllers but need to conditionally bypass verification, you can use skip_authorization. For bypassing verify_policy_scoped, use skip_policy_scope.