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.
@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.