为什么declarative_authorization会破坏控制器的功能?—Rails



我有一个具有两个动作的Coupons控制器。

class CouponsController < ApplicationController
  filter_resource_access
  def index
    @coupons = Coupon.all
  end
  #generates 10 new coupons on each call
  def generate
    plan_id = params[:plan_id]
    numdays = params[:num_days]
    (1..10).each do |i|
      validChars = %w{ 1 2 3 4 5 6 7 9 a A b B d D e E f g G h H j J k K m M n N p P q Q r R t T }.to_a
      code = (0...6).map{ validChars[ rand(validChars.size) ] }.join
      coupon = Coupon.new
      coupon.code = code
      coupon.plan_id = plan_id
      coupon.active = false
      coupon.subdays = numdays
      coupon.save
    end
    redirect_to :coupons_path
  end
end

在我的视图中像这样调用生成操作:

<h2 class="page-title">Coupons</h2>
<div class="main">
   <%= form_tag "coupons/generate" do -%>
        <%= hidden_field_tag 'user[plan_id]', "1" %>
        <%= hidden_field_tag 'user[num_days]', "150" %>
      <%= submit_tag "Generate 10 Coupons!", :class => "primary button" %>
   <% end -%>
    <table border="0" class="list">
        <thead>
            <tr>
                <th>Code</th><th>Plan</th><th>Duration</th><th>Activated</th>
            </tr>
        </thead>
        <tbody>
        <% if !@coupons.nil? %>
            <% @coupons.each do |coupon| %>
            <tr class="<%#= list_entry or list_entry_alt %>">
                <td><%= coupon.code %></td>
                <td><%= coupon.plan_id  %></td>
                <td><%= coupon.subdays %> days</td>
                <td><% if coupon.started.nil? == true  %>
                    <%= 'Not yet' %>
                    <% else %>
                    <%= time_ago_or_time_stamp coupon.started %>
                    <% end %>
                    </td>
            </tr>
            <% end %>
        <% end %>
        </tbody>
     </table>
</div>

我的config/authorization_rules.rb看起来像这样:

has_permission_on [:coupons], :to => [:index, :show, :step, :new, :create, :edit, :update, :destroy, :generate]

上述所有方法抛出的错误是:

filter_access_to tried to find Coupon from params[:id] (nil), because attribute_check is enabled and @coupon isn't set, but failed: ActiveRecord::RecordNotFound: Couldn't find Coupon without an ID
Completed 404 Not Found in 245ms
ActiveRecord::RecordNotFound (Couldn't find Coupon without an ID):

然而,一旦我把filter_resource_access改为filter_access_to :all, :except => :generate,它就不会再给我错误了,而且有点奏效。

。它生成了一些我正在寻找的优惠券代码,但它不包括plan_id号或视图中输出的天数。

为什么?我做错了什么?

编辑1:顺便说一下,它确实限制了合适的人……只有指定的角色可以查看coupons index view。所以过滤器部分有效。

filter_resource_access触发声明性认证框架对资源的:id参数执行查找(在非集合/创建者方法中)。

在您的示例中,它将执行@coupon = Coupon.find(params[:id])并为控制器设置该成员变量。但是,由于没有参数[:id]通过该路由进入该操作,因此它失败了。

绕过它的方法,同时仍然保留您的控制器权限非常容易。把它放在控制器的顶部filter_resource_access行:

filter_resource_access :additional_collection => { :generate => :read } 
# I tried the two lines below but to no avail
#filter_resource_access :additional_collection => { :generate => :read }, :no_attribute_check => [ :generate ]
# filter_access_to :generate, :attribute_check => false

这将维护您对其他控制器操作以及"generate"操作的权限检查,但将"generate"方法从自动查找器查找中豁免。

更多信息见:http://rubydoc.info/github/stffn/declarative_authorization/master/Authorization/AuthorizationInController/ClassMethods

我的猜测是filter_resource_access正在处理控制器中的所有动作,好像它们正在处理单个资源并试图做Coupon.find(params[:id])。把它改成这样:

filter_access_to :all, :except => :generate

你告诉它不要在操作之前运行该方法,这意味着它不会尝试查找优惠券

问题在于,使用filter_resource_access时,它假设控制器使用默认的资源操作,并尝试在非标准crud操作的params[:id]中找到一个经销商。你需要做的是添加

filter_access_to :all

并将相应的:generate规则添加到authorization_rules.rb中。像

role :someone do
    has_permission_on :coupon, :to => :generate
    ...
end

声明式授权需要您将coupon声明为实例变量。

在控制器中,尝试将coupon = Coupon.new更改为@coupon = Coupon.new(显然相应地更改后续行)

filter_resource_access尝试为您创建资源对象,因此您需要查看:additional_member:additional_collection选项,或者使用您注意到的except选项。

关于缺少的计划参数,你确定它们是params[:plan_id]吗?检查你的开发日志,看看传入的参数是什么样子的。

你也没有检查save调用的成功,所以如果有一个错误,你不会知道它

最新更新