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,67 +1,119 @@
1
- require 'active_support/core_ext/hash/keys'
2
- require 'active_support/core_ext/hash/indifferent_access'
3
- require 'active_support/deprecation'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
4
 
5
5
  module ActionDispatch
6
6
  module Http
7
7
  module Parameters
8
- PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
8
+ extend ActiveSupport::Concern
9
9
 
10
- # Returns both GET and POST \parameters in a single hash.
11
- def parameters
12
- @env["action_dispatch.request.parameters"] ||= begin
13
- params = begin
14
- request_parameters.merge(query_parameters)
15
- rescue EOFError
16
- query_parameters.dup
17
- end
18
- params.merge!(path_parameters)
10
+ PARAMETERS_KEY = "action_dispatch.request.path_parameters"
11
+
12
+ DEFAULT_PARSERS = {
13
+ Mime[:json].symbol => -> (raw_post) {
14
+ data = ActiveSupport::JSON.decode(raw_post)
15
+ data.is_a?(Hash) ? data : { _json: data }
16
+ }
17
+ }
18
+
19
+ # Raised when raw data from the request cannot be parsed by the parser defined
20
+ # for request's content MIME type.
21
+ class ParseError < StandardError
22
+ def initialize(message = $!.message)
23
+ super(message)
19
24
  end
20
25
  end
21
- alias :params :parameters
22
26
 
23
- def path_parameters=(parameters) #:nodoc:
24
- @env.delete('action_dispatch.request.parameters')
25
- @env[PARAMETERS_KEY] = parameters
27
+ included do
28
+ class << self
29
+ # Returns the parameter parsers.
30
+ attr_reader :parameter_parsers
31
+ end
32
+
33
+ self.parameter_parsers = DEFAULT_PARSERS
26
34
  end
27
35
 
28
- def symbolized_path_parameters
29
- ActiveSupport::Deprecation.warn(
30
- '`symbolized_path_parameters` is deprecated. Please use `path_parameters`.'
31
- )
32
- path_parameters
36
+ module ClassMethods
37
+ # Configure the parameter parser for a given MIME type.
38
+ #
39
+ # It accepts a hash where the key is the symbol of the MIME type and the value
40
+ # is a proc.
41
+ #
42
+ # original_parsers = ActionDispatch::Request.parameter_parsers
43
+ # xml_parser = -> (raw_post) { Hash.from_xml(raw_post) || {} }
44
+ # new_parsers = original_parsers.merge(xml: xml_parser)
45
+ # ActionDispatch::Request.parameter_parsers = new_parsers
46
+ def parameter_parsers=(parsers)
47
+ @parameter_parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key }
48
+ end
33
49
  end
34
50
 
35
- # Returns a hash with the \parameters used to form the \path of the request.
36
- # Returned hash keys are strings:
51
+ # Returns both GET and POST parameters in a single hash.
52
+ def parameters
53
+ params = get_header("action_dispatch.request.parameters")
54
+ return params if params
55
+
56
+ params = begin
57
+ request_parameters.merge(query_parameters)
58
+ rescue EOFError
59
+ query_parameters.dup
60
+ end
61
+ params.merge!(path_parameters)
62
+ set_header("action_dispatch.request.parameters", params)
63
+ params
64
+ end
65
+ alias :params :parameters
66
+
67
+ def path_parameters=(parameters) # :nodoc:
68
+ delete_header("action_dispatch.request.parameters")
69
+
70
+ parameters = Request::Utils.set_binary_encoding(self, parameters, parameters[:controller], parameters[:action])
71
+ # If any of the path parameters has an invalid encoding then raise since it's
72
+ # likely to trigger errors further on.
73
+ Request::Utils.check_param_encoding(parameters)
74
+
75
+ set_header PARAMETERS_KEY, parameters
76
+ rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
77
+ raise ActionController::BadRequest.new("Invalid path parameters: #{e.message}")
78
+ end
79
+
80
+ # Returns a hash with the parameters used to form the path of the request.
81
+ # Returned hash keys are symbols:
37
82
  #
38
- # {'action' => 'my_action', 'controller' => 'my_controller'}
83
+ # { action: "my_action", controller: "my_controller" }
39
84
  def path_parameters
40
- @env[PARAMETERS_KEY] ||= {}
85
+ get_header(PARAMETERS_KEY) || set_header(PARAMETERS_KEY, {})
41
86
  end
42
87
 
43
- private
88
+ private
89
+ def parse_formatted_parameters(parsers)
90
+ return yield if content_length.zero? || content_mime_type.nil?
44
91
 
45
- # Convert nested Hash to HashWithIndifferentAccess.
46
- #
47
- def normalize_encode_params(params)
48
- case params
49
- when Hash
50
- if params.has_key?(:tempfile)
51
- UploadedFile.new(params)
52
- else
53
- params.each_with_object({}) do |(key, val), new_hash|
54
- new_hash[key] = if val.is_a?(Array)
55
- val.map! { |el| normalize_encode_params(el) }
56
- else
57
- normalize_encode_params(val)
58
- end
59
- end.with_indifferent_access
92
+ strategy = parsers.fetch(content_mime_type.symbol) { return yield }
93
+
94
+ begin
95
+ strategy.call(raw_post)
96
+ rescue # JSON or Ruby code block errors.
97
+ log_parse_error_once
98
+ raise ParseError, "Error occurred while parsing request parameters"
60
99
  end
61
- else
62
- params
63
100
  end
64
- end
101
+
102
+ def log_parse_error_once
103
+ @parse_error_logged ||= begin
104
+ parse_logger = logger || ActiveSupport::Logger.new($stderr)
105
+ parse_logger.debug <<~MSG.chomp
106
+ Error occurred while parsing request parameters.
107
+ Contents:
108
+
109
+ #{raw_post}
110
+ MSG
111
+ end
112
+ end
113
+
114
+ def params_parsers
115
+ ActionDispatch::Request.parameter_parsers
116
+ end
65
117
  end
66
118
  end
67
119
  end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/core_ext/object/deep_dup"
6
+
7
+ module ActionDispatch # :nodoc:
8
+ # # Action Dispatch PermissionsPolicy
9
+ #
10
+ # Configures the HTTP
11
+ # [Feature-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy)
12
+ # response header to specify which browser features the current
13
+ # document and its iframes can use.
14
+ #
15
+ # Example global policy:
16
+ #
17
+ # Rails.application.config.permissions_policy do |policy|
18
+ # policy.camera :none
19
+ # policy.gyroscope :none
20
+ # policy.microphone :none
21
+ # policy.usb :none
22
+ # policy.fullscreen :self
23
+ # policy.payment :self, "https://secure.example.com"
24
+ # end
25
+ #
26
+ # The Feature-Policy header has been renamed to Permissions-Policy. The
27
+ # Permissions-Policy requires a different implementation and isn't yet supported
28
+ # by all browsers. To avoid having to rename this middleware in the future we
29
+ # use the new name for the middleware but keep the old header name and
30
+ # implementation for now.
31
+ class PermissionsPolicy
32
+ class Middleware
33
+ def initialize(app)
34
+ @app = app
35
+ end
36
+
37
+ def call(env)
38
+ _, headers, _ = response = @app.call(env)
39
+
40
+ return response if policy_present?(headers)
41
+
42
+ request = ActionDispatch::Request.new(env)
43
+
44
+ if policy = request.permissions_policy
45
+ headers[ActionDispatch::Constants::FEATURE_POLICY] = policy.build(request.controller_instance)
46
+ end
47
+
48
+ if policy_empty?(policy)
49
+ headers.delete(ActionDispatch::Constants::FEATURE_POLICY)
50
+ end
51
+
52
+ response
53
+ end
54
+
55
+ private
56
+ def policy_present?(headers)
57
+ headers[ActionDispatch::Constants::FEATURE_POLICY]
58
+ end
59
+
60
+ def policy_empty?(policy)
61
+ policy&.directives&.empty?
62
+ end
63
+ end
64
+
65
+ module Request
66
+ POLICY = "action_dispatch.permissions_policy"
67
+
68
+ def permissions_policy
69
+ get_header(POLICY)
70
+ end
71
+
72
+ def permissions_policy=(policy)
73
+ set_header(POLICY, policy)
74
+ end
75
+ end
76
+
77
+ MAPPINGS = {
78
+ self: "'self'",
79
+ none: "'none'",
80
+ }.freeze
81
+
82
+ # List of available permissions can be found at
83
+ # https://github.com/w3c/webappsec-permissions-policy/blob/main/features.md#policy-controlled-features
84
+ DIRECTIVES = {
85
+ accelerometer: "accelerometer",
86
+ ambient_light_sensor: "ambient-light-sensor",
87
+ autoplay: "autoplay",
88
+ camera: "camera",
89
+ encrypted_media: "encrypted-media",
90
+ fullscreen: "fullscreen",
91
+ geolocation: "geolocation",
92
+ gyroscope: "gyroscope",
93
+ hid: "hid",
94
+ idle_detection: "idle-detection",
95
+ magnetometer: "magnetometer",
96
+ microphone: "microphone",
97
+ midi: "midi",
98
+ payment: "payment",
99
+ picture_in_picture: "picture-in-picture",
100
+ screen_wake_lock: "screen-wake-lock",
101
+ serial: "serial",
102
+ sync_xhr: "sync-xhr",
103
+ usb: "usb",
104
+ web_share: "web-share",
105
+ }.freeze
106
+
107
+ private_constant :MAPPINGS, :DIRECTIVES
108
+
109
+ attr_reader :directives
110
+
111
+ def initialize
112
+ @directives = {}
113
+ yield self if block_given?
114
+ end
115
+
116
+ def initialize_copy(other)
117
+ @directives = other.directives.deep_dup
118
+ end
119
+
120
+ DIRECTIVES.each do |name, directive|
121
+ define_method(name) do |*sources|
122
+ if sources.first
123
+ @directives[directive] = apply_mappings(sources)
124
+ else
125
+ @directives.delete(directive)
126
+ end
127
+ end
128
+ end
129
+
130
+ def build(context = nil)
131
+ build_directives(context).compact.join("; ")
132
+ end
133
+
134
+ private
135
+ def apply_mappings(sources)
136
+ sources.map do |source|
137
+ case source
138
+ when Symbol
139
+ apply_mapping(source)
140
+ when String, Proc
141
+ source
142
+ else
143
+ raise ArgumentError, "Invalid HTTP permissions policy source: #{source.inspect}"
144
+ end
145
+ end
146
+ end
147
+
148
+ def apply_mapping(source)
149
+ MAPPINGS.fetch(source) do
150
+ raise ArgumentError, "Unknown HTTP permissions policy source mapping: #{source.inspect}"
151
+ end
152
+ end
153
+
154
+ def build_directives(context)
155
+ @directives.map do |directive, sources|
156
+ if sources.is_a?(Array)
157
+ "#{directive} #{build_directive(sources, context).join(' ')}"
158
+ elsif sources
159
+ directive
160
+ else
161
+ nil
162
+ end
163
+ end
164
+ end
165
+
166
+ def build_directive(sources, context)
167
+ sources.map { |source| resolve_source(source, context) }
168
+ end
169
+
170
+ def resolve_source(source, context)
171
+ case source
172
+ when String
173
+ source
174
+ when Symbol
175
+ source.to_s
176
+ when Proc
177
+ if context.nil?
178
+ raise RuntimeError, "Missing context for the dynamic permissions policy source: #{source.inspect}"
179
+ else
180
+ context.instance_exec(&source)
181
+ end
182
+ else
183
+ raise RuntimeError, "Unexpected permissions policy source: #{source.inspect}"
184
+ end
185
+ end
186
+ end
187
+ end
@@ -1,3 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :enddoc:
4
+
5
+ # :markup: markdown
6
+
1
7
  require "rack/cache"
2
8
  require "rack/cache/context"
3
9
  require "active_support/cache"