module ActiveModel::MassAssignmentSecurity::ClassMethods
Public Instance Methods
Returns an instance of
ActiveModel::MassAssignmentSecurity::WhiteList
with the
attributes protected by attr_accessible
method. If no role
is provided, then :default
is
used.
class Customer include ActiveModel::MassAssignmentSecurity attr_accessor :name, :credit_rating attr_accessible :name, as: [:admin, :default] attr_accessible :credit_rating, as: :admin end Customer.accessible_attributes # => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name"}> Customer.accessible_attributes(:default) # => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name"}> Customer.accessible_attributes(:admin) # => #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name", "credit_rating"}>
# File lib/active_model/mass_assignment_security.rb, line 246 def accessible_attributes(role = :default) accessible_attributes_configs[role] end
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 with a symbol or an array of
symbols as the value.
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 # Both admin and default user can change name of a customer attr_accessible :name, as: [:admin, :default] # Only admin can change credit rating of a customer attr_accessible :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 lib/active_model/mass_assignment_security.rb, line 186 def attr_accessible(*args) options = args.extract_options! role = options[:as] || :default self._accessible_attributes = accessible_attributes_configs.dup Array(role).each do |name| self._accessible_attributes[name] = self.accessible_attributes(name) + args end self._uses_mass_assignment_security = true self._active_authorizer = self._accessible_attributes 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 with a symbol or an
array of symbols as the value.
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.
class Customer include ActiveModel::MassAssignmentSecurity attr_accessor :name, :email, :logins_count attr_protected :logins_count # Suppose that admin can not change email for customer attr_protected :logins_count, :email, 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', email: 'a@b.com', logins_count: 5 }, as: :default) customer.name # => "David" customer.email # => "a@b.com" customer.logins_count # => nil
And using the :admin
role:
customer = Customer.new customer.assign_attributes({ name: 'David', email: 'a@b.com', logins_count: 5}, as: :admin) customer.name # => "David" customer.email # => nil customer.logins_count # => nil customer.email = 'c@d.com' customer.email # => "c@d.com"
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 lib/active_model/mass_assignment_security.rb, line 119 def attr_protected(*args) options = args.extract_options! role = options[:as] || :default self._protected_attributes = protected_attributes_configs.dup Array(role).each do |name| self._protected_attributes[name] = self.protected_attributes(name) + args end self._uses_mass_assignment_security = true self._active_authorizer = self._protected_attributes end
Returns an empty array by default. You can still override this to define the default attributes protected by attr_protected method.
class Customer include ActiveModel::MassAssignmentSecurity def self.attributes_protected_by_default [:name] end end Customer.protected_attributes # => #<ActiveModel::MassAssignmentSecurity::BlackList: {:name}>
# File lib/active_model/mass_assignment_security.rb, line 285 def attributes_protected_by_default [] end
Defines sanitize method.
class Customer include ActiveModel::MassAssignmentSecurity attr_accessor :name attr_protected :name def assign_attributes(values) sanitize_for_mass_assignment(values).each do |k, v| send("#{k}=", v) end end end # See ActiveModel::MassAssignmentSecurity::StrictSanitizer for more information. Customer.mass_assignment_sanitizer = :strict customer = Customer.new customer.assign_attributes(name: 'David') # => ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes for Customer: name
Also, you can specify your own sanitizer object.
class CustomSanitizer < ActiveModel::MassAssignmentSecurity::Sanitizer def process_removed_attributes(klass, attrs) raise StandardError end end Customer.mass_assignment_sanitizer = CustomSanitizer.new customer = Customer.new customer.assign_attributes(name: 'David') # => StandardError: StandardError
# File lib/active_model/mass_assignment_security.rb, line 325 def mass_assignment_sanitizer=(value) self._mass_assignment_sanitizer = if value.is_a?(Symbol) const_get(:"#{value.to_s.camelize}Sanitizer").new(self) else value end end
Returns an instance of
ActiveModel::MassAssignmentSecurity::BlackList
with the
attributes protected by attr_protected method.
If no role
is provided, then :default
is used.
class Customer include ActiveModel::MassAssignmentSecurity attr_accessor :name, :email, :logins_count attr_protected :logins_count attr_protected :logins_count, :email, as: :admin end Customer.protected_attributes # => #<ActiveModel::MassAssignmentSecurity::BlackList: {"logins_count"}> Customer.protected_attributes(:default) # => #<ActiveModel::MassAssignmentSecurity::BlackList: {"logins_count"}> Customer.protected_attributes(:admin) # => #<ActiveModel::MassAssignmentSecurity::BlackList: {"logins_count", "email"}>
# File lib/active_model/mass_assignment_security.rb, line 221 def protected_attributes(role = :default) protected_attributes_configs[role] end
Private Instance Methods
# File lib/active_model/mass_assignment_security.rb, line 341 def accessible_attributes_configs self._accessible_attributes ||= begin Hash.new { |h,k| h[k] = WhiteList.new } end end
# File lib/active_model/mass_assignment_security.rb, line 335 def protected_attributes_configs self._protected_attributes ||= begin Hash.new { |h,k| h[k] = BlackList.new(attributes_protected_by_default) } end end