mass_assignment.rdoc

doc/mass_assignment.rdoc
Last Update: 2023-08-03 09:06:34 -0700

Sequel::Model Mass Assignment

Most Model methods that take a hash of attribute keys and values, including Model.new, Model.create, Model#set and Model#update are subject to Sequel’s mass assignment rules.

If you have an instance of a plain Sequel::Model class:

class Post < Sequel::Model
end
post = Post.new

and you call a mass assignment method with a hash:

post.set(title: 'T', body: 'B')

the mass assignment method will go through each key in the hash, append = to it to determine the setter method, and if the setter method is defined and access to it is not restricted, Sequel will call the setter method with the hash value. So if we assume that the posts table has title and body columns, what the above mass assignment call actually does is:

post.title=('T')
post.body=('B')

By default, there are two types of setter methods that are restricted. The first is methods like typecast_on_assignment= and ==, which don’t affect columns. These methods cannot be enabled for mass assignment. The second is primary key setters.

So if you do:

post = Post.new(id: 1)

Sequel will raise a Sequel::MassAssignmentRestriction exception, since by default setting the primary key is not allowed.

To enable use of primary key setters, you need to call unrestrict_primary_key for that model:

Post.unrestrict_primary_key

If you want to change mass assignment so it ignores attempts to access restricted setter methods, you can do:

# Global default
Sequel::Model.strict_param_setting = false
# Class level
Post.strict_param_setting = false
# Instance level
post.strict_param_setting = false

Since mass assignment by default allows modification of all column values except for primary key columns, it can be a security risk in some cases. If you are dealing with untrusted input, you are generally going to want to restrict what should be updated.

Sequel has Model#set_fields and Model#update_fields methods, which are designed to be used with untrusted input. These methods take two arguments, the untrusted hash as the first argument, and a trusted array of field names as the second argument:

post.set_fields({title: 'T', body: 'B'}, [:title, :body])

Instead of looking at every key in the untrusted hash, set_fields will iterate over the trusted field names, looking each up in the hash, and calling the setter method appropriately with the result. set_fields basically translates the above method call to:

post.title=('T')
post.body=('B')

By using this method, you can be sure that the mass assignment method only sets the fields you expect it to set.

Note that if one of the fields does not exist in the hash:

post.set_fields({title: 'T'}, [:title, :body])

set_fields will set the value to nil (the default hash value) by default, with behavior equivalent to:

post.title=('T')
post.body=(nil)

You can use the :missing option to set_fields to change the behavior:

post.set_fields({title: 'T'}, [:title, :body], missing: :skip)
# post.title=('T') # only

post.set_fields({title: 'T'}, [:title, :body], missing: :raise)
# raises Sequel::Error

If you want to set a model level default for the set_fields options, you can use the default_set_fields_options class accessor:

# Global default
Sequel::Model.default_set_fields_options[:missing] = :skip
# Class level
Post.default_set_fields_options[:missing] = :skip

Here’s a table describing Sequel’s default mass assignment methods:

Model.new(hash)

Creates a new model instance, then calls Model#set(hash)

Model.create(hash)

Calls Model.new(hash).save

Model#set(hash)

Calls related setter method (unless access is restricted) for each key in the hash, then returns self

Model#update(hash)

Calls set(hash).save_changes

Model#set_fields(hash, columns, options)

For each column in columns, looks up related entry in hash, and calls the related setter method

Model#update_fields(hash, columns, options)

Calls set_fields(hash, columns, options).save_changes

For backwards compatibility, Sequel also ships with a whitelist_security and blacklist_security plugins that offer additional mass assignment methods, but it is recommended to use set_fields or update_fields for untrusted input, and the other methods for trusted input.