roda 3.72.0 → 3.73.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 +4 -4
- data/CHANGELOG +6 -0
- data/doc/release_notes/3.73.0.txt +33 -0
- data/lib/roda/plugins/_base64.rb +34 -0
- data/lib/roda/plugins/exception_page.rb +1 -1
- data/lib/roda/plugins/middleware.rb +46 -0
- data/lib/roda/plugins/route_csrf.rb +7 -4
- data/lib/roda/plugins/sessions.rb +6 -3
- data/lib/roda/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a47f703af806a15f523b99b38d084520bada92f23196374d6d9781d5953faa8
|
4
|
+
data.tar.gz: 4612153820b89e6dbcfa2370c04269666c49277d8f11256869f6c1f0bb0b571a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdc42cdc9540750195327dac290e3f0bb08c3ed0100b610441b644c85849e7e9f9f974013cea041cdf640f590e2027996d68b62eea9c31d41bd42a57df6530bb
|
7
|
+
data.tar.gz: 3ab05db704cf45803ad706e78b558ea7798d8d9782a06c7dec79814a4d862582249e5554946c59fff88e1f1d2d360e242268c593a8c9c95fe87b5489e4a4a44c
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
= 3.73.0 (2023-10-13)
|
2
|
+
|
3
|
+
* Support :next_if_not_found option for middleware plugin (jeremyevans) (#334)
|
4
|
+
|
5
|
+
* Remove dependency on base64 library from sessions and route_csrf plugin, as it will not be part of the standard library in Ruby 3.4+ (jeremyevans)
|
6
|
+
|
1
7
|
= 3.72.0 (2023-09-12)
|
2
8
|
|
3
9
|
* Add invalid_request_body plugin for custom handling of invalid request bodies (jeremyevans)
|
@@ -0,0 +1,33 @@
|
|
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.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
module Base64_
|
7
|
+
class << self
|
8
|
+
if RUBY_VERSION >= '2.4'
|
9
|
+
def decode64(str)
|
10
|
+
str.unpack1("m0")
|
11
|
+
end
|
12
|
+
# :nocov:
|
13
|
+
else
|
14
|
+
def decode64(str)
|
15
|
+
str.unpack("m0")[0]
|
16
|
+
end
|
17
|
+
# :nocov:
|
18
|
+
end
|
19
|
+
|
20
|
+
def urlsafe_encode64(bin)
|
21
|
+
str = [bin].pack("m0")
|
22
|
+
str.tr!("+/", "-_")
|
23
|
+
str
|
24
|
+
end
|
25
|
+
|
26
|
+
def urlsafe_decode64(str)
|
27
|
+
decode64(str.tr("-_", "+/"))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
register_plugin(:_base64, Base64_)
|
33
|
+
end
|
34
|
+
end
|
@@ -33,6 +33,43 @@ class Roda
|
|
33
33
|
#
|
34
34
|
# run App
|
35
35
|
#
|
36
|
+
# By default, when the app is used as middleware and handles the request at
|
37
|
+
# all, it does not forward the request to the next middleware. For the
|
38
|
+
# following setup:
|
39
|
+
#
|
40
|
+
# class Mid < Roda
|
41
|
+
# plugin :middleware
|
42
|
+
#
|
43
|
+
# route do |r|
|
44
|
+
# r.on "foo" do
|
45
|
+
# r.is "mid" do
|
46
|
+
# "Mid"
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# class App < Roda
|
53
|
+
# use Mid
|
54
|
+
#
|
55
|
+
# route do |r|
|
56
|
+
# r.on "foo" do
|
57
|
+
# r.is "app" do
|
58
|
+
# "App"
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# run App
|
65
|
+
#
|
66
|
+
# Requests for +/foo/mid will+ return +Mid+, but requests for +/foo/app+
|
67
|
+
# will return an empty 404 response, because the middleware handles the
|
68
|
+
# +/foo/app+ request in the <tt>r.on "foo" do</tt> block, but does not
|
69
|
+
# have the block return a result, which Roda treats as an empty 404 response.
|
70
|
+
# If you would like the middleware to forward +/foo/app+ request to the
|
71
|
+
# application, you should use the +:next_if_not_found+ plugin option.
|
72
|
+
#
|
36
73
|
# It is possible to use the Roda app as a regular app even when using
|
37
74
|
# the middleware plugin. Using an app as middleware automatically creates
|
38
75
|
# a subclass of the app for the middleware. Because a subclass is automatically
|
@@ -64,6 +101,9 @@ class Roda
|
|
64
101
|
# # Request to App for /mid returns
|
65
102
|
# # "foo bar baz"
|
66
103
|
module Middleware
|
104
|
+
NEXT_PROC = lambda{throw :next, true}
|
105
|
+
private_constant :NEXT_PROC
|
106
|
+
|
67
107
|
# Configure the middleware plugin. Options:
|
68
108
|
# :env_var :: Set the environment variable to use to indicate to the roda
|
69
109
|
# application that the current request is a middleware request.
|
@@ -77,12 +117,15 @@ class Roda
|
|
77
117
|
# the middleware's route block should be applied to the
|
78
118
|
# final response when the request is forwarded to the app.
|
79
119
|
# Defaults to false.
|
120
|
+
# :next_if_not_found :: If the middleware handles the request but returns a not found
|
121
|
+
# result (404 with no body), forward the result to the next middleware.
|
80
122
|
def self.configure(app, opts={}, &block)
|
81
123
|
app.opts[:middleware_env_var] = opts[:env_var] if opts.has_key?(:env_var)
|
82
124
|
app.opts[:middleware_env_var] ||= 'roda.forward_next'
|
83
125
|
app.opts[:middleware_configure] = block if block
|
84
126
|
app.opts[:middleware_handle_result] = opts[:handle_result]
|
85
127
|
app.opts[:middleware_forward_response_headers] = opts[:forward_response_headers]
|
128
|
+
app.opts[:middleware_next_if_not_found] = opts[:next_if_not_found]
|
86
129
|
end
|
87
130
|
|
88
131
|
# Forwarder instances are what is actually used as middleware.
|
@@ -91,6 +134,9 @@ class Roda
|
|
91
134
|
# and store +app+ as the next middleware to call.
|
92
135
|
def initialize(mid, app, *args, &block)
|
93
136
|
@mid = Class.new(mid)
|
137
|
+
if @mid.opts[:middleware_next_if_not_found]
|
138
|
+
@mid.plugin(:not_found, &NEXT_PROC)
|
139
|
+
end
|
94
140
|
if configure = @mid.opts[:middleware_configure]
|
95
141
|
configure.call(@mid, *args, &block)
|
96
142
|
elsif block || !args.empty?
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
-
require 'base64'
|
4
3
|
require 'openssl'
|
5
4
|
require 'securerandom'
|
6
5
|
require 'uri'
|
@@ -163,6 +162,10 @@ class Roda
|
|
163
162
|
# a valid CSRF token was not provided.
|
164
163
|
class InvalidToken < RodaError; end
|
165
164
|
|
165
|
+
def self.load_dependencies(app, opts=OPTS)
|
166
|
+
app.plugin :_base64
|
167
|
+
end
|
168
|
+
|
166
169
|
def self.configure(app, opts=OPTS, &block)
|
167
170
|
options = app.opts[:route_csrf] = (app.opts[:route_csrf] || DEFAULTS).merge(opts)
|
168
171
|
if block || opts[:csrf_failure].is_a?(Proc)
|
@@ -260,7 +263,7 @@ class Roda
|
|
260
263
|
def csrf_token(path=nil, method=('POST' if path))
|
261
264
|
token = SecureRandom.random_bytes(31)
|
262
265
|
token << csrf_hmac(token, method, path)
|
263
|
-
|
266
|
+
[token].pack("m0")
|
264
267
|
end
|
265
268
|
|
266
269
|
# Whether request-specific CSRF tokens should be used by default.
|
@@ -314,7 +317,7 @@ class Roda
|
|
314
317
|
end
|
315
318
|
|
316
319
|
begin
|
317
|
-
submitted_hmac =
|
320
|
+
submitted_hmac = Base64_.decode64(encoded_token)
|
318
321
|
rescue ArgumentError
|
319
322
|
return "encoded token is not valid base64"
|
320
323
|
end
|
@@ -354,7 +357,7 @@ class Roda
|
|
354
357
|
# JSON is used for session serialization).
|
355
358
|
def csrf_secret
|
356
359
|
key = session[csrf_options[:key]] ||= SecureRandom.base64(32)
|
357
|
-
|
360
|
+
Base64_.decode64(key)
|
358
361
|
end
|
359
362
|
end
|
360
363
|
end
|
@@ -10,7 +10,6 @@ rescue OpenSSL::Cipher::CipherError
|
|
10
10
|
# :nocov:
|
11
11
|
end
|
12
12
|
|
13
|
-
require 'base64'
|
14
13
|
require 'json'
|
15
14
|
require 'securerandom'
|
16
15
|
require 'zlib'
|
@@ -171,6 +170,10 @@ class Roda
|
|
171
170
|
[cipher_secret.freeze, hmac_secret.freeze]
|
172
171
|
end
|
173
172
|
|
173
|
+
def self.load_dependencies(app, opts=OPTS)
|
174
|
+
app.plugin :_base64
|
175
|
+
end
|
176
|
+
|
174
177
|
# Configure the plugin, see Sessions for details on options.
|
175
178
|
def self.configure(app, opts=OPTS)
|
176
179
|
opts = (app.opts[:sessions] || DEFAULT_OPTIONS).merge(opts)
|
@@ -344,7 +347,7 @@ class Roda
|
|
344
347
|
opts = roda_class.opts[:sessions]
|
345
348
|
|
346
349
|
begin
|
347
|
-
data =
|
350
|
+
data = Base64_.urlsafe_decode64(data)
|
348
351
|
rescue ArgumentError
|
349
352
|
return _session_serialization_error("Unable to decode session: invalid base64")
|
350
353
|
end
|
@@ -493,7 +496,7 @@ class Roda
|
|
493
496
|
data << encrypted_data
|
494
497
|
data << OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, opts[:hmac_secret], data+opts[:key])
|
495
498
|
|
496
|
-
data =
|
499
|
+
data = Base64_.urlsafe_encode64(data)
|
497
500
|
|
498
501
|
if data.bytesize >= 4096
|
499
502
|
raise CookieTooLarge, "attempted to create cookie larger than 4096 bytes"
|
data/lib/roda/version.rb
CHANGED
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.
|
4
|
+
version: 3.73.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-
|
11
|
+
date: 2023-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -246,6 +246,7 @@ extra_rdoc_files:
|
|
246
246
|
- doc/release_notes/3.70.0.txt
|
247
247
|
- doc/release_notes/3.71.0.txt
|
248
248
|
- doc/release_notes/3.72.0.txt
|
249
|
+
- doc/release_notes/3.73.0.txt
|
249
250
|
- doc/release_notes/3.8.0.txt
|
250
251
|
- doc/release_notes/3.9.0.txt
|
251
252
|
files:
|
@@ -325,6 +326,7 @@ files:
|
|
325
326
|
- doc/release_notes/3.70.0.txt
|
326
327
|
- doc/release_notes/3.71.0.txt
|
327
328
|
- doc/release_notes/3.72.0.txt
|
329
|
+
- doc/release_notes/3.73.0.txt
|
328
330
|
- doc/release_notes/3.8.0.txt
|
329
331
|
- doc/release_notes/3.9.0.txt
|
330
332
|
- lib/roda.rb
|
@@ -332,6 +334,7 @@ files:
|
|
332
334
|
- lib/roda/plugins.rb
|
333
335
|
- lib/roda/plugins/Integer_matcher_max.rb
|
334
336
|
- lib/roda/plugins/_after_hook.rb
|
337
|
+
- lib/roda/plugins/_base64.rb
|
335
338
|
- lib/roda/plugins/_before_hook.rb
|
336
339
|
- lib/roda/plugins/_optimized_matching.rb
|
337
340
|
- lib/roda/plugins/_symbol_regexp_matchers.rb
|