actionpack 4.2.10 → 7.2.0.rc1

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.

Files changed (202) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +86 -600
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -14
  5. data/lib/abstract_controller/asset_paths.rb +5 -1
  6. data/lib/abstract_controller/base.rb +166 -136
  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 +126 -57
  10. data/lib/abstract_controller/collector.rb +13 -15
  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 +181 -132
  14. data/lib/abstract_controller/logger.rb +5 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
  16. data/lib/abstract_controller/rendering.rb +56 -56
  17. data/lib/abstract_controller/translation.rb +29 -15
  18. data/lib/abstract_controller/url_for.rb +15 -11
  19. data/lib/abstract_controller.rb +21 -5
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +154 -0
  22. data/lib/action_controller/base.rb +219 -155
  23. data/lib/action_controller/caching.rb +28 -68
  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 +35 -22
  27. data/lib/action_controller/metal/allow_browser.rb +119 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +259 -122
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +9 -5
  32. data/lib/action_controller/metal/data_streaming.rb +87 -104
  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 +35 -26
  36. data/lib/action_controller/metal/exceptions.rb +71 -24
  37. data/lib/action_controller/metal/flash.rb +26 -19
  38. data/lib/action_controller/metal/head.rb +45 -36
  39. data/lib/action_controller/metal/helpers.rb +80 -64
  40. data/lib/action_controller/metal/http_authentication.rb +297 -244
  41. data/lib/action_controller/metal/implicit_render.rb +57 -9
  42. data/lib/action_controller/metal/instrumentation.rb +76 -64
  43. data/lib/action_controller/metal/live.rb +238 -176
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +177 -166
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +145 -118
  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 +203 -64
  51. data/lib/action_controller/metal/renderers.rb +108 -65
  52. data/lib/action_controller/metal/rendering.rb +216 -56
  53. data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
  54. data/lib/action_controller/metal/rescue.rb +19 -21
  55. data/lib/action_controller/metal/streaming.rb +179 -138
  56. data/lib/action_controller/metal/strong_parameters.rb +1058 -382
  57. data/lib/action_controller/metal/testing.rb +11 -17
  58. data/lib/action_controller/metal/url_for.rb +37 -21
  59. data/lib/action_controller/metal.rb +236 -138
  60. data/lib/action_controller/railtie.rb +89 -11
  61. data/lib/action_controller/railties/helpers.rb +5 -1
  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 +425 -497
  65. data/lib/action_controller.rb +44 -22
  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 +119 -63
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +364 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +36 -34
  72. data/lib/action_dispatch/http/filter_redirect.rb +24 -12
  73. data/lib/action_dispatch/http/headers.rb +66 -31
  74. data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
  75. data/lib/action_dispatch/http/mime_type.rb +196 -136
  76. data/lib/action_dispatch/http/mime_types.rb +25 -7
  77. data/lib/action_dispatch/http/parameters.rb +97 -45
  78. data/lib/action_dispatch/http/permissions_policy.rb +187 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +6 -0
  80. data/lib/action_dispatch/http/request.rb +299 -170
  81. data/lib/action_dispatch/http/response.rb +311 -160
  82. data/lib/action_dispatch/http/upload.rb +52 -23
  83. data/lib/action_dispatch/http/url.rb +201 -125
  84. data/lib/action_dispatch/journey/formatter.rb +110 -50
  85. data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
  88. data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
  89. data/lib/action_dispatch/journey/nodes/node.rb +100 -20
  90. data/lib/action_dispatch/journey/parser.rb +19 -17
  91. data/lib/action_dispatch/journey/parser.y +4 -3
  92. data/lib/action_dispatch/journey/parser_extras.rb +14 -4
  93. data/lib/action_dispatch/journey/path/pattern.rb +79 -63
  94. data/lib/action_dispatch/journey/route.rb +108 -44
  95. data/lib/action_dispatch/journey/router/utils.rb +41 -29
  96. data/lib/action_dispatch/journey/router.rb +64 -57
  97. data/lib/action_dispatch/journey/routes.rb +23 -21
  98. data/lib/action_dispatch/journey/scanner.rb +28 -17
  99. data/lib/action_dispatch/journey/visitors.rb +100 -54
  100. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  101. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  102. data/lib/action_dispatch/journey.rb +7 -5
  103. data/lib/action_dispatch/log_subscriber.rb +25 -0
  104. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  105. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  106. data/lib/action_dispatch/middleware/callbacks.rb +7 -6
  107. data/lib/action_dispatch/middleware/cookies.rb +471 -328
  108. data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
  109. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  110. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  111. data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
  112. data/lib/action_dispatch/middleware/executor.rb +32 -0
  113. data/lib/action_dispatch/middleware/flash.rb +143 -101
  114. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  115. data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
  116. data/lib/action_dispatch/middleware/reloader.rb +10 -92
  117. data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
  118. data/lib/action_dispatch/middleware/request_id.rb +29 -15
  119. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  120. data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
  121. data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
  122. data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
  123. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
  124. data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
  125. data/lib/action_dispatch/middleware/ssl.rb +134 -36
  126. data/lib/action_dispatch/middleware/stack.rb +109 -44
  127. data/lib/action_dispatch/middleware/static.rb +159 -90
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +7 -24
  132. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
  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 +26 -7
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
  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 +139 -15
  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 +6 -6
  146. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
  147. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
  148. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  149. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  150. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  151. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
  152. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
  153. data/lib/action_dispatch/railtie.rb +44 -16
  154. data/lib/action_dispatch/request/session.rb +159 -69
  155. data/lib/action_dispatch/request/utils.rb +97 -23
  156. data/lib/action_dispatch/routing/endpoint.rb +11 -2
  157. data/lib/action_dispatch/routing/inspector.rb +195 -106
  158. data/lib/action_dispatch/routing/mapper.rb +1338 -955
  159. data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
  160. data/lib/action_dispatch/routing/redirection.rb +78 -51
  161. data/lib/action_dispatch/routing/route_set.rb +460 -374
  162. data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
  163. data/lib/action_dispatch/routing/url_for.rb +172 -124
  164. data/lib/action_dispatch/routing.rb +159 -158
  165. data/lib/action_dispatch/system_test_case.rb +206 -0
  166. data/lib/action_dispatch/system_testing/browser.rb +84 -0
  167. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  168. data/lib/action_dispatch/system_testing/server.rb +33 -0
  169. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  170. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  171. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  172. data/lib/action_dispatch/testing/assertions/response.rb +71 -39
  173. data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
  174. data/lib/action_dispatch/testing/assertions.rb +9 -6
  175. data/lib/action_dispatch/testing/integration.rb +486 -306
  176. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  177. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  178. data/lib/action_dispatch/testing/test_process.rb +35 -22
  179. data/lib/action_dispatch/testing/test_request.rb +29 -34
  180. data/lib/action_dispatch/testing/test_response.rb +48 -15
  181. data/lib/action_dispatch.rb +82 -40
  182. data/lib/action_pack/gem_version.rb +8 -4
  183. data/lib/action_pack/version.rb +6 -2
  184. data/lib/action_pack.rb +21 -18
  185. metadata +146 -56
  186. data/lib/action_controller/caching/fragments.rb +0 -103
  187. data/lib/action_controller/metal/force_ssl.rb +0 -97
  188. data/lib/action_controller/metal/hide_actions.rb +0 -40
  189. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  190. data/lib/action_controller/middleware.rb +0 -39
  191. data/lib/action_controller/model_naming.rb +0 -12
  192. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  193. data/lib/action_dispatch/journey/backwards.rb +0 -5
  194. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  195. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  196. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  197. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  199. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +0 -27
  200. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  201. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  202. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,26 +1,27 @@
1
- require 'rack/utils'
2
- require 'rack/request'
3
- require 'rack/session/abstract/id'
4
- require 'action_dispatch/middleware/cookies'
5
- require 'action_dispatch/request/session'
1
+ # frozen_string_literal: true
6
2
 
7
- module ActionDispatch
8
- module Session
9
- class SessionRestoreError < StandardError #:nodoc:
10
- attr_reader :original_exception
3
+ # :markup: markdown
11
4
 
12
- def initialize(const_error)
13
- @original_exception = const_error
5
+ require "rack/utils"
6
+ require "rack/request"
7
+ require "rack/session/abstract/id"
8
+ require "action_dispatch/middleware/cookies"
9
+ require "action_dispatch/request/session"
14
10
 
15
- super("Session contains objects whose class definition isn't available.\n" +
16
- "Remember to require the classes for all objects kept in the session.\n" +
17
- "(Original exception: #{const_error.message} [#{const_error.class}])\n")
11
+ module ActionDispatch
12
+ module Session
13
+ class SessionRestoreError < StandardError # :nodoc:
14
+ def initialize
15
+ super("Session contains objects whose class definition isn't available.\n" \
16
+ "Remember to require the classes for all objects kept in the session.\n" \
17
+ "(Original exception: #{$!.message} [#{$!.class}])\n")
18
+ set_backtrace $!.backtrace
18
19
  end
19
20
  end
20
21
 
21
22
  module Compatibility
22
23
  def initialize(app, options = {})
23
- options[:key] ||= '_session_id'
24
+ options[:key] ||= "_session_id"
24
25
  super
25
26
  end
26
27
 
@@ -30,12 +31,15 @@ module ActionDispatch
30
31
  sid
31
32
  end
32
33
 
33
- protected
34
-
35
- def initialize_sid
34
+ private
35
+ def initialize_sid # :doc:
36
36
  @default_options.delete(:sidbits)
37
37
  @default_options.delete(:secure_random)
38
38
  end
39
+
40
+ def make_request(env)
41
+ ActionDispatch::Request.new env
42
+ end
39
43
  end
40
44
 
41
45
  module StaleSessionCheck
@@ -52,10 +56,10 @@ module ActionDispatch
52
56
  rescue ArgumentError => argument_error
53
57
  if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
54
58
  begin
55
- # Note that the regexp does not allow $1 to end with a ':'
59
+ # Note that the regexp does not allow $1 to end with a ':'.
56
60
  $1.constantize
57
- rescue LoadError, NameError => e
58
- raise ActionDispatch::Session::SessionRestoreError, e, e.backtrace
61
+ rescue LoadError, NameError
62
+ raise ActionDispatch::Session::SessionRestoreError
59
63
  end
60
64
  retry
61
65
  else
@@ -65,8 +69,13 @@ module ActionDispatch
65
69
  end
66
70
 
67
71
  module SessionObject # :nodoc:
68
- def prepare_session(env)
69
- Request::Session.create(self, env, @default_options)
72
+ def commit_session(req, res)
73
+ req.commit_csrf_token
74
+ super(req, res)
75
+ end
76
+
77
+ def prepare_session(req)
78
+ Request::Session.create(self, req, @default_options)
70
79
  end
71
80
 
72
81
  def loaded_session?(session)
@@ -74,17 +83,30 @@ module ActionDispatch
74
83
  end
75
84
  end
76
85
 
77
- class AbstractStore < Rack::Session::Abstract::ID
86
+ class AbstractStore < Rack::Session::Abstract::Persisted
78
87
  include Compatibility
79
88
  include StaleSessionCheck
80
89
  include SessionObject
81
90
 
82
91
  private
92
+ def set_cookie(request, response, cookie)
93
+ request.cookie_jar[key] = cookie
94
+ end
95
+ end
96
+
97
+ class AbstractSecureStore < Rack::Session::Abstract::PersistedSecure
98
+ include Compatibility
99
+ include StaleSessionCheck
100
+ include SessionObject
83
101
 
84
- def set_cookie(env, session_id, cookie)
85
- request = ActionDispatch::Request.new(env)
86
- request.cookie_jar[key] = cookie
102
+ def generate_sid
103
+ Rack::Session::SessionId.new(super)
87
104
  end
105
+
106
+ private
107
+ def set_cookie(request, response, cookie)
108
+ request.cookie_jar[key] = cookie
109
+ end
88
110
  end
89
111
  end
90
112
  end
@@ -1,13 +1,25 @@
1
- require 'action_dispatch/middleware/session/abstract_store'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/middleware/session/abstract_store"
2
6
 
3
7
  module ActionDispatch
4
8
  module Session
5
- # Session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful
6
- # if you don't store critical data in your sessions and you don't need them to live for extended periods
7
- # of time.
8
- class CacheStore < AbstractStore
9
- # Create a new store. The cache to use can be passed in the <tt>:cache</tt> option. If it is
10
- # not specified, <tt>Rails.cache</tt> will be used.
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
11
23
  def initialize(app, options = {})
12
24
  @cache = options[:cache] || Rails.cache
13
25
  options[:expire_after] ||= @cache.options[:expires_in]
@@ -15,18 +27,18 @@ module ActionDispatch
15
27
  end
16
28
 
17
29
  # Get a session from the cache.
18
- def get_session(env, sid)
19
- unless sid and session = @cache.read(cache_key(sid))
30
+ def find_session(env, sid)
31
+ unless sid && (session = get_session_with_fallback(sid))
20
32
  sid, session = generate_sid, {}
21
33
  end
22
34
  [sid, session]
23
35
  end
24
36
 
25
37
  # Set a session in the cache.
26
- def set_session(env, sid, session, options)
27
- key = cache_key(sid)
38
+ def write_session(env, sid, session, options)
39
+ key = cache_key(sid.private_id)
28
40
  if session
29
- @cache.write(key, session, :expires_in => options[:expire_after])
41
+ @cache.write(key, session, expires_in: options[:expire_after])
30
42
  else
31
43
  @cache.delete(key)
32
44
  end
@@ -34,15 +46,20 @@ module ActionDispatch
34
46
  end
35
47
 
36
48
  # Remove a session from the cache.
37
- def destroy_session(env, sid, options)
38
- @cache.delete(cache_key(sid))
49
+ def delete_session(env, sid, options)
50
+ @cache.delete(cache_key(sid.private_id))
51
+ @cache.delete(cache_key(sid.public_id))
39
52
  generate_sid
40
53
  end
41
54
 
42
55
  private
43
56
  # Turn the session id into a cache key.
44
- def cache_key(sid)
45
- "_session_id:#{sid}"
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))
46
63
  end
47
64
  end
48
65
  end
@@ -1,123 +1,129 @@
1
- require 'active_support/core_ext/hash/keys'
2
- require 'action_dispatch/middleware/session/abstract_store'
3
- require 'rack/session/cookie'
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"
4
8
 
5
9
  module ActionDispatch
6
10
  module Session
7
- # This cookie-based session store is the Rails default. It is
8
- # dramatically faster than the alternatives.
11
+ # # Action Dispatch Session CookieStore
9
12
  #
10
- # Sessions typically contain at most a user_id and flash message; both fit
11
- # within the 4K cookie size limit. A CookieOverflow exception is raised if
12
- # you attempt to store more than 4K of data.
13
+ # This cookie-based session store is the Rails default. It is dramatically
14
+ # faster than the alternatives.
13
15
  #
14
- # The cookie jar used for storage is automatically configured to be the
15
- # best possible option given your application's configuration.
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.
16
19
  #
17
- # If you only have secret_token set, your cookies will be signed, but
18
- # not encrypted. This means a user cannot alter their +user_id+ without
19
- # knowing your app's secret key, but can easily read their +user_id+. This
20
- # was the default for Rails 3 apps.
20
+ # The cookie jar used for storage is automatically configured to be the best
21
+ # possible option given your application's configuration.
21
22
  #
22
- # If you have secret_key_base set, your cookies will be encrypted. This
23
- # goes a step further than signed cookies in that encrypted cookies cannot
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
24
25
  # be altered or read by users. This is the default starting in Rails 4.
25
26
  #
26
- # If you have both secret_token and secret_key base set, your cookies will
27
- # be encrypted, and signed cookies generated by Rails 3 will be
28
- # transparently read and encrypted to provide a smooth upgrade path.
27
+ # Configure your session store in an initializer:
29
28
  #
30
- # Configure your session store in config/initializers/session_store.rb:
29
+ # Rails.application.config.session_store :cookie_store, key: '_your_app_session'
31
30
  #
32
- # Rails.application.config.session_store :cookie_store, key: '_your_app_session'
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.
33
35
  #
34
- # Configure your secret key in config/secrets.yml:
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.
35
38
  #
36
- # development:
37
- # secret_key_base: 'secret key'
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.
38
43
  #
39
- # To generate a secret key for an existing application, run `rake secret`.
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:
40
47
  #
41
- # If you are upgrading an existing Rails 3 app, you should leave your
42
- # existing secret_token in place and simply add the new secret_key_base.
43
- # Note that you should wait to set secret_key_base until you have 100% of
44
- # your userbase on Rails 4 and are reasonably sure you will not need to
45
- # rollback to Rails 3. This is because cookies signed based on the new
46
- # secret_key_base in Rails 4 are not backwards compatible with Rails 3.
47
- # You are free to leave your existing secret_token in place, not set the
48
- # new secret_key_base, and ignore the deprecation warnings until you are
49
- # reasonably sure that your upgrade is otherwise complete. Additionally,
50
- # you should take care to make sure you are not relying on the ability to
51
- # decode signed cookies generated by your app in external applications or
52
- # JavaScript before upgrading.
48
+ # Rails.application.config.session_store :cookie_store, expire_after: 14.days
53
49
  #
54
- # Note that changing the secret key will invalidate all existing sessions!
55
- class CookieStore < Rack::Session::Abstract::ID
56
- include Compatibility
57
- include StaleSessionCheck
58
- include SessionObject
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:
59
63
 
60
- def initialize(app, options={})
61
- super(app, options.merge!(:cookie_only => true))
64
+ def initialize(app, options = {})
65
+ options[:cookie_only] = true
66
+ options[:same_site] = DEFAULT_SAME_SITE if !options.key?(:same_site)
67
+ super
62
68
  end
63
69
 
64
- def destroy_session(env, session_id, options)
70
+ def delete_session(req, session_id, options)
65
71
  new_sid = generate_sid unless options[:drop]
66
72
  # Reset hash and Assign the new session id
67
- env["action_dispatch.request.unsigned_session_cookie"] = new_sid ? { "session_id" => new_sid } : {}
73
+ req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid.public_id } : {})
68
74
  new_sid
69
75
  end
70
76
 
71
- def load_session(env)
77
+ def load_session(req)
72
78
  stale_session_check! do
73
- data = unpacked_cookie_data(env)
79
+ data = unpacked_cookie_data(req)
74
80
  data = persistent_session_id!(data)
75
- [data["session_id"], data]
81
+ [Rack::Session::SessionId.new(data["session_id"]), data]
76
82
  end
77
83
  end
78
84
 
79
85
  private
80
-
81
- def extract_session_id(env)
82
- stale_session_check! do
83
- unpacked_cookie_data(env)["session_id"]
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
84
91
  end
85
- end
86
92
 
87
- def unpacked_cookie_data(env)
88
- env["action_dispatch.request.unsigned_session_cookie"] ||= begin
89
- stale_session_check! do
90
- if data = get_cookie(env)
91
- data.stringify_keys!
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 || {}
92
100
  end
93
- data || {}
101
+ req.set_header k, v
94
102
  end
95
103
  end
96
- end
97
104
 
98
- def persistent_session_id!(data, sid=nil)
99
- data ||= {}
100
- data["session_id"] ||= sid || generate_sid
101
- data
102
- end
105
+ def persistent_session_id!(data, sid = nil)
106
+ data ||= {}
107
+ data["session_id"] ||= sid || generate_sid.public_id
108
+ data
109
+ end
103
110
 
104
- def set_session(env, sid, session_data, options)
105
- session_data["session_id"] = sid
106
- session_data
107
- end
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
108
115
 
109
- def set_cookie(env, session_id, cookie)
110
- cookie_jar(env)[@key] = cookie
111
- end
116
+ def set_cookie(request, session_id, cookie)
117
+ cookie_jar(request)[@key] = cookie
118
+ end
112
119
 
113
- def get_cookie(env)
114
- cookie_jar(env)[@key]
115
- end
120
+ def get_cookie(req)
121
+ cookie_jar(req)[@key]
122
+ end
116
123
 
117
- def cookie_jar(env)
118
- request = ActionDispatch::Request.new(env)
119
- request.cookie_jar.signed_or_encrypted
120
- end
124
+ def cookie_jar(request)
125
+ request.cookie_jar.signed_or_encrypted
126
+ end
121
127
  end
122
128
  end
123
129
  end
@@ -1,13 +1,25 @@
1
- require 'action_dispatch/middleware/session/abstract_store'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/middleware/session/abstract_store"
2
6
  begin
3
- require 'rack/session/dalli'
7
+ require "rack/session/dalli"
4
8
  rescue LoadError => e
5
- $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
9
+ warn "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
6
10
  raise e
7
11
  end
8
12
 
9
13
  module ActionDispatch
10
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
+ #
11
23
  class MemCacheStore < Rack::Session::Dalli
12
24
  include Compatibility
13
25
  include StaleSessionCheck
@@ -1,26 +1,28 @@
1
- require 'action_dispatch/http/request'
2
- require 'action_dispatch/middleware/exception_wrapper'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/middleware/exception_wrapper"
3
6
 
4
7
  module ActionDispatch
5
- # This middleware rescues any exception returned by the application
6
- # and calls an exceptions app that will wrap it in a format for the end user.
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.
7
17
  #
8
- # The exceptions app should be passed as parameter on initialization
9
- # of ShowExceptions. Every time there is an exception, ShowExceptions will
10
- # store the exception in env["action_dispatch.exception"], rewrite the
11
- # PATH_INFO to the exception status code and call the rack app.
18
+ # In Rails applications, the exceptions app can be configured with
19
+ # `config.exceptions_app`, which defaults to ActionDispatch::PublicExceptions.
12
20
  #
13
- # If the application returns a "X-Cascade" pass response, this middleware
14
- # will send an empty response as result with the correct status code.
15
- # If any exception happens inside the exceptions app, this middleware
16
- # catches the exceptions and returns a FAILSAFE_RESPONSE.
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.
17
25
  class ShowExceptions
18
- FAILSAFE_RESPONSE = [500, { 'Content-Type' => 'text/plain' },
19
- ["500 Internal Server Error\n" \
20
- "If you are the administrator of this website, then please read this web " \
21
- "application's log file and/or the web server's log file to find out what " \
22
- "went wrong."]]
23
-
24
26
  def initialize(app, exceptions_app)
25
27
  @app = app
26
28
  @exceptions_app = exceptions_app
@@ -29,30 +31,58 @@ module ActionDispatch
29
31
  def call(env)
30
32
  @app.call(env)
31
33
  rescue Exception => exception
32
- if env['action_dispatch.show_exceptions'] == false
33
- raise 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)
34
42
  else
35
- render_exception(env, exception)
43
+ raise exception
36
44
  end
37
45
  end
38
46
 
39
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 "}"
40
59
 
41
- def render_exception(env, exception)
42
- wrapper = ExceptionWrapper.new(env, exception)
43
- status = wrapper.status_code
44
- env["action_dispatch.exception"] = wrapper.exception
45
- env["action_dispatch.original_path"] = env["PATH_INFO"]
46
- env["PATH_INFO"] = "/#{status}"
47
- response = @exceptions_app.call(env)
48
- response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
49
- rescue Exception => failsafe_error
50
- $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
51
- FAILSAFE_RESPONSE
52
- end
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
53
66
 
54
- def pass_response(status)
55
- [status, {"Content-Type" => "text/html; charset=#{Response.default_charset}", "Content-Length" => "0"}, []]
56
- end
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
57
87
  end
58
88
  end