before_filter,用于检查控制器实例变量是否属于current_user

时间:2013-06-24 04:18:22

标签: ruby-on-rails-3 authentication activerecord devise before-filter

目前我为餐馆设置了CRUD资源。所有者拥有多家餐馆,餐馆属于所有者。餐厅#index和#show视图可供任何用户访问。但是,为了创建一个新的餐厅,业主必须登录。我实施了设计,这很好。我的问题是确保current_owner在能够编辑,更新或销毁餐厅之前拥有餐厅。

我在创建一个before_filter时遇到问题,该问题将检查登录的所有者(current_owner)是否是该登录所有者正在尝试查看的该餐馆的所有者。

在设置before_filter之前,我快速制作了一个#check_if_owner方法并将其放在编辑操作中。如果所有者不拥有餐馆,他们应该重定向到上一页。但是,由于某种原因,我收到以下错误:

ActiveRecord::RecordNotFound in RestaurantsController#edit

Couldn't find Restaurant with id=4 [WHERE "restaurants"."owner_id" = 1]

我不确定为什么会发生这种情况,因为当我在控制台中运行#check_ownership方法时它会返回false,这正是我希望方法在current_owner不拥有餐厅的情况下执行的操作。如果它在控制台中返回false,则不应该将用户重定向到上一页而不是接收RecordNotFound错误?我不确定为什么会这样。

已发布是代码的其余部分......

class RestaurantsController < ApplicationController
  before_filter :authenticate_owner!, except: [:index, :show]
  # before_filter :check_if_owner, only: [:edit, :update, :destroy]
  def index
    @restaurants = Restaurant.all
  end

  def show
    @restaurant = Restaurant.find(params[:id])
  end

  def new
    @restaurant = current_owner.restaurants.new
  end

  def create
    @restaurant = current_owner.restaurants.build(params[:restaurant])
    if @restaurant.save
      redirect_to restaurants_path
    else
      flash[:error] = "<ul>" + @restaurant.errors.full_messages.map{|o| "<li>" + o + "</li>" }.join("") + "</ul>"
      redirect_to new_restaurant_path
    end
  end

  def edit
    check_if_owner(Restaurant.find(params[:id]))
    @restaurant = current_owner.restaurants.find(params[:id])
  end

  def update
    @restaurant = current_owner.restaurants.find(params[:id])
    @restaurant.update_attributes(params[:restaurant])
    redirect_to restaurant_path(@restaurant)
  end

  def destroy
    @restaurant = current_owner.restaurants.find(params[:id])
    @restaurant.destroy
    redirect_to restaurants_path
  end

  private

    def check_if_owner(restaurant)
      debugger
      if current_owner.check_ownership(restaurant)
        return
      else
        redirect_to :back
      end
    end

end


class Owner < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable,
  # :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me, :name
  # attr_accessible :title, :body

  has_many :restaurants

  validates :name, presence: true
  validates :email, presence: true

  def check_ownership(restaurant)
    !self.restaurants.find_by_id(restaurant.id).nil?
  end

end

2 个答案:

答案 0 :(得分:2)

解决了以下问题:

class RestaurantsController < ApplicationController
  before_filter :authenticate_owner!, except: [:index, :show]
  before_filter :check_if_owner, only: [:edit, :update, :destroy]

  private

  def check_if_owner
    if current_owner.has_ownership?(Restaurant.find(params[:id]))
      return
    else
      flash[:error]= "You do not have permission to do that." 
      redirect_to :back
    end
  end
end

Owner.rb

  def has_ownership?(restaurant)
    self.restaurants.find_by_id(restaurant.id).present?
  end

答案 1 :(得分:1)

首先,我负责检查餐厅的所有权,而不是检查所有者,特别是因为您在RestaurantsController中实施此检查。

此外,您似乎过度设计了所有权检查。实际上,您只需要检查restaurant.owner == current_owner

<强> restaurant.rb

def owned_by?(current_owner)
  owner == current_owner
end

如果您认为您将在其他地方重复使用此方法,则只需要进入模型(而不是在过滤器之前作为控制器中的单行生存)。

或者,或者,如果您的所有者要管理许多不同类型的对象,您可以在权限模型中保留权限,并使其更灵活。

<强> owner.rb

def manages?(object)
  object.respond_to?(:owner) && object.owner == self
end

您使用的方法,find和find_by_id在ActiveRecord查询中是脆弱的,非首选。具体来说,find_by_ *会在没有结果时抛出异常。另一方面,使用where(:id =&gt; id)将返回空数组,如果没有结果,则返回nil。

请查看这些内容以获得更多见解和最佳实践 http://tenmiles.com/blog/2011/07/activerecord-finders-returns-nil-or-throws-exception/ http://guides.rubyonrails.org/active_record_querying.html