1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

singular and collection relations in AR can now specify mass-assignment security options (:as and :without_protection) in build, create and create! methods.

This commit is contained in:
Josh Kalderimis 2011-05-01 23:30:07 +02:00
parent 7c5ae0a88f
commit 86d7ed3375
6 changed files with 346 additions and 59 deletions

View file

@ -93,20 +93,20 @@ module ActiveRecord
first_or_last(:last, *args)
end
def build(attributes = {}, &block)
build_or_create(attributes, :build, &block)
def build(attributes = {}, options = {}, &block)
build_or_create(:build, attributes, options, &block)
end
def create(attributes = {}, &block)
def create(attributes = {}, options = {}, &block)
unless owner.persisted?
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
end
build_or_create(attributes, :create, &block)
build_or_create(:create, attributes, options, &block)
end
def create!(attrs = {}, &block)
record = create(attrs, &block)
def create!(attrs = {}, options = {}, &block)
record = create(attrs, options, &block)
Array.wrap(record).each(&:save!)
record
end
@ -403,9 +403,9 @@ module ActiveRecord
end + existing
end
def build_or_create(attributes, method)
def build_or_create(method, attributes, options)
records = Array.wrap(attributes).map do |attrs|
record = build_record(attrs)
record = build_record(attrs, options)
add_to_target(record) do
yield(record) if block_given?
@ -421,8 +421,8 @@ module ActiveRecord
raise NotImplementedError
end
def build_record(attributes)
reflection.build_association(scoped.scope_for_create.merge(attributes))
def build_record(attributes, options)
reflection.build_association(scoped.scope_for_create.merge(attributes), options)
end
def delete_or_destroy(records, method)

View file

@ -60,10 +60,10 @@ module ActiveRecord
through_record
end
def build_record(attributes)
def build_record(attributes, options = {})
ensure_not_nested
record = super(attributes)
record = super(attributes, options)
inverse = source_reflection.inverse_of
if inverse

View file

@ -17,16 +17,16 @@ module ActiveRecord
replace(record)
end
def create(attributes = {})
new_record(:create, attributes)
def create(attributes = {}, options = {})
new_record(:create, attributes, options)
end
def create!(attributes = {})
build(attributes).tap { |record| record.save! }
def create!(attributes = {}, options = {})
build(attributes, options).tap { |record| record.save! }
end
def build(attributes = {})
new_record(:build, attributes)
def build(attributes = {}, options = {})
new_record(:build, attributes, options)
end
private
@ -44,9 +44,9 @@ module ActiveRecord
replace(record)
end
def new_record(method, attributes)
def new_record(method, attributes, options)
attributes = scoped.scope_for_create.merge(attributes || {})
record = reflection.send("#{method}_association", attributes)
record = reflection.send("#{method}_association", attributes, options)
set_new_record(record)
record
end

View file

@ -5,14 +5,64 @@ require 'models/keyboard'
require 'models/task'
require 'models/person'
class MassAssignmentSecurityTest < ActiveRecord::TestCase
module MassAssignmentTestHelpers
def setup
# another AR test modifies the columns which causes issues with create calls
TightPerson.reset_column_information
LoosePerson.reset_column_information
end
def attributes_hash
{
:id => 5,
:first_name => 'Josh',
:gender => 'm',
: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
module MassAssignmentRelationTestHelpers
def setup
super
@person = LoosePerson.create(attributes_hash)
end
end
class MassAssignmentSecurityTest < ActiveRecord::TestCase
include MassAssignmentTestHelpers
def test_customized_primary_key_remains_protected
subscriber = Subscriber.new(:nick => 'webster123', :name => 'nice try')
assert_nil subscriber.id
@ -161,44 +211,268 @@ class MassAssignmentSecurityTest < ActiveRecord::TestCase
end
end
private
end
def attributes_hash
{
:id => 5,
:first_name => 'Josh',
:gender => 'm',
:comments => 'rides a sweet bike'
}
class MassAssignmentSecurityHasOneRelationsTest < ActiveRecord::TestCase
include MassAssignmentTestHelpers
include MassAssignmentRelationTestHelpers
# build
def test_has_one_build_with_attr_protected_attributes
best_friend = @person.build_best_friend(attributes_hash)
assert_default_attributes(best_friend)
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
def test_has_one_build_with_attr_accessible_attributes
best_friend = @person.build_best_friend(attributes_hash)
assert_default_attributes(best_friend)
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
def test_has_one_build_with_admin_scope_with_attr_protected_attributes
best_friend = @person.build_best_friend(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend)
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
def test_has_one_build_with_admin_scope_with_attr_accessible_attributes
best_friend = @person.build_best_friend(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend)
end
end
def test_has_one_build_without_protection
best_friend = @person.build_best_friend(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
# create
def test_has_one_create_with_attr_protected_attributes
best_friend = @person.create_best_friend(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_attr_accessible_attributes
best_friend = @person.create_best_friend(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_admin_scope_with_attr_protected_attributes
best_friend = @person.create_best_friend(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_with_admin_scope_with_attr_accessible_attributes
best_friend = @person.create_best_friend(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_without_protection
best_friend = @person.create_best_friend(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
# create!
def test_has_one_create_with_bang_with_attr_protected_attributes
best_friend = @person.create_best_friend!(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_bang_with_attr_accessible_attributes
best_friend = @person.create_best_friend!(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_bang_with_admin_scope_with_attr_protected_attributes
best_friend = @person.create_best_friend!(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_with_bang_with_admin_scope_with_attr_accessible_attributes
best_friend = @person.create_best_friend!(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_with_bang_without_protection
best_friend = @person.create_best_friend!(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
end
class MassAssignmentSecurityBelongsToRelationsTest < ActiveRecord::TestCase
include MassAssignmentTestHelpers
include MassAssignmentRelationTestHelpers
# build
def test_has_one_build_with_attr_protected_attributes
best_friend = @person.build_best_friend_of(attributes_hash)
assert_default_attributes(best_friend)
end
def test_has_one_build_with_attr_accessible_attributes
best_friend = @person.build_best_friend_of(attributes_hash)
assert_default_attributes(best_friend)
end
def test_has_one_build_with_admin_scope_with_attr_protected_attributes
best_friend = @person.build_best_friend_of(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend)
end
def test_has_one_build_with_admin_scope_with_attr_accessible_attributes
best_friend = @person.build_best_friend_of(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend)
end
def test_has_one_build_without_protection
best_friend = @person.build_best_friend_of(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
# create
def test_has_one_create_with_attr_protected_attributes
best_friend = @person.create_best_friend_of(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_attr_accessible_attributes
best_friend = @person.create_best_friend_of(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_admin_scope_with_attr_protected_attributes
best_friend = @person.create_best_friend_of(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_with_admin_scope_with_attr_accessible_attributes
best_friend = @person.create_best_friend_of(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_without_protection
best_friend = @person.create_best_friend_of(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
# create!
def test_has_one_create_with_bang_with_attr_protected_attributes
best_friend = @person.create_best_friend!(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_bang_with_attr_accessible_attributes
best_friend = @person.create_best_friend!(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_bang_with_admin_scope_with_attr_protected_attributes
best_friend = @person.create_best_friend!(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_with_bang_with_admin_scope_with_attr_accessible_attributes
best_friend = @person.create_best_friend!(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_with_bang_without_protection
best_friend = @person.create_best_friend!(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
end
class MassAssignmentSecurityHasManyRelationsTest < ActiveRecord::TestCase
include MassAssignmentTestHelpers
include MassAssignmentRelationTestHelpers
# build
def test_has_one_build_with_attr_protected_attributes
best_friend = @person.best_friends.build(attributes_hash)
assert_default_attributes(best_friend)
end
def test_has_one_build_with_attr_accessible_attributes
best_friend = @person.best_friends.build(attributes_hash)
assert_default_attributes(best_friend)
end
def test_has_one_build_with_admin_scope_with_attr_protected_attributes
best_friend = @person.best_friends.build(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend)
end
def test_has_one_build_with_admin_scope_with_attr_accessible_attributes
best_friend = @person.best_friends.build(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend)
end
def test_has_one_build_without_protection
best_friend = @person.best_friends.build(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
# create
def test_has_one_create_with_attr_protected_attributes
best_friend = @person.best_friends.create(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_attr_accessible_attributes
best_friend = @person.best_friends.create(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_admin_scope_with_attr_protected_attributes
best_friend = @person.best_friends.create(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_with_admin_scope_with_attr_accessible_attributes
best_friend = @person.best_friends.create(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_without_protection
best_friend = @person.best_friends.create(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
# create!
def test_has_one_create_with_bang_with_attr_protected_attributes
best_friend = @person.best_friends.create!(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_bang_with_attr_accessible_attributes
best_friend = @person.best_friends.create!(attributes_hash)
assert_default_attributes(best_friend, true)
end
def test_has_one_create_with_bang_with_admin_scope_with_attr_protected_attributes
best_friend = @person.best_friends.create!(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_with_bang_with_admin_scope_with_attr_accessible_attributes
best_friend = @person.best_friends.create!(attributes_hash, :as => :admin)
assert_admin_attributes(best_friend, true)
end
def test_has_one_create_with_bang_without_protection
best_friend = @person.best_friends.create!(attributes_hash, :without_protection => true)
assert_all_attributes(best_friend)
end
end

View file

@ -1,6 +1,6 @@
class Person < ActiveRecord::Base
has_many :readers
has_one :reader
has_one :reader
has_many :posts, :through => :readers
has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments, :conditions => 'comments.id is null'
@ -8,23 +8,23 @@ class Person < ActiveRecord::Base
has_many :references
has_many :bad_references
has_many :fixed_bad_references, :conditions => { :favourite => true }, :class_name => 'BadReference'
has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true]
has_one :favourite_reference, :class_name => 'Reference', :conditions => ['favourite=?', true]
has_many :posts_with_comments_sorted_by_comment_id, :through => :readers, :source => :post, :include => :comments, :order => 'comments.id'
has_many :jobs, :through => :references
has_many :jobs_with_dependent_destroy, :source => :job, :through => :references, :dependent => :destroy
has_many :jobs_with_dependent_destroy, :source => :job, :through => :references, :dependent => :destroy
has_many :jobs_with_dependent_delete_all, :source => :job, :through => :references, :dependent => :delete_all
has_many :jobs_with_dependent_nullify, :source => :job, :through => :references, :dependent => :nullify
has_many :jobs_with_dependent_nullify, :source => :job, :through => :references, :dependent => :nullify
belongs_to :primary_contact, :class_name => 'Person'
has_many :agents, :class_name => 'Person', :foreign_key => 'primary_contact_id'
has_many :agents_of_agents, :through => :agents, :source => :agents
belongs_to :number1_fan, :class_name => 'Person'
has_many :agents_posts, :through => :agents, :source => :posts
has_many :agents_posts, :through => :agents, :source => :posts
has_many :agents_posts_authors, :through => :agents_posts, :source => :author
scope :males, :conditions => { :gender => 'M' }
scope :males, :conditions => { :gender => 'M' }
scope :females, :conditions => { :gender => 'F' }
end
@ -56,14 +56,25 @@ class LoosePerson < ActiveRecord::Base
attr_protected :comments
attr_protected :as => :admin
has_one :best_friend, :class_name => 'LoosePerson', :foreign_key => :best_friend_id
belongs_to :best_friend_of, :class_name => 'LoosePerson', :foreign_key => :best_friend_of_id
has_many :best_friends, :class_name => 'LoosePerson', :foreign_key => :best_friend_id
end
class LooseDescendant < LoosePerson; end
class TightPerson < ActiveRecord::Base
self.table_name = 'people'
attr_accessible :first_name, :gender
attr_accessible :first_name, :gender, :comments, :as => :admin
has_one :best_friend, :class_name => 'TightPerson', :foreign_key => :best_friend_id
belongs_to :best_friend_of, :class_name => 'TightPerson', :foreign_key => :best_friend_of_id
has_many :best_friends, :class_name => 'TightPerson', :foreign_key => :best_friend_id
end
class TightDescendant < TightPerson; end

View file

@ -438,6 +438,8 @@ ActiveRecord::Schema.define do
t.references :number1_fan
t.integer :lock_version, :null => false, :default => 0
t.string :comments
t.references :best_friend
t.references :best_friend_of
t.timestamps
end