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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +86 -600
- data/MIT-LICENSE +1 -1
- data/README.rdoc +13 -14
- data/lib/abstract_controller/asset_paths.rb +5 -1
- data/lib/abstract_controller/base.rb +166 -136
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +126 -57
- data/lib/abstract_controller/collector.rb +13 -15
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +181 -132
- data/lib/abstract_controller/logger.rb +5 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
- data/lib/abstract_controller/rendering.rb +56 -56
- data/lib/abstract_controller/translation.rb +29 -15
- data/lib/abstract_controller/url_for.rb +15 -11
- data/lib/abstract_controller.rb +21 -5
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +154 -0
- data/lib/action_controller/base.rb +219 -155
- data/lib/action_controller/caching.rb +28 -68
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +35 -22
- data/lib/action_controller/metal/allow_browser.rb +119 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +259 -122
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +9 -5
- data/lib/action_controller/metal/data_streaming.rb +87 -104
- data/lib/action_controller/metal/default_headers.rb +21 -0
- data/lib/action_controller/metal/etag_with_flash.rb +22 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
- data/lib/action_controller/metal/exceptions.rb +71 -24
- data/lib/action_controller/metal/flash.rb +26 -19
- data/lib/action_controller/metal/head.rb +45 -36
- data/lib/action_controller/metal/helpers.rb +80 -64
- data/lib/action_controller/metal/http_authentication.rb +297 -244
- data/lib/action_controller/metal/implicit_render.rb +57 -9
- data/lib/action_controller/metal/instrumentation.rb +76 -64
- data/lib/action_controller/metal/live.rb +238 -176
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +177 -166
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +145 -118
- data/lib/action_controller/metal/permissions_policy.rb +38 -0
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +203 -64
- data/lib/action_controller/metal/renderers.rb +108 -65
- data/lib/action_controller/metal/rendering.rb +216 -56
- data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
- data/lib/action_controller/metal/rescue.rb +19 -21
- data/lib/action_controller/metal/streaming.rb +179 -138
- data/lib/action_controller/metal/strong_parameters.rb +1058 -382
- data/lib/action_controller/metal/testing.rb +11 -17
- data/lib/action_controller/metal/url_for.rb +37 -21
- data/lib/action_controller/metal.rb +236 -138
- data/lib/action_controller/railtie.rb +89 -11
- data/lib/action_controller/railties/helpers.rb +5 -1
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +425 -497
- data/lib/action_controller.rb +44 -22
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +119 -63
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +364 -0
- data/lib/action_dispatch/http/filter_parameters.rb +36 -34
- data/lib/action_dispatch/http/filter_redirect.rb +24 -12
- data/lib/action_dispatch/http/headers.rb +66 -31
- data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
- data/lib/action_dispatch/http/mime_type.rb +196 -136
- data/lib/action_dispatch/http/mime_types.rb +25 -7
- data/lib/action_dispatch/http/parameters.rb +97 -45
- data/lib/action_dispatch/http/permissions_policy.rb +187 -0
- data/lib/action_dispatch/http/rack_cache.rb +6 -0
- data/lib/action_dispatch/http/request.rb +299 -170
- data/lib/action_dispatch/http/response.rb +311 -160
- data/lib/action_dispatch/http/upload.rb +52 -23
- data/lib/action_dispatch/http/url.rb +201 -125
- data/lib/action_dispatch/journey/formatter.rb +110 -50
- data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
- data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
- data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
- data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
- data/lib/action_dispatch/journey/nodes/node.rb +100 -20
- data/lib/action_dispatch/journey/parser.rb +19 -17
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +14 -4
- data/lib/action_dispatch/journey/path/pattern.rb +79 -63
- data/lib/action_dispatch/journey/route.rb +108 -44
- data/lib/action_dispatch/journey/router/utils.rb +41 -29
- data/lib/action_dispatch/journey/router.rb +64 -57
- data/lib/action_dispatch/journey/routes.rb +23 -21
- data/lib/action_dispatch/journey/scanner.rb +28 -17
- data/lib/action_dispatch/journey/visitors.rb +100 -54
- data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
- data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
- data/lib/action_dispatch/journey.rb +7 -5
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +7 -6
- data/lib/action_dispatch/middleware/cookies.rb +471 -328
- data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
- data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
- data/lib/action_dispatch/middleware/debug_view.rb +73 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +143 -101
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/reloader.rb +10 -92
- data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
- data/lib/action_dispatch/middleware/request_id.rb +29 -15
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
- data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
- data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
- data/lib/action_dispatch/middleware/ssl.rb +134 -36
- data/lib/action_dispatch/middleware/stack.rb +109 -44
- data/lib/action_dispatch/middleware/static.rb +159 -90
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +7 -24
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +6 -6
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
- data/lib/action_dispatch/railtie.rb +44 -16
- data/lib/action_dispatch/request/session.rb +159 -69
- data/lib/action_dispatch/request/utils.rb +97 -23
- data/lib/action_dispatch/routing/endpoint.rb +11 -2
- data/lib/action_dispatch/routing/inspector.rb +195 -106
- data/lib/action_dispatch/routing/mapper.rb +1338 -955
- data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
- data/lib/action_dispatch/routing/redirection.rb +78 -51
- data/lib/action_dispatch/routing/route_set.rb +460 -374
- data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
- data/lib/action_dispatch/routing/url_for.rb +172 -124
- data/lib/action_dispatch/routing.rb +159 -158
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +84 -0
- data/lib/action_dispatch/system_testing/driver.rb +85 -0
- data/lib/action_dispatch/system_testing/server.rb +33 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
- data/lib/action_dispatch/testing/assertion_response.rb +48 -0
- data/lib/action_dispatch/testing/assertions/response.rb +71 -39
- data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
- data/lib/action_dispatch/testing/assertions.rb +9 -6
- data/lib/action_dispatch/testing/integration.rb +486 -306
- data/lib/action_dispatch/testing/request_encoder.rb +60 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +35 -22
- data/lib/action_dispatch/testing/test_request.rb +29 -34
- data/lib/action_dispatch/testing/test_response.rb +48 -15
- data/lib/action_dispatch.rb +82 -40
- data/lib/action_pack/gem_version.rb +8 -4
- data/lib/action_pack/version.rb +6 -2
- data/lib/action_pack.rb +21 -18
- metadata +146 -56
- data/lib/action_controller/caching/fragments.rb +0 -103
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +0 -27
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,26 +1,33 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
require
|
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
|
12
|
-
#
|
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
|
-
#
|
16
|
-
#
|
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
|
23
|
-
#
|
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
|
33
|
-
#
|
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
|
-
#
|
39
|
-
#
|
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
|
46
|
-
#
|
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
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
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
|
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,
|
63
|
-
#
|
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
|
69
|
-
# determine the wrapper key respectively. If both models don't exist,
|
70
|
-
#
|
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 ||
|
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
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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
|
-
|
156
|
-
|
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
|
170
|
-
#
|
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
|
-
#
|
173
|
-
#
|
174
|
-
# # enables the parameter wrapper for XML format
|
200
|
+
# wrap_parameters :person
|
201
|
+
# # wraps parameters into +params[:person]+ hash
|
175
202
|
#
|
176
|
-
#
|
177
|
-
#
|
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
|
-
#
|
180
|
-
#
|
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
|
-
#
|
184
|
-
#
|
210
|
+
# wrap_parameters false
|
211
|
+
# # disables parameters wrapping for this controller altogether.
|
185
212
|
#
|
186
|
-
#
|
187
|
-
#
|
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(:
|
228
|
+
options = options.merge(format: [])
|
204
229
|
when Symbol, String
|
205
|
-
options = options.merge(:
|
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
|
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
|
-
#
|
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
|
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
|
-
|
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
|
-
|
282
|
-
|
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
|