roda 3.83.0 → 3.85.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/lib/roda/plugins/Integer_matcher_max.rb +9 -8
  3. data/lib/roda/plugins/_optimized_matching.rb +2 -2
  4. data/lib/roda/plugins/_symbol_class_matchers.rb +107 -0
  5. data/lib/roda/plugins/capture_erb.rb +6 -5
  6. data/lib/roda/plugins/class_matchers.rb +91 -14
  7. data/lib/roda/plugins/hsts.rb +35 -0
  8. data/lib/roda/plugins/placeholder_string_matchers.rb +4 -0
  9. data/lib/roda/plugins/public.rb +1 -1
  10. data/lib/roda/plugins/render.rb +1 -1
  11. data/lib/roda/plugins/symbol_matchers.rb +70 -15
  12. data/lib/roda/request.rb +16 -13
  13. data/lib/roda/response.rb +1 -1
  14. data/lib/roda/version.rb +1 -1
  15. data/lib/roda.rb +7 -0
  16. metadata +5 -179
  17. data/CHANGELOG +0 -691
  18. data/README.rdoc +0 -1136
  19. data/doc/conventions.rdoc +0 -177
  20. data/doc/release_notes/3.0.0.txt +0 -84
  21. data/doc/release_notes/3.1.0.txt +0 -24
  22. data/doc/release_notes/3.10.0.txt +0 -132
  23. data/doc/release_notes/3.11.0.txt +0 -54
  24. data/doc/release_notes/3.12.0.txt +0 -19
  25. data/doc/release_notes/3.13.0.txt +0 -38
  26. data/doc/release_notes/3.14.0.txt +0 -36
  27. data/doc/release_notes/3.14.1.txt +0 -43
  28. data/doc/release_notes/3.15.0.txt +0 -21
  29. data/doc/release_notes/3.16.0.txt +0 -52
  30. data/doc/release_notes/3.17.0.txt +0 -62
  31. data/doc/release_notes/3.18.0.txt +0 -170
  32. data/doc/release_notes/3.19.0.txt +0 -229
  33. data/doc/release_notes/3.2.0.txt +0 -22
  34. data/doc/release_notes/3.20.0.txt +0 -7
  35. data/doc/release_notes/3.21.0.txt +0 -5
  36. data/doc/release_notes/3.22.0.txt +0 -24
  37. data/doc/release_notes/3.23.0.txt +0 -28
  38. data/doc/release_notes/3.24.0.txt +0 -14
  39. data/doc/release_notes/3.25.0.txt +0 -12
  40. data/doc/release_notes/3.26.0.txt +0 -15
  41. data/doc/release_notes/3.27.0.txt +0 -15
  42. data/doc/release_notes/3.28.0.txt +0 -13
  43. data/doc/release_notes/3.29.0.txt +0 -15
  44. data/doc/release_notes/3.3.0.txt +0 -291
  45. data/doc/release_notes/3.30.0.txt +0 -14
  46. data/doc/release_notes/3.31.0.txt +0 -11
  47. data/doc/release_notes/3.32.0.txt +0 -42
  48. data/doc/release_notes/3.33.0.txt +0 -8
  49. data/doc/release_notes/3.34.0.txt +0 -17
  50. data/doc/release_notes/3.35.0.txt +0 -12
  51. data/doc/release_notes/3.36.0.txt +0 -17
  52. data/doc/release_notes/3.37.0.txt +0 -42
  53. data/doc/release_notes/3.38.0.txt +0 -5
  54. data/doc/release_notes/3.39.0.txt +0 -16
  55. data/doc/release_notes/3.4.0.txt +0 -24
  56. data/doc/release_notes/3.40.0.txt +0 -24
  57. data/doc/release_notes/3.41.0.txt +0 -9
  58. data/doc/release_notes/3.42.0.txt +0 -21
  59. data/doc/release_notes/3.43.0.txt +0 -34
  60. data/doc/release_notes/3.44.0.txt +0 -23
  61. data/doc/release_notes/3.45.0.txt +0 -22
  62. data/doc/release_notes/3.46.0.txt +0 -19
  63. data/doc/release_notes/3.47.0.txt +0 -13
  64. data/doc/release_notes/3.48.0.txt +0 -10
  65. data/doc/release_notes/3.49.0.txt +0 -18
  66. data/doc/release_notes/3.5.0.txt +0 -31
  67. data/doc/release_notes/3.50.0.txt +0 -21
  68. data/doc/release_notes/3.51.0.txt +0 -20
  69. data/doc/release_notes/3.52.0.txt +0 -20
  70. data/doc/release_notes/3.53.0.txt +0 -14
  71. data/doc/release_notes/3.54.0.txt +0 -48
  72. data/doc/release_notes/3.55.0.txt +0 -12
  73. data/doc/release_notes/3.56.0.txt +0 -33
  74. data/doc/release_notes/3.57.0.txt +0 -34
  75. data/doc/release_notes/3.58.0.txt +0 -16
  76. data/doc/release_notes/3.59.0.txt +0 -17
  77. data/doc/release_notes/3.6.0.txt +0 -21
  78. data/doc/release_notes/3.60.0.txt +0 -56
  79. data/doc/release_notes/3.61.0.txt +0 -24
  80. data/doc/release_notes/3.62.0.txt +0 -41
  81. data/doc/release_notes/3.63.0.txt +0 -36
  82. data/doc/release_notes/3.64.0.txt +0 -26
  83. data/doc/release_notes/3.65.0.txt +0 -12
  84. data/doc/release_notes/3.66.0.txt +0 -23
  85. data/doc/release_notes/3.67.0.txt +0 -25
  86. data/doc/release_notes/3.68.0.txt +0 -21
  87. data/doc/release_notes/3.69.0.txt +0 -33
  88. data/doc/release_notes/3.7.0.txt +0 -123
  89. data/doc/release_notes/3.70.0.txt +0 -19
  90. data/doc/release_notes/3.71.0.txt +0 -33
  91. data/doc/release_notes/3.72.0.txt +0 -48
  92. data/doc/release_notes/3.73.0.txt +0 -33
  93. data/doc/release_notes/3.74.0.txt +0 -28
  94. data/doc/release_notes/3.75.0.txt +0 -19
  95. data/doc/release_notes/3.76.0.txt +0 -18
  96. data/doc/release_notes/3.77.0.txt +0 -8
  97. data/doc/release_notes/3.78.0.txt +0 -99
  98. data/doc/release_notes/3.79.0.txt +0 -148
  99. data/doc/release_notes/3.8.0.txt +0 -27
  100. data/doc/release_notes/3.80.0.txt +0 -31
  101. data/doc/release_notes/3.81.0.txt +0 -24
  102. data/doc/release_notes/3.82.0.txt +0 -43
  103. data/doc/release_notes/3.83.0.txt +0 -6
  104. data/doc/release_notes/3.9.0.txt +0 -67
@@ -1,33 +0,0 @@
1
- = New Feature
2
-
3
- * The symbol_matcher method in the symbol_matchers plugin now
4
- supports a block to allow for type conversion of matched
5
- segments:
6
-
7
- symbol_matcher(:date, /(\d\d\d\d)-(\d\d)-(\d\d)/) do |y, m, d|
8
- [Date.new(y.to_i, m.to_i, d.to_i)]
9
- end
10
-
11
- route do |r|
12
- r.on :date do |date|
13
- # date is an instance of Date
14
- end
15
- end
16
-
17
- As shown above, the block should return an array of objects to yield
18
- to the match block.
19
-
20
- If you have a segment match the passed regexp, but decide during block
21
- processing that you do not want to treat it as a match, you can have the
22
- block return nil or false. This is useful if you want to make sure you
23
- are using valid data:
24
-
25
- symbol_matcher(:date, /(\d\d\d\d)-(\d\d)-(\d\d)/) do |y, m, d|
26
- y = y.to_i
27
- m = m.to_i
28
- d = d.to_i
29
- [Date.new(y, m, d)] if Date.valid_date?(y, m, d)
30
- end
31
-
32
- When providing a block when using the symbol_matchers method, that
33
- symbol may not work with the params_capturing plugin.
@@ -1,123 +0,0 @@
1
- = New Features
2
-
3
- * A content_security_policy plugin has been added for setting up an
4
- appropriate Content-Security-Policy header. To configure the
5
- default policy, load the plugin with a block:
6
-
7
- plugin :content_security_policy do |csp|
8
- csp.default_src :none
9
- csp.img_src :self
10
- csp.style_src :self, 'fonts.googleapis.com'
11
- csp.script_src :self
12
- csp.font_src :self, 'fonts.gstatic.com'
13
- csp.form_action :self
14
- csp.base_uri :none
15
- csp.frame_ancestors :none
16
- csp.block_all_mixed_content
17
- end
18
-
19
- It's recommended that use use a default_src of :none at the top
20
- of the policy, then explicitly change other settings (e.g. img_src)
21
- when you want to allow content.
22
-
23
- Anywhere in the routing tree, you can use the content_security_policy
24
- method to override the default policy. You can pass this method a
25
- block:
26
-
27
- r.get 'foo' do
28
- content_security_policy do |csp|
29
- csp.object_src :self
30
- csp.add_style_src 'bar.com'
31
- end
32
- # ...
33
- end
34
-
35
- Or just call a method on it:
36
-
37
- r.get 'foo' do
38
- content_security_policy.script_src :self, 'example.com', [:nonce, 'foobarbaz']
39
- # ...
40
- end
41
-
42
- The following methods exist for configuring the content security policy, they set
43
- the appropriate directive, with the underscores replaced by a dash.
44
-
45
- * base_uri
46
- * child_src
47
- * connect_src
48
- * default_src
49
- * font_src
50
- * form_action
51
- * frame_ancestors
52
- * frame_src
53
- * img_src
54
- * manifest_src
55
- * media_src
56
- * object_src
57
- * plugin_types
58
- * report_uri
59
- * require_sri_for
60
- * sandbox
61
- * script_src
62
- * style_src
63
- * worker_src
64
-
65
- All of these methods support any number of arguments, and each argument
66
- should be one of the following types:
67
-
68
- String :: used verbatim
69
- Symbol :: Substitutes underscore with dash and surrounds with single
70
- quotes
71
- Array :: only accepts 2 element arrays, joins elements with a dash
72
- and surrounds them with single quotes
73
-
74
- Example:
75
-
76
- content_security_policy.script_src :self, :unsafe_eval,
77
- 'example.com', [:nonce, 'foobarbaz']
78
- # script-src 'self' 'unsafe-eval' example.com 'nonce-foobarbaz';
79
-
80
- When calling a method with no arguments, the setting is removed from
81
- the policy instead of being left empty, since all of these setting
82
- require at least one value. Likewise, if the policy does not have
83
- any settings, the header will not be added.
84
-
85
- Calling the method overrides any previous setting. Each of the
86
- methods has a add_* method (e.g. add_script_src) for appending to the
87
- current setting, and a get_* method (e.g. get_script_src) for
88
- retrieving the current value of the setting, or nil if it is not
89
- defined.
90
-
91
- content_security_policy.script_src :self, :unsafe_eval
92
- # script-src 'self' 'unsafe-eval';
93
- content_security_policy.add_script_src 'example.com', [:nonce, 'foobarbaz']
94
- # script-src 'self' 'unsafe-eval' example.com 'nonce-foobarbaz';
95
-
96
- content_security_policy.get_script_src 'example.com', [:nonce, 'foobarbaz']
97
- # => [:self, :unsafe_eval, 'example.com', [:nonce, 'foobarbaz']]
98
-
99
- The clear method can be used to remove all settings from the policy.
100
-
101
- The following methods to set boolean directives are also defined:
102
-
103
- * block_all_mixed_content
104
- * upgrade_insecure_requests
105
-
106
- Calling these methods will turn on the related setting. To turn the
107
- setting off again, you can call them with a false argument (e.g.
108
- block_all_mixed_content(false)). Each method also an *? method
109
- (e.g. block_all_mixed_content?) for returning whether the setting is
110
- currently enabled.
111
-
112
- Likewise there is also a report_only method for turning on report
113
- only mode (the default is enforcement mode), or turning off report
114
- only mode if a false argument is given. Also, there is a
115
- report_only? method for returning whether report only mode is
116
- enabled. In report only mode, the Content-Security-Policy-Report-Only
117
- header is used.
118
-
119
- = Other Improvements
120
-
121
- * The response_request plugin now integrates with the error_handler and
122
- class_level_routing plugins. Those plugins now reinitialize the
123
- current response object instead of creating a new response object.
@@ -1,19 +0,0 @@
1
- = New Features
2
-
3
- * A plain_hash_response_headers plugin has been added. On Rack 3,
4
- this changes Roda to use a plain hash for response headers (as it
5
- does on Rack 2), instead of using Rack::Headers (the default on
6
- Rack 3). For a minimal app, using this plugin can almost double
7
- the performance on Rack 3. Before using this plugin, you should
8
- make sure that all response headers set explictly in your
9
- application are already lower-case.
10
-
11
- = Improvements
12
-
13
- * Roda now natively uses lower-case for all response headers set
14
- implicitly when using Rack 3. Previously, Roda used mixed-case
15
- response headers and had Rack::Headers handle the conversion to
16
- lower-case (Rack 3 requires lower-case response headers). Note
17
- that Rack::Headers is still used for response headers by default
18
- on Rack 3, as applications may not have converted to using
19
- lower-case response headers.
@@ -1,33 +0,0 @@
1
- = New Feature
2
-
3
- * A match_hook_args plugin has been added. This is similar to the
4
- existing match_hook plugin, but passes through the matchers and
5
- block arguments (values yielded to the match block). Example:
6
-
7
- plugin :match_hook_args
8
-
9
- add_match_hook do |matchers, block_args|
10
- logger.debug("matchers: #{matchers.inspect}. #{block_args.inspect} yielded.")
11
- end
12
-
13
- # Term is an implicit matcher used for terminating matches, and
14
- # will be included in the array of matchers yielded to the match hook
15
- # if a terminating match is used.
16
- term = self.class::RodaRequest::TERM
17
-
18
- route do |r|
19
- r.root do
20
- # for a request for /
21
- # matchers: nil, block_args: nil
22
- end
23
-
24
- r.on 'a', ['b', 'c'], Integer do |segment, id|
25
- # for a request for /a/b/1
26
- # matchers: ["a", ["b", "c"], Integer], block_args: ["b", 1]
27
- end
28
-
29
- r.get 'd' do
30
- # for a request for /d
31
- # matchers: ["d", term], block_args: []
32
- end
33
- end
@@ -1,48 +0,0 @@
1
- = New Features
2
-
3
- * An invalid_request_body plugin has been added for allowing custom
4
- handling of invalid request bodies. Roda uses Rack's request body
5
- parsing, and by default invalid request bodies can result in
6
- different exceptions based on how the body is invalid and which
7
- version of Rack is in use.
8
-
9
- If you want to treat an invalid request body as the submission of
10
- no parameters, you can use the :empty_hash argument when loading
11
- the plugin:
12
-
13
- plugin :invalid_request_body, :empty_hash
14
-
15
- If you want to return a empty 400 (Bad Request) response if an
16
- invalid request body is submitted, you can use the :empty_400
17
- argument when loading the plugin:
18
-
19
- plugin :invalid_request_body, :empty_400
20
-
21
- If you want to raise a Roda::RodaPlugins::InvalidRequestBody::Error
22
- exception if an invalid request body is submitted (which makes it
23
- easier to handle these exceptions when using the error_handler
24
- plugin), you can use the :raise argument when loading the plugin:
25
-
26
- plugin :invalid_request_body, :raise
27
-
28
- For custom behavior, you can pass a block when loading the plugin
29
- The block is called with the exception Rack raised when parsing the
30
- body. The block will be used to define a method in the application's
31
- RodaRequest class. It can either return a hash of parameters, or
32
- you can raise a different exception, or you can halt processing and
33
- return a response:
34
-
35
- plugin :invalid_request_body do |exception|
36
- # To treat the exception raised as a submitted parameter
37
- {body_error: exception}
38
- end
39
-
40
- = Other Improvements
41
-
42
- * When using the check_arity: :warn Roda option, Roda now correctly
43
- warns when defining a method that expects a single argument when
44
- the provided block requires multiple arguments.
45
-
46
- * The match_hooks plugin is now implemented using the match_hook_args
47
- plugin, simplifying the implementation. This change should be
48
- transparent unless you were reaching into the internals.
@@ -1,33 +0,0 @@
1
- = New Features
2
-
3
- * The middleware plugin now accepts a :next_if_not_found option.
4
- This allows the middleware plugin to pass the request to the next
5
- application if the current application handles the request but
6
- ends up calling the not_found handler. With the following
7
- middleware:
8
-
9
- class Mid < Roda
10
- plugin :middleware
11
-
12
- route do |r|
13
- r.on "foo" do
14
- r.get "bar" do
15
- 'bar'
16
- end
17
- end
18
- end
19
- end
20
-
21
- Requests for /x would be forwarded to the next application, since
22
- the application doesn't handle the request, but requests for /foo/x
23
- would not be, because the middleware is partially handling the
24
- request in the r.on "foo" block. With the :next_if_not_found
25
- option, only requests for /foo/bar would be handled by the
26
- middleware, and all other requests would be forwarded to the next
27
- application.
28
-
29
- = Other Improvements
30
-
31
- * The sessions and route_csrf plugins no longer depend on the base64
32
- library. base64 will be removed from Ruby's standard library
33
- starting in Ruby 3.4.
@@ -1,28 +0,0 @@
1
- = New Features
2
-
3
- * A redirect_http_to_https plugin has been added, redirecting HTTP
4
- requests to the same path on an HTTPS site. Using the routing tree,
5
- you can control where to do the redirection, which allows you to
6
- easily have part of your site accessible via HTTP, with sensitive
7
- sections requiring HTTPS:
8
-
9
- plugin :redirect_http_to_https
10
-
11
- route do |r|
12
- # routes available via both HTTP and HTTPS
13
- r.redirect_http_to_https
14
- # routes available only via HTTPS
15
- end
16
-
17
- If you want to redirect to HTTPS for all routes in the routing tree, you
18
- can have r.redirect_http_to_https as the very first method call in the
19
- routing tree. Note that in Roda it is possible to handle routing before
20
- the normal routing tree using before hooks. The static_routing and
21
- heartbeat plugins use this feature. If you would like to handle routes
22
- before the normal routing tree, you can setup a before hook:
23
-
24
- plugin :hooks
25
-
26
- before do
27
- request.redirect_http_to_https
28
- end
@@ -1,19 +0,0 @@
1
- = New Features
2
-
3
- * A cookie_flags plugin has been added, for overriding, warning, or
4
- raising for incorrect cookie flags. The plugin by default checks
5
- whether the secure, httponly, and samesite=strict flags are set.
6
- The default behavior is to add the appropriate flags if they are
7
- not set, and change the samesite flag to strict if it is set to
8
- something else. You can configure the flag checking behavior
9
- via the :httponly, :same_site, and :secure options.
10
-
11
- You can configure the action the plugin takes via the :action option.
12
- The default action is to modify the flags, but the :action option can
13
- be set to :raise, :warn, or :warn_and_modify to override the behavior.
14
-
15
- The recommended way to use the plugin is to use it during testing,
16
- and specify action: :raise, so you can catch places where cookies
17
- are set with the wrong flags. Then you can fix those places to
18
- use the correct flags, which is better than relying on the plugin
19
- at runtime in production to fix incorrect flags.
@@ -1,18 +0,0 @@
1
- = New Features
2
-
3
- * A break plugin has been added, allowing you to use break from
4
- inside a routing block and continue routing after the block. This
5
- offers the same feature as the pass plugin, but using the standard
6
- break keyword instead of the r.pass method.
7
-
8
- * The error_mail and error_email features now both accept a :filter
9
- plugin option. The value should respond to call with two arguments.
10
- The first arguments is the key, and the second is the value, and
11
- should return a truthy value if the value should be filtered. This
12
- will be used for filtering parameter values, ENV values, and session
13
- values in the generated emails.
14
-
15
- = Other Improvements
16
-
17
- * On Ruby 3.3+, the middleware plugin sets a temporary class name for
18
- the created middleware, based on the class name of the Roda app.
@@ -1,8 +0,0 @@
1
- = New Features
2
-
3
- * The route_csrf plugin now supports formaction/formmethod attributes
4
- in forms. A csrf_formaction_tag method has been added for creating
5
- a hidden input for a particular path and method. When a form is
6
- submitted, the check_csrf! method will fix check for a path-specific
7
- csrf token (set by the hidden tag added by the csrf_formaction_tag
8
- method), before checking for the default csrf token.
@@ -1,99 +0,0 @@
1
- = New Features
2
-
3
- * A permissions_policy plugin has been added that allows you to easily set a
4
- Permissions-Policy header for the application, which browsers can use to
5
- determine whether to allow specific functionality on the returned page
6
- (mainly related to which JavaScript APIs the page is allowed to use).
7
-
8
- You would generally call the plugin with a block to set the default policy:
9
-
10
- plugin :permissions_policy do |pp|
11
- pp.camera :none
12
- pp.fullscreen :self
13
- pp.clipboard_read :self, 'https://example.com'
14
- end
15
-
16
- Then, anywhere in the routing tree, you can customize the policy for just that
17
- branch or action using the same block syntax:
18
-
19
- r.get 'foo' do
20
- permissions_policy do |pp|
21
- pp.camera :self
22
- end
23
- # ...
24
- end
25
-
26
- In addition to using a block, you can also call methods on the object returned
27
- by the method:
28
-
29
- r.get 'foo' do
30
- permissions_policy.camera :self
31
- # ...
32
- end
33
-
34
- You can use the :default plugin option to set the default for all settings.
35
- For example, to disallow all access for each setting by default:
36
-
37
- plugin :permissions_policy, default: :none
38
-
39
- The following methods are available for configuring the permissions policy,
40
- which specify the setting (substituting _ with -):
41
-
42
- * accelerometer
43
- * ambient_light_sensor
44
- * autoplay
45
- * bluetooth
46
- * camera
47
- * clipboard_read
48
- * clipboard_write
49
- * display_capture
50
- * encrypted_media
51
- * fullscreen
52
- * geolocation
53
- * gyroscope
54
- * hid
55
- * idle_detection
56
- * keyboard_map
57
- * magnetometer
58
- * microphone
59
- * midi
60
- * payment
61
- * picture_in_picture
62
- * publickey_credentials_get
63
- * screen_wake_lock
64
- * serial
65
- * sync_xhr
66
- * usb
67
- * web_share
68
- * window_management
69
-
70
- All of these methods support any number of arguments, and each argument should
71
- be one of the following values:
72
-
73
- :all :: Grants permission to all domains (must be only argument)
74
- :none :: Does not allow permission at all (must be only argument)
75
- :self :: Allows feature in current document and any nested browsing contexts
76
- that use the same domain as the current document.
77
- :src :: Allows feature in current document and any nested browsing contexts
78
- that use the same domain as the src of the iframe.
79
- String :: Specifies origin domain where access is allowed
80
-
81
- When calling a method with no arguments, the setting is removed from the policy instead
82
- of being left empty, since all of these setting require at least one value. Likewise,
83
- if the policy does not have any settings, the header will not be added.
84
-
85
- Calling the method overrides any previous setting. Each of the methods has +add_*+ and
86
- +get_*+ methods defined. The +add_*+ method appends to any existing setting, and the +get_*+ method
87
- returns the current value for the setting (this will be +:all+ if all domains are allowed, or
88
- any array of strings/:self/:src).
89
-
90
- permissions_policy.fullscreen :self, 'https://example.com'
91
- # fullscreen (self "https://example.com")
92
-
93
- permissions_policy.add_fullscreen 'https://*.example.com'
94
- # fullscreen (self "https://example.com" "https://*.example.com")
95
-
96
- permissions_policy.get_fullscreen
97
- # => [:self, "https://example.com", "https://*.example.com"]
98
-
99
- The clear method can be used to remove all settings from the policy.
@@ -1,148 +0,0 @@
1
- = New Features
2
-
3
- * The hmac_paths plugin allows protection of paths using an HMAC. This can be used
4
- to prevent users enumerating paths, since only paths with valid HMACs will be
5
- respected.
6
-
7
- To use the plugin, you must provide a :secret option. This sets the secret for
8
- the HMACs. Make sure to keep this value secret, as this plugin does not provide
9
- protection against users who know the secret value. The secret must be at least
10
- 32 bytes.
11
-
12
- plugin :hmac_paths, secret: 'some-secret-value-with-at-least-32-bytes'
13
-
14
- To generate a valid HMAC path, you call the hmac_path method:
15
-
16
- hmac_path('/widget/1')
17
- # => "/0c2feaefdfc80cc73da19b060c713d4193c57022815238c6657ce2d99b5925eb/0/widget/1"
18
-
19
- The first segment in the returned path is the HMAC. The second segment is flags for
20
- the type of paths (see below), and the rest of the path is as given.
21
-
22
- To protect a path or any subsection in the routing tree, you wrap the related code
23
- in an +r.hmac_path+ block.
24
-
25
- route do |r|
26
- r.hmac_path do
27
- r.get 'widget', Integer do |widget_id|
28
- # ...
29
- end
30
- end
31
- end
32
-
33
- If first segment of the remaining path contains a valid HMAC for the rest of the path (considering
34
- the flags), then r.hmac_path will match and yield to the block, and routing continues inside
35
- the block with the HMAC and flags segments removed.
36
-
37
- In the above example, if you provide a user a link for widget with ID 1, there is no way
38
- for them to guess the valid path for the widget with ID 2, preventing a user from
39
- enumerating widgets, without relying on custom access control. Users can only access
40
- paths that have been generated by the application and provided to them, either directly
41
- or indirectly.
42
-
43
- In the above example, r.hmac_path is used at the root of the routing tree. If you
44
- would like to call it below the root of the routing tree, it works correctly, but you
45
- must pass hmac_path the :root option specifying where r.hmac_paths will be called from.
46
- Consider this example:
47
-
48
- route do |r|
49
- r.on 'widget' do
50
- r.hmac_path do
51
- r.get Integer do |widget_id|
52
- # ...
53
- end
54
- end
55
- end
56
-
57
- r.on 'foobar' do
58
- r.hmac_path do
59
- r.get Integer do |foobar_id|
60
- # ...
61
- end
62
- end
63
- end
64
- end
65
-
66
- For security reasons, the hmac_path plugin does not allow an HMAC path designed for
67
- widgets to be a valid match in the r.hmac_path call inside the "r.on 'foobar'"
68
- block, preventing users who have a valid HMAC for a widget from looking at the page for
69
- a foobar with the same ID. When generating HMAC paths where the matching r.hmac_path
70
- call is not at the root of the routing tree, you must pass the :root option:
71
-
72
- hmac_path('/1', root: '/widget')
73
- # => "/widget/daccafce3ce0df52e5ce774626779eaa7286085fcbde1e4681c74175ff0bbacd/0/1"
74
-
75
- hmac_path('/1', root: '/foobar')
76
- # => "/foobar/c5fdaf482771d4f9f38cc13a1b2832929026a4ceb05e98ed6a0cd5a00bf180b7/0/1"
77
-
78
- Note how the HMAC changes even though the path is the same.
79
-
80
- In addition to the +:root+ option, there are additional options that further constrain
81
- use of the generated paths.
82
-
83
- The :method option creates a path that can only be called with a certain request
84
- method:
85
-
86
- hmac_path('/widget/1', method: :get)
87
- # => "/d38c1e634ecf9a3c0ab9d0832555b035d91b35069efcbf2670b0dfefd4b62fdd/m/widget/1"
88
-
89
- Note how this results in a different HMAC than the original hmac_path('/widget/1')
90
- call. This sets the flags segment to "m", which means r.hmac_path will consider the
91
- request mehod when checking the HMAC, and will only match if the provided request method
92
- is GET. This allows you to provide a user the ability to submit a GET request for the
93
- underlying path, without providing them the ability to submit a POST request for the
94
- underlying path, with no other access control.
95
-
96
- The :params option accepts a hash of params, converts it into a query string, and
97
- includes the query string in the returned path. It sets the flags segment to +p+, which
98
- means r.hmac_path will check for that exact query string. Requests with an empty query
99
- string or a different string will not match.
100
-
101
- hmac_path('/widget/1', params: {foo: 'bar'})
102
- # => "/fe8d03f9572d5af6c2866295bd3c12c2ea11d290b1cbd016c3b68ee36a678139/p/widget/1?foo=bar"
103
-
104
- For GET requests, which cannot have request bodies, that is sufficient to ensure that the
105
- submitted params are exactly as specified. However, POST requests can have request bodies,
106
- and request body params override query string params in r.params. So if you are using
107
- this for POST requests (or other HTTP verbs that can have request bodies), use r.GET
108
- instead of r.params to specifically check query string parameters.
109
-
110
- You can use +:root+, +:method+, and +:params+ at the same time:
111
-
112
- hmac_path('/1', root: '/widget', method: :get, params: {foo: 'bar'})
113
- # => "/widget/9169af1b8f40c62a1c2bb15b1b377c65bda681b8efded0e613a4176387468c15/mp/1?foo=bar"
114
-
115
- This gives you a path only valid for a GET request with a root of "/widget" and
116
- a query string of "foo=bar".
117
-
118
- To handle secret rotation, you can provide an :old_secret option when loading the
119
- plugin.
120
-
121
- plugin :hmac_paths, secret: 'some-secret-value-with-at-least-32-bytes',
122
- old_secret: 'previous-secret-value-with-at-least-32-bytes'
123
-
124
- This will use :secret for constructing new paths, but will respect paths generated by
125
- :old_secret.
126
-
127
- = Other Improvements
128
-
129
- * When not using cached templates in the render plugin, the render plugin
130
- now has better handling when a template is modified and results in an
131
- error. Previously, the error would be raised on the first request after
132
- the template modification, but subsequent requests would use the
133
- previous template value. The render plugin will no longer update the
134
- last modified time in this case, so if a template is modified and
135
- introduces an error (e.g. SyntaxError in an erb template), all future
136
- requests that use the template will result in the error being raised,
137
- until the template is fixed.
138
-
139
- = Backwards Compatibility
140
-
141
- * The internal TemplateMtimeWrapper API has been modified. As documented,
142
- this is an internal class and the API can change in any Roda version.
143
- However, if any code was relying on the previous implementation of
144
- TemplateMtimeWrapper#modified?, it will need to be modified, as that
145
- method has been replaced with TemplateMtimeWrapper#if_modified.
146
-
147
- Additionally, the TemplateMtimeWrapper#compiled_method_lambda API has
148
- also changed.
@@ -1,27 +0,0 @@
1
- = New Features
2
-
3
- * The convert_each! method in the typecast_params plugin now
4
- accepts a Proc or Method value for the :keys option. The proc
5
- or method is called with the current array or hash that
6
- typecast params is operating on, and should return an
7
- array of keys to use for the conversion.
8
-
9
- * The convert_each! method in the typecast_params plugin will
10
- now automatically handle hashes with keys from '0'..'N',
11
- without a :keys option being provided.
12
-
13
- This makes it possible to handle parameter names such as
14
- foo[0][bar], foo[0][baz], foo[1][bar], and foo[1][baz], if you
15
- want to avoid the issues related to rack's issues when parsing
16
- array parameters.
17
-
18
- = Other Improvements
19
-
20
- * The Roda::RodaVersionNumber constant has been added for easier
21
- version comparisons. It is 30080 for version 3.8.0.
22
-
23
- = Backwards Compatibility
24
-
25
- * When an unsupported type is given as value of the :keys option
26
- to the convert_each! method in the typecast_params plugin, a
27
- ProgrammerError exception is now raised.
@@ -1,31 +0,0 @@
1
- = New Features
2
-
3
- * The hmac_paths plugin now supports a :namespace option for both hmac_path and
4
- r.hmac_path. The :namespace option makes the generated HMAC values unique
5
- per namespace, allowing easy use of per user/group HMAC paths. This can
6
- be useful if the same path will show different information to different
7
- users/groups, and you want to prevent path enumeration for each user/group
8
- (not allow paths enumerated by one user/group to be valid for a different
9
- user/group). Example:
10
-
11
- hmac_path('/widget/1', namespace: '1')
12
- # => "/3793ac2a72ea399c40cbd63f154d19f0fe34cdf8d347772134c506a0b756d590/n/widget/1"
13
-
14
- hmac_path('/widget/1', namespace: '2')
15
- # => "/0e1e748860d4fd17fe9b7c8259b1e26996502c38e465f802c2c9a0a13000087c/n/widget/1"
16
-
17
- The HMAC path created with namespace: '1' will only be valid when calling
18
- r.hmac_path with namespace: '1' (similar for namespace: '2').
19
-
20
- It is expected that the most common use of the :namespace option is to
21
- reference session values, so the value of each path depends on the logged in
22
- user. You can use the :namespace_session_key plugin option to set the
23
- default namespace for both hmac_path and r.hmac_path:
24
-
25
- plugin :hmac_paths, secret: 'some-secret-value-with-at-least-32-bytes',
26
- namespace_session_key: 'account_id'
27
-
28
- This will use <tt>session['account_id']</tt> (converted to a string) as the namespace
29
- for both hmac_path and r.hmac_path, unless a specific :namespace option is
30
- given, making it simple to implement per user/group HMAC paths across an
31
- application.