Mass assignment security provides an interface for protecting attributes from end-user assignment. For more complex permissions, mass assignment security may be handled outside the model by extending a non-ActiveRecord class, such as a controller, with this behavior.
For example, a logged in user may need to assign additional attributes depending on their role:
class AccountsController < ApplicationController include ActiveModel::MassAssignmentSecurity attr_accessible :first_name, :last_name attr_accessible :first_name, :last_name, :plan_id, :as => :admin def update ... @account.update_attributes(account_params) ... end protected def account_params role = admin ? :admin : :default sanitize_for_mass_assignment(params[:account], role) end end
Configuration options
- mass_assignment_sanitizer - Defines sanitize method. Possible
values are:
- :logger (default) - writes filtered attributes to logger
- :strict - raise ActiveModel::MassAssignmentSecurity::Error on any protected attribute update
You can specify your own sanitizer object eg. MySanitizer.new. See ActiveModel::MassAssignmentSecurity::LoggerSanitizer for example implementation.
- A
- M
- P
Alias for active_authorizers
Specifies a white list of model attributes that can be set via mass-assignment.
Like attr_protected, a role for the attributes is optional, if no role is provided then :default is used. A role can be defined by using the :as option.
This is the opposite of the attr_protected macro: Mass-assignment will only set attributes in this list, to assign to the rest of attributes you can use direct writer methods. This is meant to protect sensitive attributes from being overwritten by malicious users tampering with URLs or forms. If you’d rather start from an all-open default and restrict attributes as needed, have a look at attr_protected.
class Customer include ActiveModel::MassAssignmentSecurity attr_accessor :name, :credit_rating attr_accessible :name attr_accessible :name, :credit_rating, :as => :admin def assign_attributes(values, options = {}) sanitize_for_mass_assignment(values, options[:as]).each do |k, v| send("#{k}=", v) end end end
When using the :default role:
customer = Customer.new customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :default) customer.name # => "David" customer.credit_rating # => nil customer.credit_rating = "Average" customer.credit_rating # => "Average"
And using the :admin role:
customer = Customer.new customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :admin) customer.name # => "David" customer.credit_rating # => "Excellent"
Note that using Hash#except or Hash#slice in place of attr_accessible to sanitize attributes provides basically the same functionality, but it makes a bit tricky to deal with nested attributes.
# File activemodel/lib/active_model/mass_assignment_security.rb, line 174 174: def attr_accessible(*args) 175: options = args.extract_options! 176: role = options[:as] || :default 177: 178: self._accessible_attributes = accessible_attributes_configs.dup 179: 180: Array.wrap(role).each do |name| 181: self._accessible_attributes[name] = self.accessible_attributes(name) + args 182: end 183: 184: self._active_authorizer = self._accessible_attributes 185: end
Attributes named in this macro are protected from mass-assignment whenever attributes are sanitized before assignment. A role for the attributes is optional, if no role is provided then :default is used. A role can be defined by using the :as option.
Mass-assignment to these attributes will simply be ignored, to assign to them you can use direct writer methods. This is meant to protect sensitive attributes from being overwritten by malicious users tampering with URLs or forms. Example:
class Customer include ActiveModel::MassAssignmentSecurity attr_accessor :name, :credit_rating attr_protected :credit_rating, :last_login attr_protected :last_login, :as => :admin def assign_attributes(values, options = {}) sanitize_for_mass_assignment(values, options[:as]).each do |k, v| send("#{k}=", v) end end end
When using the :default role:
customer = Customer.new customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :default) customer.name # => "David" customer.credit_rating # => nil customer.last_login # => nil customer.credit_rating = "Average" customer.credit_rating # => "Average"
And using the :admin role:
customer = Customer.new customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :admin) customer.name # => "David" customer.credit_rating # => "Excellent" customer.last_login # => nil
To start from an all-closed default and enable attributes as needed, have a look at attr_accessible.
Note that using Hash#except or Hash#slice in place of attr_protected to sanitize attributes provides basically the same functionality, but it makes a bit tricky to deal with nested attributes.
# File activemodel/lib/active_model/mass_assignment_security.rb, line 111 111: def attr_protected(*args) 112: options = args.extract_options! 113: role = options[:as] || :default 114: 115: self._protected_attributes = protected_attributes_configs.dup 116: 117: Array.wrap(role).each do |name| 118: self._protected_attributes[name] = self.protected_attributes(name) + args 119: end 120: 121: self._active_authorizer = self._protected_attributes 122: end
# File activemodel/lib/active_model/mass_assignment_security.rb, line 204 204: def mass_assignment_sanitizer=(value) 205: self._mass_assignment_sanitizer = if value.is_a?(Symbol) 206: const_get(:"#{value.to_s.camelize}Sanitizer").new(self) 207: else 208: value 209: end 210: end