Active Admin、Devise 和 Pundit (Pundit::PolicyScopingNotPerformedError)

Active Admin, Devise and Pundit (Pundit::PolicyScopingNotPerformedError)

我有一个现有的 Rails 应用,其中 DeviseUser 模型进行身份验证,Pundit 针对链接 UserEnrollment 模型进行身份验证] 到我的 Company 模型。 UserCompany 都在公寓 gem 的 public 架构中。我不怀疑公寓是问题的一部分,但我想我会提到它。

我使用 AdminUser 添加了 Active Admin class - 我想将我的管理员用户与应用程序用户分开。

如果我尝试访问 /admin/admin/dashboard,我得到:

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

如果我尝试像 /admin/users 这样的模型,Pundit 似乎会忽略 active_admin 政策并转到主要应用程序政策。在我的例子中,应用程序抛出异常,因为它期望 EnrollmentAdminUser.

如果我禁用:

##/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]

一切正常,但后来我在主应用程序中丢失了 Pundit 等。

这是我的代码的要点:

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

以下是可以找到的关于此问题的相关 post:

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

我希望在此 post (Can you disable Pundit with Devise and Active Admin?) 中一起禁用 Pundit,但让它工作会很好。

更新

我已经解决了,但我仍然不知道这是否应该开箱即用,而且我有一些奇怪的问题导致了所有这些。要点已更新。

我最终使用了:

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

还有一点:

Documentation for conditional before_action/before_filter

和下面的一些答案。我硬塞进一个过滤器以强制 AA 调用对 AA 内部资源和集合的授权。接下来是添加策略范围,但现在我的脑袋太疼了。

我还必须添加另一个过滤器来绕过仪表板上的身份验证,因为它是无头的。到目前为止似乎有效。

更新 2

嗯……我想我说得太早了。只有当我以常规方式登录时,这一切才有效 User - 如果我注销,一切都会再次崩溃。

您可以使用 skip_policy_scope 跳过 Active Admin 控制器中的范围界定吗?

参见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.

@dan-tappin 我想您已经根据您的评论找到了类似的解决方案,但这是我最终添加到我的每个 AA 模型注册中的内容:

#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

基本上,这利用了使用正常 rails before_filter 语法在控制器范围内执行的能力,以使用 :only 限制执行。然后因为 before_filter 发生在 inherrited_resources 过滤器之后,我们可以访问 "resource" 并且我们可以像通常对任何模型实例一样对其进行授权。参见:https://github.com/activeadmin/activeadmin/issues/1108#issuecomment-14711733

首先需要策略范围的原因是因为正常的专家安装需要 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

它期望调用所有索引操作的模型策略范围。仪表板控制器默认呈现索引操作,因此需要此 before_filter hack。