roda 3.83.0 → 3.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/lib/roda/plugins/hsts.rb +35 -0
  3. data/lib/roda/response.rb +1 -1
  4. data/lib/roda/version.rb +1 -1
  5. metadata +4 -179
  6. data/CHANGELOG +0 -691
  7. data/README.rdoc +0 -1136
  8. data/doc/conventions.rdoc +0 -177
  9. data/doc/release_notes/3.0.0.txt +0 -84
  10. data/doc/release_notes/3.1.0.txt +0 -24
  11. data/doc/release_notes/3.10.0.txt +0 -132
  12. data/doc/release_notes/3.11.0.txt +0 -54
  13. data/doc/release_notes/3.12.0.txt +0 -19
  14. data/doc/release_notes/3.13.0.txt +0 -38
  15. data/doc/release_notes/3.14.0.txt +0 -36
  16. data/doc/release_notes/3.14.1.txt +0 -43
  17. data/doc/release_notes/3.15.0.txt +0 -21
  18. data/doc/release_notes/3.16.0.txt +0 -52
  19. data/doc/release_notes/3.17.0.txt +0 -62
  20. data/doc/release_notes/3.18.0.txt +0 -170
  21. data/doc/release_notes/3.19.0.txt +0 -229
  22. data/doc/release_notes/3.2.0.txt +0 -22
  23. data/doc/release_notes/3.20.0.txt +0 -7
  24. data/doc/release_notes/3.21.0.txt +0 -5
  25. data/doc/release_notes/3.22.0.txt +0 -24
  26. data/doc/release_notes/3.23.0.txt +0 -28
  27. data/doc/release_notes/3.24.0.txt +0 -14
  28. data/doc/release_notes/3.25.0.txt +0 -12
  29. data/doc/release_notes/3.26.0.txt +0 -15
  30. data/doc/release_notes/3.27.0.txt +0 -15
  31. data/doc/release_notes/3.28.0.txt +0 -13
  32. data/doc/release_notes/3.29.0.txt +0 -15
  33. data/doc/release_notes/3.3.0.txt +0 -291
  34. data/doc/release_notes/3.30.0.txt +0 -14
  35. data/doc/release_notes/3.31.0.txt +0 -11
  36. data/doc/release_notes/3.32.0.txt +0 -42
  37. data/doc/release_notes/3.33.0.txt +0 -8
  38. data/doc/release_notes/3.34.0.txt +0 -17
  39. data/doc/release_notes/3.35.0.txt +0 -12
  40. data/doc/release_notes/3.36.0.txt +0 -17
  41. data/doc/release_notes/3.37.0.txt +0 -42
  42. data/doc/release_notes/3.38.0.txt +0 -5
  43. data/doc/release_notes/3.39.0.txt +0 -16
  44. data/doc/release_notes/3.4.0.txt +0 -24
  45. data/doc/release_notes/3.40.0.txt +0 -24
  46. data/doc/release_notes/3.41.0.txt +0 -9
  47. data/doc/release_notes/3.42.0.txt +0 -21
  48. data/doc/release_notes/3.43.0.txt +0 -34
  49. data/doc/release_notes/3.44.0.txt +0 -23
  50. data/doc/release_notes/3.45.0.txt +0 -22
  51. data/doc/release_notes/3.46.0.txt +0 -19
  52. data/doc/release_notes/3.47.0.txt +0 -13
  53. data/doc/release_notes/3.48.0.txt +0 -10
  54. data/doc/release_notes/3.49.0.txt +0 -18
  55. data/doc/release_notes/3.5.0.txt +0 -31
  56. data/doc/release_notes/3.50.0.txt +0 -21
  57. data/doc/release_notes/3.51.0.txt +0 -20
  58. data/doc/release_notes/3.52.0.txt +0 -20
  59. data/doc/release_notes/3.53.0.txt +0 -14
  60. data/doc/release_notes/3.54.0.txt +0 -48
  61. data/doc/release_notes/3.55.0.txt +0 -12
  62. data/doc/release_notes/3.56.0.txt +0 -33
  63. data/doc/release_notes/3.57.0.txt +0 -34
  64. data/doc/release_notes/3.58.0.txt +0 -16
  65. data/doc/release_notes/3.59.0.txt +0 -17
  66. data/doc/release_notes/3.6.0.txt +0 -21
  67. data/doc/release_notes/3.60.0.txt +0 -56
  68. data/doc/release_notes/3.61.0.txt +0 -24
  69. data/doc/release_notes/3.62.0.txt +0 -41
  70. data/doc/release_notes/3.63.0.txt +0 -36
  71. data/doc/release_notes/3.64.0.txt +0 -26
  72. data/doc/release_notes/3.65.0.txt +0 -12
  73. data/doc/release_notes/3.66.0.txt +0 -23
  74. data/doc/release_notes/3.67.0.txt +0 -25
  75. data/doc/release_notes/3.68.0.txt +0 -21
  76. data/doc/release_notes/3.69.0.txt +0 -33
  77. data/doc/release_notes/3.7.0.txt +0 -123
  78. data/doc/release_notes/3.70.0.txt +0 -19
  79. data/doc/release_notes/3.71.0.txt +0 -33
  80. data/doc/release_notes/3.72.0.txt +0 -48
  81. data/doc/release_notes/3.73.0.txt +0 -33
  82. data/doc/release_notes/3.74.0.txt +0 -28
  83. data/doc/release_notes/3.75.0.txt +0 -19
  84. data/doc/release_notes/3.76.0.txt +0 -18
  85. data/doc/release_notes/3.77.0.txt +0 -8
  86. data/doc/release_notes/3.78.0.txt +0 -99
  87. data/doc/release_notes/3.79.0.txt +0 -148
  88. data/doc/release_notes/3.8.0.txt +0 -27
  89. data/doc/release_notes/3.80.0.txt +0 -31
  90. data/doc/release_notes/3.81.0.txt +0 -24
  91. data/doc/release_notes/3.82.0.txt +0 -43
  92. data/doc/release_notes/3.83.0.txt +0 -6
  93. 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.