actionpack 4.2.11.1 → 5.2.4.3
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 +287 -488
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -7
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +45 -49
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +47 -31
- data/lib/abstract_controller/collector.rb +8 -11
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +25 -25
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
- data/lib/abstract_controller/rendering.rb +42 -41
- data/lib/abstract_controller/translation.rb +10 -7
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +12 -5
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +149 -0
- data/lib/action_controller/base.rb +27 -19
- data/lib/action_controller/caching.rb +14 -57
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +10 -15
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +118 -44
- data/lib/action_controller/metal/content_security_policy.rb +52 -0
- data/lib/action_controller/metal/cookies.rb +3 -3
- data/lib/action_controller/metal/data_streaming.rb +27 -46
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
- data/lib/action_controller/metal/exceptions.rb +8 -14
- data/lib/action_controller/metal/flash.rb +4 -3
- data/lib/action_controller/metal/force_ssl.rb +23 -21
- data/lib/action_controller/metal/head.rb +21 -19
- data/lib/action_controller/metal/helpers.rb +24 -14
- data/lib/action_controller/metal/http_authentication.rb +64 -57
- data/lib/action_controller/metal/implicit_render.rb +62 -8
- data/lib/action_controller/metal/instrumentation.rb +19 -21
- data/lib/action_controller/metal/live.rb +90 -106
- data/lib/action_controller/metal/mime_responds.rb +33 -46
- data/lib/action_controller/metal/parameter_encoding.rb +51 -0
- data/lib/action_controller/metal/params_wrapper.rb +61 -53
- data/lib/action_controller/metal/redirecting.rb +49 -28
- data/lib/action_controller/metal/renderers.rb +87 -44
- data/lib/action_controller/metal/rendering.rb +72 -50
- data/lib/action_controller/metal/request_forgery_protection.rb +229 -93
- data/lib/action_controller/metal/rescue.rb +9 -16
- data/lib/action_controller/metal/streaming.rb +12 -10
- data/lib/action_controller/metal/strong_parameters.rb +583 -164
- data/lib/action_controller/metal/testing.rb +2 -17
- data/lib/action_controller/metal/url_for.rb +19 -10
- data/lib/action_controller/metal.rb +98 -83
- data/lib/action_controller/railtie.rb +28 -10
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +117 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +280 -411
- data/lib/action_controller.rb +29 -21
- data/lib/action_dispatch/http/cache.rb +93 -47
- data/lib/action_dispatch/http/content_security_policy.rb +272 -0
- data/lib/action_dispatch/http/filter_parameters.rb +26 -20
- data/lib/action_dispatch/http/filter_redirect.rb +10 -11
- data/lib/action_dispatch/http/headers.rb +55 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +56 -41
- data/lib/action_dispatch/http/mime_type.rb +134 -121
- data/lib/action_dispatch/http/mime_types.rb +20 -6
- data/lib/action_dispatch/http/parameter_filter.rb +25 -11
- data/lib/action_dispatch/http/parameters.rb +98 -39
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +200 -118
- data/lib/action_dispatch/http/response.rb +225 -110
- data/lib/action_dispatch/http/upload.rb +12 -6
- data/lib/action_dispatch/http/url.rb +110 -28
- data/lib/action_dispatch/journey/formatter.rb +55 -32
- data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
- data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
- data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
- data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
- data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
- data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
- data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
- data/lib/action_dispatch/journey/nodes/node.rb +18 -6
- data/lib/action_dispatch/journey/parser.rb +23 -22
- data/lib/action_dispatch/journey/parser.y +3 -2
- data/lib/action_dispatch/journey/parser_extras.rb +12 -4
- data/lib/action_dispatch/journey/path/pattern.rb +50 -44
- data/lib/action_dispatch/journey/route.rb +106 -28
- data/lib/action_dispatch/journey/router/utils.rb +20 -11
- data/lib/action_dispatch/journey/router.rb +35 -23
- data/lib/action_dispatch/journey/routes.rb +18 -16
- data/lib/action_dispatch/journey/scanner.rb +18 -15
- data/lib/action_dispatch/journey/visitors.rb +99 -52
- data/lib/action_dispatch/journey.rb +7 -5
- data/lib/action_dispatch/middleware/callbacks.rb +1 -2
- data/lib/action_dispatch/middleware/cookies.rb +304 -193
- data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +78 -54
- data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
- data/lib/action_dispatch/middleware/reloader.rb +5 -91
- data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
- data/lib/action_dispatch/middleware/request_id.rb +17 -9
- data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
- data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
- data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
- data/lib/action_dispatch/middleware/ssl.rb +114 -36
- data/lib/action_dispatch/middleware/stack.rb +31 -44
- data/lib/action_dispatch/middleware/static.rb +57 -50
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
- data/lib/action_dispatch/railtie.rb +19 -11
- data/lib/action_dispatch/request/session.rb +106 -59
- data/lib/action_dispatch/request/utils.rb +67 -24
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +58 -67
- data/lib/action_dispatch/routing/mapper.rb +733 -447
- data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
- data/lib/action_dispatch/routing/redirection.rb +36 -26
- data/lib/action_dispatch/routing/route_set.rb +321 -291
- data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
- data/lib/action_dispatch/routing/url_for.rb +65 -25
- data/lib/action_dispatch/routing.rb +17 -18
- data/lib/action_dispatch/system_test_case.rb +147 -0
- data/lib/action_dispatch/system_testing/browser.rb +49 -0
- data/lib/action_dispatch/system_testing/driver.rb +59 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
- data/lib/action_dispatch/testing/assertion_response.rb +47 -0
- data/lib/action_dispatch/testing/assertions/response.rb +45 -20
- data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
- data/lib/action_dispatch/testing/assertions.rb +6 -4
- data/lib/action_dispatch/testing/integration.rb +347 -209
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +28 -22
- data/lib/action_dispatch/testing/test_request.rb +27 -34
- data/lib/action_dispatch/testing/test_response.rb +35 -7
- data/lib/action_dispatch.rb +27 -19
- data/lib/action_pack/gem_version.rb +5 -3
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +4 -2
- metadata +56 -38
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "active_support/security_utils"
|
3
5
|
|
4
6
|
module ActionController
|
5
7
|
# Makes it dead easy to do HTTP Basic, Digest and Token authentication.
|
@@ -28,14 +30,14 @@ module ActionController
|
|
28
30
|
# class ApplicationController < ActionController::Base
|
29
31
|
# before_action :set_account, :authenticate
|
30
32
|
#
|
31
|
-
#
|
33
|
+
# private
|
32
34
|
# def set_account
|
33
35
|
# @account = Account.find_by(url_name: request.subdomains.first)
|
34
36
|
# end
|
35
37
|
#
|
36
38
|
# def authenticate
|
37
39
|
# case request.format
|
38
|
-
# when Mime
|
40
|
+
# when Mime[:xml], Mime[:atom]
|
39
41
|
# if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
|
40
42
|
# @current_user = user
|
41
43
|
# else
|
@@ -70,25 +72,25 @@ module ActionController
|
|
70
72
|
before_action(options.except(:name, :password, :realm)) do
|
71
73
|
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
|
72
74
|
# This comparison uses & so that it doesn't short circuit and
|
73
|
-
# uses `
|
75
|
+
# uses `secure_compare` so that length information
|
74
76
|
# isn't leaked.
|
75
|
-
ActiveSupport::SecurityUtils.
|
76
|
-
ActiveSupport::SecurityUtils.
|
77
|
+
ActiveSupport::SecurityUtils.secure_compare(name, options[:name]) &
|
78
|
+
ActiveSupport::SecurityUtils.secure_compare(password, options[:password])
|
77
79
|
end
|
78
80
|
end
|
79
81
|
end
|
80
82
|
end
|
81
83
|
|
82
|
-
def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
|
83
|
-
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
|
84
|
+
def authenticate_or_request_with_http_basic(realm = "Application", message = nil, &login_procedure)
|
85
|
+
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm, message)
|
84
86
|
end
|
85
87
|
|
86
88
|
def authenticate_with_http_basic(&login_procedure)
|
87
89
|
HttpAuthentication::Basic.authenticate(request, &login_procedure)
|
88
90
|
end
|
89
91
|
|
90
|
-
def request_http_basic_authentication(realm = "Application")
|
91
|
-
HttpAuthentication::Basic.authentication_request(self, realm)
|
92
|
+
def request_http_basic_authentication(realm = "Application", message = nil)
|
93
|
+
HttpAuthentication::Basic.authentication_request(self, realm, message)
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
@@ -99,33 +101,34 @@ module ActionController
|
|
99
101
|
end
|
100
102
|
|
101
103
|
def has_basic_credentials?(request)
|
102
|
-
request.authorization.present? && (auth_scheme(request) ==
|
104
|
+
request.authorization.present? && (auth_scheme(request).downcase == "basic")
|
103
105
|
end
|
104
106
|
|
105
107
|
def user_name_and_password(request)
|
106
|
-
decode_credentials(request).split(
|
108
|
+
decode_credentials(request).split(":", 2)
|
107
109
|
end
|
108
110
|
|
109
111
|
def decode_credentials(request)
|
110
|
-
::Base64.decode64(auth_param(request) ||
|
112
|
+
::Base64.decode64(auth_param(request) || "")
|
111
113
|
end
|
112
114
|
|
113
115
|
def auth_scheme(request)
|
114
|
-
request.authorization.split(
|
116
|
+
request.authorization.to_s.split(" ", 2).first
|
115
117
|
end
|
116
118
|
|
117
119
|
def auth_param(request)
|
118
|
-
request.authorization.split(
|
120
|
+
request.authorization.to_s.split(" ", 2).second
|
119
121
|
end
|
120
122
|
|
121
123
|
def encode_credentials(user_name, password)
|
122
124
|
"Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
|
123
125
|
end
|
124
126
|
|
125
|
-
def authentication_request(controller, realm)
|
126
|
-
|
127
|
+
def authentication_request(controller, realm, message)
|
128
|
+
message ||= "HTTP Basic: Access denied.\n"
|
129
|
+
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.tr('"'.freeze, "".freeze)}")
|
127
130
|
controller.status = 401
|
128
|
-
controller.response_body =
|
131
|
+
controller.response_body = message
|
129
132
|
end
|
130
133
|
end
|
131
134
|
|
@@ -175,8 +178,8 @@ module ActionController
|
|
175
178
|
extend self
|
176
179
|
|
177
180
|
module ControllerMethods
|
178
|
-
def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure)
|
179
|
-
authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm)
|
181
|
+
def authenticate_or_request_with_http_digest(realm = "Application", message = nil, &password_procedure)
|
182
|
+
authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm, message)
|
180
183
|
end
|
181
184
|
|
182
185
|
# Authenticate with HTTP Digest, returns true or false
|
@@ -207,7 +210,7 @@ module ActionController
|
|
207
210
|
password = password_procedure.call(credentials[:username])
|
208
211
|
return false unless password
|
209
212
|
|
210
|
-
method = request.
|
213
|
+
method = request.get_header("rack.methodoverride.original_method") || request.get_header("REQUEST_METHOD")
|
211
214
|
uri = credentials[:uri]
|
212
215
|
|
213
216
|
[true, false].any? do |trailing_question_mark|
|
@@ -223,19 +226,19 @@ module ActionController
|
|
223
226
|
# Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
|
224
227
|
# Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
|
225
228
|
# of a plain-text password.
|
226
|
-
def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
|
229
|
+
def expected_response(http_method, uri, credentials, password, password_is_ha1 = true)
|
227
230
|
ha1 = password_is_ha1 ? password : ha1(credentials, password)
|
228
|
-
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(
|
229
|
-
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(
|
231
|
+
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(":"))
|
232
|
+
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(":"))
|
230
233
|
end
|
231
234
|
|
232
235
|
def ha1(credentials, password)
|
233
|
-
::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(
|
236
|
+
::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(":"))
|
234
237
|
end
|
235
238
|
|
236
239
|
def encode_credentials(http_method, credentials, password, password_is_ha1)
|
237
240
|
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
|
238
|
-
"Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(
|
241
|
+
"Digest " + credentials.sort_by { |x| x[0].to_s }.map { |v| "#{v[0]}='#{v[1]}'" }.join(", ")
|
239
242
|
end
|
240
243
|
|
241
244
|
def decode_credentials_header(request)
|
@@ -243,9 +246,9 @@ module ActionController
|
|
243
246
|
end
|
244
247
|
|
245
248
|
def decode_credentials(header)
|
246
|
-
ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/,
|
247
|
-
key, value = pair.split(
|
248
|
-
[key.strip, value.to_s.gsub(/^"|"$/,
|
249
|
+
ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, "").split(",").map do |pair|
|
250
|
+
key, value = pair.split("=", 2)
|
251
|
+
[key.strip, value.to_s.gsub(/^"|"$/, "").delete("'")]
|
249
252
|
end]
|
250
253
|
end
|
251
254
|
|
@@ -264,8 +267,8 @@ module ActionController
|
|
264
267
|
end
|
265
268
|
|
266
269
|
def secret_token(request)
|
267
|
-
key_generator = request.
|
268
|
-
http_auth_salt = request.
|
270
|
+
key_generator = request.key_generator
|
271
|
+
http_auth_salt = request.http_auth_salt
|
269
272
|
key_generator.generate_key(http_auth_salt)
|
270
273
|
end
|
271
274
|
|
@@ -309,21 +312,20 @@ module ActionController
|
|
309
312
|
end
|
310
313
|
|
311
314
|
# Might want a shorter timeout depending on whether the request
|
312
|
-
# is a PATCH, PUT, or POST, and if client is browser or web service.
|
315
|
+
# is a PATCH, PUT, or POST, and if the client is a browser or web service.
|
313
316
|
# Can be much shorter if the Stale directive is implemented. This would
|
314
|
-
# allow a user to use new nonce without prompting user again for their
|
317
|
+
# allow a user to use new nonce without prompting the user again for their
|
315
318
|
# username and password.
|
316
|
-
def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
|
319
|
+
def validate_nonce(secret_key, request, value, seconds_to_timeout = 5 * 60)
|
317
320
|
return false if value.nil?
|
318
321
|
t = ::Base64.decode64(value).split(":").first.to_i
|
319
322
|
nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
|
320
323
|
end
|
321
324
|
|
322
|
-
# Opaque based on
|
325
|
+
# Opaque based on digest of secret key
|
323
326
|
def opaque(secret_key)
|
324
327
|
::Digest::MD5.hexdigest(secret_key)
|
325
328
|
end
|
326
|
-
|
327
329
|
end
|
328
330
|
|
329
331
|
# Makes it dead easy to do HTTP Token authentication.
|
@@ -346,7 +348,9 @@ module ActionController
|
|
346
348
|
# private
|
347
349
|
# def authenticate
|
348
350
|
# authenticate_or_request_with_http_token do |token, options|
|
349
|
-
#
|
351
|
+
# # Compare the tokens in a time-constant manner, to mitigate
|
352
|
+
# # timing attacks.
|
353
|
+
# ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
|
350
354
|
# end
|
351
355
|
# end
|
352
356
|
# end
|
@@ -358,14 +362,14 @@ module ActionController
|
|
358
362
|
# class ApplicationController < ActionController::Base
|
359
363
|
# before_action :set_account, :authenticate
|
360
364
|
#
|
361
|
-
#
|
365
|
+
# private
|
362
366
|
# def set_account
|
363
367
|
# @account = Account.find_by(url_name: request.subdomains.first)
|
364
368
|
# end
|
365
369
|
#
|
366
370
|
# def authenticate
|
367
371
|
# case request.format
|
368
|
-
# when Mime
|
372
|
+
# when Mime[:xml], Mime[:atom]
|
369
373
|
# if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
|
370
374
|
# @current_user = user
|
371
375
|
# else
|
@@ -400,22 +404,22 @@ module ActionController
|
|
400
404
|
#
|
401
405
|
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
|
402
406
|
module Token
|
403
|
-
TOKEN_KEY =
|
404
|
-
TOKEN_REGEX = /^Token
|
407
|
+
TOKEN_KEY = "token="
|
408
|
+
TOKEN_REGEX = /^(Token|Bearer)\s+/
|
405
409
|
AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
|
406
410
|
extend self
|
407
411
|
|
408
412
|
module ControllerMethods
|
409
|
-
def authenticate_or_request_with_http_token(realm = "Application", &login_procedure)
|
410
|
-
authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm)
|
413
|
+
def authenticate_or_request_with_http_token(realm = "Application", message = nil, &login_procedure)
|
414
|
+
authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm, message)
|
411
415
|
end
|
412
416
|
|
413
417
|
def authenticate_with_http_token(&login_procedure)
|
414
418
|
Token.authenticate(self, &login_procedure)
|
415
419
|
end
|
416
420
|
|
417
|
-
def request_http_token_authentication(realm = "Application")
|
418
|
-
Token.authentication_request(self, realm)
|
421
|
+
def request_http_token_authentication(realm = "Application", message = nil)
|
422
|
+
Token.authentication_request(self, realm, message)
|
419
423
|
end
|
420
424
|
end
|
421
425
|
|
@@ -440,15 +444,17 @@ module ActionController
|
|
440
444
|
end
|
441
445
|
end
|
442
446
|
|
443
|
-
# Parses the token and options out of the token
|
444
|
-
# the header
|
447
|
+
# Parses the token and options out of the token Authorization header.
|
448
|
+
# The value for the Authorization header is expected to have the prefix
|
449
|
+
# <tt>"Token"</tt> or <tt>"Bearer"</tt>. If the header looks like this:
|
445
450
|
# Authorization: Token token="abc", nonce="def"
|
446
|
-
# Then the returned token is "abc"
|
451
|
+
# Then the returned token is <tt>"abc"</tt>, and the options are
|
452
|
+
# <tt>{nonce: "def"}</tt>
|
447
453
|
#
|
448
454
|
# request - ActionDispatch::Request instance with the current headers.
|
449
455
|
#
|
450
|
-
# Returns an Array of [String, Hash] if a token is present.
|
451
|
-
# Returns nil if no token is found.
|
456
|
+
# Returns an +Array+ of <tt>[String, Hash]</tt> if a token is present.
|
457
|
+
# Returns +nil+ if no token is found.
|
452
458
|
def token_and_options(request)
|
453
459
|
authorization_request = request.authorization.to_s
|
454
460
|
if authorization_request[TOKEN_REGEX]
|
@@ -468,14 +474,14 @@ module ActionController
|
|
468
474
|
|
469
475
|
# This removes the <tt>"</tt> characters wrapping the value.
|
470
476
|
def rewrite_param_values(array_params)
|
471
|
-
array_params.each { |param| (param[1] || "").gsub! %r/^"|"$/,
|
477
|
+
array_params.each { |param| (param[1] || "".dup).gsub! %r/^"|"$/, "" }
|
472
478
|
end
|
473
479
|
|
474
480
|
# This method takes an authorization body and splits up the key-value
|
475
481
|
# pairs by the standardized <tt>:</tt>, <tt>;</tt>, or <tt>\t</tt>
|
476
482
|
# delimiters defined in +AUTHN_PAIR_DELIMITERS+.
|
477
483
|
def raw_params(auth)
|
478
|
-
_raw_params = auth.sub(TOKEN_REGEX,
|
484
|
+
_raw_params = auth.sub(TOKEN_REGEX, "").split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
|
479
485
|
|
480
486
|
if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}})
|
481
487
|
_raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
|
@@ -497,15 +503,16 @@ module ActionController
|
|
497
503
|
"Token #{values * ", "}"
|
498
504
|
end
|
499
505
|
|
500
|
-
# Sets a WWW-Authenticate to let the client know a token is desired.
|
506
|
+
# Sets a WWW-Authenticate header to let the client know a token is desired.
|
501
507
|
#
|
502
508
|
# controller - ActionController::Base instance for the outgoing response.
|
503
509
|
# realm - String realm to use in the header.
|
504
510
|
#
|
505
511
|
# Returns nothing.
|
506
|
-
def authentication_request(controller, realm)
|
507
|
-
|
508
|
-
controller.
|
512
|
+
def authentication_request(controller, realm, message = nil)
|
513
|
+
message ||= "HTTP Token: Access denied.\n"
|
514
|
+
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.tr('"'.freeze, "".freeze)}")
|
515
|
+
controller.__send__ :render, plain: message, status: :unauthorized
|
509
516
|
end
|
510
517
|
end
|
511
518
|
end
|
@@ -1,19 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionController
|
4
|
+
# Handles implicit rendering for a controller action that does not
|
5
|
+
# explicitly respond with +render+, +respond_to+, +redirect+, or +head+.
|
6
|
+
#
|
7
|
+
# For API controllers, the implicit response is always <tt>204 No Content</tt>.
|
8
|
+
#
|
9
|
+
# For all other controllers, we use these heuristics to decide whether to
|
10
|
+
# render a template, raise an error for a missing template, or respond with
|
11
|
+
# <tt>204 No Content</tt>:
|
12
|
+
#
|
13
|
+
# First, if we DO find a template, it's rendered. Template lookup accounts
|
14
|
+
# for the action name, locales, format, variant, template handlers, and more
|
15
|
+
# (see +render+ for details).
|
16
|
+
#
|
17
|
+
# Second, if we DON'T find a template but the controller action does have
|
18
|
+
# templates for other formats, variants, etc., then we trust that you meant
|
19
|
+
# to provide a template for this response, too, and we raise
|
20
|
+
# <tt>ActionController::UnknownFormat</tt> with an explanation.
|
21
|
+
#
|
22
|
+
# Third, if we DON'T find a template AND the request is a page load in a web
|
23
|
+
# browser (technically, a non-XHR GET request for an HTML response) where
|
24
|
+
# you reasonably expect to have rendered a template, then we raise
|
25
|
+
# <tt>ActionView::UnknownFormat</tt> with an explanation.
|
26
|
+
#
|
27
|
+
# Finally, if we DON'T find a template AND the request isn't a browser page
|
28
|
+
# load, then we implicitly respond with <tt>204 No Content</tt>.
|
2
29
|
module ImplicitRender
|
3
|
-
|
4
|
-
|
5
|
-
default_render unless performed?
|
6
|
-
ret
|
7
|
-
end
|
30
|
+
# :stopdoc:
|
31
|
+
include BasicImplicitRender
|
8
32
|
|
9
33
|
def default_render(*args)
|
10
|
-
|
34
|
+
if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
|
35
|
+
render(*args)
|
36
|
+
elsif any_templates?(action_name.to_s, _prefixes)
|
37
|
+
message = "#{self.class.name}\##{action_name} is missing a template " \
|
38
|
+
"for this request format and variant.\n" \
|
39
|
+
"\nrequest.formats: #{request.formats.map(&:to_s).inspect}" \
|
40
|
+
"\nrequest.variant: #{request.variant.inspect}"
|
41
|
+
|
42
|
+
raise ActionController::UnknownFormat, message
|
43
|
+
elsif interactive_browser_request?
|
44
|
+
message = "#{self.class.name}\##{action_name} is missing a template " \
|
45
|
+
"for this request format and variant.\n\n" \
|
46
|
+
"request.formats: #{request.formats.map(&:to_s).inspect}\n" \
|
47
|
+
"request.variant: #{request.variant.inspect}\n\n" \
|
48
|
+
"NOTE! For XHR/Ajax or API requests, this action would normally " \
|
49
|
+
"respond with 204 No Content: an empty white screen. Since you're " \
|
50
|
+
"loading it in a web browser, we assume that you expected to " \
|
51
|
+
"actually render a template, not nothing, so we're showing an " \
|
52
|
+
"error to be extra-clear. If you expect 204 No Content, carry on. " \
|
53
|
+
"That's what you'll get from an XHR or API request. Give it a shot."
|
54
|
+
|
55
|
+
raise ActionController::UnknownFormat, message
|
56
|
+
else
|
57
|
+
logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
|
58
|
+
super
|
59
|
+
end
|
11
60
|
end
|
12
61
|
|
13
62
|
def method_for_action(action_name)
|
14
63
|
super || if template_exists?(action_name.to_s, _prefixes)
|
15
|
-
|
16
|
-
|
64
|
+
"default_render"
|
65
|
+
end
|
17
66
|
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def interactive_browser_request?
|
70
|
+
request.get? && request.format == Mime[:html] && !request.xhr?
|
71
|
+
end
|
18
72
|
end
|
19
73
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "benchmark"
|
4
|
+
require "abstract_controller/logger"
|
3
5
|
|
4
6
|
module ActionController
|
5
7
|
# Adds instrumentation to several ends in ActionController::Base. It also provides
|
6
|
-
# some hooks related with process_action
|
8
|
+
# some hooks related with process_action. This allows an ORM like Active Record
|
7
9
|
# and/or DataMapper to plug in ActionController and show related information.
|
8
10
|
#
|
9
11
|
# Check ActiveRecord::Railties::ControllerRuntime for an example.
|
@@ -11,18 +13,18 @@ module ActionController
|
|
11
13
|
extend ActiveSupport::Concern
|
12
14
|
|
13
15
|
include AbstractController::Logger
|
14
|
-
include ActionController::RackDelegation
|
15
16
|
|
16
17
|
attr_internal :view_runtime
|
17
18
|
|
18
19
|
def process_action(*args)
|
19
20
|
raw_payload = {
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
21
|
+
controller: self.class.name,
|
22
|
+
action: action_name,
|
23
|
+
params: request.filtered_parameters,
|
24
|
+
headers: request.headers,
|
25
|
+
format: request.format.ref,
|
26
|
+
method: request.request_method,
|
27
|
+
path: request.fullpath
|
26
28
|
}
|
27
29
|
|
28
30
|
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
|
@@ -46,9 +48,9 @@ module ActionController
|
|
46
48
|
render_output
|
47
49
|
end
|
48
50
|
|
49
|
-
def send_file(path, options={})
|
51
|
+
def send_file(path, options = {})
|
50
52
|
ActiveSupport::Notifications.instrument("send_file.action_controller",
|
51
|
-
options.merge(:
|
53
|
+
options.merge(path: path)) do
|
52
54
|
super
|
53
55
|
end
|
54
56
|
end
|
@@ -72,25 +74,22 @@ module ActionController
|
|
72
74
|
|
73
75
|
# A hook invoked every time a before callback is halted.
|
74
76
|
def halted_callback_hook(filter)
|
75
|
-
ActiveSupport::Notifications.instrument("halted_callback.action_controller", :
|
77
|
+
ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
|
76
78
|
end
|
77
79
|
|
78
|
-
# A hook which allows you to clean up any time taken into account in
|
79
|
-
# views
|
80
|
+
# A hook which allows you to clean up any time, wrongly taken into account in
|
81
|
+
# views, like database querying time.
|
80
82
|
#
|
81
83
|
# def cleanup_view_runtime
|
82
84
|
# super - time_taken_in_something_expensive
|
83
85
|
# end
|
84
|
-
#
|
85
|
-
# :api: plugin
|
86
|
-
def cleanup_view_runtime #:nodoc:
|
86
|
+
def cleanup_view_runtime # :doc:
|
87
87
|
yield
|
88
88
|
end
|
89
89
|
|
90
90
|
# Every time after an action is processed, this method is invoked
|
91
91
|
# with the payload, so you can add more information.
|
92
|
-
# :
|
93
|
-
def append_info_to_payload(payload) #:nodoc:
|
92
|
+
def append_info_to_payload(payload) # :doc:
|
94
93
|
payload[:view_runtime] = view_runtime
|
95
94
|
end
|
96
95
|
|
@@ -98,7 +97,6 @@ module ActionController
|
|
98
97
|
# A hook which allows other frameworks to log what happened during
|
99
98
|
# controller process action. This method should return an array
|
100
99
|
# with the messages to be added.
|
101
|
-
# :api: plugin
|
102
100
|
def log_process_action(payload) #:nodoc:
|
103
101
|
messages, view_runtime = [], payload[:view_runtime]
|
104
102
|
messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
|