aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/metal/strong_parameters.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller/metal/strong_parameters.rb')
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb62
1 files changed, 59 insertions, 3 deletions
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index 9c82abb640..e14da22e01 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -334,6 +334,15 @@ module ActionController
# params = ActionController::Parameters.new(tags: ['rails', 'parameters'])
# params.permit(tags: [])
#
+ # Sometimes it is not possible or convenient to declare the valid keys of
+ # a hash parameter or its internal structure. Just map to an empty hash:
+ #
+ # params.permit(preferences: {})
+ #
+ # but be careful because this opens the door to arbitrary input. In this
+ # case, +permit+ ensures values in the returned structure are permitted
+ # scalars and filters out anything else.
+ #
# You can also use +permit+ on nested parameters, like:
#
# params = ActionController::Parameters.new({
@@ -382,14 +391,15 @@ module ActionController
case filter
when Symbol, String
permitted_scalar_filter(params, filter)
- when Hash then
+ when Hash
hash_filter(params, filter)
end
end
unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters
- params.permit!
+ params.permitted = true
+ params
end
# Returns a parameter for the given +key+. If not found,
@@ -539,7 +549,7 @@ module ActionController
new_instance_with_inherited_permitted_status(@parameters.select(&block))
end
- # Equivalent to Hash#keep_if, but returns nil if no changes were made.
+ # Equivalent to Hash#keep_if, but returns +nil+ if no changes were made.
def select!(&block)
@parameters.select!(&block)
self
@@ -573,6 +583,13 @@ module ActionController
)
end
+ # Returns current <tt>ActionController::Parameters</tt> instance which
+ # +other_hash+ merges into current hash.
+ def merge!(other_hash)
+ @parameters.merge!(other_hash.to_h)
+ self
+ end
+
# This is required by ActiveModel attribute assignment, so that user can
# pass +Parameters+ to a mass assignment methods in a model. It should not
# matter as we are using +HashWithIndifferentAccess+ internally.
@@ -759,6 +776,7 @@ module ActionController
end
EMPTY_ARRAY = []
+ EMPTY_HASH = {}
def hash_filter(params, filter)
filter = filter.with_indifferent_access
@@ -772,6 +790,11 @@ module ActionController
array_of_permitted_scalars?(self[key]) do |val|
params[key] = val
end
+ elsif filter[key] == EMPTY_HASH
+ # Declaration { preferences: {} }.
+ if value.is_a?(Parameters)
+ params[key] = permit_any_in_parameters(value)
+ end
elsif non_scalar?(value)
# Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
params[key] = each_element(value) do |element|
@@ -781,6 +804,39 @@ module ActionController
end
end
+ def permit_any_in_parameters(params)
+ self.class.new.tap do |sanitized|
+ params.each do |key, value|
+ case value
+ when ->(v) { permitted_scalar?(v) }
+ sanitized[key] = value
+ when Array
+ sanitized[key] = permit_any_in_array(value)
+ when Parameters
+ sanitized[key] = permit_any_in_parameters(value)
+ else
+ # Filter this one out.
+ end
+ end
+ sanitized.permitted = true
+ end
+ end
+
+ def permit_any_in_array(array)
+ [].tap do |sanitized|
+ array.each do |element|
+ case element
+ when ->(e) { permitted_scalar?(e) }
+ sanitized << element
+ when Parameters
+ sanitized << permit_any_in_parameters(element)
+ else
+ # Filter this one out.
+ end
+ end
+ end
+ end
+
def initialize_copy(source)
super
@parameters = @parameters.dup