actionpack 4.1.16 → 4.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +163 -690
- data/README.rdoc +7 -2
- data/lib/abstract_controller/base.rb +16 -6
- data/lib/abstract_controller/callbacks.rb +28 -51
- data/lib/abstract_controller/helpers.rb +0 -3
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
- data/lib/abstract_controller/rendering.rb +1 -7
- data/lib/abstract_controller/url_for.rb +1 -1
- data/lib/action_controller.rb +1 -0
- data/lib/action_controller/base.rb +2 -1
- data/lib/action_controller/caching.rb +1 -1
- data/lib/action_controller/caching/fragments.rb +7 -1
- data/lib/action_controller/log_subscriber.rb +26 -25
- data/lib/action_controller/metal.rb +11 -7
- data/lib/action_controller/metal/conditional_get.rb +31 -6
- data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
- data/lib/action_controller/metal/force_ssl.rb +1 -1
- data/lib/action_controller/metal/head.rb +2 -0
- data/lib/action_controller/metal/http_authentication.rb +3 -15
- data/lib/action_controller/metal/instrumentation.rb +4 -7
- data/lib/action_controller/metal/live.rb +57 -6
- data/lib/action_controller/metal/mime_responds.rb +17 -227
- data/lib/action_controller/metal/redirecting.rb +14 -8
- data/lib/action_controller/metal/renderers.rb +19 -3
- data/lib/action_controller/metal/rendering.rb +2 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +75 -7
- data/lib/action_controller/metal/streaming.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +111 -11
- data/lib/action_controller/metal/url_for.rb +11 -12
- data/lib/action_controller/model_naming.rb +1 -1
- data/lib/action_controller/railtie.rb +4 -0
- data/lib/action_controller/test_case.rb +87 -75
- data/lib/action_dispatch/http/cache.rb +1 -1
- data/lib/action_dispatch/http/filter_parameters.rb +2 -2
- data/lib/action_dispatch/http/headers.rb +43 -9
- data/lib/action_dispatch/http/mime_negotiation.rb +10 -4
- data/lib/action_dispatch/http/mime_type.rb +2 -16
- data/lib/action_dispatch/http/parameter_filter.rb +1 -1
- data/lib/action_dispatch/http/parameters.rb +11 -26
- data/lib/action_dispatch/http/request.rb +30 -10
- data/lib/action_dispatch/http/response.rb +52 -17
- data/lib/action_dispatch/http/upload.rb +3 -8
- data/lib/action_dispatch/http/url.rb +87 -70
- data/lib/action_dispatch/journey/formatter.rb +18 -17
- data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +18 -26
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
- data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
- data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
- data/lib/action_dispatch/journey/nodes/node.rb +4 -0
- data/lib/action_dispatch/journey/parser.rb +52 -60
- data/lib/action_dispatch/journey/parser.y +11 -10
- data/lib/action_dispatch/journey/path/pattern.rb +16 -19
- data/lib/action_dispatch/journey/route.rb +3 -18
- data/lib/action_dispatch/journey/router.rb +34 -65
- data/lib/action_dispatch/journey/router/strexp.rb +9 -6
- data/lib/action_dispatch/journey/routes.rb +0 -4
- data/lib/action_dispatch/journey/visitors.rb +81 -92
- data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
- data/lib/action_dispatch/middleware/cookies.rb +27 -31
- data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -3
- data/lib/action_dispatch/middleware/exception_wrapper.rb +19 -17
- data/lib/action_dispatch/middleware/flash.rb +7 -4
- data/lib/action_dispatch/middleware/public_exceptions.rb +13 -8
- data/lib/action_dispatch/middleware/remote_ip.rb +3 -3
- data/lib/action_dispatch/middleware/request_id.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -0
- data/lib/action_dispatch/middleware/static.rb +22 -23
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +22 -18
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +36 -8
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +2 -8
- data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -24
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +0 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +119 -63
- data/lib/action_dispatch/routing/endpoint.rb +10 -0
- data/lib/action_dispatch/routing/inspector.rb +4 -11
- data/lib/action_dispatch/routing/mapper.rb +399 -278
- data/lib/action_dispatch/routing/polymorphic_routes.rb +190 -78
- data/lib/action_dispatch/routing/redirection.rb +10 -12
- data/lib/action_dispatch/routing/route_set.rb +224 -177
- data/lib/action_dispatch/routing/url_for.rb +9 -4
- data/lib/action_dispatch/testing/assertions.rb +11 -7
- data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
- data/lib/action_dispatch/testing/assertions/response.rb +2 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +9 -9
- data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
- data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
- data/lib/action_dispatch/testing/integration.rb +15 -18
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +5 -1
- data/lib/action_pack/gem_version.rb +3 -3
- metadata +57 -15
- data/lib/action_controller/metal/responder.rb +0 -297
@@ -14,7 +14,7 @@ module ActionController
|
|
14
14
|
include ActionController::RackDelegation
|
15
15
|
include ActionController::UrlFor
|
16
16
|
|
17
|
-
# Redirects the browser to the target specified in +options+. This parameter can
|
17
|
+
# Redirects the browser to the target specified in +options+. This parameter can be any one of:
|
18
18
|
#
|
19
19
|
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
|
20
20
|
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
|
@@ -24,6 +24,8 @@ module ActionController
|
|
24
24
|
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
|
25
25
|
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
|
26
26
|
#
|
27
|
+
# === Examples:
|
28
|
+
#
|
27
29
|
# redirect_to action: "show", id: 5
|
28
30
|
# redirect_to post
|
29
31
|
# redirect_to "http://www.rubyonrails.org"
|
@@ -32,7 +34,7 @@ module ActionController
|
|
32
34
|
# redirect_to :back
|
33
35
|
# redirect_to proc { edit_post_url(@post) }
|
34
36
|
#
|
35
|
-
# The redirection happens as a "302 Found" header unless otherwise specified
|
37
|
+
# The redirection happens as a "302 Found" header unless otherwise specified using the <tt>:status</tt> option:
|
36
38
|
#
|
37
39
|
# redirect_to post_url(@post), status: :found
|
38
40
|
# redirect_to action: 'atom', status: :moved_permanently
|
@@ -60,19 +62,21 @@ module ActionController
|
|
60
62
|
# redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
|
61
63
|
# redirect_to({ action: 'atom' }, alert: "Something serious happened")
|
62
64
|
#
|
63
|
-
# When using <tt>redirect_to :back</tt>, if there is no referrer,
|
64
|
-
#
|
65
|
+
# When using <tt>redirect_to :back</tt>, if there is no referrer,
|
66
|
+
# <tt>ActionController::RedirectBackError</tt> will be raised. You
|
67
|
+
# may specify some fallback behavior for this case by rescuing
|
68
|
+
# <tt>ActionController::RedirectBackError</tt>.
|
65
69
|
def redirect_to(options = {}, response_status = {}) #:doc:
|
66
70
|
raise ActionControllerError.new("Cannot redirect to nil!") unless options
|
67
71
|
raise ActionControllerError.new("Cannot redirect to a parameter hash!") if options.is_a?(ActionController::Parameters)
|
68
72
|
raise AbstractController::DoubleRenderError if response_body
|
69
73
|
|
70
74
|
self.status = _extract_redirect_to_status(options, response_status)
|
71
|
-
self.location = _compute_redirect_to_location(options)
|
72
|
-
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.
|
75
|
+
self.location = _compute_redirect_to_location(request, options)
|
76
|
+
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
|
73
77
|
end
|
74
78
|
|
75
|
-
def _compute_redirect_to_location(options) #:nodoc:
|
79
|
+
def _compute_redirect_to_location(request, options) #:nodoc:
|
76
80
|
case options
|
77
81
|
# The scheme name consist of a letter followed by any combination of
|
78
82
|
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
|
@@ -86,11 +90,13 @@ module ActionController
|
|
86
90
|
when :back
|
87
91
|
request.headers["Referer"] or raise RedirectBackError
|
88
92
|
when Proc
|
89
|
-
_compute_redirect_to_location options.call
|
93
|
+
_compute_redirect_to_location request, options.call
|
90
94
|
else
|
91
95
|
url_for(options)
|
92
96
|
end.delete("\0\r\n")
|
93
97
|
end
|
98
|
+
module_function :_compute_redirect_to_location
|
99
|
+
public :_compute_redirect_to_location
|
94
100
|
|
95
101
|
private
|
96
102
|
def _extract_redirect_to_status(options, response_status)
|
@@ -6,6 +6,11 @@ module ActionController
|
|
6
6
|
Renderers.add(key, &block)
|
7
7
|
end
|
8
8
|
|
9
|
+
# See <tt>Renderers.remove</tt>
|
10
|
+
def self.remove_renderer(key)
|
11
|
+
Renderers.remove(key)
|
12
|
+
end
|
13
|
+
|
9
14
|
class MissingRenderer < LoadError
|
10
15
|
def initialize(format)
|
11
16
|
super "No renderer defined for format: #{format}"
|
@@ -42,8 +47,8 @@ module ActionController
|
|
42
47
|
nil
|
43
48
|
end
|
44
49
|
|
45
|
-
#
|
46
|
-
# Default
|
50
|
+
# A Set containing renderer names that correspond to available renderer procs.
|
51
|
+
# Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
|
47
52
|
RENDERERS = Set.new
|
48
53
|
|
49
54
|
# Adds a new renderer to call within controller actions.
|
@@ -73,7 +78,7 @@ module ActionController
|
|
73
78
|
# respond_to do |format|
|
74
79
|
# format.html
|
75
80
|
# format.csv { render csv: @csvable, filename: @csvable.name }
|
76
|
-
#
|
81
|
+
# end
|
77
82
|
# end
|
78
83
|
# To use renderers and their mime types in more concise ways, see
|
79
84
|
# <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
|
@@ -83,6 +88,17 @@ module ActionController
|
|
83
88
|
RENDERERS << key.to_sym
|
84
89
|
end
|
85
90
|
|
91
|
+
# This method is the opposite of add method.
|
92
|
+
#
|
93
|
+
# Usage:
|
94
|
+
#
|
95
|
+
# ActionController::Renderers.remove(:csv)
|
96
|
+
def self.remove(key)
|
97
|
+
RENDERERS.delete(key.to_sym)
|
98
|
+
method = "_render_option_#{key}"
|
99
|
+
remove_method(method) if method_defined?(method)
|
100
|
+
end
|
101
|
+
|
86
102
|
module All
|
87
103
|
extend ActiveSupport::Concern
|
88
104
|
include Renderers
|
@@ -67,8 +67,8 @@ module ActionController
|
|
67
67
|
options[:html] = ERB::Util.html_escape(options[:html])
|
68
68
|
end
|
69
69
|
|
70
|
-
if options.delete(:nothing)
|
71
|
-
options[:body] =
|
70
|
+
if options.delete(:nothing)
|
71
|
+
options[:body] = nil
|
72
72
|
end
|
73
73
|
|
74
74
|
if options[:status]
|
@@ -86,10 +86,6 @@ module ActionController
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
def _any_render_format_is_nil?(options)
|
90
|
-
RENDER_FORMATS_IN_PRIORITY.any? { |format| options.key?(format) && options[format].nil? }
|
91
|
-
end
|
92
|
-
|
93
89
|
# Process controller specific options, as status, content-type and location.
|
94
90
|
def _process_options(options) #:nodoc:
|
95
91
|
status, content_type, location = options.values_at(:status, :content_type, :location)
|
@@ -9,7 +9,7 @@ module ActionController #:nodoc:
|
|
9
9
|
end
|
10
10
|
|
11
11
|
# Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
|
12
|
-
# by including a token in the rendered
|
12
|
+
# by including a token in the rendered HTML for your application. This token is
|
13
13
|
# stored as a random string in the session, to which an attacker does not have
|
14
14
|
# access. When a request reaches your application, \Rails verifies the received
|
15
15
|
# token with the token in the session. Only HTML and JavaScript requests are checked,
|
@@ -44,7 +44,7 @@ module ActionController #:nodoc:
|
|
44
44
|
#
|
45
45
|
# The token parameter is named <tt>authenticity_token</tt> by default. The name and
|
46
46
|
# value of this token must be added to every layout that renders forms by including
|
47
|
-
# <tt>csrf_meta_tags</tt> in the
|
47
|
+
# <tt>csrf_meta_tags</tt> in the HTML +head+.
|
48
48
|
#
|
49
49
|
# Learn more about CSRF attacks and securing your application in the
|
50
50
|
# {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
|
@@ -68,12 +68,16 @@ module ActionController #:nodoc:
|
|
68
68
|
config_accessor :allow_forgery_protection
|
69
69
|
self.allow_forgery_protection = true if allow_forgery_protection.nil?
|
70
70
|
|
71
|
+
# Controls whether a CSRF failure logs a warning. On by default.
|
72
|
+
config_accessor :log_warning_on_csrf_failure
|
73
|
+
self.log_warning_on_csrf_failure = true
|
74
|
+
|
71
75
|
helper_method :form_authenticity_token
|
72
76
|
helper_method :protect_against_forgery?
|
73
77
|
end
|
74
78
|
|
75
79
|
module ClassMethods
|
76
|
-
# Turn on request forgery protection. Bear in mind that
|
80
|
+
# Turn on request forgery protection. Bear in mind that GET and HEAD requests are not checked.
|
77
81
|
#
|
78
82
|
# class ApplicationController < ActionController::Base
|
79
83
|
# protect_from_forgery
|
@@ -193,7 +197,9 @@ module ActionController #:nodoc:
|
|
193
197
|
mark_for_same_origin_verification!
|
194
198
|
|
195
199
|
if !verified_request?
|
196
|
-
logger
|
200
|
+
if logger && log_warning_on_csrf_failure
|
201
|
+
logger.warn "Can't verify CSRF token authenticity"
|
202
|
+
end
|
197
203
|
handle_unverified_request
|
198
204
|
end
|
199
205
|
end
|
@@ -234,6 +240,8 @@ module ActionController #:nodoc:
|
|
234
240
|
content_type =~ %r(\Atext/javascript) && !request.xhr?
|
235
241
|
end
|
236
242
|
|
243
|
+
AUTHENTICITY_TOKEN_LENGTH = 32
|
244
|
+
|
237
245
|
# Returns true or false if a request is verified. Checks:
|
238
246
|
#
|
239
247
|
# * is it a GET or HEAD request? Gets should be safe and idempotent
|
@@ -241,13 +249,73 @@ module ActionController #:nodoc:
|
|
241
249
|
# * Does the X-CSRF-Token header match the form_authenticity_token
|
242
250
|
def verified_request?
|
243
251
|
!protect_against_forgery? || request.get? || request.head? ||
|
244
|
-
|
245
|
-
|
252
|
+
valid_authenticity_token?(session, form_authenticity_param) ||
|
253
|
+
valid_authenticity_token?(session, request.headers['X-CSRF-Token'])
|
246
254
|
end
|
247
255
|
|
248
256
|
# Sets the token value for the current session.
|
249
257
|
def form_authenticity_token
|
250
|
-
session
|
258
|
+
masked_authenticity_token(session)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Creates a masked version of the authenticity token that varies
|
262
|
+
# on each request. The masking is used to mitigate SSL attacks
|
263
|
+
# like BREACH.
|
264
|
+
def masked_authenticity_token(session)
|
265
|
+
one_time_pad = SecureRandom.random_bytes(AUTHENTICITY_TOKEN_LENGTH)
|
266
|
+
encrypted_csrf_token = xor_byte_strings(one_time_pad, real_csrf_token(session))
|
267
|
+
masked_token = one_time_pad + encrypted_csrf_token
|
268
|
+
Base64.strict_encode64(masked_token)
|
269
|
+
end
|
270
|
+
|
271
|
+
# Checks the client's masked token to see if it matches the
|
272
|
+
# session token. Essentially the inverse of
|
273
|
+
# +masked_authenticity_token+.
|
274
|
+
def valid_authenticity_token?(session, encoded_masked_token)
|
275
|
+
return false if encoded_masked_token.nil? || encoded_masked_token.empty?
|
276
|
+
|
277
|
+
begin
|
278
|
+
masked_token = Base64.strict_decode64(encoded_masked_token)
|
279
|
+
rescue ArgumentError # encoded_masked_token is invalid Base64
|
280
|
+
return false
|
281
|
+
end
|
282
|
+
|
283
|
+
# See if it's actually a masked token or not. In order to
|
284
|
+
# deploy this code, we should be able to handle any unmasked
|
285
|
+
# tokens that we've issued without error.
|
286
|
+
|
287
|
+
if masked_token.length == AUTHENTICITY_TOKEN_LENGTH
|
288
|
+
# This is actually an unmasked token. This is expected if
|
289
|
+
# you have just upgraded to masked tokens, but should stop
|
290
|
+
# happening shortly after installing this gem
|
291
|
+
compare_with_real_token masked_token, session
|
292
|
+
|
293
|
+
elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2
|
294
|
+
# Split the token into the one-time pad and the encrypted
|
295
|
+
# value and decrypt it
|
296
|
+
one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
|
297
|
+
encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
|
298
|
+
csrf_token = xor_byte_strings(one_time_pad, encrypted_csrf_token)
|
299
|
+
|
300
|
+
compare_with_real_token csrf_token, session
|
301
|
+
|
302
|
+
else
|
303
|
+
false # Token is malformed
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def compare_with_real_token(token, session)
|
308
|
+
# Borrow a constant-time comparison from Rack
|
309
|
+
Rack::Utils.secure_compare(token, real_csrf_token(session))
|
310
|
+
end
|
311
|
+
|
312
|
+
def real_csrf_token(session)
|
313
|
+
session[:_csrf_token] ||= SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
|
314
|
+
Base64.strict_decode64(session[:_csrf_token])
|
315
|
+
end
|
316
|
+
|
317
|
+
def xor_byte_strings(s1, s2)
|
318
|
+
s1.bytes.zip(s2.bytes).map { |(c1,c2)| c1 ^ c2 }.pack('c*')
|
251
319
|
end
|
252
320
|
|
253
321
|
# The form's authenticity parameter. Override to provide your own.
|
@@ -183,7 +183,7 @@ module ActionController #:nodoc:
|
|
183
183
|
# You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
|
184
184
|
# Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen
|
185
185
|
#
|
186
|
-
# If you are using Unicorn with
|
186
|
+
# If you are using Unicorn with NGINX, you may need to tweak NGINX.
|
187
187
|
# Streaming should work out of the box on Rainbows.
|
188
188
|
#
|
189
189
|
# ==== Passenger
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext/hash/indifferent_access'
|
2
2
|
require 'active_support/core_ext/array/wrap'
|
3
|
+
require 'active_support/deprecation'
|
3
4
|
require 'active_support/rescuable'
|
4
5
|
require 'action_dispatch/http/upload'
|
5
6
|
require 'stringio'
|
@@ -32,14 +33,14 @@ module ActionController
|
|
32
33
|
|
33
34
|
def initialize(params) # :nodoc:
|
34
35
|
@params = params
|
35
|
-
super("found unpermitted
|
36
|
+
super("found unpermitted parameter#{'s' if params.size > 1 }: #{params.join(", ")}")
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
40
|
# == Action Controller \Parameters
|
40
41
|
#
|
41
42
|
# Allows to choose which attributes should be whitelisted for mass updating
|
42
|
-
# and thus prevent accidentally exposing that which shouldn
|
43
|
+
# and thus prevent accidentally exposing that which shouldn't be exposed.
|
43
44
|
# Provides two methods for this purpose: #require and #permit. The former is
|
44
45
|
# used to mark parameters as required. The latter is used to set the parameter
|
45
46
|
# as permitted and limit which attributes should be allowed for mass updating.
|
@@ -101,9 +102,23 @@ module ActionController
|
|
101
102
|
cattr_accessor :permit_all_parameters, instance_accessor: false
|
102
103
|
cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
|
103
104
|
|
104
|
-
#
|
105
|
-
# are present.
|
106
|
-
|
105
|
+
# By default, never raise an UnpermittedParameters exception if these
|
106
|
+
# params are present. The default includes both 'controller' and 'action'
|
107
|
+
# because they are added by Rails and should be of no concern. One way
|
108
|
+
# to change these is to specify `always_permitted_parameters` in your
|
109
|
+
# config. For instance:
|
110
|
+
#
|
111
|
+
# config.always_permitted_parameters = %w( controller action format )
|
112
|
+
cattr_accessor :always_permitted_parameters
|
113
|
+
self.always_permitted_parameters = %w( controller action )
|
114
|
+
|
115
|
+
def self.const_missing(const_name)
|
116
|
+
super unless const_name == :NEVER_UNPERMITTED_PARAMS
|
117
|
+
ActiveSupport::Deprecation.warn "`ActionController::Parameters::NEVER_UNPERMITTED_PARAMS`"\
|
118
|
+
" has been deprecated. Use "\
|
119
|
+
"`ActionController::Parameters.always_permitted_parameters` instead."
|
120
|
+
self.always_permitted_parameters
|
121
|
+
end
|
107
122
|
|
108
123
|
# Returns a new instance of <tt>ActionController::Parameters</tt>.
|
109
124
|
# Also, sets the +permitted+ attribute to the default value of
|
@@ -126,9 +141,44 @@ module ActionController
|
|
126
141
|
@permitted = self.class.permit_all_parameters
|
127
142
|
end
|
128
143
|
|
144
|
+
# Returns a safe +Hash+ representation of this parameter with all
|
145
|
+
# unpermitted keys removed.
|
146
|
+
#
|
147
|
+
# params = ActionController::Parameters.new({
|
148
|
+
# name: 'Senjougahara Hitagi',
|
149
|
+
# oddity: 'Heavy stone crab'
|
150
|
+
# })
|
151
|
+
# params.to_h # => {}
|
152
|
+
#
|
153
|
+
# safe_params = params.permit(:name)
|
154
|
+
# safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
|
155
|
+
def to_h
|
156
|
+
if permitted?
|
157
|
+
to_hash
|
158
|
+
else
|
159
|
+
slice(*self.class.always_permitted_parameters).permit!.to_h
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Convert all hashes in values into parameters, then yield each pair like
|
164
|
+
# the same way as <tt>Hash#each_pair</tt>
|
165
|
+
def each_pair(&block)
|
166
|
+
super do |key, value|
|
167
|
+
convert_hashes_to_parameters(key, value)
|
168
|
+
end
|
169
|
+
|
170
|
+
super
|
171
|
+
end
|
172
|
+
|
173
|
+
alias_method :each, :each_pair
|
174
|
+
|
129
175
|
# Attribute that keeps track of converted arrays, if any, to avoid double
|
130
176
|
# looping in the common use case permit + mass-assignment. Defined in a
|
131
177
|
# method to instantiate it only if needed.
|
178
|
+
#
|
179
|
+
# Testing membership still loops, but it's going to be faster than our own
|
180
|
+
# loop that converts values. Also, we are not going to build a new array
|
181
|
+
# object per fetch.
|
132
182
|
def converted_arrays
|
133
183
|
@converted_arrays ||= Set.new
|
134
184
|
end
|
@@ -157,9 +207,8 @@ module ActionController
|
|
157
207
|
# Person.new(params) # => #<Person id: nil, name: "Francesco">
|
158
208
|
def permit!
|
159
209
|
each_pair do |key, value|
|
160
|
-
value
|
161
|
-
|
162
|
-
_.permit! if _.respond_to? :permit!
|
210
|
+
Array.wrap(value).each do |v|
|
211
|
+
v.permit! if v.respond_to? :permit!
|
163
212
|
end
|
164
213
|
end
|
165
214
|
|
@@ -312,11 +361,56 @@ module ActionController
|
|
312
361
|
# params.slice(:a, :b) # => {"a"=>1, "b"=>2}
|
313
362
|
# params.slice(:d) # => {}
|
314
363
|
def slice(*keys)
|
315
|
-
|
316
|
-
|
364
|
+
new_instance_with_inherited_permitted_status(super)
|
365
|
+
end
|
366
|
+
|
367
|
+
# Removes and returns the key/value pairs matching the given keys.
|
368
|
+
#
|
369
|
+
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
370
|
+
# params.extract!(:a, :b) # => {"a"=>1, "b"=>2}
|
371
|
+
# params # => {"c"=>3}
|
372
|
+
def extract!(*keys)
|
373
|
+
new_instance_with_inherited_permitted_status(super)
|
374
|
+
end
|
375
|
+
|
376
|
+
# Returns a new <tt>ActionController::Parameters</tt> with the results of
|
377
|
+
# running +block+ once for every value. The keys are unchanged.
|
378
|
+
#
|
379
|
+
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
|
380
|
+
# params.transform_values { |x| x * 2 }
|
381
|
+
# # => {"a"=>2, "b"=>4, "c"=>6}
|
382
|
+
def transform_values
|
383
|
+
if block_given?
|
384
|
+
new_instance_with_inherited_permitted_status(super)
|
385
|
+
else
|
386
|
+
super
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
# This method is here only to make sure that the returned object has the
|
391
|
+
# correct +permitted+ status. It should not matter since the parent of
|
392
|
+
# this object is +HashWithIndifferentAccess+
|
393
|
+
def transform_keys # :nodoc:
|
394
|
+
if block_given?
|
395
|
+
new_instance_with_inherited_permitted_status(super)
|
396
|
+
else
|
397
|
+
super
|
317
398
|
end
|
318
399
|
end
|
319
400
|
|
401
|
+
# Deletes and returns a key-value pair from +Parameters+ whose key is equal
|
402
|
+
# to key. If the key is not found, returns the default value. If the
|
403
|
+
# optional code block is given and the key is not found, pass in the key
|
404
|
+
# and return the result of block.
|
405
|
+
def delete(key, &block)
|
406
|
+
convert_hashes_to_parameters(key, super, false)
|
407
|
+
end
|
408
|
+
|
409
|
+
# Equivalent to Hash#keep_if, but returns nil if no changes were made.
|
410
|
+
def select!(&block)
|
411
|
+
convert_value_to_parameters(super)
|
412
|
+
end
|
413
|
+
|
320
414
|
# Returns an exact copy of the <tt>ActionController::Parameters</tt>
|
321
415
|
# instance. +permitted+ state is kept on the duped object.
|
322
416
|
#
|
@@ -337,6 +431,12 @@ module ActionController
|
|
337
431
|
end
|
338
432
|
|
339
433
|
private
|
434
|
+
def new_instance_with_inherited_permitted_status(hash)
|
435
|
+
self.class.new(hash).tap do |new_instance|
|
436
|
+
new_instance.permitted = @permitted
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
340
440
|
def convert_hashes_to_parameters(key, value, assign_if_converted=true)
|
341
441
|
converted = convert_value_to_parameters(value)
|
342
442
|
self[key] = converted if assign_if_converted && !converted.equal?(value)
|
@@ -385,7 +485,7 @@ module ActionController
|
|
385
485
|
end
|
386
486
|
|
387
487
|
def unpermitted_keys(params)
|
388
|
-
self.keys - params.keys -
|
488
|
+
self.keys - params.keys - self.always_permitted_parameters
|
389
489
|
end
|
390
490
|
|
391
491
|
#
|