代码之家  ›  专栏  ›  技术社区  ›  d11wtq Vadim Baryshev

基于用户权限的Rails 3 ActiveRecord验证

  •  10
  • d11wtq Vadim Baryshev  · 技术社区  · 16 年前

    我正在将代码从一个在非标准定制PHP框架中构建的应用程序转移到Ruby on Rails(版本3)。在PHP版本中,所有的控制器都非常胖,有瘦模型,这是我一直不同意的,所以我很喜欢Rails在模型级别进行验证的方式,这可能是目前胖控制器中发生的事情的90%。

    但是,我面临的一个不确定如何解决的问题是,基于对模型进行更改的人的不同验证规则。例如,管理员或记录的原始创建者应该能够执行诸如将记录标记为已删除(软删除)之类的操作,而其他人不应该这样做。

    class Something < ActiveRecord::Base
      ...
      validates :deleted, :owned_by_active_user => true
      ...
    end
    
    class OwnedByActiveUserValidator < ActiveModel::EachValidator
      validate_each(record, attr_name, attr_value)
        # Bad idea to have the model know about things such as sessions?
        unless active_user.admin? || active_user.own?(record)
          record.errors.add :base, "You do not have permission to delete this record"
        end
      end
    end
    

    既然模型本身(理论上)不知道做出更改的用户,那么“rails方式”是什么呢?我应该将活动用户设置为记录上的虚拟属性(实际上没有保存到DB),还是应该在控制器中执行这些检查?我不得不承认,拥有对活动用户的模型检查权限确实让人感到奇怪,而且在测试模型时会增加复杂性。

    3 回复  |  直到 16 年前
        1
  •  11
  •   yfeldblum    16 年前

    控制器的工作实际上是处理授权,或者将授权委托给授权层。模型不应该知道,也不必关心,谁是当前登录者,他/她的权限是什么-这是控制器的工作,或者控制器委托给的任何身份验证帮助层。

    :deleted 在- attr_accessible 通过 new , create ,或 update_attributes . 控制器应单独检查经过身份验证的用户的授权,并调用 deleted= 另外,如果经过身份验证的用户被授权。

    有几个授权库和框架可以帮助进行授权或充当授权层,例如 cancan .

        2
  •  6
  •   Mischa    16 年前

    我会用一个 before_filter

    class SomethingController < ApplicationController
      before_filter :require_delete_permission, :only => [:destroy]
    
      def destroy
        # delete the record
      end
    
      private
    
      def require_delete_permission
        unless current_user.is_admin || record.owner == current_user
          flash[:error] = 'You do not have delete permissions'
          redirect_to somewhere
        end
      end
    end
    
        3
  •  3
  •   cryo28    16 年前

    我在Rails 2.3中遇到了同样的问题,最后提出了这个解决方案。在您的模型中,您定义了一些atribute,这取决于您打开/关闭哪个验证。根据控制器可用的日期(如您的情况中的用户权限),您可以设置此属性,如下所示:

    Class Model < ActiveRecord::Base
       attr_accessor :perform_validation_of_field1 #This is an attribute which controller will use to turn on/off some validation logic depending on the current user
    
       validates_presence_of :field1, :if => :perform_validation_of_field1
       #This validation (or any similar one) will occur only if controller sets model.perform_validation_of_field1 to true.
    end
    
    Class MyController < ActionController::Base
       def update
         @item = Model.find(params[:id])
         @item.update_attribute(params[:item])
    
         #The controller decides whether to turn on optional validations depending on current user privileges (without the knowledge of internal implementation of this validation logic)
         @item.perform_validation_of_field1 = true unless active_user.admin?
    
         if @item.save
            flash[:success] = 'The record has been saved'
            redirect_to ...
         else
            flash.now[:error] = 'The record has not passed validation checks'
            render :action => :edit
         end
       end
    

    我认为在Rails 3中可以用类似的方式来实现。