mass assignment を安全にするプラグイン?
はじめてRailsに触ったとき一番「落とし穴」と感じたのがmass assignment だった。
たとえば下のような、attr_accessible を忘れたモデルの会員制ブログアプリがあって、
class Entry belongs_to :user end class User has_many :entries end
scaffold で自動生成されるまま
def update @entry = Entry.find(params[:id]) if @entry.update_attributes(params[:entry]) ...
みたいにmass assignmentを使ってしまうと、entry[user_id]=1 とか適当なパラメータを送るだけで誰か他のユーザの投稿として Entry を更新することができたりするわけで。もちろん手を抜くなとか忘れるのが阿呆だとかはあるかもしれないけど、わたしのような初心者には結構危なく感じた。
なんでRailsはデフォルトでmass assignment不可にしないんだろう?*1 デフォルト不許可にしてしまうと、スキーマが変更されるたびに合わせて attr_accessible を修正しなきゃいけないとかDRYじゃねえよウゼーという感じなんだろうか。
で、安全にするにはどうすればいいか考えて、バリデーションが無い属性→ユーザからの入力を想定していない属性→mass assignment不要であるはず、という前提のもとに、validates_* が設定された属性のみを自動で mass assignment 可能にするようなプラグインを読ませればいいんじゃなかろうかとか思ったり。
module ActiveRecord class Base # デフォルトでは全部不許可 attr_accessible end # validates_ なんちゃらを定義した属性に attr_accessible を自動で設定する module Validations module ClassMethods def attr_accessible_by_options(attrs) allowed = attrs.dup allowed.extract_options! allowed.flatten! attr_accessible *allowed end def validates_each_with_allowing_mass_assignment(*attrs, &block) attr_accessible_by_options(attrs) validates_each_without_allowing_mass_assignment(*attrs, &block) end alias_method_chain :validates_each, :allowing_mass_assignment def validates_presence_of_with_allowing_mass_assignment(*attrs) attr_accessible_by_options(attrs) validates_presence_of_without_allowing_mass_assignment(*attrs) end alias_method_chain :validates_presence_of, :allowing_mass_assignment end end end
これならウッカリさんでも安心。もちろん、簡単な動作確認以上のテストは全くしておりませんが。
もっといい方法があったらいいなあ。
*1:デフォルトで不許可なのは id と type のみ? 少なくとも関連の外部キーになっているものも全部デフォルト不許可でいいと思うんだけど