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,33 @@
1
- require 'active_support/core_ext/hash/slice'
2
- require 'active_support/core_ext/hash/except'
3
- require 'active_support/core_ext/module/anonymous'
4
- require 'active_support/core_ext/struct'
5
- require 'action_dispatch/http/mime_type'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/core_ext/hash/slice"
6
+ require "active_support/core_ext/hash/except"
7
+ require "active_support/core_ext/module/anonymous"
8
+ require "action_dispatch/http/mime_type"
6
9
 
7
10
  module ActionController
11
+ # # Action Controller Params Wrapper
12
+ #
8
13
  # Wraps the parameters hash into a nested hash. This will allow clients to
9
14
  # submit requests without having to specify any root elements.
10
15
  #
11
- # This functionality is enabled in +config/initializers/wrap_parameters.rb+
12
- # and can be customized. If you are upgrading to \Rails 3.1, this file will
13
- # need to be created for the functionality to be enabled.
16
+ # This functionality is enabled by default for JSON, and can be customized by
17
+ # setting the format array:
14
18
  #
15
- # You could also turn it on per controller by setting the format array to
16
- # a non-empty array:
19
+ # class ApplicationController < ActionController::Base
20
+ # wrap_parameters format: [:json, :xml]
21
+ # end
22
+ #
23
+ # You could also turn it on per controller:
17
24
  #
18
25
  # class UsersController < ApplicationController
19
26
  # wrap_parameters format: [:json, :xml, :url_encoded_form, :multipart_form]
20
27
  # end
21
28
  #
22
- # If you enable +ParamsWrapper+ for +:json+ format, instead of having to
23
- # send JSON parameters like this:
29
+ # If you enable `ParamsWrapper` for `:json` format, instead of having to send
30
+ # JSON parameters like this:
24
31
  #
25
32
  # {"user": {"name": "Konata"}}
26
33
  #
@@ -29,55 +36,56 @@ module ActionController
29
36
  # {"name": "Konata"}
30
37
  #
31
38
  # And it will be wrapped into a nested hash with the key name matching the
32
- # controller's name. For example, if you're posting to +UsersController+,
33
- # your new +params+ hash will look like this:
39
+ # controller's name. For example, if you're posting to `UsersController`, your
40
+ # new `params` hash will look like this:
34
41
  #
35
42
  # {"name" => "Konata", "user" => {"name" => "Konata"}}
36
43
  #
37
- # You can also specify the key in which the parameters should be wrapped to,
38
- # and also the list of attributes it should wrap by using either +:include+ or
39
- # +:exclude+ options like this:
44
+ # You can also specify the key in which the parameters should be wrapped to, and
45
+ # also the list of attributes it should wrap by using either `:include` or
46
+ # `:exclude` options like this:
40
47
  #
41
48
  # class UsersController < ApplicationController
42
49
  # wrap_parameters :person, include: [:username, :password]
43
50
  # end
44
51
  #
45
- # On ActiveRecord models with no +:include+ or +:exclude+ option set,
46
- # it will only wrap the parameters returned by the class method
47
- # <tt>attribute_names</tt>.
52
+ # On Active Record models with no `:include` or `:exclude` option set, it will
53
+ # only wrap the parameters returned by the class method `attribute_names`.
48
54
  #
49
- # If you're going to pass the parameters to an +ActiveModel+ object (such as
50
- # <tt>User.new(params[:user])</tt>), you might consider passing the model class to
51
- # the method instead. The +ParamsWrapper+ will actually try to determine the
52
- # list of attribute names from the model and only wrap those attributes:
55
+ # If you're going to pass the parameters to an `ActiveModel` object (such as
56
+ # `User.new(params[:user])`), you might consider passing the model class to the
57
+ # method instead. The `ParamsWrapper` will actually try to determine the list of
58
+ # attribute names from the model and only wrap those attributes:
53
59
  #
54
60
  # class UsersController < ApplicationController
55
61
  # wrap_parameters Person
56
62
  # end
57
63
  #
58
- # You still could pass +:include+ and +:exclude+ to set the list of attributes
64
+ # You still could pass `:include` and `:exclude` to set the list of attributes
59
65
  # you want to wrap.
60
66
  #
61
67
  # By default, if you don't specify the key in which the parameters would be
62
- # wrapped to, +ParamsWrapper+ will actually try to determine if there's
63
- # a model related to it or not. This controller, for example:
68
+ # wrapped to, `ParamsWrapper` will actually try to determine if there's a model
69
+ # related to it or not. This controller, for example:
64
70
  #
65
71
  # class Admin::UsersController < ApplicationController
66
72
  # end
67
73
  #
68
- # will try to check if <tt>Admin::User</tt> or +User+ model exists, and use it to
69
- # determine the wrapper key respectively. If both models don't exist,
70
- # it will then fallback to use +user+ as the key.
74
+ # will try to check if `Admin::User` or `User` model exists, and use it to
75
+ # determine the wrapper key respectively. If both models don't exist, it will
76
+ # then fall back to use `user` as the key.
77
+ #
78
+ # To disable this functionality for a controller:
79
+ #
80
+ # class UsersController < ApplicationController
81
+ # wrap_parameters false
82
+ # end
71
83
  module ParamsWrapper
72
84
  extend ActiveSupport::Concern
73
85
 
74
86
  EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
75
87
 
76
- require 'mutex_m'
77
-
78
88
  class Options < Struct.new(:name, :format, :include, :exclude, :klass, :model) # :nodoc:
79
- include Mutex_m
80
-
81
89
  def self.from_hash(hash)
82
90
  name = hash[:name]
83
91
  format = Array(hash[:format])
@@ -86,21 +94,22 @@ module ActionController
86
94
  new name, format, include, exclude, nil, nil
87
95
  end
88
96
 
89
- def initialize(name, format, include, exclude, klass, model) # nodoc
97
+ def initialize(name, format, include, exclude, klass, model) # :nodoc:
90
98
  super
99
+ @mutex = Mutex.new
91
100
  @include_set = include
92
101
  @name_set = name
93
102
  end
94
103
 
95
104
  def model
96
- super || synchronize { super || self.model = _default_wrap_model }
105
+ super || self.model = _default_wrap_model
97
106
  end
98
107
 
99
108
  def include
100
109
  return super if @include_set
101
110
 
102
111
  m = model
103
- synchronize do
112
+ @mutex.synchronize do
104
113
  return super if @include_set
105
114
 
106
115
  @include_set = true
@@ -108,6 +117,22 @@ module ActionController
108
117
  unless super || exclude
109
118
  if m.respond_to?(:attribute_names) && m.attribute_names.any?
110
119
  self.include = m.attribute_names
120
+
121
+ if m.respond_to?(:stored_attributes) && !m.stored_attributes.empty?
122
+ self.include += m.stored_attributes.values.flatten.map(&:to_s)
123
+ end
124
+
125
+ if m.respond_to?(:attribute_aliases) && m.attribute_aliases.any?
126
+ self.include += m.attribute_aliases.keys
127
+ end
128
+
129
+ if m.respond_to?(:nested_attributes_options) && m.nested_attributes_options.keys.any?
130
+ self.include += m.nested_attributes_options.keys.map do |key|
131
+ (+key.to_s).concat("_attributes")
132
+ end
133
+ end
134
+
135
+ self.include
111
136
  end
112
137
  end
113
138
  end
@@ -117,7 +142,7 @@ module ActionController
117
142
  return super if @name_set
118
143
 
119
144
  m = model
120
- synchronize do
145
+ @mutex.synchronize do
121
146
  return super if @name_set
122
147
 
123
148
  @name_set = true
@@ -130,35 +155,34 @@ module ActionController
130
155
  end
131
156
 
132
157
  private
133
- # Determine the wrapper model from the controller's name. By convention,
134
- # this could be done by trying to find the defined model that has the
135
- # same singularize name as the controller. For example, +UsersController+
136
- # will try to find if the +User+ model exists.
137
- #
138
- # This method also does namespace lookup. Foo::Bar::UsersController will
139
- # try to find Foo::Bar::User, Foo::User and finally User.
140
- def _default_wrap_model #:nodoc:
141
- return nil if klass.anonymous?
142
- model_name = klass.name.sub(/Controller$/, '').classify
143
-
144
- begin
145
- if model_klass = model_name.safe_constantize
146
- model_klass
147
- else
148
- namespaces = model_name.split("::")
149
- namespaces.delete_at(-2)
150
- break if namespaces.last == model_name
151
- model_name = namespaces.join("::")
152
- end
153
- end until model_klass
158
+ # Determine the wrapper model from the controller's name. By convention, this
159
+ # could be done by trying to find the defined model that has the same singular
160
+ # name as the controller. For example, `UsersController` will try to find if the
161
+ # `User` model exists.
162
+ #
163
+ # This method also does namespace lookup. Foo::Bar::UsersController will try to
164
+ # find Foo::Bar::User, Foo::User and finally User.
165
+ def _default_wrap_model
166
+ return nil if klass.anonymous?
167
+ model_name = klass.name.delete_suffix("Controller").classify
168
+
169
+ begin
170
+ if model_klass = model_name.safe_constantize
171
+ model_klass
172
+ else
173
+ namespaces = model_name.split("::")
174
+ namespaces.delete_at(-2)
175
+ break if namespaces.last == model_name
176
+ model_name = namespaces.join("::")
177
+ end
178
+ end until model_klass
154
179
 
155
- model_klass
156
- end
180
+ model_klass
181
+ end
157
182
  end
158
183
 
159
184
  included do
160
- class_attribute :_wrapper_options
161
- self._wrapper_options = Options.from_hash(format: [])
185
+ class_attribute :_wrapper_options, default: Options.from_hash(format: [])
162
186
  end
163
187
 
164
188
  module ClassMethods
@@ -166,33 +190,34 @@ module ActionController
166
190
  self._wrapper_options = Options.from_hash(options)
167
191
  end
168
192
 
169
- # Sets the name of the wrapper key, or the model which +ParamsWrapper+
170
- # would use to determine the attribute names from.
193
+ # Sets the name of the wrapper key, or the model which `ParamsWrapper` would use
194
+ # to determine the attribute names from.
195
+ #
196
+ # #### Examples
197
+ # wrap_parameters format: :xml
198
+ # # enables the parameter wrapper for XML format
171
199
  #
172
- # ==== Examples
173
- # wrap_parameters format: :xml
174
- # # enables the parameter wrapper for XML format
200
+ # wrap_parameters :person
201
+ # # wraps parameters into +params[:person]+ hash
175
202
  #
176
- # wrap_parameters :person
177
- # # wraps parameters into +params[:person]+ hash
203
+ # wrap_parameters Person
204
+ # # wraps parameters by determining the wrapper key from Person class
205
+ # # (+person+, in this case) and the list of attribute names
178
206
  #
179
- # wrap_parameters Person
180
- # # wraps parameters by determining the wrapper key from Person class
181
- # (+person+, in this case) and the list of attribute names
207
+ # wrap_parameters include: [:username, :title]
208
+ # # wraps only +:username+ and +:title+ attributes from parameters.
182
209
  #
183
- # wrap_parameters include: [:username, :title]
184
- # # wraps only +:username+ and +:title+ attributes from parameters.
210
+ # wrap_parameters false
211
+ # # disables parameters wrapping for this controller altogether.
185
212
  #
186
- # wrap_parameters false
187
- # # disables parameters wrapping for this controller altogether.
213
+ # #### Options
214
+ # * `:format` - The list of formats in which the parameters wrapper will be
215
+ # enabled.
216
+ # * `:include` - The list of attribute names which parameters wrapper will
217
+ # wrap into a nested hash.
218
+ # * `:exclude` - The list of attribute names which parameters wrapper will
219
+ # exclude from a nested hash.
188
220
  #
189
- # ==== Options
190
- # * <tt>:format</tt> - The list of formats in which the parameters wrapper
191
- # will be enabled.
192
- # * <tt>:include</tt> - The list of attribute names which parameters wrapper
193
- # will wrap into a nested hash.
194
- # * <tt>:exclude</tt> - The list of attribute names which parameters wrapper
195
- # will exclude from a nested hash.
196
221
  def wrap_parameters(name_or_model_or_options, options = {})
197
222
  model = nil
198
223
 
@@ -200,23 +225,22 @@ module ActionController
200
225
  when Hash
201
226
  options = name_or_model_or_options
202
227
  when false
203
- options = options.merge(:format => [])
228
+ options = options.merge(format: [])
204
229
  when Symbol, String
205
- options = options.merge(:name => name_or_model_or_options)
230
+ options = options.merge(name: name_or_model_or_options)
206
231
  else
207
232
  model = name_or_model_or_options
208
233
  end
209
234
 
210
- opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
235
+ opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
211
236
  opts.model = model
212
237
  opts.klass = self
213
238
 
214
239
  self._wrapper_options = opts
215
240
  end
216
241
 
217
- # Sets the default wrapper key or model which will be used to determine
218
- # wrapper key and attribute names. Will be called automatically when the
219
- # module is inherited.
242
+ # Sets the default wrapper key or model which will be used to determine wrapper
243
+ # key and attribute names. Called automatically when the module is inherited.
220
244
  def inherited(klass)
221
245
  if klass._wrapper_options.format.any?
222
246
  params = klass._wrapper_options.dup
@@ -227,32 +251,15 @@ module ActionController
227
251
  end
228
252
  end
229
253
 
230
- # Performs parameters wrapping upon the request. Will be called automatically
231
- # by the metal call stack.
232
- def process_action(*args)
233
- if _wrapper_enabled?
234
- if request.parameters[_wrapper_key].present?
235
- wrapped_hash = _extract_parameters(request.parameters)
236
- else
237
- wrapped_hash = _wrap_parameters request.request_parameters
238
- end
239
-
240
- wrapped_keys = request.request_parameters.keys
241
- wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
242
-
243
- # This will make the wrapped hash accessible from controller and view
244
- request.parameters.merge! wrapped_hash
245
- request.request_parameters.merge! wrapped_hash
246
-
247
- # This will display the wrapped hash in the log file
248
- request.filtered_parameters.merge! wrapped_filtered_hash
249
- end
250
- super
251
- end
252
-
253
254
  private
255
+ # Performs parameters wrapping upon the request. Called automatically by the
256
+ # metal call stack.
257
+ def process_action(*)
258
+ _perform_parameter_wrapping if _wrapper_enabled?
259
+ super
260
+ end
254
261
 
255
- # Returns the wrapper key which will be used to stored wrapped parameters.
262
+ # Returns the wrapper key which will be used to store wrapped parameters.
256
263
  def _wrapper_key
257
264
  _wrapper_options.name
258
265
  end
@@ -270,16 +277,36 @@ module ActionController
270
277
  def _extract_parameters(parameters)
271
278
  if include_only = _wrapper_options.include
272
279
  parameters.slice(*include_only)
280
+ elsif _wrapper_options.exclude
281
+ exclude = _wrapper_options.exclude + EXCLUDE_PARAMETERS
282
+ parameters.except(*exclude)
273
283
  else
274
- exclude = _wrapper_options.exclude || []
275
- parameters.except(*(exclude + EXCLUDE_PARAMETERS))
284
+ parameters.except(*EXCLUDE_PARAMETERS)
276
285
  end
277
286
  end
278
287
 
279
288
  # Checks if we should perform parameters wrapping.
280
289
  def _wrapper_enabled?
281
- ref = request.content_mime_type.try(:ref)
282
- _wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key]
290
+ return false unless request.has_content_type?
291
+
292
+ ref = request.content_mime_type.ref
293
+
294
+ _wrapper_formats.include?(ref) && _wrapper_key && !request.parameters.key?(_wrapper_key)
295
+ rescue ActionDispatch::Http::Parameters::ParseError
296
+ false
297
+ end
298
+
299
+ def _perform_parameter_wrapping
300
+ wrapped_hash = _wrap_parameters request.request_parameters
301
+ wrapped_keys = request.request_parameters.keys
302
+ wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
303
+
304
+ # This will make the wrapped hash accessible from controller and view.
305
+ request.parameters.merge! wrapped_hash
306
+ request.request_parameters.merge! wrapped_hash
307
+
308
+ # This will display the wrapped hash in the log file.
309
+ request.filtered_parameters.merge! wrapped_filtered_hash
283
310
  end
284
311
  end
285
312
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionController # :nodoc:
6
+ module PermissionsPolicy
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ # Overrides parts of the globally configured `Feature-Policy` header:
11
+ #
12
+ # class PagesController < ApplicationController
13
+ # permissions_policy do |policy|
14
+ # policy.geolocation "https://example.com"
15
+ # end
16
+ # end
17
+ #
18
+ # Options can be passed similar to `before_action`. For example, pass `only:
19
+ # :index` to override the header on the index action only:
20
+ #
21
+ # class PagesController < ApplicationController
22
+ # permissions_policy(only: :index) do |policy|
23
+ # policy.camera :self
24
+ # end
25
+ # end
26
+ #
27
+ def permissions_policy(**options, &block)
28
+ before_action(options) do
29
+ if block_given?
30
+ policy = request.permissions_policy.clone
31
+ instance_exec(policy, &block)
32
+ request.permissions_policy = policy
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionController # :nodoc:
6
+ module RateLimiting
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ # Applies a rate limit to all actions or those specified by the normal
11
+ # `before_action` filters with `only:` and `except:`.
12
+ #
13
+ # The maximum number of requests allowed is specified `to:` and constrained to
14
+ # the window of time given by `within:`.
15
+ #
16
+ # Rate limits are by default unique to the ip address making the request, but
17
+ # you can provide your own identity function by passing a callable in the `by:`
18
+ # parameter. It's evaluated within the context of the controller processing the
19
+ # request.
20
+ #
21
+ # Requests that exceed the rate limit are refused with a `429 Too Many Requests`
22
+ # response. You can specialize this by passing a callable in the `with:`
23
+ # parameter. It's evaluated within the context of the controller processing the
24
+ # request.
25
+ #
26
+ # Rate limiting relies on a backing `ActiveSupport::Cache` store and defaults to
27
+ # `config.action_controller.cache_store`, which itself defaults to the global
28
+ # `config.cache_store`. If you don't want to store rate limits in the same
29
+ # datastore as your general caches, you can pass a custom store in the `store`
30
+ # parameter.
31
+ #
32
+ # Examples:
33
+ #
34
+ # class SessionsController < ApplicationController
35
+ # rate_limit to: 10, within: 3.minutes, only: :create
36
+ # end
37
+ #
38
+ # class SignupsController < ApplicationController
39
+ # rate_limit to: 1000, within: 10.seconds,
40
+ # by: -> { request.domain }, with: -> { redirect_to busy_controller_url, alert: "Too many signups on domain!" }, only: :new
41
+ # end
42
+ #
43
+ # class APIController < ApplicationController
44
+ # RATE_LIMIT_STORE = ActiveSupport::Cache::RedisCacheStore.new(url: ENV["REDIS_URL"])
45
+ # rate_limit to: 10, within: 3.minutes, store: RATE_LIMIT_STORE
46
+ # end
47
+ def rate_limit(to:, within:, by: -> { request.remote_ip }, with: -> { head :too_many_requests }, store: cache_store, **options)
48
+ before_action -> { rate_limiting(to: to, within: within, by: by, with: with, store: store) }, **options
49
+ end
50
+ end
51
+
52
+ private
53
+ def rate_limiting(to:, within:, by:, with:, store:)
54
+ count = store.increment("rate-limit:#{controller_path}:#{instance_exec(&by)}", 1, expires_in: within)
55
+ if count && count > to
56
+ ActiveSupport::Notifications.instrument("rate_limit.action_controller", request: request) do
57
+ instance_exec(&with)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end