Проверка ActiveRecord в Rails 3 на основе разрешений пользователей - PullRequest
10 голосов
/ 18 октября 2010

Я перевожу код из приложения, созданного в нестандартной пользовательской среде 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

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

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

Ответы [ 3 ]

11 голосов
/ 18 октября 2010

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

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

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

6 голосов
/ 18 октября 2010

Я бы решил это с помощью 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 голосов
/ 18 октября 2010

Я столкнулся с той же проблемой в 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 это можно сделать аналогичным образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...