让Pundit授权在Rails 5上使用命名空间

时间:2017-02-25 09:46:43

标签: ruby-on-rails ruby ruby-on-rails-5 pundit

关于让Rails 5和Pundit授权使用命名空间的问题。

使用Pundit,在控制器中我想使用policy_scope([:admin, @car],它将使用位于app/policies/admin/car_policy.rb的Pundit策略文件。我在尝试Pundit使用此命名空间时遇到问题 - 没有命名空间,它可以正常工作。

应用程序正在运行:

  • Rails 5
  • 设计身份验证
  • Pundit授权

例如,我的命名空间用于admins

route.rb文件如下所示:

# config/routes.rb

devise_for :admins

root: 'cars#index'
resources :cars

namespace :admin do
  root 'cars#index'
  resources :cars
end

我已经设置了一个Pundit ApplicationPolicy并让命名空间使用Pundit的authorize方法:@record = record.is_a?(Array) ? record.last : record

# app/policies/application_policy.rb

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record.is_a?(Array) ? record.last : record
  end

  def scope
    Pundit.policy_scope!(user, record.class)
  end

  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      scope
    end
  end
end

Admin::CarsController这有效authorize [:admin, @cars]

class Admin::CarsController < Admin::BaseController
  def index
    @cars = Car.order(created_at: :desc)
    authorize [:admin, @cars]
  end

  def show
    @car = Car.find(params[:id])
    authorize [:admin, @car]
  end
end

但我想使用政策范围

class Admin::CarPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      if user?
        scope.all
      else
        scope.where(published: true)
      end
    end
  end

  def update?
    user.admin? or not post.published?
  end
end

Admin::CarsController

class Admin::CarssController < Admin::BaseController
  def index
    # @cars = Car.order(created_at: :desc) without a policy scope
    @cars = policy_scope([:admin, @cars]) # With policy scope / doesn't work because of array.
    authorize [:admin, @cars]
  end

  def show
    # @car = Car.find(params[:id]) without a policy scope
    @car = policy_scope([:admin, @car]) # With policy scope / doesn't work because of array.
    authorize [:admin, @car]
  end
end

我收到错误是因为Pundit没有找Admin::CarPolicy。我认为它是因为它是一个数组。

我认为在控制器中我可以做类似policy_scope(Admin::Car)的事情,但这不起作用:)。

非常感谢任何助理。

更新

我在Pundit Github问题页面上找到了这个:https://github.com/elabs/pundit/pull/391

这修复了policy_scope的名称空间处理,这是我想要的。

它更新了Pudit宝石 - &gt; policy_scope!中的lib/pundit.rb方法。

自:

def policy_scope!(user, scope)
  PolicyFinder.new(scope).scope!.new(user, scope).resolve
end

要:

def policy_scope!(user, scope)
  model = scope.is_a?(Array) ? scope.last : scope
  PolicyFinder.new(scope).scope!.new(user, model).resolve
end

我的问题是,如何在我的Rails应用程序中使用它?它被称为重载或猴子修补吗?

我在考虑在pundit.rb目录中添加config/initializer并使用module_eval但不确定如何执行此操作,因为policy_scope!位于module Pundit内且{ {1}}。

我认为这样可行,但事实并非如此 - 因为class << self位于policy_scope!内。

class << self

1 个答案:

答案 0 :(得分:2)

Medir在Pundit Github问题页面上发布了该解决方案。

只需将您的application_policy.rb更改为:

class ApplicationPolicy
  index? show?....

  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user = user
      # @scope = scope
      @scope = scope.is_a?(Array) ? scope.last : scope #This fixes the problem
    end

    def resolve
      scope
    end
  end
end

然后您可以使用:

policy_scope([:admin, @cars])