actionpack 3.2.19 → 4.2.11.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 +7 -0
- data/CHANGELOG.md +412 -503
- data/MIT-LICENSE +1 -1
- data/README.rdoc +11 -294
- data/lib/abstract_controller/asset_paths.rb +2 -2
- data/lib/abstract_controller/base.rb +52 -18
- data/lib/abstract_controller/callbacks.rb +87 -89
- data/lib/abstract_controller/collector.rb +17 -3
- data/lib/abstract_controller/helpers.rb +41 -14
- data/lib/abstract_controller/logger.rb +1 -2
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
- data/lib/abstract_controller/rendering.rb +65 -118
- data/lib/abstract_controller/translation.rb +16 -1
- data/lib/abstract_controller/url_for.rb +7 -7
- data/lib/abstract_controller.rb +2 -10
- data/lib/action_controller/base.rb +61 -28
- data/lib/action_controller/caching/fragments.rb +30 -54
- data/lib/action_controller/caching.rb +38 -35
- data/lib/action_controller/log_subscriber.rb +35 -18
- data/lib/action_controller/metal/conditional_get.rb +103 -34
- data/lib/action_controller/metal/data_streaming.rb +20 -26
- data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
- data/lib/action_controller/metal/exceptions.rb +19 -6
- data/lib/action_controller/metal/flash.rb +41 -9
- data/lib/action_controller/metal/force_ssl.rb +70 -12
- data/lib/action_controller/metal/head.rb +30 -7
- data/lib/action_controller/metal/helpers.rb +11 -11
- data/lib/action_controller/metal/hide_actions.rb +0 -1
- data/lib/action_controller/metal/http_authentication.rb +140 -94
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/instrumentation.rb +11 -7
- data/lib/action_controller/metal/live.rb +328 -0
- data/lib/action_controller/metal/mime_responds.rb +161 -152
- data/lib/action_controller/metal/params_wrapper.rb +126 -81
- data/lib/action_controller/metal/rack_delegation.rb +10 -4
- data/lib/action_controller/metal/redirecting.rb +44 -41
- data/lib/action_controller/metal/renderers.rb +48 -19
- data/lib/action_controller/metal/rendering.rb +46 -11
- data/lib/action_controller/metal/request_forgery_protection.rb +250 -29
- data/lib/action_controller/metal/streaming.rb +30 -38
- data/lib/action_controller/metal/strong_parameters.rb +669 -0
- data/lib/action_controller/metal/testing.rb +12 -18
- data/lib/action_controller/metal/url_for.rb +31 -29
- data/lib/action_controller/metal.rb +31 -40
- data/lib/action_controller/model_naming.rb +12 -0
- data/lib/action_controller/railtie.rb +38 -18
- data/lib/action_controller/railties/helpers.rb +22 -0
- data/lib/action_controller/test_case.rb +359 -173
- data/lib/action_controller.rb +9 -16
- data/lib/action_dispatch/http/cache.rb +64 -11
- data/lib/action_dispatch/http/filter_parameters.rb +20 -10
- data/lib/action_dispatch/http/filter_redirect.rb +38 -0
- data/lib/action_dispatch/http/headers.rb +85 -17
- data/lib/action_dispatch/http/mime_negotiation.rb +55 -5
- data/lib/action_dispatch/http/mime_type.rb +167 -114
- data/lib/action_dispatch/http/mime_types.rb +2 -1
- data/lib/action_dispatch/http/parameter_filter.rb +44 -46
- data/lib/action_dispatch/http/parameters.rb +30 -46
- data/lib/action_dispatch/http/rack_cache.rb +2 -3
- data/lib/action_dispatch/http/request.rb +108 -45
- data/lib/action_dispatch/http/response.rb +247 -48
- data/lib/action_dispatch/http/upload.rb +60 -29
- data/lib/action_dispatch/http/url.rb +135 -45
- data/lib/action_dispatch/journey/backwards.rb +5 -0
- data/lib/action_dispatch/journey/formatter.rb +166 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +47 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +157 -0
- data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
- data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
- data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
- data/lib/action_dispatch/journey/nodes/node.rb +128 -0
- data/lib/action_dispatch/journey/parser.rb +198 -0
- data/lib/action_dispatch/journey/parser.y +49 -0
- data/lib/action_dispatch/journey/parser_extras.rb +23 -0
- data/lib/action_dispatch/journey/path/pattern.rb +193 -0
- data/lib/action_dispatch/journey/route.rb +125 -0
- data/lib/action_dispatch/journey/router/strexp.rb +27 -0
- data/lib/action_dispatch/journey/router/utils.rb +93 -0
- data/lib/action_dispatch/journey/router.rb +144 -0
- data/lib/action_dispatch/journey/routes.rb +80 -0
- data/lib/action_dispatch/journey/scanner.rb +61 -0
- data/lib/action_dispatch/journey/visitors.rb +221 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/journey.rb +5 -0
- data/lib/action_dispatch/middleware/callbacks.rb +16 -11
- data/lib/action_dispatch/middleware/cookies.rb +346 -125
- data/lib/action_dispatch/middleware/debug_exceptions.rb +52 -24
- data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -9
- data/lib/action_dispatch/middleware/flash.rb +85 -72
- data/lib/action_dispatch/middleware/params_parser.rb +16 -31
- data/lib/action_dispatch/middleware/public_exceptions.rb +39 -14
- data/lib/action_dispatch/middleware/reloader.rb +16 -7
- data/lib/action_dispatch/middleware/remote_ip.rb +132 -40
- data/lib/action_dispatch/middleware/request_id.rb +3 -7
- data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
- data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
- data/lib/action_dispatch/middleware/session/cookie_store.rb +84 -29
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +15 -44
- data/lib/action_dispatch/middleware/ssl.rb +72 -0
- data/lib/action_dispatch/middleware/stack.rb +6 -1
- data/lib/action_dispatch/middleware/static.rb +80 -23
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +34 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +27 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +133 -5
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
- data/lib/action_dispatch/railtie.rb +19 -6
- data/lib/action_dispatch/request/session.rb +193 -0
- data/lib/action_dispatch/request/utils.rb +35 -0
- data/lib/action_dispatch/routing/endpoint.rb +10 -0
- data/lib/action_dispatch/routing/inspector.rb +234 -0
- data/lib/action_dispatch/routing/mapper.rb +897 -436
- data/lib/action_dispatch/routing/polymorphic_routes.rb +213 -92
- data/lib/action_dispatch/routing/redirection.rb +97 -37
- data/lib/action_dispatch/routing/route_set.rb +432 -239
- data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
- data/lib/action_dispatch/routing/url_for.rb +63 -34
- data/lib/action_dispatch/routing.rb +57 -89
- data/lib/action_dispatch/testing/assertions/dom.rb +2 -36
- data/lib/action_dispatch/testing/assertions/response.rb +24 -38
- data/lib/action_dispatch/testing/assertions/routing.rb +55 -54
- data/lib/action_dispatch/testing/assertions/selector.rb +2 -434
- data/lib/action_dispatch/testing/assertions/tag.rb +2 -137
- data/lib/action_dispatch/testing/assertions.rb +11 -7
- data/lib/action_dispatch/testing/integration.rb +88 -72
- data/lib/action_dispatch/testing/test_process.rb +9 -6
- data/lib/action_dispatch/testing/test_request.rb +13 -9
- data/lib/action_dispatch/testing/test_response.rb +1 -5
- data/lib/action_dispatch.rb +24 -21
- data/lib/action_pack/gem_version.rb +15 -0
- data/lib/action_pack/version.rb +5 -7
- data/lib/action_pack.rb +1 -1
- metadata +181 -292
- data/lib/abstract_controller/layouts.rb +0 -423
- data/lib/abstract_controller/view_paths.rb +0 -96
- data/lib/action_controller/caching/actions.rb +0 -185
- data/lib/action_controller/caching/pages.rb +0 -187
- data/lib/action_controller/caching/sweeping.rb +0 -97
- data/lib/action_controller/deprecated/integration_test.rb +0 -2
- data/lib/action_controller/deprecated/performance_test.rb +0 -1
- data/lib/action_controller/deprecated.rb +0 -3
- data/lib/action_controller/metal/compatibility.rb +0 -65
- data/lib/action_controller/metal/responder.rb +0 -286
- data/lib/action_controller/metal/session_management.rb +0 -14
- data/lib/action_controller/railties/paths.rb +0 -25
- data/lib/action_controller/record_identifier.rb +0 -85
- data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
- data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
- data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
- data/lib/action_controller/vendor/html-scanner.rb +0 -20
- data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
- data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
- data/lib/action_dispatch/middleware/head.rb +0 -18
- data/lib/action_dispatch/middleware/rescue.rb +0 -26
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
- data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
- data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
- data/lib/action_dispatch/testing/performance_test.rb +0 -10
- data/lib/action_view/asset_paths.rb +0 -142
- data/lib/action_view/base.rb +0 -220
- data/lib/action_view/buffers.rb +0 -43
- data/lib/action_view/context.rb +0 -36
- data/lib/action_view/flows.rb +0 -79
- data/lib/action_view/helpers/active_model_helper.rb +0 -50
- data/lib/action_view/helpers/asset_paths.rb +0 -7
- data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
- data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
- data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
- data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
- data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
- data/lib/action_view/helpers/cache_helper.rb +0 -64
- data/lib/action_view/helpers/capture_helper.rb +0 -203
- data/lib/action_view/helpers/controller_helper.rb +0 -25
- data/lib/action_view/helpers/csrf_helper.rb +0 -32
- data/lib/action_view/helpers/date_helper.rb +0 -1062
- data/lib/action_view/helpers/debug_helper.rb +0 -40
- data/lib/action_view/helpers/form_helper.rb +0 -1486
- data/lib/action_view/helpers/form_options_helper.rb +0 -658
- data/lib/action_view/helpers/form_tag_helper.rb +0 -685
- data/lib/action_view/helpers/javascript_helper.rb +0 -110
- data/lib/action_view/helpers/number_helper.rb +0 -622
- data/lib/action_view/helpers/output_safety_helper.rb +0 -38
- data/lib/action_view/helpers/record_tag_helper.rb +0 -111
- data/lib/action_view/helpers/rendering_helper.rb +0 -90
- data/lib/action_view/helpers/sanitize_helper.rb +0 -259
- data/lib/action_view/helpers/tag_helper.rb +0 -160
- data/lib/action_view/helpers/text_helper.rb +0 -426
- data/lib/action_view/helpers/translation_helper.rb +0 -91
- data/lib/action_view/helpers/url_helper.rb +0 -693
- data/lib/action_view/helpers.rb +0 -60
- data/lib/action_view/locale/en.yml +0 -160
- data/lib/action_view/log_subscriber.rb +0 -28
- data/lib/action_view/lookup_context.rb +0 -254
- data/lib/action_view/path_set.rb +0 -89
- data/lib/action_view/railtie.rb +0 -55
- data/lib/action_view/renderer/abstract_renderer.rb +0 -41
- data/lib/action_view/renderer/partial_renderer.rb +0 -415
- data/lib/action_view/renderer/renderer.rb +0 -54
- data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
- data/lib/action_view/renderer/template_renderer.rb +0 -94
- data/lib/action_view/template/error.rb +0 -128
- data/lib/action_view/template/handlers/builder.rb +0 -26
- data/lib/action_view/template/handlers/erb.rb +0 -125
- data/lib/action_view/template/handlers.rb +0 -50
- data/lib/action_view/template/resolver.rb +0 -272
- data/lib/action_view/template/text.rb +0 -30
- data/lib/action_view/template.rb +0 -337
- data/lib/action_view/test_case.rb +0 -245
- data/lib/action_view/testing/resolvers.rb +0 -50
- data/lib/action_view.rb +0 -84
- data/lib/sprockets/assets.rake +0 -99
- data/lib/sprockets/bootstrap.rb +0 -37
- data/lib/sprockets/compressors.rb +0 -83
- data/lib/sprockets/helpers/isolated_helper.rb +0 -13
- data/lib/sprockets/helpers/rails_helper.rb +0 -182
- data/lib/sprockets/helpers.rb +0 -6
- data/lib/sprockets/railtie.rb +0 -62
- data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,21 +1,22 @@
|
|
1
|
-
require '
|
2
|
-
require 'active_support/
|
1
|
+
require 'base64'
|
2
|
+
require 'active_support/security_utils'
|
3
3
|
|
4
4
|
module ActionController
|
5
|
+
# Makes it dead easy to do HTTP Basic, Digest and Token authentication.
|
5
6
|
module HttpAuthentication
|
6
|
-
# Makes it dead easy to do HTTP \Basic
|
7
|
+
# Makes it dead easy to do HTTP \Basic authentication.
|
7
8
|
#
|
8
9
|
# === Simple \Basic example
|
9
10
|
#
|
10
11
|
# class PostsController < ApplicationController
|
11
|
-
# http_basic_authenticate_with :
|
12
|
+
# http_basic_authenticate_with name: "dhh", password: "secret", except: :index
|
12
13
|
#
|
13
14
|
# def index
|
14
|
-
# render :
|
15
|
+
# render plain: "Everyone can see me!"
|
15
16
|
# end
|
16
17
|
#
|
17
18
|
# def edit
|
18
|
-
# render :
|
19
|
+
# render plain: "I'm only accessible if you know the password"
|
19
20
|
# end
|
20
21
|
# end
|
21
22
|
#
|
@@ -25,11 +26,11 @@ module ActionController
|
|
25
26
|
# the regular HTML interface is protected by a session approach:
|
26
27
|
#
|
27
28
|
# class ApplicationController < ActionController::Base
|
28
|
-
#
|
29
|
+
# before_action :set_account, :authenticate
|
29
30
|
#
|
30
31
|
# protected
|
31
32
|
# def set_account
|
32
|
-
# @account = Account.
|
33
|
+
# @account = Account.find_by(url_name: request.subdomains.first)
|
33
34
|
# end
|
34
35
|
#
|
35
36
|
# def authenticate
|
@@ -53,54 +54,11 @@ module ActionController
|
|
53
54
|
# In your integration tests, you can do something like this:
|
54
55
|
#
|
55
56
|
# def test_access_granted_from_xml
|
56
|
-
#
|
57
|
-
#
|
58
|
-
# 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
|
59
|
-
# )
|
57
|
+
# @request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
|
58
|
+
# get "/notes/1.xml"
|
60
59
|
#
|
61
60
|
# assert_equal 200, status
|
62
61
|
# end
|
63
|
-
#
|
64
|
-
# === Simple \Digest example
|
65
|
-
#
|
66
|
-
# require 'digest/md5'
|
67
|
-
# class PostsController < ApplicationController
|
68
|
-
# REALM = "SuperSecret"
|
69
|
-
# USERS = {"dhh" => "secret", #plain text password
|
70
|
-
# "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
|
71
|
-
#
|
72
|
-
# before_filter :authenticate, :except => [:index]
|
73
|
-
#
|
74
|
-
# def index
|
75
|
-
# render :text => "Everyone can see me!"
|
76
|
-
# end
|
77
|
-
#
|
78
|
-
# def edit
|
79
|
-
# render :text => "I'm only accessible if you know the password"
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# private
|
83
|
-
# def authenticate
|
84
|
-
# authenticate_or_request_with_http_digest(REALM) do |username|
|
85
|
-
# USERS[username]
|
86
|
-
# end
|
87
|
-
# end
|
88
|
-
# end
|
89
|
-
#
|
90
|
-
# === Notes
|
91
|
-
#
|
92
|
-
# The +authenticate_or_request_with_http_digest+ block must return the user's password
|
93
|
-
# or the ha1 digest hash so the framework can appropriately hash to check the user's
|
94
|
-
# credentials. Returning +nil+ will cause authentication to fail.
|
95
|
-
#
|
96
|
-
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
|
97
|
-
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
|
98
|
-
# authenticate as the user at this +realm+, but would not have the user's password to try using at
|
99
|
-
# other sites.
|
100
|
-
#
|
101
|
-
# In rare instances, web servers or front proxies strip authorization headers before
|
102
|
-
# they reach your application. You can debug this situation by logging all environment
|
103
|
-
# variables, and check for HTTP_AUTHORIZATION, amongst others.
|
104
62
|
module Basic
|
105
63
|
extend self
|
106
64
|
|
@@ -109,9 +67,13 @@ module ActionController
|
|
109
67
|
|
110
68
|
module ClassMethods
|
111
69
|
def http_basic_authenticate_with(options = {})
|
112
|
-
|
70
|
+
before_action(options.except(:name, :password, :realm)) do
|
113
71
|
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
|
114
|
-
|
72
|
+
# This comparison uses & so that it doesn't short circuit and
|
73
|
+
# uses `variable_size_secure_compare` so that length information
|
74
|
+
# isn't leaked.
|
75
|
+
ActiveSupport::SecurityUtils.variable_size_secure_compare(name, options[:name]) &
|
76
|
+
ActiveSupport::SecurityUtils.variable_size_secure_compare(password, options[:password])
|
115
77
|
end
|
116
78
|
end
|
117
79
|
end
|
@@ -131,17 +93,29 @@ module ActionController
|
|
131
93
|
end
|
132
94
|
|
133
95
|
def authenticate(request, &login_procedure)
|
134
|
-
|
96
|
+
if has_basic_credentials?(request)
|
135
97
|
login_procedure.call(*user_name_and_password(request))
|
136
98
|
end
|
137
99
|
end
|
138
100
|
|
101
|
+
def has_basic_credentials?(request)
|
102
|
+
request.authorization.present? && (auth_scheme(request) == 'Basic')
|
103
|
+
end
|
104
|
+
|
139
105
|
def user_name_and_password(request)
|
140
|
-
decode_credentials(request).split(
|
106
|
+
decode_credentials(request).split(':', 2)
|
141
107
|
end
|
142
108
|
|
143
109
|
def decode_credentials(request)
|
144
|
-
::Base64.decode64(request
|
110
|
+
::Base64.decode64(auth_param(request) || '')
|
111
|
+
end
|
112
|
+
|
113
|
+
def auth_scheme(request)
|
114
|
+
request.authorization.split(' ', 2).first
|
115
|
+
end
|
116
|
+
|
117
|
+
def auth_param(request)
|
118
|
+
request.authorization.split(' ', 2).second
|
145
119
|
end
|
146
120
|
|
147
121
|
def encode_credentials(user_name, password)
|
@@ -150,11 +124,53 @@ module ActionController
|
|
150
124
|
|
151
125
|
def authentication_request(controller, realm)
|
152
126
|
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
|
153
|
-
controller.response_body = "HTTP Basic: Access denied.\n"
|
154
127
|
controller.status = 401
|
128
|
+
controller.response_body = "HTTP Basic: Access denied.\n"
|
155
129
|
end
|
156
130
|
end
|
157
131
|
|
132
|
+
# Makes it dead easy to do HTTP \Digest authentication.
|
133
|
+
#
|
134
|
+
# === Simple \Digest example
|
135
|
+
#
|
136
|
+
# require 'digest/md5'
|
137
|
+
# class PostsController < ApplicationController
|
138
|
+
# REALM = "SuperSecret"
|
139
|
+
# USERS = {"dhh" => "secret", #plain text password
|
140
|
+
# "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
|
141
|
+
#
|
142
|
+
# before_action :authenticate, except: [:index]
|
143
|
+
#
|
144
|
+
# def index
|
145
|
+
# render plain: "Everyone can see me!"
|
146
|
+
# end
|
147
|
+
#
|
148
|
+
# def edit
|
149
|
+
# render plain: "I'm only accessible if you know the password"
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# private
|
153
|
+
# def authenticate
|
154
|
+
# authenticate_or_request_with_http_digest(REALM) do |username|
|
155
|
+
# USERS[username]
|
156
|
+
# end
|
157
|
+
# end
|
158
|
+
# end
|
159
|
+
#
|
160
|
+
# === Notes
|
161
|
+
#
|
162
|
+
# The +authenticate_or_request_with_http_digest+ block must return the user's password
|
163
|
+
# or the ha1 digest hash so the framework can appropriately hash to check the user's
|
164
|
+
# credentials. Returning +nil+ will cause authentication to fail.
|
165
|
+
#
|
166
|
+
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
|
167
|
+
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
|
168
|
+
# authenticate as the user at this +realm+, but would not have the user's password to try using at
|
169
|
+
# other sites.
|
170
|
+
#
|
171
|
+
# In rare instances, web servers or front proxies strip authorization headers before
|
172
|
+
# they reach your application. You can debug this situation by logging all environment
|
173
|
+
# variables, and check for HTTP_AUTHORIZATION, amongst others.
|
158
174
|
module Digest
|
159
175
|
extend self
|
160
176
|
|
@@ -192,7 +208,7 @@ module ActionController
|
|
192
208
|
return false unless password
|
193
209
|
|
194
210
|
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
|
195
|
-
uri = credentials[:uri]
|
211
|
+
uri = credentials[:uri]
|
196
212
|
|
197
213
|
[true, false].any? do |trailing_question_mark|
|
198
214
|
[true, false].any? do |password_is_ha1|
|
@@ -227,7 +243,7 @@ module ActionController
|
|
227
243
|
end
|
228
244
|
|
229
245
|
def decode_credentials(header)
|
230
|
-
HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
|
246
|
+
ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, '').split(',').map do |pair|
|
231
247
|
key, value = pair.split('=', 2)
|
232
248
|
[key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')]
|
233
249
|
end]
|
@@ -243,14 +259,14 @@ module ActionController
|
|
243
259
|
def authentication_request(controller, realm, message = nil)
|
244
260
|
message ||= "HTTP Digest: Access denied.\n"
|
245
261
|
authentication_header(controller, realm)
|
246
|
-
controller.response_body = message
|
247
262
|
controller.status = 401
|
263
|
+
controller.response_body = message
|
248
264
|
end
|
249
265
|
|
250
266
|
def secret_token(request)
|
251
|
-
|
252
|
-
|
253
|
-
|
267
|
+
key_generator = request.env["action_dispatch.key_generator"]
|
268
|
+
http_auth_salt = request.env["action_dispatch.http_auth_salt"]
|
269
|
+
key_generator.generate_key(http_auth_salt)
|
254
270
|
end
|
255
271
|
|
256
272
|
# Uses an MD5 digest based on time to generate a value to be used only once.
|
@@ -263,7 +279,7 @@ module ActionController
|
|
263
279
|
# The quality of the implementation depends on a good choice.
|
264
280
|
# A nonce might, for example, be constructed as the base 64 encoding of
|
265
281
|
#
|
266
|
-
#
|
282
|
+
# time-stamp H(time-stamp ":" ETag ":" private-key)
|
267
283
|
#
|
268
284
|
# where time-stamp is a server-generated time or other non-repeating value,
|
269
285
|
# ETag is the value of the HTTP ETag header associated with the requested entity,
|
@@ -279,7 +295,7 @@ module ActionController
|
|
279
295
|
#
|
280
296
|
# An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
|
281
297
|
# protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
|
282
|
-
# POST or
|
298
|
+
# POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
|
283
299
|
# of this document.
|
284
300
|
#
|
285
301
|
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
|
@@ -289,15 +305,16 @@ module ActionController
|
|
289
305
|
t = time.to_i
|
290
306
|
hashed = [t, secret_key]
|
291
307
|
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
|
292
|
-
::Base64.
|
308
|
+
::Base64.strict_encode64("#{t}:#{digest}")
|
293
309
|
end
|
294
310
|
|
295
311
|
# Might want a shorter timeout depending on whether the request
|
296
|
-
# is a PUT or POST, and if client is browser or web service.
|
312
|
+
# is a PATCH, PUT, or POST, and if client is browser or web service.
|
297
313
|
# Can be much shorter if the Stale directive is implemented. This would
|
298
314
|
# allow a user to use new nonce without prompting user again for their
|
299
315
|
# username and password.
|
300
316
|
def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
|
317
|
+
return false if value.nil?
|
301
318
|
t = ::Base64.decode64(value).split(":").first.to_i
|
302
319
|
nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
|
303
320
|
end
|
@@ -316,14 +333,14 @@ module ActionController
|
|
316
333
|
# class PostsController < ApplicationController
|
317
334
|
# TOKEN = "secret"
|
318
335
|
#
|
319
|
-
#
|
336
|
+
# before_action :authenticate, except: [ :index ]
|
320
337
|
#
|
321
338
|
# def index
|
322
|
-
# render :
|
339
|
+
# render plain: "Everyone can see me!"
|
323
340
|
# end
|
324
341
|
#
|
325
342
|
# def edit
|
326
|
-
# render :
|
343
|
+
# render plain: "I'm only accessible if you know the password"
|
327
344
|
# end
|
328
345
|
#
|
329
346
|
# private
|
@@ -339,11 +356,11 @@ module ActionController
|
|
339
356
|
# the regular HTML interface is protected by a session approach:
|
340
357
|
#
|
341
358
|
# class ApplicationController < ActionController::Base
|
342
|
-
#
|
359
|
+
# before_action :set_account, :authenticate
|
343
360
|
#
|
344
361
|
# protected
|
345
362
|
# def set_account
|
346
|
-
# @account = Account.
|
363
|
+
# @account = Account.find_by(url_name: request.subdomains.first)
|
347
364
|
# end
|
348
365
|
#
|
349
366
|
# def authenticate
|
@@ -370,7 +387,7 @@ module ActionController
|
|
370
387
|
# def test_access_granted_from_xml
|
371
388
|
# get(
|
372
389
|
# "/notes/1.xml", nil,
|
373
|
-
#
|
390
|
+
# 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
|
374
391
|
# )
|
375
392
|
#
|
376
393
|
# assert_equal 200, status
|
@@ -383,6 +400,9 @@ module ActionController
|
|
383
400
|
#
|
384
401
|
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
|
385
402
|
module Token
|
403
|
+
TOKEN_KEY = 'token='
|
404
|
+
TOKEN_REGEX = /^Token /
|
405
|
+
AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
|
386
406
|
extend self
|
387
407
|
|
388
408
|
module ControllerMethods
|
@@ -399,16 +419,20 @@ module ActionController
|
|
399
419
|
end
|
400
420
|
end
|
401
421
|
|
402
|
-
# If token Authorization header is present, call the login
|
403
|
-
# the present token and options.
|
422
|
+
# If token Authorization header is present, call the login
|
423
|
+
# procedure with the present token and options.
|
404
424
|
#
|
405
|
-
# controller
|
406
|
-
#
|
407
|
-
# take 2 arguments:
|
408
|
-
# authenticate(controller) { |token, options| ... }
|
425
|
+
# [controller]
|
426
|
+
# ActionController::Base instance for the current request.
|
409
427
|
#
|
410
|
-
#
|
411
|
-
#
|
428
|
+
# [login_procedure]
|
429
|
+
# Proc to call if a token is present. The Proc should take two arguments:
|
430
|
+
#
|
431
|
+
# authenticate(controller) { |token, options| ... }
|
432
|
+
#
|
433
|
+
# Returns the return value of <tt>login_procedure</tt> if a
|
434
|
+
# token is found. Returns <tt>nil</tt> if no token is found.
|
435
|
+
|
412
436
|
def authenticate(controller, &login_procedure)
|
413
437
|
token, options = token_and_options(controller.request)
|
414
438
|
unless token.blank?
|
@@ -419,25 +443,47 @@ module ActionController
|
|
419
443
|
# Parses the token and options out of the token authorization header. If
|
420
444
|
# the header looks like this:
|
421
445
|
# Authorization: Token token="abc", nonce="def"
|
422
|
-
# Then the returned token is "abc", and the options is {:
|
446
|
+
# Then the returned token is "abc", and the options is {nonce: "def"}
|
423
447
|
#
|
424
448
|
# request - ActionDispatch::Request instance with the current headers.
|
425
449
|
#
|
426
450
|
# Returns an Array of [String, Hash] if a token is present.
|
427
451
|
# Returns nil if no token is found.
|
428
452
|
def token_and_options(request)
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
value.chomp!('"') # chomp trailing " in value
|
434
|
-
value.gsub!(/\\\"/, '"') # unescape remaining quotes
|
435
|
-
[key, value]
|
436
|
-
end]
|
437
|
-
[values.delete("token"), values.with_indifferent_access]
|
453
|
+
authorization_request = request.authorization.to_s
|
454
|
+
if authorization_request[TOKEN_REGEX]
|
455
|
+
params = token_params_from authorization_request
|
456
|
+
[params.shift[1], Hash[params].with_indifferent_access]
|
438
457
|
end
|
439
458
|
end
|
440
459
|
|
460
|
+
def token_params_from(auth)
|
461
|
+
rewrite_param_values params_array_from raw_params auth
|
462
|
+
end
|
463
|
+
|
464
|
+
# Takes raw_params and turns it into an array of parameters
|
465
|
+
def params_array_from(raw_params)
|
466
|
+
raw_params.map { |param| param.split %r/=(.+)?/ }
|
467
|
+
end
|
468
|
+
|
469
|
+
# This removes the <tt>"</tt> characters wrapping the value.
|
470
|
+
def rewrite_param_values(array_params)
|
471
|
+
array_params.each { |param| (param[1] || "").gsub! %r/^"|"$/, '' }
|
472
|
+
end
|
473
|
+
|
474
|
+
# This method takes an authorization body and splits up the key-value
|
475
|
+
# pairs by the standardized <tt>:</tt>, <tt>;</tt>, or <tt>\t</tt>
|
476
|
+
# delimiters defined in +AUTHN_PAIR_DELIMITERS+.
|
477
|
+
def raw_params(auth)
|
478
|
+
_raw_params = auth.sub(TOKEN_REGEX, '').split(/\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
|
479
|
+
|
480
|
+
if !(_raw_params.first =~ %r{\A#{TOKEN_KEY}})
|
481
|
+
_raw_params[0] = "#{TOKEN_KEY}#{_raw_params.first}"
|
482
|
+
end
|
483
|
+
|
484
|
+
_raw_params
|
485
|
+
end
|
486
|
+
|
441
487
|
# Encodes the given token and options into an Authorization header value.
|
442
488
|
#
|
443
489
|
# token - String token.
|
@@ -445,7 +491,7 @@ module ActionController
|
|
445
491
|
#
|
446
492
|
# Returns String.
|
447
493
|
def encode_credentials(token, options = {})
|
448
|
-
values = ["
|
494
|
+
values = ["#{TOKEN_KEY}#{token.to_s.inspect}"] + options.map do |key, value|
|
449
495
|
"#{key}=#{value.to_s.inspect}"
|
450
496
|
end
|
451
497
|
"Token #{values * ", "}"
|
@@ -11,6 +11,7 @@ module ActionController
|
|
11
11
|
extend ActiveSupport::Concern
|
12
12
|
|
13
13
|
include AbstractController::Logger
|
14
|
+
include ActionController::RackDelegation
|
14
15
|
|
15
16
|
attr_internal :view_runtime
|
16
17
|
|
@@ -20,17 +21,20 @@ module ActionController
|
|
20
21
|
:action => self.action_name,
|
21
22
|
:params => request.filtered_parameters,
|
22
23
|
:format => request.format.try(:ref),
|
23
|
-
:method => request.
|
24
|
+
:method => request.request_method,
|
24
25
|
:path => (request.fullpath rescue "unknown")
|
25
26
|
}
|
26
27
|
|
27
28
|
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
|
28
29
|
|
29
30
|
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
begin
|
32
|
+
result = super
|
33
|
+
payload[:status] = response.status
|
34
|
+
result
|
35
|
+
ensure
|
36
|
+
append_info_to_payload(payload)
|
37
|
+
end
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
@@ -59,14 +63,14 @@ module ActionController
|
|
59
63
|
ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
|
60
64
|
result = super
|
61
65
|
payload[:status] = response.status
|
62
|
-
payload[:location] = response.
|
66
|
+
payload[:location] = response.filtered_location
|
63
67
|
result
|
64
68
|
end
|
65
69
|
end
|
66
70
|
|
67
71
|
private
|
68
72
|
|
69
|
-
# A hook invoked
|
73
|
+
# A hook invoked every time a before callback is halted.
|
70
74
|
def halted_callback_hook(filter)
|
71
75
|
ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter)
|
72
76
|
end
|