roda 3.75.0 → 3.77.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ebdbc5b5707ba21044b5f4c83445ff8fca6a3925488c72a5176cc662c2ad15b
4
- data.tar.gz: ac528e50bfe1e778b5b4d32d16d12e117e87dc55b2f7cad1170b682e2f979586
3
+ metadata.gz: 8afa46b8055c19e63e1efeebf444b422cd810701c759936d476797515630245d
4
+ data.tar.gz: 4e857447435707de1d586857126795e2a1191f685ca8893e99cd1c78aeb50c02
5
5
  SHA512:
6
- metadata.gz: b7d13975d6c7705f1d7b184f27430320e1dfd5fea96e532f119f86b4c09fdb5c7e460107033d8d099c6ef8da354a514289981163d74cdb293b89074299a57eda
7
- data.tar.gz: d16c1b3a2edd401a73b104bf9458fcb865e441f4de649dcfcb8144eb3102abcd6abee741ecbbbf1cad3b13d8efca7260d7785b7e5a2505f29541ad001de9b1e4
6
+ metadata.gz: 2ecfeb211d574d46bdc31995c3551afb97af50acf8567cce83b46808e87ff797a5ef7c769e42c0477ed6a6d9271040b818da72e77c91e4f91f9760201d2cd5ca
7
+ data.tar.gz: 45e67fbd1da64839c6dc5a8dc0975a0dca5f67a77c83ae79a62ab22e7d9cf0002cb8b8f6a22661bdaf97ad973b6bc6ff928af2e416a96c35b12150e9fe0eaf4d
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ = 3.77.0 (2024-02-12)
2
+
3
+ * Support formaction/formmethod attributes in forms in route_csrf plugin (jeremyevans)
4
+
5
+ = 3.76.0 (2024-01-12)
6
+
7
+ * Support :filter plugin option in error_mail and error_email for filtering parameters, environment variables, and session values (jeremyevans) (#346)
8
+
9
+ * Set temporary name on Ruby 3.3 in middleware plugin for middleware class created (janko) (#344)
10
+
11
+ * Add break plugin, for using break inside a routing block to return from the block and keep routing (jeremyevans)
12
+
1
13
  = 3.75.0 (2023-12-14)
2
14
 
3
15
  * Add cookie_flags plugin, for overriding, warning, or raising for incorrect cookie flags (jeremyevans)
@@ -0,0 +1,18 @@
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.
@@ -0,0 +1,8 @@
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.
@@ -0,0 +1,43 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The break plugin supports calling break inside a match block, to
7
+ # return from the block and continue in the routing tree, restoring
8
+ # the remaining path so that future matchers operating on the path
9
+ # operate as expected.
10
+ #
11
+ # plugin :break
12
+ #
13
+ # route do |r|
14
+ # r.on "foo", :bar do |bar|
15
+ # break if bar == 'baz'
16
+ # "/foo/#{bar} (not baz)"
17
+ # end
18
+ #
19
+ # r.on "foo/baz" do
20
+ # "/foo/baz"
21
+ # end
22
+ # end
23
+ #
24
+ # This provides the same basic feature as the pass plugin, but
25
+ # uses Ruby's standard control flow primative instead of a
26
+ # separate method.
27
+ module Break
28
+ module RequestMethods
29
+ private
30
+
31
+ # Handle break inside match blocks, restoring remaining path.
32
+ def if_match(_)
33
+ rp = @remaining_path
34
+ super
35
+ ensure
36
+ @remaining_path = rp
37
+ end
38
+ end
39
+ end
40
+
41
+ register_plugin(:break, Break)
42
+ end
43
+ end
@@ -21,6 +21,9 @@ class Roda
21
21
  #
22
22
  # Options:
23
23
  #
24
+ # :filter :: Callable called with the key and value for each parameter, environment
25
+ # variable, and session value. If it returns true, the value of the
26
+ # parameter is filtered in the email.
24
27
  # :from :: The From address to use in the email (required)
25
28
  # :headers :: A hash of additional headers to use in the email (default: empty hash)
26
29
  # :host :: The SMTP server to use to send the email (default: localhost)
@@ -38,6 +41,7 @@ class Roda
38
41
  # use an error reporting service instead of this plugin.
39
42
  module ErrorEmail
40
43
  DEFAULTS = {
44
+ :filter=>lambda{|k,v| false},
41
45
  :headers=>OPTS,
42
46
  :host=>'localhost',
43
47
  # :nocov:
@@ -52,7 +56,12 @@ class Roda
52
56
  {'From'=>h[:from], 'To'=>h[:to], 'Subject'=>"#{h[:prefix]}#{subject}"}
53
57
  end,
54
58
  :body=>lambda do |s, e|
55
- format = lambda{|h| h.map{|k, v| "#{k.inspect} => #{v.inspect}"}.sort.join("\n")}
59
+ filter = s.opts[:error_email][:filter]
60
+ format = lambda do |h|
61
+ h = h.map{|k, v| "#{k.inspect} => #{filter.call(k, v) ? 'FILTERED' : v.inspect}"}
62
+ h.sort!
63
+ h.join("\n")
64
+ end
56
65
 
57
66
  begin
58
67
  params = s.request.params
@@ -21,6 +21,9 @@ class Roda
21
21
  #
22
22
  # Options:
23
23
  #
24
+ # :filter :: Callable called with the key and value for each parameter, environment
25
+ # variable, and session value. If it returns true, the value of the
26
+ # parameter is filtered in the email.
24
27
  # :from :: The From address to use in the email (required)
25
28
  # :headers :: A hash of additional headers to use in the email (default: empty hash)
26
29
  # :prefix :: A prefix to use in the email's subject line (default: no prefix)
@@ -36,9 +39,12 @@ class Roda
36
39
  # for low traffic web applications. For high traffic web applications,
37
40
  # use an error reporting service instead of this plugin.
38
41
  module ErrorMail
42
+ DEFAULT_FILTER = lambda{|k,v| false}
43
+ private_constant :DEFAULT_FILTER
44
+
39
45
  # Set default opts for plugin. See ErrorEmail module RDoc for options.
40
46
  def self.configure(app, opts=OPTS)
41
- app.opts[:error_mail] = email_opts = (app.opts[:error_mail] || OPTS).merge(opts).freeze
47
+ app.opts[:error_mail] = email_opts = (app.opts[:error_mail] || {:filter=>DEFAULT_FILTER}).merge(opts).freeze
42
48
  unless email_opts[:to] && email_opts[:from]
43
49
  raise RodaError, "must provide :to and :from options to error_mail plugin"
44
50
  end
@@ -68,8 +74,13 @@ class Roda
68
74
  e.to_s
69
75
  end
70
76
  subject = "#{email_opts[:prefix]}#{subject}"
77
+ filter = email_opts[:filter]
71
78
 
72
- format = lambda{|h| h.map{|k, v| "#{k.inspect} => #{v.inspect}"}.sort.join("\n")}
79
+ format = lambda do |h|
80
+ h = h.map{|k, v| "#{k.inspect} => #{filter.call(k, v) ? 'FILTERED' : v.inspect}"}
81
+ h.sort!
82
+ h.join("\n")
83
+ end
73
84
 
74
85
  begin
75
86
  params = request.params
@@ -134,6 +134,9 @@ class Roda
134
134
  # and store +app+ as the next middleware to call.
135
135
  def initialize(mid, app, *args, &block)
136
136
  @mid = Class.new(mid)
137
+ # :nocov:
138
+ @mid.set_temporary_name("#{mid.name}(middleware)") if mid.name && RUBY_VERSION >= "3.3"
139
+ # :nocov:
137
140
  if @mid.opts[:middleware_next_if_not_found]
138
141
  @mid.plugin(:not_found, &NEXT_PROC)
139
142
  end
@@ -42,6 +42,9 @@ class Roda
42
42
  # This plugin supports the following options:
43
43
  #
44
44
  # :field :: Form input parameter name for CSRF token (default: '_csrf')
45
+ # :formaction_field :: Form input parameter name for path-specific CSRF tokens (used by the
46
+ # +csrf_formaction_tag+ method). If present, this parameter should be
47
+ # submitted as a hash, keyed by path, with CSRF token values.
45
48
  # :header :: HTTP header name for CSRF token (default: 'X-CSRF-Token')
46
49
  # :key :: Session key for CSRF secret (default: '_roda_csrf_secret')
47
50
  # :require_request_specific_tokens :: Whether request-specific tokens are required (default: true).
@@ -86,6 +89,10 @@ class Roda
86
89
  # override any of the plugin options for this specific call.
87
90
  # The :token option can be used to specify the provided CSRF token
88
91
  # (instead of looking for the token in the submitted parameters).
92
+ # csrf_formaction_tag(path, method='POST') :: An HTML hidden input tag string containing the CSRF token, suitable
93
+ # for placing in an HTML form that has inputs that use formaction
94
+ # attributes to change the endpoint to which the form is submitted.
95
+ # Takes the same arguments as csrf_token.
89
96
  # csrf_field :: The field name to use for the hidden tag containing the CSRF token.
90
97
  # csrf_path(action) :: This takes an argument that would be the value of the HTML form's
91
98
  # action attribute, and returns a path you can pass to csrf_token
@@ -152,6 +159,7 @@ class Roda
152
159
  # Default CSRF option values
153
160
  DEFAULTS = {
154
161
  :field => '_csrf'.freeze,
162
+ :formaction_field => '_csrfs'.freeze,
155
163
  :header => 'X-CSRF-Token'.freeze,
156
164
  :key => '_roda_csrf_secret'.freeze,
157
165
  :require_request_specific_tokens => true,
@@ -252,6 +260,14 @@ class Roda
252
260
  end
253
261
  end
254
262
 
263
+ # An HTML hidden input tag string containing the CSRF token, used for inputs
264
+ # with formaction, so the same form can be used to submit to multiple endpoints
265
+ # depending on which button was clicked. See csrf_token for arguments, but the
266
+ # path argument is required.
267
+ def csrf_formaction_tag(path, *args)
268
+ "<input type=\"hidden\" name=\"#{csrf_options[:formaction_field]}[#{Rack::Utils.escape_html(path)}]\" value=\"#{csrf_token(path, *args)}\" \/>"
269
+ end
270
+
255
271
  # An HTML hidden input tag string containing the CSRF token. See csrf_token for
256
272
  # arguments.
257
273
  def csrf_tag(*args)
@@ -291,6 +307,8 @@ class Roda
291
307
  return
292
308
  end
293
309
 
310
+ path = @_request.path
311
+
294
312
  unless encoded_token = opts[:token]
295
313
  encoded_token = case opts[:check_header]
296
314
  when :only
@@ -298,7 +316,8 @@ class Roda
298
316
  when true
299
317
  return (csrf_invalid_message(opts.merge(:check_header=>false)) && csrf_invalid_message(opts.merge(:check_header=>:only)))
300
318
  else
301
- @_request.params[opts[:field]]
319
+ params = @_request.params
320
+ ((formactions = params[opts[:formaction_field]]).is_a?(Hash) && (formactions[path])) || params[opts[:field]]
302
321
  end
303
322
  end
304
323
 
@@ -326,7 +345,7 @@ class Roda
326
345
 
327
346
  random_data = submitted_hmac.slice!(0...31)
328
347
 
329
- if csrf_compare(csrf_hmac(random_data, method, @_request.path), submitted_hmac)
348
+ if csrf_compare(csrf_hmac(random_data, method, path), submitted_hmac)
330
349
  return
331
350
  end
332
351
 
data/lib/roda/version.rb CHANGED
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 75
7
+ RodaMinorVersion = 77
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roda
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.75.0
4
+ version: 3.77.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-14 00:00:00.000000000 Z
11
+ date: 2024-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -249,6 +249,8 @@ extra_rdoc_files:
249
249
  - doc/release_notes/3.73.0.txt
250
250
  - doc/release_notes/3.74.0.txt
251
251
  - doc/release_notes/3.75.0.txt
252
+ - doc/release_notes/3.76.0.txt
253
+ - doc/release_notes/3.77.0.txt
252
254
  - doc/release_notes/3.8.0.txt
253
255
  - doc/release_notes/3.9.0.txt
254
256
  files:
@@ -331,6 +333,8 @@ files:
331
333
  - doc/release_notes/3.73.0.txt
332
334
  - doc/release_notes/3.74.0.txt
333
335
  - doc/release_notes/3.75.0.txt
336
+ - doc/release_notes/3.76.0.txt
337
+ - doc/release_notes/3.77.0.txt
334
338
  - doc/release_notes/3.8.0.txt
335
339
  - doc/release_notes/3.9.0.txt
336
340
  - lib/roda.rb
@@ -351,6 +355,7 @@ files:
351
355
  - lib/roda/plugins/autoload_named_routes.rb
352
356
  - lib/roda/plugins/backtracking_array.rb
353
357
  - lib/roda/plugins/branch_locals.rb
358
+ - lib/roda/plugins/break.rb
354
359
  - lib/roda/plugins/caching.rb
355
360
  - lib/roda/plugins/capture_erb.rb
356
361
  - lib/roda/plugins/chunked.rb
@@ -494,7 +499,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
494
499
  - !ruby/object:Gem::Version
495
500
  version: '0'
496
501
  requirements: []
497
- rubygems_version: 3.4.10
502
+ rubygems_version: 3.5.3
498
503
  signing_key:
499
504
  specification_version: 4
500
505
  summary: Routing tree web toolkit