Added mass-assignment security :as and :without_protection support to AR.new and AR.create
This commit is contained in:
parent
b8ccd05524
commit
7c5ae0a88f
@ -475,10 +475,19 @@ def find_by_sql(sql, binds = [])
|
||||
# The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the
|
||||
# attributes on the objects that are to be created.
|
||||
#
|
||||
# +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
|
||||
# in the +options+ parameter.
|
||||
#
|
||||
# ==== Examples
|
||||
# # Create a single new object
|
||||
# User.create(:first_name => 'Jamie')
|
||||
#
|
||||
# # Create a single new object using the :admin mass-assignment security scope
|
||||
# User.create({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
|
||||
#
|
||||
# # Create a single new object bypassing mass-assignment security
|
||||
# User.create({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
|
||||
#
|
||||
# # Create an Array of new objects
|
||||
# User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
|
||||
#
|
||||
@ -491,11 +500,11 @@ def find_by_sql(sql, binds = [])
|
||||
# User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u|
|
||||
# u.is_admin = false
|
||||
# end
|
||||
def create(attributes = nil, &block)
|
||||
def create(attributes = nil, options = {}, &block)
|
||||
if attributes.is_a?(Array)
|
||||
attributes.collect { |attr| create(attr, &block) }
|
||||
attributes.collect { |attr| create(attr, options, &block) }
|
||||
else
|
||||
object = new(attributes)
|
||||
object = new(attributes, options)
|
||||
yield(object) if block_given?
|
||||
object.save
|
||||
object
|
||||
@ -1484,7 +1493,20 @@ def encode_quoted_value(value) #:nodoc:
|
||||
# attributes but not yet saved (pass a hash with key names matching the associated table column names).
|
||||
# In both instances, valid attribute keys are determined by the column names of the associated table --
|
||||
# hence you can't have attributes that aren't part of the table columns.
|
||||
def initialize(attributes = nil)
|
||||
#
|
||||
# +initialize+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
|
||||
# in the +options+ parameter.
|
||||
#
|
||||
# ==== Examples
|
||||
# # Instantiates a single new object
|
||||
# User.new(:first_name => 'Jamie')
|
||||
#
|
||||
# # Instantiates a single new object using the :admin mass-assignment security scope
|
||||
# User.new({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
|
||||
#
|
||||
# # Instantiates a single new object bypassing mass-assignment security
|
||||
# User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
|
||||
def initialize(attributes = nil, options = {})
|
||||
@attributes = attributes_from_column_definition
|
||||
@association_cache = {}
|
||||
@aggregation_cache = {}
|
||||
@ -1500,7 +1522,8 @@ def initialize(attributes = nil)
|
||||
set_serialized_attributes
|
||||
|
||||
populate_with_current_scope_attributes
|
||||
self.attributes = attributes unless attributes.nil?
|
||||
|
||||
assign_attributes(attributes, options) if attributes
|
||||
|
||||
result = yield self if block_given?
|
||||
run_callbacks :initialize
|
||||
|
@ -7,6 +7,12 @@
|
||||
|
||||
class MassAssignmentSecurityTest < ActiveRecord::TestCase
|
||||
|
||||
def setup
|
||||
# another AR test modifies the columns which causes issues with create calls
|
||||
TightPerson.reset_column_information
|
||||
LoosePerson.reset_column_information
|
||||
end
|
||||
|
||||
def test_customized_primary_key_remains_protected
|
||||
subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try')
|
||||
assert_nil subscriber.id
|
||||
@ -35,60 +41,114 @@ def test_assign_attributes_uses_default_scope_when_no_scope_is_provided
|
||||
p = LoosePerson.new
|
||||
p.assign_attributes(attributes_hash)
|
||||
|
||||
assert_equal nil, p.id
|
||||
assert_equal 'Josh', p.first_name
|
||||
assert_equal 'm', p.gender
|
||||
assert_equal nil, p.comments
|
||||
assert_default_attributes(p)
|
||||
end
|
||||
|
||||
def test_assign_attributes_skips_mass_assignment_security_protection_when_without_protection_is_used
|
||||
p = LoosePerson.new
|
||||
p.assign_attributes(attributes_hash, :without_protection => true)
|
||||
|
||||
assert_equal 5, p.id
|
||||
assert_equal 'Josh', p.first_name
|
||||
assert_equal 'm', p.gender
|
||||
assert_equal 'rides a sweet bike', p.comments
|
||||
assert_all_attributes(p)
|
||||
end
|
||||
|
||||
def test_assign_attributes_with_default_scope_and_attr_protected_attributes
|
||||
p = LoosePerson.new
|
||||
p.assign_attributes(attributes_hash, :as => :default)
|
||||
|
||||
assert_equal nil, p.id
|
||||
assert_equal 'Josh', p.first_name
|
||||
assert_equal 'm', p.gender
|
||||
assert_equal nil, p.comments
|
||||
assert_default_attributes(p)
|
||||
end
|
||||
|
||||
def test_assign_attributes_with_admin_scope_and_attr_protected_attributes
|
||||
p = LoosePerson.new
|
||||
p.assign_attributes(attributes_hash, :as => :admin)
|
||||
|
||||
assert_equal nil, p.id
|
||||
assert_equal 'Josh', p.first_name
|
||||
assert_equal 'm', p.gender
|
||||
assert_equal 'rides a sweet bike', p.comments
|
||||
assert_admin_attributes(p)
|
||||
end
|
||||
|
||||
def test_assign_attributes_with_default_scope_and_attr_accessible_attributes
|
||||
p = TightPerson.new
|
||||
p.assign_attributes(attributes_hash, :as => :default)
|
||||
|
||||
assert_equal nil, p.id
|
||||
assert_equal 'Josh', p.first_name
|
||||
assert_equal 'm', p.gender
|
||||
assert_equal nil, p.comments
|
||||
assert_default_attributes(p)
|
||||
end
|
||||
|
||||
def test_assign_attributes_with_admin_scope_and_attr_accessible_attributes
|
||||
p = TightPerson.new
|
||||
p.assign_attributes(attributes_hash, :as => :admin)
|
||||
|
||||
assert_equal nil, p.id
|
||||
assert_equal 'Josh', p.first_name
|
||||
assert_equal 'm', p.gender
|
||||
assert_equal 'rides a sweet bike', p.comments
|
||||
assert_admin_attributes(p)
|
||||
end
|
||||
|
||||
def test_new_with_attr_accessible_attributes
|
||||
p = TightPerson.new(attributes_hash)
|
||||
|
||||
assert_default_attributes(p)
|
||||
end
|
||||
|
||||
def test_new_with_attr_protected_attributes
|
||||
p = LoosePerson.new(attributes_hash)
|
||||
|
||||
assert_default_attributes(p)
|
||||
end
|
||||
|
||||
def test_create_with_attr_accessible_attributes
|
||||
p = TightPerson.create(attributes_hash)
|
||||
|
||||
assert_default_attributes(p, true)
|
||||
end
|
||||
|
||||
def test_create_with_attr_protected_attributes
|
||||
p = LoosePerson.create(attributes_hash)
|
||||
|
||||
assert_default_attributes(p, true)
|
||||
end
|
||||
|
||||
def test_new_with_admin_scope_with_attr_accessible_attributes
|
||||
p = TightPerson.new(attributes_hash, :as => :admin)
|
||||
|
||||
assert_admin_attributes(p)
|
||||
end
|
||||
|
||||
def test_new_with_admin_scope_with_attr_protected_attributes
|
||||
p = LoosePerson.new(attributes_hash, :as => :admin)
|
||||
|
||||
assert_admin_attributes(p)
|
||||
end
|
||||
|
||||
def test_create_with_admin_scope_with_attr_accessible_attributes
|
||||
p = TightPerson.create(attributes_hash, :as => :admin)
|
||||
|
||||
assert_admin_attributes(p, true)
|
||||
end
|
||||
|
||||
def test_create_with_admin_scope_with_attr_protected_attributes
|
||||
p = LoosePerson.create(attributes_hash, :as => :admin)
|
||||
|
||||
assert_admin_attributes(p, true)
|
||||
end
|
||||
|
||||
def test_new_with_without_protection_with_attr_accessible_attributes
|
||||
p = TightPerson.new(attributes_hash, :without_protection => true)
|
||||
|
||||
assert_all_attributes(p)
|
||||
end
|
||||
|
||||
def test_new_with_without_protection_with_attr_protected_attributes
|
||||
p = LoosePerson.new(attributes_hash, :without_protection => true)
|
||||
|
||||
assert_all_attributes(p)
|
||||
end
|
||||
|
||||
def test_create_with_without_protection_with_attr_accessible_attributes
|
||||
p = TightPerson.create(attributes_hash, :without_protection => true)
|
||||
|
||||
assert_all_attributes(p)
|
||||
end
|
||||
|
||||
def test_create_with_without_protection_with_attr_protected_attributes
|
||||
p = LoosePerson.create(attributes_hash, :without_protection => true)
|
||||
|
||||
assert_all_attributes(p)
|
||||
end
|
||||
|
||||
def test_protection_against_class_attribute_writers
|
||||
@ -111,4 +171,34 @@ def attributes_hash
|
||||
:comments => 'rides a sweet bike'
|
||||
}
|
||||
end
|
||||
|
||||
def assert_default_attributes(person, create = false)
|
||||
unless create
|
||||
assert_nil person.id
|
||||
else
|
||||
assert !!person.id
|
||||
end
|
||||
assert_equal 'Josh', person.first_name
|
||||
assert_equal 'm', person.gender
|
||||
assert_nil person.comments
|
||||
end
|
||||
|
||||
def assert_admin_attributes(person, create = false)
|
||||
unless create
|
||||
assert_nil person.id
|
||||
else
|
||||
assert !!person.id
|
||||
end
|
||||
assert_equal 'Josh', person.first_name
|
||||
assert_equal 'm', person.gender
|
||||
assert_equal 'rides a sweet bike', person.comments
|
||||
end
|
||||
|
||||
def assert_all_attributes(person)
|
||||
assert_equal 5, person.id
|
||||
assert_equal 'Josh', person.first_name
|
||||
assert_equal 'm', person.gender
|
||||
assert_equal 'rides a sweet bike', person.comments
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue
Block a user