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