class ActionController::Parameters
Action Controller Parameters¶ ↑
Allows to choose which attributes should be whitelisted for mass updating and thus prevent accidentally exposing that which shouldn’t be exposed. Provides two methods for this purpose: require
and permit
. The former is used to mark parameters as required. The latter is used to set the parameter as permitted and limit which attributes should be allowed for mass updating.
params = ActionController::Parameters.new({ person: { name: 'Francesco', age: 22, role: 'admin' } }) permitted = params.require(:person).permit(:name, :age) permitted # => {"name"=>"Francesco", "age"=>22} permitted.class # => ActionController::Parameters permitted.permitted? # => true Person.first.update!(permitted) # => #<Person id: 1, name: "Francesco", age: 22, role: "user">
It provides two options that controls the top-level behavior of new instances:
-
permit_all_parameters
- If it’strue
, all the parameters will be permitted by default. The default isfalse
. -
action_on_unpermitted_parameters
- Allow to control the behavior when parameters that are not explicitly permitted are found. The values can be:log
to write a message on the logger or:raise
to raiseActionController::UnpermittedParameters
exception. The default value is:log
in test and development environments,false
otherwise.
Examples:
params = ActionController::Parameters.new params.permitted? # => false ActionController::Parameters.permit_all_parameters = true params = ActionController::Parameters.new params.permitted? # => true params = ActionController::Parameters.new(a: "123", b: "456") params.permit(:c) # => {} ActionController::Parameters.action_on_unpermitted_parameters = :raise params = ActionController::Parameters.new(a: "123", b: "456") params.permit(:c) # => ActionController::UnpermittedParameters: found unpermitted keys: a, b
Please note that these options *are not thread-safe*. In a multi-threaded environment they should only be set once at boot-time and never mutated at runtime.
ActionController::Parameters
inherits from ActiveSupport::HashWithIndifferentAccess
, this means that you can fetch values using either :key
or "key"
.
params = ActionController::Parameters.new(key: 'value') params[:key] # => "value" params["key"] # => "value"
Constants
- EMPTY_ARRAY
- PERMITTED_SCALAR_TYPES
This is a white list of permitted scalar types that includes the ones supported in XML and JSON requests.
This list is in particular used to filter ordinary requests, String goes as first element to quickly short-circuit the common case.
If you modify this collection please update the API of
permit
above.
Public Class Methods
# File lib/backports/action_controller/metal/strong_parameters.rb 124 def self.const_missing(const_name) 125 super unless const_name == :NEVER_UNPERMITTED_PARAMS 126 ActiveSupport::Deprecation.warn(<<-MSG.squish) 127 `ActionController::Parameters::NEVER_UNPERMITTED_PARAMS` has been deprecated. 128 Use `ActionController::Parameters.always_permitted_parameters` instead. 129 MSG 130 131 always_permitted_parameters 132 end
Returns a new instance of ActionController::Parameters
. Also, sets the permitted
attribute to the default value of ActionController::Parameters.permit_all_parameters
.
class Person < ActiveRecord::Base end params = ActionController::Parameters.new(name: 'Francesco') params.permitted? # => false Person.new(params) # => ActiveModel::ForbiddenAttributesError ActionController::Parameters.permit_all_parameters = true params = ActionController::Parameters.new(name: 'Francesco') params.permitted? # => true Person.new(params) # => #<Person id: nil, name: "Francesco">
# File lib/backports/action_controller/metal/strong_parameters.rb 150 def initialize(attributes = nil) 151 super(attributes) 152 @permitted = self.class.permit_all_parameters 153 end
Public Instance Methods
Returns a parameter for the given key
. If not found, returns nil
.
params = ActionController::Parameters.new(person: { name: 'Francesco' }) params[:person] # => {"name"=>"Francesco"} params[:none] # => nil
# File lib/backports/action_controller/metal/strong_parameters.rb 352 def [](key) 353 convert_hashes_to_parameters(key, super) 354 end
Attribute that keeps track of converted arrays, if any, to avoid double looping in the common use case permit + mass-assignment. Defined in a method to instantiate it only if needed.
Testing membership still loops, but it’s going to be faster than our own loop that converts values. Also, we are not going to build a new array object per fetch.
# File lib/backports/action_controller/metal/strong_parameters.rb 199 def converted_arrays 200 @converted_arrays ||= Set.new 201 end
Deletes and returns a key-value pair from Parameters
whose key is equal to key. If the key is not found, returns the default value. If the optional code block is given and the key is not found, pass in the key and return the result of block.
# File lib/backports/action_controller/metal/strong_parameters.rb 422 def delete(key, &block) 423 convert_hashes_to_parameters(key, super, false) 424 end
Returns an exact copy of the ActionController::Parameters
instance. permitted
state is kept on the duped object.
params = ActionController::Parameters.new(a: 1) params.permit! params.permitted? # => true copy_params = params.dup # => {"a"=>1} copy_params.permitted? # => true
# File lib/backports/action_controller/metal/strong_parameters.rb 439 def dup 440 super.tap do |duplicate| 441 duplicate.permitted = @permitted 442 end 443 end
Convert all hashes in values into parameters, then yield each pair like the same way as Hash#each_pair
# File lib/backports/action_controller/metal/strong_parameters.rb 182 def each_pair(&block) 183 super do |key, value| 184 convert_hashes_to_parameters(key, value) 185 end 186 187 super 188 end
Removes and returns the key/value pairs matching the given keys.
params = ActionController::Parameters.new(a: 1, b: 2, c: 3) params.extract!(:a, :b) # => {"a"=>1, "b"=>2} params # => {"c"=>3}
# File lib/backports/action_controller/metal/strong_parameters.rb 389 def extract!(*keys) 390 new_instance_with_inherited_permitted_status(super) 391 end
Returns a parameter for the given key
. If the key
can’t be found, there are several options: With no other arguments, it will raise an ActionController::ParameterMissing
error; if more arguments are given, then that will be returned; if a block is given, then that will be run and its result returned.
params = ActionController::Parameters.new(person: { name: 'Francesco' }) params.fetch(:person) # => {"name"=>"Francesco"} params.fetch(:none) # => ActionController::ParameterMissing: param not found: none params.fetch(:none, 'Francesco') # => "Francesco" params.fetch(:none) { 'Francesco' } # => "Francesco"
# File lib/backports/action_controller/metal/strong_parameters.rb 367 def fetch(key, *args) 368 convert_hashes_to_parameters(key, super, false) 369 rescue KeyError 370 raise ActionController::ParameterMissing.new(key) 371 end
Returns a new ActionController::Parameters
instance that includes only the given filters
and sets the permitted
attribute for the object to true
. This is useful for limiting which attributes should be allowed for mass updating.
params = ActionController::Parameters.new(user: { name: 'Francesco', age: 22, role: 'admin' }) permitted = params.require(:user).permit(:name, :age) permitted.permitted? # => true permitted.has_key?(:name) # => true permitted.has_key?(:age) # => true permitted.has_key?(:role) # => false
Only permitted scalars pass the filter. For example, given
params.permit(:name)
:name
passes it is a key of params
whose associated value is of type String
, Symbol
, NilClass
, Numeric
, TrueClass
, FalseClass
, Date
, Time
, DateTime
, StringIO
, IO
, ActionDispatch::Http::UploadedFile
or Rack::Test::UploadedFile
. Otherwise, the key :name
is filtered out.
You may declare that the parameter should be an array of permitted scalars by mapping it to an empty array:
params = ActionController::Parameters.new(tags: ['rails', 'parameters']) params.permit(tags: [])
You can also use permit
on nested parameters, like:
params = ActionController::Parameters.new({ person: { name: 'Francesco', age: 22, pets: [{ name: 'Purplish', category: 'dogs' }] } }) permitted = params.permit(person: [ :name, { pets: :name } ]) permitted.permitted? # => true permitted[:person][:name] # => "Francesco" permitted[:person][:age] # => nil permitted[:person][:pets][0][:name] # => "Purplish" permitted[:person][:pets][0][:category] # => nil
Note that if you use permit
in a key that points to a hash, it won’t allow all the hash. You also need to specify which attributes inside the hash should be whitelisted.
params = ActionController::Parameters.new({ person: { contact: { email: 'none@test.com', phone: '555-1234' } } }) params.require(:person).permit(:contact) # => {} params.require(:person).permit(contact: :phone) # => {"contact"=>{"phone"=>"555-1234"}} params.require(:person).permit(contact: [ :email, :phone ]) # => {"contact"=>{"email"=>"none@test.com", "phone"=>"555-1234"}}
# File lib/backports/action_controller/metal/strong_parameters.rb 329 def permit(*filters) 330 params = self.class.new 331 332 filters.flatten.each do |filter| 333 case filter 334 when Symbol, String 335 permitted_scalar_filter(params, filter) 336 when Hash then 337 hash_filter(params, filter) 338 end 339 end 340 341 unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters 342 343 params.permit! 344 end
Sets the permitted
attribute to true
. This can be used to pass mass assignment. Returns self
.
class Person < ActiveRecord::Base end params = ActionController::Parameters.new(name: 'Francesco') params.permitted? # => false Person.new(params) # => ActiveModel::ForbiddenAttributesError params.permit! params.permitted? # => true Person.new(params) # => #<Person id: nil, name: "Francesco">
# File lib/backports/action_controller/metal/strong_parameters.rb 225 def permit! 226 each_pair do |key, value| 227 Array.wrap(value).each do |v| 228 v.permit! if v.respond_to? :permit! 229 end 230 end 231 232 @permitted = true 233 self 234 end
Returns true
if the parameter is permitted, false
otherwise.
params = ActionController::Parameters.new params.permitted? # => false params.permit! params.permitted? # => true
# File lib/backports/action_controller/metal/strong_parameters.rb 209 def permitted? 210 @permitted 211 end
Ensures that a parameter is present. If it’s present, returns the parameter at the given key
, otherwise raises an ActionController::ParameterMissing
error.
ActionController::Parameters.new(person: { name: 'Francesco' }).require(:person) # => {"name"=>"Francesco"} ActionController::Parameters.new(person: nil).require(:person) # => ActionController::ParameterMissing: param not found: person ActionController::Parameters.new(person: {}).require(:person) # => ActionController::ParameterMissing: param not found: person
# File lib/backports/action_controller/metal/strong_parameters.rb 248 def require(key) 249 value = self[key] 250 if value.present? || value == false 251 value 252 else 253 raise ParameterMissing.new(key) 254 end 255 end
Equivalent to Hash#keep_if, but returns nil if no changes were made.
# File lib/backports/action_controller/metal/strong_parameters.rb 427 def select!(&block) 428 convert_value_to_parameters(super) 429 end
Returns a new ActionController::Parameters
instance that includes only the given keys
. If the given keys
don’t exist, returns an empty hash.
params = ActionController::Parameters.new(a: 1, b: 2, c: 3) params.slice(:a, :b) # => {"a"=>1, "b"=>2} params.slice(:d) # => {}
# File lib/backports/action_controller/metal/strong_parameters.rb 380 def slice(*keys) 381 new_instance_with_inherited_permitted_status(super) 382 end
Returns a safe Hash
representation of this parameter with all unpermitted keys removed.
params = ActionController::Parameters.new({ name: 'Senjougahara Hitagi', oddity: 'Heavy stone crab' }) params.to_h # => {} safe_params = params.permit(:name) safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
# File lib/backports/action_controller/metal/strong_parameters.rb 166 def to_h 167 if permitted? 168 to_hash 169 else 170 slice(*self.class.always_permitted_parameters).permit!.to_h 171 end 172 end
Returns an unsafe, unfiltered Hash
representation of this parameter.
# File lib/backports/action_controller/metal/strong_parameters.rb 175 def to_unsafe_h 176 to_hash 177 end
Returns a new ActionController::Parameters
with the results of running block
once for every value. The keys are unchanged.
params = ActionController::Parameters.new(a: 1, b: 2, c: 3) params.transform_values { |x| x * 2 } # => {"a"=>2, "b"=>4, "c"=>6}
# File lib/backports/action_controller/metal/strong_parameters.rb 399 def transform_values 400 if block_given? 401 new_instance_with_inherited_permitted_status(super) 402 else 403 super 404 end 405 end
Protected Instance Methods
# File lib/backports/action_controller/metal/strong_parameters.rb 446 def permitted=(new_permitted) 447 @permitted = new_permitted 448 end
Private Instance Methods
# File lib/backports/action_controller/metal/strong_parameters.rb 551 def array_of_permitted_scalars?(value) 552 if value.is_a?(Array) 553 value.all? {|element| permitted_scalar?(element)} 554 end 555 end
# File lib/backports/action_controller/metal/strong_parameters.rb 557 def array_of_permitted_scalars_filter(params, key) 558 if has_key?(key) && array_of_permitted_scalars?(self[key]) 559 params[key] = self[key] 560 end 561 end
# File lib/backports/action_controller/metal/strong_parameters.rb 457 def convert_hashes_to_parameters(key, value, assign_if_converted=true) 458 converted = convert_value_to_parameters(value) 459 self[key] = converted if assign_if_converted && !converted.equal?(value) 460 converted 461 end
# File lib/backports/action_controller/metal/strong_parameters.rb 463 def convert_value_to_parameters(value) 464 if value.is_a?(Array) && !converted_arrays.member?(value) 465 converted = value.map { |_| convert_value_to_parameters(_) } 466 converted_arrays << converted 467 converted 468 elsif value.is_a?(Parameters) || !value.is_a?(Hash) 469 value 470 else 471 self.class.new(value) 472 end 473 end
# File lib/backports/action_controller/metal/strong_parameters.rb 475 def each_element(object) 476 if object.is_a?(Array) 477 object.map { |el| yield el }.compact 478 elsif fields_for_style?(object) 479 hash = object.class.new 480 object.each { |k,v| hash[k] = yield v } 481 hash 482 else 483 yield object 484 end 485 end
# File lib/backports/action_controller/metal/strong_parameters.rb 487 def fields_for_style?(object) 488 object.is_a?(Hash) && object.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) } 489 end
# File lib/backports/action_controller/metal/strong_parameters.rb 564 def hash_filter(params, filter) 565 filter = filter.with_indifferent_access 566 567 # Slicing filters out non-declared keys. 568 slice(*filter.keys).each do |key, value| 569 next unless value 570 571 if filter[key] == EMPTY_ARRAY 572 # Declaration { comment_ids: [] }. 573 array_of_permitted_scalars_filter(params, key) 574 else 575 # Declaration { user: :name } or { user: [:name, :age, { address: ... }] }. 576 params[key] = each_element(value) do |element| 577 if element.is_a?(Hash) 578 element = self.class.new(element) unless element.respond_to?(:permit) 579 element.permit(*Array.wrap(filter[key])) 580 end 581 end 582 end 583 end 584 end
# File lib/backports/action_controller/metal/strong_parameters.rb 451 def new_instance_with_inherited_permitted_status(hash) 452 self.class.new(hash).tap do |new_instance| 453 new_instance.permitted = @permitted 454 end 455 end
# File lib/backports/action_controller/metal/strong_parameters.rb 535 def permitted_scalar?(value) 536 PERMITTED_SCALAR_TYPES.any? {|type| value.is_a?(type)} 537 end
# File lib/backports/action_controller/metal/strong_parameters.rb 539 def permitted_scalar_filter(params, key) 540 if has_key?(key) && permitted_scalar?(self[key]) 541 params[key] = self[key] 542 end 543 544 keys.grep(/\A#{Regexp.escape(key)}\(\d+[if]?\)\z/) do |k| 545 if permitted_scalar?(self[k]) 546 params[k] = self[k] 547 end 548 end 549 end
# File lib/backports/action_controller/metal/strong_parameters.rb 504 def unpermitted_keys(params) 505 self.keys - params.keys - self.always_permitted_parameters 506 end
# File lib/backports/action_controller/metal/strong_parameters.rb 491 def unpermitted_parameters!(params) 492 unpermitted_keys = unpermitted_keys(params) 493 if unpermitted_keys.any? 494 case self.class.action_on_unpermitted_parameters 495 when :log 496 name = "unpermitted_parameters.action_controller" 497 ActiveSupport::Notifications.instrument(name, keys: unpermitted_keys) 498 when :raise 499 raise ActionController::UnpermittedParameters.new(unpermitted_keys) 500 end 501 end 502 end