Module: ActiveModel::MassAssignmentSecurity::ClassMethods

Defined in:
lib/active_model/mass_assignment_security.rb

Instance Method Summary collapse

Instance Method Details

#accessible_attributes(role = :default) ⇒ Object

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"}>


246
247
248
# File 'lib/active_model/mass_assignment_security.rb', line 246

def accessible_attributes(role = :default)
  accessible_attributes_configs[role]
end

#active_authorizersObject Also known as: active_authorizer

Returns a hash with the protected attributes (by #attr_accessible or #attr_protected) per role.

class Customer
  include ActiveModel::MassAssignmentSecurity

  attr_accessor :name, :credit_rating

  attr_accessible :name, as: [:admin, :default]
  attr_accessible :credit_rating, as: :admin
end

Customer.active_authorizers
# => {
#       :admin=> #<ActiveModel::MassAssignmentSecurity::WhiteList: {"name", "credit_rating"}>,
#       :default=>#<ActiveModel::MassAssignmentSecurity::WhiteList: {"name"}>
#    }


267
268
269
# File 'lib/active_model/mass_assignment_security.rb', line 267

def active_authorizers
  self._active_authorizer ||= protected_attributes_configs
end

#attr_accessible(*args) ⇒ Object

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.



186
187
188
189
190
191
192
193
194
195
196
197
198
# 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

#attr_protected(*args) ⇒ Object

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: '[email protected]', logins_count: 5 }, as: :default)
customer.name         # => "David"
customer.email        # => "[email protected]"
customer.logins_count # => nil

And using the :admin role:

customer = Customer.new
customer.assign_attributes({ name: 'David', email: '[email protected]', logins_count: 5}, as: :admin)
customer.name         # => "David"
customer.email        # => nil
customer.logins_count # => nil

customer.email = '[email protected]'
customer.email # => "[email protected]"

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.



119
120
121
122
123
124
125
126
127
128
129
130
131
# 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

#attributes_protected_by_defaultObject

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}>


285
286
287
# File 'lib/active_model/mass_assignment_security.rb', line 285

def attributes_protected_by_default
  []
end

#mass_assignment_sanitizer=(value) ⇒ Object

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


325
326
327
328
329
330
331
# 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

#protected_attributes(role = :default) ⇒ Object

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"}>


221
222
223
# File 'lib/active_model/mass_assignment_security.rb', line 221

def protected_attributes(role = :default)
  protected_attributes_configs[role]
end