omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +129 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller/asset_paths.rb +14 -0
  6. data/lib/abstract_controller/base.rb +299 -0
  7. data/lib/abstract_controller/caching/fragments.rb +149 -0
  8. data/lib/abstract_controller/caching.rb +68 -0
  9. data/lib/abstract_controller/callbacks.rb +265 -0
  10. data/lib/abstract_controller/collector.rb +44 -0
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +8 -0
  13. data/lib/abstract_controller/helpers.rb +243 -0
  14. data/lib/abstract_controller/logger.rb +16 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
  16. data/lib/abstract_controller/rendering.rb +126 -0
  17. data/lib/abstract_controller/translation.rb +42 -0
  18. data/lib/abstract_controller/url_for.rb +37 -0
  19. data/lib/abstract_controller.rb +36 -0
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +155 -0
  22. data/lib/action_controller/base.rb +332 -0
  23. data/lib/action_controller/caching.rb +49 -0
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +55 -0
  26. data/lib/action_controller/log_subscriber.rb +96 -0
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +341 -0
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +20 -0
  32. data/lib/action_controller/metal/data_streaming.rb +154 -0
  33. data/lib/action_controller/metal/default_headers.rb +21 -0
  34. data/lib/action_controller/metal/etag_with_flash.rb +22 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +59 -0
  36. data/lib/action_controller/metal/exceptions.rb +106 -0
  37. data/lib/action_controller/metal/flash.rb +67 -0
  38. data/lib/action_controller/metal/head.rb +67 -0
  39. data/lib/action_controller/metal/helpers.rb +129 -0
  40. data/lib/action_controller/metal/http_authentication.rb +565 -0
  41. data/lib/action_controller/metal/implicit_render.rb +67 -0
  42. data/lib/action_controller/metal/instrumentation.rb +120 -0
  43. data/lib/action_controller/metal/live.rb +398 -0
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +337 -0
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +312 -0
  48. data/lib/action_controller/metal/permissions_policy.rb +38 -0
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +251 -0
  51. data/lib/action_controller/metal/renderers.rb +181 -0
  52. data/lib/action_controller/metal/rendering.rb +260 -0
  53. data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
  54. data/lib/action_controller/metal/rescue.rb +33 -0
  55. data/lib/action_controller/metal/streaming.rb +183 -0
  56. data/lib/action_controller/metal/strong_parameters.rb +1546 -0
  57. data/lib/action_controller/metal/testing.rb +25 -0
  58. data/lib/action_controller/metal/url_for.rb +65 -0
  59. data/lib/action_controller/metal.rb +339 -0
  60. data/lib/action_controller/railtie.rb +149 -0
  61. data/lib/action_controller/railties/helpers.rb +26 -0
  62. data/lib/action_controller/renderer.rb +161 -0
  63. data/lib/action_controller/template_assertions.rb +13 -0
  64. data/lib/action_controller/test_case.rb +691 -0
  65. data/lib/action_controller.rb +80 -0
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +249 -0
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +365 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +80 -0
  72. data/lib/action_dispatch/http/filter_redirect.rb +50 -0
  73. data/lib/action_dispatch/http/headers.rb +134 -0
  74. data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
  75. data/lib/action_dispatch/http/mime_type.rb +389 -0
  76. data/lib/action_dispatch/http/mime_types.rb +54 -0
  77. data/lib/action_dispatch/http/parameters.rb +119 -0
  78. data/lib/action_dispatch/http/permissions_policy.rb +189 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +67 -0
  80. data/lib/action_dispatch/http/request.rb +498 -0
  81. data/lib/action_dispatch/http/response.rb +556 -0
  82. data/lib/action_dispatch/http/upload.rb +107 -0
  83. data/lib/action_dispatch/http/url.rb +344 -0
  84. data/lib/action_dispatch/journey/formatter.rb +226 -0
  85. data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
  88. data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +208 -0
  90. data/lib/action_dispatch/journey/parser.rb +103 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +209 -0
  92. data/lib/action_dispatch/journey/route.rb +189 -0
  93. data/lib/action_dispatch/journey/router/utils.rb +105 -0
  94. data/lib/action_dispatch/journey/router.rb +151 -0
  95. data/lib/action_dispatch/journey/routes.rb +82 -0
  96. data/lib/action_dispatch/journey/scanner.rb +70 -0
  97. data/lib/action_dispatch/journey/visitors.rb +267 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/journey.rb +7 -0
  102. data/lib/action_dispatch/log_subscriber.rb +25 -0
  103. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  104. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  105. data/lib/action_dispatch/middleware/callbacks.rb +38 -0
  106. data/lib/action_dispatch/middleware/cookies.rb +719 -0
  107. data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
  108. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  109. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  110. data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
  111. data/lib/action_dispatch/middleware/executor.rb +32 -0
  112. data/lib/action_dispatch/middleware/flash.rb +318 -0
  113. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  114. data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
  115. data/lib/action_dispatch/middleware/reloader.rb +16 -0
  116. data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
  117. data/lib/action_dispatch/middleware/request_id.rb +50 -0
  118. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  119. data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
  120. data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
  121. data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
  122. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
  123. data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
  124. data/lib/action_dispatch/middleware/ssl.rb +180 -0
  125. data/lib/action_dispatch/middleware/stack.rb +194 -0
  126. data/lib/action_dispatch/middleware/static.rb +192 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +35 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +284 -0
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  148. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  149. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  150. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  151. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  152. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  153. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
  154. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
  155. data/lib/action_dispatch/railtie.rb +77 -0
  156. data/lib/action_dispatch/request/session.rb +283 -0
  157. data/lib/action_dispatch/request/utils.rb +109 -0
  158. data/lib/action_dispatch/routing/endpoint.rb +19 -0
  159. data/lib/action_dispatch/routing/inspector.rb +323 -0
  160. data/lib/action_dispatch/routing/mapper.rb +2372 -0
  161. data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
  162. data/lib/action_dispatch/routing/redirection.rb +218 -0
  163. data/lib/action_dispatch/routing/route_set.rb +958 -0
  164. data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
  165. data/lib/action_dispatch/routing/url_for.rb +244 -0
  166. data/lib/action_dispatch/routing.rb +262 -0
  167. data/lib/action_dispatch/system_test_case.rb +206 -0
  168. data/lib/action_dispatch/system_testing/browser.rb +75 -0
  169. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  170. data/lib/action_dispatch/system_testing/server.rb +33 -0
  171. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  172. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  173. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  174. data/lib/action_dispatch/testing/assertions/response.rb +114 -0
  175. data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
  176. data/lib/action_dispatch/testing/assertions.rb +25 -0
  177. data/lib/action_dispatch/testing/integration.rb +694 -0
  178. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  179. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  180. data/lib/action_dispatch/testing/test_process.rb +57 -0
  181. data/lib/action_dispatch/testing/test_request.rb +73 -0
  182. data/lib/action_dispatch/testing/test_response.rb +58 -0
  183. data/lib/action_dispatch.rb +147 -0
  184. data/lib/action_pack/gem_version.rb +19 -0
  185. data/lib/action_pack/version.rb +12 -0
  186. data/lib/action_pack.rb +27 -0
  187. metadata +375 -0
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/middleware/session/abstract_store"
6
+
7
+ module ActionDispatch
8
+ module Session
9
+ # # Action Dispatch Session CacheStore
10
+ #
11
+ # A session store that uses an ActiveSupport::Cache::Store to store the
12
+ # sessions. This store is most useful if you don't store critical data in your
13
+ # sessions and you don't need them to live for extended periods of time.
14
+ #
15
+ # #### Options
16
+ # * `cache` - The cache to use. If it is not specified, `Rails.cache`
17
+ # will be used.
18
+ # * `expire_after` - The length of time a session will be stored before
19
+ # automatically expiring. By default, the `:expires_in` option of the cache
20
+ # is used.
21
+ #
22
+ class CacheStore < AbstractSecureStore
23
+ def initialize(app, options = {})
24
+ @cache = options[:cache] || Rails.cache
25
+ options[:expire_after] ||= @cache.options[:expires_in]
26
+ super
27
+ end
28
+
29
+ # Get a session from the cache.
30
+ def find_session(env, sid)
31
+ unless sid && (session = get_session_with_fallback(sid))
32
+ sid, session = generate_sid, {}
33
+ end
34
+ [sid, session]
35
+ end
36
+
37
+ # Set a session in the cache.
38
+ def write_session(env, sid, session, options)
39
+ key = cache_key(sid.private_id)
40
+ if session
41
+ @cache.write(key, session, expires_in: options[:expire_after])
42
+ else
43
+ @cache.delete(key)
44
+ end
45
+ sid
46
+ end
47
+
48
+ # Remove a session from the cache.
49
+ def delete_session(env, sid, options)
50
+ @cache.delete(cache_key(sid.private_id))
51
+ @cache.delete(cache_key(sid.public_id))
52
+ generate_sid
53
+ end
54
+
55
+ private
56
+ # Turn the session id into a cache key.
57
+ def cache_key(id)
58
+ "_session_id:#{id}"
59
+ end
60
+
61
+ def get_session_with_fallback(sid)
62
+ @cache.read(cache_key(sid.private_id)) || @cache.read(cache_key(sid.public_id))
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/core_ext/hash/keys"
6
+ require "action_dispatch/middleware/session/abstract_store"
7
+ require "rack/session/cookie"
8
+
9
+ module ActionDispatch
10
+ module Session
11
+ # # Action Dispatch Session CookieStore
12
+ #
13
+ # This cookie-based session store is the Rails default. It is dramatically
14
+ # faster than the alternatives.
15
+ #
16
+ # Sessions typically contain at most a user ID and flash message; both fit
17
+ # within the 4096 bytes cookie size limit. A `CookieOverflow` exception is
18
+ # raised if you attempt to store more than 4096 bytes of data.
19
+ #
20
+ # The cookie jar used for storage is automatically configured to be the best
21
+ # possible option given your application's configuration.
22
+ #
23
+ # Your cookies will be encrypted using your application's `secret_key_base`.
24
+ # This goes a step further than signed cookies in that encrypted cookies cannot
25
+ # be altered or read by users. This is the default starting in Rails 4.
26
+ #
27
+ # Configure your session store in an initializer:
28
+ #
29
+ # Rails.application.config.session_store :cookie_store, key: '_your_app_session'
30
+ #
31
+ # In the development and test environments your application's `secret_key_base`
32
+ # is generated by Rails and stored in a temporary file in
33
+ # `tmp/local_secret.txt`. In all other environments, it is stored encrypted in
34
+ # the `config/credentials.yml.enc` file.
35
+ #
36
+ # If your application was not updated to Rails 5.2 defaults, the
37
+ # `secret_key_base` will be found in the old `config/secrets.yml` file.
38
+ #
39
+ # Note that changing your `secret_key_base` will invalidate all existing
40
+ # session. Additionally, you should take care to make sure you are not relying
41
+ # on the ability to decode signed cookies generated by your app in external
42
+ # applications or JavaScript before changing it.
43
+ #
44
+ # Because CookieStore extends `Rack::Session::Abstract::Persisted`, many of the
45
+ # options described there can be used to customize the session cookie that is
46
+ # generated. For example:
47
+ #
48
+ # Rails.application.config.session_store :cookie_store, expire_after: 14.days
49
+ #
50
+ # would set the session cookie to expire automatically 14 days after creation.
51
+ # Other useful options include `:key`, `:secure`, `:httponly`, and `:same_site`.
52
+ class CookieStore < AbstractSecureStore
53
+ class SessionId < DelegateClass(Rack::Session::SessionId)
54
+ attr_reader :cookie_value
55
+
56
+ def initialize(session_id, cookie_value = {})
57
+ super(session_id)
58
+ @cookie_value = cookie_value
59
+ end
60
+ end
61
+
62
+ DEFAULT_SAME_SITE = proc { |request| request.cookies_same_site_protection } # :nodoc:
63
+
64
+ def initialize(app, options = {})
65
+ options[:cookie_only] = true
66
+ options[:same_site] = DEFAULT_SAME_SITE if !options.key?(:same_site)
67
+ super
68
+ end
69
+
70
+ def delete_session(req, session_id, options)
71
+ new_sid = generate_sid unless options[:drop]
72
+ # Reset hash and Assign the new session id
73
+ req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid.public_id } : {})
74
+ new_sid
75
+ end
76
+
77
+ def load_session(req)
78
+ stale_session_check! do
79
+ data = unpacked_cookie_data(req)
80
+ data = persistent_session_id!(data)
81
+ [Rack::Session::SessionId.new(data["session_id"]), data]
82
+ end
83
+ end
84
+
85
+ private
86
+ def extract_session_id(req)
87
+ stale_session_check! do
88
+ sid = unpacked_cookie_data(req)["session_id"]
89
+ sid && Rack::Session::SessionId.new(sid)
90
+ end
91
+ end
92
+
93
+ def unpacked_cookie_data(req)
94
+ req.fetch_header("action_dispatch.request.unsigned_session_cookie") do |k|
95
+ v = stale_session_check! do
96
+ if data = get_cookie(req)
97
+ data.stringify_keys!
98
+ end
99
+ data || {}
100
+ end
101
+ req.set_header k, v
102
+ end
103
+ end
104
+
105
+ def persistent_session_id!(data, sid = nil)
106
+ data ||= {}
107
+ data["session_id"] ||= sid || generate_sid.public_id
108
+ data
109
+ end
110
+
111
+ def write_session(req, sid, session_data, options)
112
+ session_data["session_id"] = sid.public_id
113
+ SessionId.new(sid, session_data)
114
+ end
115
+
116
+ def set_cookie(request, session_id, cookie)
117
+ cookie_jar(request)[@key] = cookie
118
+ end
119
+
120
+ def get_cookie(req)
121
+ cookie_jar(req)[@key]
122
+ end
123
+
124
+ def cookie_jar(request)
125
+ request.cookie_jar.signed_or_encrypted
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/middleware/session/abstract_store"
6
+ begin
7
+ require "rack/session/dalli"
8
+ rescue LoadError => e
9
+ warn "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
10
+ raise e
11
+ end
12
+
13
+ module ActionDispatch
14
+ module Session
15
+ # # Action Dispatch Session MemCacheStore
16
+ #
17
+ # A session store that uses MemCache to implement storage.
18
+ #
19
+ # #### Options
20
+ # * `expire_after` - The length of time a session will be stored before
21
+ # automatically expiring.
22
+ #
23
+ class MemCacheStore < Rack::Session::Dalli
24
+ include Compatibility
25
+ include StaleSessionCheck
26
+ include SessionObject
27
+
28
+ def initialize(app, options = {})
29
+ options[:expire_after] ||= options[:expires]
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/middleware/exception_wrapper"
6
+
7
+ module ActionDispatch
8
+ # # Action Dispatch ShowExceptions
9
+ #
10
+ # This middleware rescues any exception returned by the application and calls an
11
+ # exceptions app that will wrap it in a format for the end user.
12
+ #
13
+ # The exceptions app should be passed as a parameter on initialization of
14
+ # `ShowExceptions`. Every time there is an exception, `ShowExceptions` will
15
+ # store the exception in `env["action_dispatch.exception"]`, rewrite the
16
+ # `PATH_INFO` to the exception status code, and call the Rack app.
17
+ #
18
+ # In Rails applications, the exceptions app can be configured with
19
+ # `config.exceptions_app`, which defaults to ActionDispatch::PublicExceptions.
20
+ #
21
+ # If the application returns a response with the `X-Cascade` header set to
22
+ # `"pass"`, this middleware will send an empty response as a result with the
23
+ # correct status code. If any exception happens inside the exceptions app, this
24
+ # middleware catches the exceptions and returns a failsafe response.
25
+ class ShowExceptions
26
+ def initialize(app, exceptions_app)
27
+ @app = app
28
+ @exceptions_app = exceptions_app
29
+ end
30
+
31
+ def call(env)
32
+ @app.call(env)
33
+ rescue Exception => exception
34
+ request = ActionDispatch::Request.new env
35
+ backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
36
+ wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
37
+ request.set_header "action_dispatch.exception", wrapper.unwrapped_exception
38
+ request.set_header "action_dispatch.report_exception", !wrapper.rescue_response?
39
+
40
+ if wrapper.show?(request)
41
+ render_exception(request.dup, wrapper)
42
+ else
43
+ raise exception
44
+ end
45
+ end
46
+
47
+ private
48
+ def render_exception(request, wrapper)
49
+ status = wrapper.status_code
50
+ request.set_header "action_dispatch.original_path", request.path_info
51
+ request.set_header "action_dispatch.original_request_method", request.raw_request_method
52
+ fallback_to_html_format_if_invalid_mime_type(request)
53
+ request.path_info = "/#{status}"
54
+ request.request_method = "GET"
55
+ response = @exceptions_app.call(request.env)
56
+ response[1][Constants::X_CASCADE] == "pass" ? pass_response(status) : response
57
+ rescue Exception => failsafe_error
58
+ $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
59
+
60
+ [500, { Rack::CONTENT_TYPE => "text/plain; charset=utf-8" },
61
+ ["500 Internal Server Error\n" \
62
+ "If you are the administrator of this website, then please read this web " \
63
+ "application's log file and/or the web server's log file to find out what " \
64
+ "went wrong."]]
65
+ end
66
+
67
+ def fallback_to_html_format_if_invalid_mime_type(request)
68
+ # If the MIME type for the request is invalid then the @exceptions_app may not
69
+ # be able to handle it. To make it easier to handle, we switch to HTML.
70
+ begin
71
+ request.content_mime_type
72
+ rescue ActionDispatch::Http::MimeNegotiation::InvalidType
73
+ request.set_header "CONTENT_TYPE", "text/html"
74
+ end
75
+
76
+ begin
77
+ request.formats
78
+ rescue ActionDispatch::Http::MimeNegotiation::InvalidType
79
+ request.set_header "HTTP_ACCEPT", "text/html"
80
+ end
81
+ end
82
+
83
+ def pass_response(status)
84
+ [status, { Rack::CONTENT_TYPE => "text/html; charset=#{Response.default_charset}",
85
+ Rack::CONTENT_LENGTH => "0" }, []]
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionDispatch
6
+ # # Action Dispatch SSL
7
+ #
8
+ # This middleware is added to the stack when `config.force_ssl = true`, and is
9
+ # passed the options set in `config.ssl_options`. It does three jobs to enforce
10
+ # secure HTTP requests:
11
+ #
12
+ # 1. **TLS redirect**: Permanently redirects `http://` requests to `https://`
13
+ # with the same URL host, path, etc. Enabled by default. Set
14
+ # `config.ssl_options` to modify the destination URL:
15
+ #
16
+ # config.ssl_options = { redirect: { host: "secure.widgets.com", port: 8080 }`
17
+ #
18
+ # Or set `redirect: false` to disable redirection.
19
+ #
20
+ # Requests can opt-out of redirection with `exclude`:
21
+ #
22
+ # config.ssl_options = { redirect: { exclude: -> request { /healthcheck/.match?(request.path) } } }
23
+ #
24
+ # Cookies will not be flagged as secure for excluded requests.
25
+ #
26
+ # When proxying through a load balancer that terminates SSL, the forwarded
27
+ # request will appear as though it's HTTP instead of HTTPS to the application.
28
+ # This makes redirects and cookie security target HTTP instead of HTTPS.
29
+ # To make the server assume that the proxy already terminated SSL, and
30
+ # that the request really is HTTPS, set `config.assume_ssl` to `true`:
31
+ #
32
+ # config.assume_ssl = true
33
+ #
34
+ # 2. **Secure cookies**: Sets the `secure` flag on cookies to tell browsers
35
+ # they must not be sent along with `http://` requests. Enabled by default.
36
+ # Set `config.ssl_options` with `secure_cookies: false` to disable this
37
+ # feature.
38
+ #
39
+ # 3. **HTTP Strict Transport Security (HSTS)**: Tells the browser to remember
40
+ # this site as TLS-only and automatically redirect non-TLS requests. Enabled
41
+ # by default. Configure `config.ssl_options` with `hsts: false` to disable.
42
+ #
43
+ # Set `config.ssl_options` with `hsts: { ... }` to configure HSTS:
44
+ #
45
+ # * `expires`: How long, in seconds, these settings will stick. The
46
+ # minimum required to qualify for browser preload lists is 1 year.
47
+ # Defaults to 2 years (recommended).
48
+ #
49
+ # * `subdomains`: Set to `true` to tell the browser to apply these
50
+ # settings to all subdomains. This protects your cookies from
51
+ # interception by a vulnerable site on a subdomain. Defaults to `true`.
52
+ #
53
+ # * `preload`: Advertise that this site may be included in browsers'
54
+ # preloaded HSTS lists. HSTS protects your site on every visit *except
55
+ # the first visit* since it hasn't seen your HSTS header yet. To close
56
+ # this gap, browser vendors include a baked-in list of HSTS-enabled
57
+ # sites. Go to https://hstspreload.org to submit your site for
58
+ # inclusion. Defaults to `false`.
59
+ #
60
+ #
61
+ # To turn off HSTS, omitting the header is not enough. Browsers will
62
+ # remember the original HSTS directive until it expires. Instead, use the
63
+ # header to tell browsers to expire HSTS immediately. Setting `hsts: false`
64
+ # is a shortcut for `hsts: { expires: 0 }`.
65
+ #
66
+ class SSL
67
+ # :stopdoc: Default to 2 years as recommended on hstspreload.org.
68
+ HSTS_EXPIRES_IN = 63072000
69
+
70
+ PERMANENT_REDIRECT_REQUEST_METHODS = %w[GET HEAD] # :nodoc:
71
+
72
+ def self.default_hsts_options
73
+ { expires: HSTS_EXPIRES_IN, subdomains: true, preload: false }
74
+ end
75
+
76
+ def initialize(app, redirect: {}, hsts: {}, secure_cookies: true, ssl_default_redirect_status: nil)
77
+ @app = app
78
+
79
+ @redirect = redirect
80
+
81
+ @exclude = @redirect && @redirect[:exclude] || proc { !@redirect }
82
+ @secure_cookies = secure_cookies
83
+
84
+ @hsts_header = build_hsts_header(normalize_hsts_options(hsts))
85
+ @ssl_default_redirect_status = ssl_default_redirect_status
86
+ end
87
+
88
+ def call(env)
89
+ request = Request.new env
90
+
91
+ if request.ssl?
92
+ @app.call(env).tap do |status, headers, body|
93
+ set_hsts_header! headers
94
+ flag_cookies_as_secure! headers if @secure_cookies && !@exclude.call(request)
95
+ end
96
+ else
97
+ return redirect_to_https request unless @exclude.call(request)
98
+ @app.call(env)
99
+ end
100
+ end
101
+
102
+ private
103
+ def set_hsts_header!(headers)
104
+ headers[Constants::STRICT_TRANSPORT_SECURITY] ||= @hsts_header
105
+ end
106
+
107
+ def normalize_hsts_options(options)
108
+ case options
109
+ # Explicitly disabling HSTS clears the existing setting from browsers by setting
110
+ # expiry to 0.
111
+ when false
112
+ self.class.default_hsts_options.merge(expires: 0)
113
+ # Default to enabled, with default options.
114
+ when nil, true
115
+ self.class.default_hsts_options
116
+ else
117
+ self.class.default_hsts_options.merge(options)
118
+ end
119
+ end
120
+
121
+ # https://tools.ietf.org/html/rfc6797#section-6.1
122
+ def build_hsts_header(hsts)
123
+ value = +"max-age=#{hsts[:expires].to_i}"
124
+ value << "; includeSubDomains" if hsts[:subdomains]
125
+ value << "; preload" if hsts[:preload]
126
+ value
127
+ end
128
+
129
+ def flag_cookies_as_secure!(headers)
130
+ cookies = headers[Rack::SET_COOKIE]
131
+ return unless cookies
132
+
133
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
134
+ cookies = cookies.split("\n")
135
+ headers[Rack::SET_COOKIE] = cookies.map { |cookie|
136
+ if !/;\s*secure\s*(;|$)/i.match?(cookie)
137
+ "#{cookie}; secure"
138
+ else
139
+ cookie
140
+ end
141
+ }.join("\n")
142
+ else
143
+ headers[Rack::SET_COOKIE] = Array(cookies).map do |cookie|
144
+ if !/;\s*secure\s*(;|$)/i.match?(cookie)
145
+ "#{cookie}; secure"
146
+ else
147
+ cookie
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ def redirect_to_https(request)
154
+ [ @redirect.fetch(:status, redirection_status(request)),
155
+ { Rack::CONTENT_TYPE => "text/html; charset=utf-8",
156
+ Constants::LOCATION => https_location_for(request) },
157
+ (@redirect[:body] || []) ]
158
+ end
159
+
160
+ def redirection_status(request)
161
+ if PERMANENT_REDIRECT_REQUEST_METHODS.include?(request.raw_request_method)
162
+ 301 # Issue a permanent redirect via a GET request.
163
+ elsif @ssl_default_redirect_status
164
+ @ssl_default_redirect_status
165
+ else
166
+ 307 # Issue a fresh request redirect to preserve the HTTP method.
167
+ end
168
+ end
169
+
170
+ def https_location_for(request)
171
+ host = @redirect[:host] || request.host
172
+ port = @redirect[:port] || request.port
173
+
174
+ location = +"https://#{host}"
175
+ location << ":#{port}" if port != 80 && port != 443
176
+ location << request.fullpath
177
+ location
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/inflector/methods"
6
+ require "active_support/dependencies"
7
+
8
+ module ActionDispatch
9
+ # # Action Dispatch MiddlewareStack
10
+ #
11
+ # Read more about [Rails middleware
12
+ # stack](https://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack)
13
+ # in the guides.
14
+ class MiddlewareStack
15
+ class Middleware
16
+ attr_reader :args, :block, :klass
17
+
18
+ def initialize(klass, args, block)
19
+ @klass = klass
20
+ @args = args
21
+ @block = block
22
+ end
23
+
24
+ def name; klass.name; end
25
+
26
+ def ==(middleware)
27
+ case middleware
28
+ when Middleware
29
+ klass == middleware.klass
30
+ when Module
31
+ klass == middleware
32
+ end
33
+ end
34
+
35
+ def inspect
36
+ if klass.is_a?(Module)
37
+ klass.to_s
38
+ else
39
+ klass.class.to_s
40
+ end
41
+ end
42
+
43
+ def build(app)
44
+ klass.new(app, *args, &block)
45
+ end
46
+
47
+ def build_instrumented(app)
48
+ InstrumentationProxy.new(build(app), inspect)
49
+ end
50
+ end
51
+
52
+ # This class is used to instrument the execution of a single middleware. It
53
+ # proxies the `call` method transparently and instruments the method call.
54
+ class InstrumentationProxy
55
+ EVENT_NAME = "process_middleware.action_dispatch"
56
+
57
+ def initialize(middleware, class_name)
58
+ @middleware = middleware
59
+
60
+ @payload = {
61
+ middleware: class_name,
62
+ }
63
+ end
64
+
65
+ def call(env)
66
+ ActiveSupport::Notifications.instrument(EVENT_NAME, @payload) do
67
+ @middleware.call(env)
68
+ end
69
+ end
70
+ end
71
+
72
+ include Enumerable
73
+
74
+ attr_accessor :middlewares
75
+
76
+ def initialize(*args)
77
+ @middlewares = []
78
+ yield(self) if block_given?
79
+ end
80
+
81
+ def each(&block)
82
+ @middlewares.each(&block)
83
+ end
84
+
85
+ def size
86
+ middlewares.size
87
+ end
88
+
89
+ def last
90
+ middlewares.last
91
+ end
92
+
93
+ def [](i)
94
+ middlewares[i]
95
+ end
96
+
97
+ def unshift(klass, *args, &block)
98
+ middlewares.unshift(build_middleware(klass, args, block))
99
+ end
100
+ ruby2_keywords(:unshift)
101
+
102
+ def initialize_copy(other)
103
+ self.middlewares = other.middlewares.dup
104
+ end
105
+
106
+ def insert(index, klass, *args, &block)
107
+ index = assert_index(index, :before)
108
+ middlewares.insert(index, build_middleware(klass, args, block))
109
+ end
110
+ ruby2_keywords(:insert)
111
+
112
+ alias_method :insert_before, :insert
113
+
114
+ def insert_after(index, *args, &block)
115
+ index = assert_index(index, :after)
116
+ insert(index + 1, *args, &block)
117
+ end
118
+ ruby2_keywords(:insert_after)
119
+
120
+ def swap(target, *args, &block)
121
+ index = assert_index(target, :before)
122
+ insert(index, *args, &block)
123
+ middlewares.delete_at(index + 1)
124
+ end
125
+ ruby2_keywords(:swap)
126
+
127
+ # Deletes a middleware from the middleware stack.
128
+ #
129
+ # Returns the array of middlewares not including the deleted item, or returns
130
+ # nil if the target is not found.
131
+ def delete(target)
132
+ middlewares.reject! { |m| m.name == target.name }
133
+ end
134
+
135
+ # Deletes a middleware from the middleware stack.
136
+ #
137
+ # Returns the array of middlewares not including the deleted item, or raises
138
+ # `RuntimeError` if the target is not found.
139
+ def delete!(target)
140
+ delete(target) || (raise "No such middleware to remove: #{target.inspect}")
141
+ end
142
+
143
+ def move(target, source)
144
+ source_index = assert_index(source, :before)
145
+ source_middleware = middlewares.delete_at(source_index)
146
+
147
+ target_index = assert_index(target, :before)
148
+ middlewares.insert(target_index, source_middleware)
149
+ end
150
+
151
+ alias_method :move_before, :move
152
+
153
+ def move_after(target, source)
154
+ source_index = assert_index(source, :after)
155
+ source_middleware = middlewares.delete_at(source_index)
156
+
157
+ target_index = assert_index(target, :after)
158
+ middlewares.insert(target_index + 1, source_middleware)
159
+ end
160
+
161
+ def use(klass, *args, &block)
162
+ middlewares.push(build_middleware(klass, args, block))
163
+ end
164
+ ruby2_keywords(:use)
165
+
166
+ def build(app = nil, &block)
167
+ instrumenting = ActiveSupport::Notifications.notifier.listening?(InstrumentationProxy::EVENT_NAME)
168
+ middlewares.freeze.reverse.inject(app || block) do |a, e|
169
+ if instrumenting
170
+ e.build_instrumented(a)
171
+ else
172
+ e.build(a)
173
+ end
174
+ end
175
+ end
176
+
177
+ private
178
+ def assert_index(index, where)
179
+ i = index.is_a?(Integer) ? index : index_of(index)
180
+ raise "No such middleware to insert #{where}: #{index.inspect}" unless i
181
+ i
182
+ end
183
+
184
+ def build_middleware(klass, args, block)
185
+ Middleware.new(klass, args, block)
186
+ end
187
+
188
+ def index_of(klass)
189
+ middlewares.index do |m|
190
+ m.name == klass.name
191
+ end
192
+ end
193
+ end
194
+ end