【问题标题】:Can I use CanCan to get around a 'nil' current_user on a view?我可以使用 CanCan 绕过视图上的“nil”current_user 吗?
【发布时间】:2013-11-25 07:10:45
【问题描述】:

所以在我的Items 控制器的index 视图中,我有一些对current_user 的引用。

现在,当我在未登录的情况下浏览到/items 时,出现以下错误:

NoMethodError at /items undefined method `items' for nil:NilClass

这是在我的Items#Index 在我的ItemsController.rb 中的这条线

@items = current_user.items.all

我知道这显然意味着在未登录用户上查找 items 时返回 nil 值,这自然是有道理的,因为未登录用户没有任何项目(根据应用程序的业务逻辑 - 您必须登录)。

这就是我的ability.rb 的样子:

  def initialize(user)
    user ||= User.new # guest user (not logged in)

    alias_action :create, :read, :update, :destroy, :to => :crud
    alias_action :create, :update, :destroy, :to => :cud

    if user.has_role? :admin
      can :manage, :all
    else
      cannot :cud, Item
      can :read, Item
      cannot :read, :items
    end

    if user.has_role? :seller
      can :cud, Item, :user_id => user.id
      can :read, Item
    end

    if user.has_role? :buyer
      can :read, Item
    end
  end

与我的:admin 角色相关的if 语句的else 分支看起来如此复杂的原因是因为所有用户(无论是否登录)都应该能够查看每个项目记录(@ 987654335@)。但是,只有登录用户(任何角色)才能查看/items(即Item#index),它根据current_user 范围自定义结果。

我真的必须将该赋值语句放入我的ItemsController.rbif 语句中吗?

我错过了什么?

编辑 1

这是我的顶部ItemsController.rb

class ItemsController < ApplicationController
  load_and_authorize_resource
  before_filter :initialize_cart

  layout "item"
  # GET /items
  # GET /items.json
  def index
    #authorize! :index, @user, :message => "Rut row. Seems this door is locked and you don't have the key."
    if params[:tag]
      @items = current_user.items.tagged_with(params[:tag])
    else
      @items = current_user.items.all
    end

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @items }
    end
  end

这是我的ApplicationController

class ApplicationController < ActionController::Base
  protect_from_forgery 
  before_filter :initialize_cart

  rescue_from CanCan::AccessDenied do |exception|
    redirect_to main_app.root_path, :alert => exception.message
  end

  def after_sign_in_path_for(resource_or_scope)
    stored_location_for(resource_or_scope) || signed_in_root_path(resource_or_scope)
  end

  def after_sign_out_path_for(resource_or_scope)
    request.referrer
  end

  private

  def initialize_cart
    if session[:cart_id]
      @cart = Cart.find(session[:cart_id])
    else
      @cart = Cart.create
      session[:cart_id] = @cart.id
    end
  end

end

编辑 2:

这是我的views/items/index.html.erb

<% @items.each do |item| %>
  <tr>
    <td><%= link_to item.name, item_path(item) %></td>
    <td><%= item.description.html_safe %></td>
    <td><%= number_to_currency(item.price, precision: 2) %></td>
    <% if item.is_approved? %>
        <td><%= l item.approved_at, format: :custom %></td> 
    <% else %>
        <td>N/A</td>
    <% end %>
    <td><%= link_to "<i class='fa fa-edit'></i>".html_safe, edit_item_path(item) %></td>
    <td><%= link_to "<i class='fa fa-trash-o'></i>".html_safe, item, method: :delete, data: { confirm: "Are you sure you want to delete #{item.name}?" } %></td>
  </tr>
<% end %>

【问题讨论】:

  • 你能把输出@items的部分放到html.erb中吗?
  • 角色是什么,只有 :admin、:buyer 和 :seller?更新了我的答案。检查能力逻辑。
  • @SteveWilhelm 是的,只是这三个角色。
  • @JoseRamonCamacho 刚刚更新了问题。

标签: ruby-on-rails ruby-on-rails-3 devise cancan


【解决方案1】:

您当前的能力逻辑允许任何人调用 ItemsController#index(管理 else 部分的第二行)

更新:我认为对能力逻辑的以下更改应该可以修复它(现在使用显式显示而不是读取)

  def initialize(user)
    user ||= User.new # guest user (not logged in)

    alias_action :create, :read, :update, :destroy, :to => :crud
    alias_action :create, :update, :destroy, :to => :cud

    if user.has_role? :admin
      can :manage, :all
    end

    if user.has_role? :seller
      can :cud, Item, :user_id => user.id
      can :read, Item
    end

    if user.has_role? :buyer
      can :read, Item
    end

    # this is the default ability, essentially anyone, logged in or not,
    # will be able to access individual items via ItemsController#show
    # Cancan has aliases like :read that are for both 
    # index and show, but you can define rules on individual actions explicitly
    # (even actions that are not default Rails REST actions) 
    can :show, Item
  end

【讨论】:

  • 是的...我在顶部添加了load_and_authorize_resource。将用我的ItemsController.rb顶部更新问题
  • 所以你所做的只是将can :read, Item 拉出admin 角色if 语句的else 分支,对吗?如果是这样......那么是的,这不起作用。
  • 我认为这和@zeantsoi 的解决方案都会奏效。我个人喜欢让能力定义你能做什么。这个初始化现在从大多数访问级联到最少。
  • 您能否更新您的答案以引起人们注意您使用 show 而不是 read 的事实以及原因 - 以便其他人可以轻松理解。
  • 我同意你的方法,我也评论了@zeantosi 自己的方法。他的最后一次改变可能会奏效,但我先测试了你的。另外,我更喜欢这个,因为我不必在我的ItemsController 上使用您的版本向load_and_authorize_resource 添加任何例外。对我来说感觉更轻巧。
【解决方案2】:

您可以通过将以下内容添加到您的 ItemsController 文件的顶部来专门针对 Items#index 控制器操作进行授权:

# app/controllers/items_controller.rb
load_and_authorize_resource :only => [:index]

这可以防止未经授权的用户访问index 操作,但不能访问show 操作(或任何其他未在数组中声明的控制器操作)。

更新

在您的ability.rb 中,您需要alias 允许(或阻止)访问indexshow 操作的规则:

# app/models/ability.rb
alias_action :index, :to => :read_index
alias_action :show, :to => :read_show

if user.has_role? :admin
  can :manage, :all
else
  cannot :cud, Item
  can :read_show, Item
  cannot :read_index, Item
end

解释

我理解为什么can :read, Item 似乎将访问仅限限制为show 操作,但事实并非如此。根据CanCan's author,该库提供了以下方便的别名:

alias_action :index, :show, :to => :read

这意味着read 别名包含both show index 操作。因此,在声明can :read, Item 时,您允许未登录的用户访问这两个操作。然后,由于 CanCan 将权限分配给类,而不是符号,声明 cannot :read, :items 实际上并没有限制对任何内容的访问。

为了允许访问 show 操作,但禁止访问 index 操作,您需要分别为它们设置别名并调用它们,如我在上面演示的那样。

【讨论】:

  • 这是一个优雅的解决方案。不过,它似乎不适用于这个问题。
  • 请看我的更新。它包括对ability.rb 的修改,以允许访问show 操作,但不允许访问index 操作。
  • 那么随着admin角色的变化,我还需要在load_and_authorize_resource中指定动作吗?
  • 您不需要指定操作,因为登录用户和未登录用户的权限都针对showindex 操作进行了处理。也就是说,我将把最初的答案留给后代和未来用户的可能利益。
  • 您仍然需要以某种方式授权控制器中的方法。详情请见github.com/ryanb/cancan/wiki/Authorizing-controller-actions
【解决方案3】:

您是否尝试过:

<% if can? :read, @items %>
  @items.each...
  ...
<% end %>

希望对你有帮助

【讨论】:

  • 希望能够避免让视图变得如此混乱。问题在于整个视图 - 而不仅仅是 1 行。
  • 您是否尝试过在控制器中进行验证,如果是 curren_user.has_role? (invalid_role) redirect_to safe_place_path ...还是需要加载项目索引视图?
  • 是的,需要加载项目索引视图。
  • 那么我认为向视图添加条件的最佳方式是上面的方式,每个有问题的地方。
  • 做可以吗?视图中的调用应该是一个危险信号。授权应在控制器级别完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多