【问题标题】:before_filter that checks whether the controller instance variable belongs to the current_userbefore_filter 检查控制器实例变量是否属于 current_user
【发布时间】:2013-06-24 04:18:22
【问题描述】:

目前我为餐馆设置了 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

【问题讨论】:

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


    【解决方案1】:

    通过以下方式解决:

    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
    

    所有者.rb

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

    【讨论】:

      【解决方案2】:

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

      此外,您似乎过度设计了所有权检查。真的,你只需要检查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 => id) 将返回一个空数组,如果没有结果则返回 nil。

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

      【讨论】:

      • 谢谢卡洛斯!这非常有帮助。
      猜你喜欢
      • 2018-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-26
      • 1970-01-01
      • 2010-12-25
      • 1970-01-01
      相关资源
      最近更新 更多