开发者

Why does declarative_authorization break the functionality of my controller? - Rails 3

I have a Coupons controller with two actions.

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

The generate action is invoked in my view like this:

<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  %&开发者_开发技巧gt;</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>

My config/authorization_rules.rb looks like this:

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

The error all of the above throws is this:

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):

However, once I change the filter_resource_access to filter_access_to :all, :except => :generate it doesn't give me the error anymore and kinda works.

i.e. it produces some of the coupon codes that I am looking for, but it doesn't include the plan_id number or the number of days in the output in the view.

Why is that? What am I doing wrong?

Edit 1: By the way, it does restrict the right people...i.e. only the specified roles can view the coupons index view. So the filter partly works.


filter_resource_access triggers the declarative auth framework to perform a lookup on the :id parameter for your resource (in non- collection/creator methods).

In your example, it would do a @coupon = Coupon.find(params[:id]) and set up that member variable for your controller. However, since there is no params[:id] coming in to that action through that route, it fails.

The way around it, while still preserving your controller permissions is pretty easy. Just put this at the top of your controller on the filter_resource_access line:

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

This maintains your permission checks against the other controller actions as well as the "generate" action but exempts the "generate" method from an automatic finder lookup.

For more info see: http://rubydoc.info/github/stffn/declarative_authorization/master/Authorization/AuthorizationInController/ClassMethods


My guess is that filter_resource_access is treating all the actions in the controller as if they're dealing with a single resource and attempting to do Coupon.find(params[:id]). By changing it to this:

filter_access_to :all, :except => :generate

You are telling it to not run that method before that action, meaning it won't try to find a coupon.


The problem is that with filter_resource_access it assumes the controller uses default resource actions and tries to find a dealer if params[:id] for the actions that are not standard crud. What you need to do is to add

filter_access_to :all

And add the corresponding rule for :generate to your authorization_rules.rb. Like

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


Declarative Authorization needs you to declare coupon as an instance variable.

In your controller, try changing coupon = Coupon.new to @coupon = Coupon.new (Obviously change the subsequent lines accordingly.)


filter_resource_access tries to create the resource object for you, so you needs to look at the :additional_member or :additional_collection options, or use the except option as you noted.

As for the missing plan parameters, are you sure they come in as params[:plan_id] ? check your devel logs to see what the incoming params look like.

You also aren't checking the success of the save call, so if there is an error, you won't know it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜