Rails 3 Проверка ActiveRecord на основе разрешений пользователя

я переношу код из приложения, построенного в нестандартной пользовательской PHP-структуре, в Ruby on Rails (версия 3). В версии PHP все контроллеры действительно толстые, с тонкими моделями, с которыми я всегда не соглашался, поэтому мне нравится, как Rails выполняет проверку на уровне модели, что, вероятно, составляет 90% того, что происходит в этих контроллерах fat В настоящее время.

одна проблема, с которой я сталкиваюсь, и не знаю, как ее решить, - это разные правила проверки кто вносит изменения в модель. Например, администратор или первоначальный создатель записи должен иметь возможность делать такие вещи, как помечать запись как удаленную (мягкое удаление), тогда как все остальные не должны.

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

поскольку сама модель (в теории) не знает о пользователе, который вносит изменения, каков "рельсовый путь" для такого рода вещей? Должен ли я установить активного пользователя в качестве виртуального атрибута в записи (фактически не сохраненной в БД), или я должен просто выполнить эти проверки в контроллере? Я должен признать, что странно иметь разрешения проверки модели для активного пользователя, и это добавляет сложности, когда дело доходит до тестирования модели.

одна из причин, по которой я хочу сохранить как можно больше этого в модели, заключается в том, что я хочу предоставить как API (доступ через OAuth), так и веб-сайт, не дублируя слишком много кода, например, эти типы проверок разрешений.

3 ответов


это действительно работа контроллера для обработки авторизации или делегирования авторизации на уровень авторизации. Модели не должны знать и не должны заботиться о том, кто в настоящее время вошел в систему и каковы его/ее разрешения - это работа контроллера или любого вспомогательного уровня auth, на который делегирует контроллер.

вы должны сделать :deleted наattr_accessible для массового назначения через new, create или update_attributes. Контроллер должен проверить подлинности авторизация пользователя отдельно и вызов deleted= отдельно, если аутентифицированный пользователь авторизован.

существует несколько библиотек авторизации и фреймворков, которые помогают с авторизацией или функционируют как уровень авторизации, например канкан.


Я бы решил это с помощью 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

я столкнулся с той же проблемой в Rails 2.3 и, наконец, придумал это решение. В вашей модели вы определяете некоторый атрибут, в зависимости от которого вы включаете/выключаете проверку. Чем вы управляете, вы устанавливаете этот атрибут в зависимости от даты, доступной контроллеру (например, привилегии пользователя в вашем случае) следующим образом:

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 Это можно сделать аналогичным образом.