rack 2.2.8 → 3.0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +213 -83
- data/CONTRIBUTING.md +53 -47
- data/MIT-LICENSE +1 -1
- data/README.md +309 -0
- data/SPEC.rdoc +174 -126
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +3 -1
- data/lib/rack/auth/basic.rb +0 -2
- data/lib/rack/auth/digest/md5.rb +1 -131
- data/lib/rack/auth/digest/nonce.rb +1 -54
- data/lib/rack/auth/digest/params.rb +1 -54
- data/lib/rack/auth/digest/request.rb +1 -43
- data/lib/rack/auth/digest.rb +256 -0
- data/lib/rack/body_proxy.rb +3 -1
- data/lib/rack/builder.rb +83 -63
- data/lib/rack/cascade.rb +2 -0
- data/lib/rack/chunked.rb +16 -13
- data/lib/rack/common_logger.rb +23 -18
- data/lib/rack/conditional_get.rb +18 -15
- data/lib/rack/constants.rb +64 -0
- data/lib/rack/content_length.rb +12 -16
- data/lib/rack/content_type.rb +8 -5
- data/lib/rack/deflater.rb +40 -26
- data/lib/rack/directory.rb +9 -3
- data/lib/rack/etag.rb +14 -23
- data/lib/rack/events.rb +4 -0
- data/lib/rack/file.rb +2 -0
- data/lib/rack/files.rb +15 -17
- data/lib/rack/head.rb +9 -8
- data/lib/rack/headers.rb +154 -0
- data/lib/rack/lint.rb +758 -646
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +9 -4
- data/lib/rack/method_override.rb +5 -1
- data/lib/rack/mime.rb +8 -0
- data/lib/rack/mock.rb +1 -271
- data/lib/rack/mock_request.rb +166 -0
- data/lib/rack/mock_response.rb +126 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +120 -64
- data/lib/rack/multipart/uploaded_file.rb +4 -0
- data/lib/rack/multipart.rb +20 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +78 -46
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +224 -106
- data/lib/rack/response.rb +138 -61
- data/lib/rack/rewindable_input.rb +24 -5
- data/lib/rack/runtime.rb +7 -6
- data/lib/rack/sendfile.rb +30 -25
- data/lib/rack/show_exceptions.rb +15 -2
- data/lib/rack/show_status.rb +17 -7
- data/lib/rack/static.rb +8 -8
- data/lib/rack/tempfile_reaper.rb +15 -4
- data/lib/rack/urlmap.rb +3 -1
- data/lib/rack/utils.rb +208 -178
- data/lib/rack/version.rb +9 -4
- data/lib/rack.rb +6 -76
- metadata +14 -34
- data/README.rdoc +0 -320
- data/Rakefile +0 -130
- data/bin/rackup +0 -5
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/contrib/rack_logo.svg +0 -164
- data/contrib/rdoc.css +0 -412
- data/example/lobster.ru +0 -6
- data/example/protectedlobster.rb +0 -16
- data/example/protectedlobster.ru +0 -10
- data/lib/rack/core_ext/regexp.rb +0 -14
- data/lib/rack/handler/cgi.rb +0 -59
- data/lib/rack/handler/fastcgi.rb +0 -100
- data/lib/rack/handler/lsws.rb +0 -61
- data/lib/rack/handler/scgi.rb +0 -71
- data/lib/rack/handler/thin.rb +0 -36
- data/lib/rack/handler/webrick.rb +0 -129
- data/lib/rack/handler.rb +0 -104
- data/lib/rack/lobster.rb +0 -70
- data/lib/rack/server.rb +0 -466
- data/lib/rack/session/abstract/id.rb +0 -523
- data/lib/rack/session/cookie.rb +0 -204
- data/lib/rack/session/memcache.rb +0 -10
- data/lib/rack/session/pool.rb +0 -85
- data/rack.gemspec +0 -46
data/SPEC.rdoc
CHANGED
@@ -1,23 +1,27 @@
|
|
1
|
-
This specification aims to formalize the Rack protocol.
|
1
|
+
This specification aims to formalize the Rack protocol. You
|
2
2
|
can (and should) use Rack::Lint to enforce it.
|
3
3
|
|
4
4
|
When you develop middleware, be sure to add a Lint before and
|
5
5
|
after to catch all mistakes.
|
6
|
+
|
6
7
|
= Rack applications
|
8
|
+
|
7
9
|
A Rack application is a Ruby object (not a class) that
|
8
10
|
responds to +call+.
|
9
11
|
It takes exactly one argument, the *environment*
|
10
|
-
and returns
|
12
|
+
and returns a non-frozen Array of exactly three values:
|
11
13
|
The *status*,
|
12
14
|
the *headers*,
|
13
15
|
and the *body*.
|
16
|
+
|
14
17
|
== The Environment
|
18
|
+
|
15
19
|
The environment must be an unfrozen instance of Hash that includes
|
16
|
-
CGI-like headers.
|
20
|
+
CGI-like headers. The Rack application is free to modify the
|
17
21
|
environment.
|
18
22
|
|
19
23
|
The environment is required to include these variables
|
20
|
-
(adopted from
|
24
|
+
(adopted from {PEP 333}[https://peps.python.org/pep-0333/]), except when they'd be empty, but see
|
21
25
|
below.
|
22
26
|
<tt>REQUEST_METHOD</tt>:: The HTTP request method, such as
|
23
27
|
"GET" or "POST". This cannot ever
|
@@ -54,6 +58,8 @@ below.
|
|
54
58
|
<tt>SERVER_PORT</tt>:: An optional +Integer+ which is the port the
|
55
59
|
server is running on. Should be specified if
|
56
60
|
the server is running on a non-standard port.
|
61
|
+
<tt>SERVER_PROTOCOL</tt>:: A string representing the HTTP version used
|
62
|
+
for the request.
|
57
63
|
<tt>HTTP_</tt> Variables:: Variables corresponding to the
|
58
64
|
client-supplied HTTP request
|
59
65
|
headers (i.e., variables whose
|
@@ -67,40 +73,19 @@ below.
|
|
67
73
|
for specific behavior.
|
68
74
|
In addition to this, the Rack environment must include these
|
69
75
|
Rack-specific variables:
|
70
|
-
<tt>rack.version</tt>:: The Array representing this version of Rack
|
71
|
-
See Rack::VERSION, that corresponds to
|
72
|
-
the version of this SPEC.
|
73
76
|
<tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the
|
74
77
|
request URL.
|
75
78
|
<tt>rack.input</tt>:: See below, the input stream.
|
76
79
|
<tt>rack.errors</tt>:: See below, the error stream.
|
77
|
-
<tt>rack.
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
process, false otherwise.
|
83
|
-
<tt>rack.run_once</tt>:: true if the server expects
|
84
|
-
(but does not guarantee!) that the
|
85
|
-
application will only be invoked this one
|
86
|
-
time during the life of its containing
|
87
|
-
process. Normally, this will only be true
|
88
|
-
for a server based on CGI
|
89
|
-
(or something similar).
|
90
|
-
<tt>rack.hijack?</tt>:: present and true if the server supports
|
91
|
-
connection hijacking. See below, hijacking.
|
92
|
-
<tt>rack.hijack</tt>:: an object responding to #call that must be
|
93
|
-
called at least once before using
|
94
|
-
rack.hijack_io.
|
95
|
-
It is recommended #call return rack.hijack_io
|
96
|
-
as well as setting it in env if necessary.
|
97
|
-
<tt>rack.hijack_io</tt>:: if rack.hijack? is true, and rack.hijack
|
98
|
-
has received #call, this will contain
|
99
|
-
an object resembling an IO. See hijacking.
|
80
|
+
<tt>rack.hijack?</tt>:: See below, if present and true, indicates
|
81
|
+
that the server supports partial hijacking.
|
82
|
+
<tt>rack.hijack</tt>:: See below, if present, an object responding
|
83
|
+
to +call+ that is used to perform a full
|
84
|
+
hijack.
|
100
85
|
Additional environment specifications have approved to
|
101
|
-
standardized middleware APIs.
|
86
|
+
standardized middleware APIs. None of these are required to
|
102
87
|
be implemented by the server.
|
103
|
-
<tt>rack.session</tt>:: A hash
|
88
|
+
<tt>rack.session</tt>:: A hash-like interface for storing
|
104
89
|
request session data.
|
105
90
|
The store must implement:
|
106
91
|
store(key, value) (aliased as []=);
|
@@ -126,6 +111,8 @@ accepted specifications and must not be used otherwise.
|
|
126
111
|
The <tt>SERVER_PORT</tt> must be an Integer if set.
|
127
112
|
The <tt>SERVER_NAME</tt> must be a valid authority as defined by RFC7540.
|
128
113
|
The <tt>HTTP_HOST</tt> must be a valid authority as defined by RFC7540.
|
114
|
+
The <tt>SERVER_PROTOCOL</tt> must match the regexp <tt>HTTP/\d(\.\d)?</tt>.
|
115
|
+
If the <tt>HTTP_VERSION</tt> is present, it must equal the <tt>SERVER_PROTOCOL</tt>.
|
129
116
|
The environment must not contain the keys
|
130
117
|
<tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
|
131
118
|
(use the versions without <tt>HTTP_</tt>).
|
@@ -133,26 +120,32 @@ The CGI keys (named without a period) must have String values.
|
|
133
120
|
If the string values for CGI keys contain non-ASCII characters,
|
134
121
|
they should use ASCII-8BIT encoding.
|
135
122
|
There are the following restrictions:
|
136
|
-
* <tt>rack.version</tt> must be an array of Integers.
|
137
123
|
* <tt>rack.url_scheme</tt> must either be +http+ or +https+.
|
138
124
|
* There must be a valid input stream in <tt>rack.input</tt>.
|
139
125
|
* There must be a valid error stream in <tt>rack.errors</tt>.
|
140
|
-
* There may be a valid hijack
|
126
|
+
* There may be a valid hijack callback in <tt>rack.hijack</tt>
|
141
127
|
* The <tt>REQUEST_METHOD</tt> must be a valid token.
|
142
128
|
* The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt>
|
143
129
|
* The <tt>PATH_INFO</tt>, if non-empty, must start with <tt>/</tt>
|
144
130
|
* The <tt>CONTENT_LENGTH</tt>, if given, must consist of digits only.
|
145
131
|
* One of <tt>SCRIPT_NAME</tt> or <tt>PATH_INFO</tt> must be
|
146
|
-
set.
|
132
|
+
set. <tt>PATH_INFO</tt> should be <tt>/</tt> if
|
147
133
|
<tt>SCRIPT_NAME</tt> is empty.
|
148
134
|
<tt>SCRIPT_NAME</tt> never should be <tt>/</tt>, but instead be empty.
|
135
|
+
<tt>rack.response_finished</tt>:: An array of callables run by the server after the response has been
|
136
|
+
processed. This would typically be invoked after sending the response to the client, but it could also be
|
137
|
+
invoked if an error occurs while generating the response or sending the response; in that case, the error
|
138
|
+
argument will be a subclass of +Exception+.
|
139
|
+
The callables are invoked with +env, status, headers, error+ arguments and should not raise any
|
140
|
+
exceptions. They should be invoked in reverse order of registration.
|
141
|
+
|
149
142
|
=== The Input Stream
|
150
143
|
|
151
144
|
The input stream is an IO-like object which contains the raw HTTP
|
152
145
|
POST data.
|
153
146
|
When applicable, its external encoding must be "ASCII-8BIT" and it
|
154
147
|
must be opened in binary mode, for Ruby 1.9 compatibility.
|
155
|
-
The input stream must respond to +gets+, +each+,
|
148
|
+
The input stream must respond to +gets+, +each+, and +read+.
|
156
149
|
* +gets+ must be called without arguments and return a string,
|
157
150
|
or +nil+ on EOF.
|
158
151
|
* +read+ behaves like IO#read.
|
@@ -173,120 +166,175 @@ The input stream must respond to +gets+, +each+, +read+ and +rewind+.
|
|
173
166
|
If +buffer+ is given, then the read data will be placed
|
174
167
|
into +buffer+ instead of a newly created String object.
|
175
168
|
* +each+ must be called without arguments and only yield Strings.
|
176
|
-
* +
|
177
|
-
|
178
|
-
|
179
|
-
developers must buffer the input data into some rewindable object
|
180
|
-
if the underlying input stream is not rewindable.
|
181
|
-
* +close+ must never be called on the input stream.
|
169
|
+
* +close+ can be called on the input stream to indicate that the
|
170
|
+
any remaining input is not needed.
|
171
|
+
|
182
172
|
=== The Error Stream
|
173
|
+
|
183
174
|
The error stream must respond to +puts+, +write+ and +flush+.
|
184
175
|
* +puts+ must be called with a single argument that responds to +to_s+.
|
185
176
|
* +write+ must be called with a single argument that is a String.
|
186
177
|
* +flush+ must be called without arguments and must be called
|
187
178
|
in order to make the error appear for sure.
|
188
179
|
* +close+ must never be called on the error stream.
|
180
|
+
|
189
181
|
=== Hijacking
|
190
|
-
==== Request (before status)
|
191
|
-
If rack.hijack? is true then rack.hijack must respond to #call.
|
192
|
-
rack.hijack must return the io that will also be assigned (or is
|
193
|
-
already present, in rack.hijack_io.
|
194
182
|
|
195
|
-
|
196
|
-
|
197
|
-
|
183
|
+
The hijacking interfaces provides a means for an application to take
|
184
|
+
control of the HTTP connection. There are two distinct hijack
|
185
|
+
interfaces: full hijacking where the application takes over the raw
|
186
|
+
connection, and partial hijacking where the application takes over
|
187
|
+
just the response body stream. In both cases, the application is
|
188
|
+
responsible for closing the hijacked stream.
|
198
189
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
body
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
if the request env has <tt>rack.hijack?</tt> <tt>true</tt>.
|
241
|
-
==== Conventions
|
242
|
-
* Middleware should not use hijack unless it is handling the whole
|
243
|
-
response.
|
244
|
-
* Middleware may wrap the IO object for the response pattern.
|
245
|
-
* Middleware should not wrap the IO object for the request pattern. The
|
246
|
-
request pattern is intended to provide the hijacker with "raw tcp".
|
190
|
+
Full hijacking only works with HTTP/1. Partial hijacking is functionally
|
191
|
+
equivalent to streaming bodies, and is still optionally supported for
|
192
|
+
backwards compatibility with older Rack versions.
|
193
|
+
|
194
|
+
==== Full Hijack
|
195
|
+
|
196
|
+
Full hijack is used to completely take over an HTTP/1 connection. It
|
197
|
+
occurs before any headers are written and causes the request to
|
198
|
+
ignores any response generated by the application.
|
199
|
+
|
200
|
+
It is intended to be used when applications need access to raw HTTP/1
|
201
|
+
connection.
|
202
|
+
|
203
|
+
If +rack.hijack+ is present in +env+, it must respond to +call+
|
204
|
+
and return an +IO+ instance which can be used to read and write
|
205
|
+
to the underlying connection using HTTP/1 semantics and
|
206
|
+
formatting.
|
207
|
+
|
208
|
+
==== Partial Hijack
|
209
|
+
|
210
|
+
Partial hijack is used for bi-directional streaming of the request and
|
211
|
+
response body. It occurs after the status and headers are written by
|
212
|
+
the server and causes the server to ignore the Body of the response.
|
213
|
+
|
214
|
+
It is intended to be used when applications need bi-directional
|
215
|
+
streaming.
|
216
|
+
|
217
|
+
If +rack.hijack?+ is present in +env+ and truthy,
|
218
|
+
an application may set the special response header +rack.hijack+
|
219
|
+
to an object that responds to +call+,
|
220
|
+
accepting a +stream+ argument.
|
221
|
+
|
222
|
+
After the response status and headers have been sent, this hijack
|
223
|
+
callback will be invoked with a +stream+ argument which follows the
|
224
|
+
same interface as outlined in "Streaming Body". Servers must
|
225
|
+
ignore the +body+ part of the response tuple when the
|
226
|
+
+rack.hijack+ response header is present. Using an empty +Array+
|
227
|
+
instance is recommended.
|
228
|
+
|
229
|
+
The special response header +rack.hijack+ must only be set
|
230
|
+
if the request +env+ has a truthy +rack.hijack?+.
|
247
231
|
== The Response
|
232
|
+
|
248
233
|
=== The Status
|
249
|
-
|
250
|
-
greater than or equal to
|
234
|
+
|
235
|
+
This is an HTTP status. It must be an Integer greater than or equal to
|
236
|
+
100.
|
237
|
+
|
251
238
|
=== The Headers
|
252
|
-
|
239
|
+
|
240
|
+
The headers must be a unfrozen Hash.
|
253
241
|
The header keys must be Strings.
|
254
242
|
Special headers starting "rack." are for communicating with the
|
255
243
|
server, and must not be sent back to the client.
|
256
244
|
The header must not contain a +Status+ key.
|
257
|
-
|
245
|
+
Header keys must conform to RFC7230 token specification, i.e. cannot
|
258
246
|
contain non-printable ASCII, DQUOTE or "(),/:;<=>?@[\]{}".
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
247
|
+
Header keys must not contain uppercase ASCII characters (A-Z).
|
248
|
+
Header values must be either a String instance,
|
249
|
+
or an Array of String instances,
|
250
|
+
such that each String instance must not contain characters below 037.
|
251
|
+
|
252
|
+
=== The content-type
|
253
|
+
|
254
|
+
There must not be a <tt>content-type</tt> header key when the +Status+ is 1xx,
|
255
|
+
204, or 304.
|
256
|
+
|
257
|
+
=== The content-length
|
258
|
+
|
259
|
+
There must not be a <tt>content-length</tt> header key when the
|
260
|
+
+Status+ is 1xx, 204, or 304.
|
261
|
+
|
269
262
|
=== The Body
|
270
|
-
|
263
|
+
|
264
|
+
The Body is typically an +Array+ of +String+ instances, an enumerable
|
265
|
+
that yields +String+ instances, a +Proc+ instance, or a File-like
|
266
|
+
object.
|
267
|
+
|
268
|
+
The Body must respond to +each+ or +call+. It may optionally respond
|
269
|
+
to +to_path+ or +to_ary+. A Body that responds to +each+ is considered
|
270
|
+
to be an Enumerable Body. A Body that responds to +call+ is considered
|
271
|
+
to be a Streaming Body.
|
272
|
+
|
273
|
+
A Body that responds to both +each+ and +call+ must be treated as an
|
274
|
+
Enumerable Body, not a Streaming Body. If it responds to +each+, you
|
275
|
+
must call +each+ and not +call+. If the Body doesn't respond to
|
276
|
+
+each+, then you can assume it responds to +call+.
|
277
|
+
|
278
|
+
The Body must either be consumed or returned. The Body is consumed by
|
279
|
+
optionally calling either +each+ or +call+.
|
280
|
+
Then, if the Body responds to +close+, it must be called to release
|
281
|
+
any resources associated with the generation of the body.
|
282
|
+
In other words, +close+ must always be called at least once; typically
|
283
|
+
after the web server has sent the response to the client, but also in
|
284
|
+
cases where the Rack application makes internal/virtual requests and
|
285
|
+
discards the response.
|
286
|
+
|
287
|
+
|
288
|
+
After calling +close+, the Body is considered closed and should not
|
289
|
+
be consumed again.
|
290
|
+
If the original Body is replaced by a new Body, the new Body must
|
291
|
+
also consume the original Body by calling +close+ if possible.
|
292
|
+
|
293
|
+
If the Body responds to +to_path+, it must return a +String+
|
294
|
+
path for the local file system whose contents are identical
|
295
|
+
to that produced by calling +each+; this may be used by the
|
296
|
+
server as an alternative, possibly more efficient way to
|
297
|
+
transport the response. The +to_path+ method does not consume
|
298
|
+
the body.
|
299
|
+
|
300
|
+
==== Enumerable Body
|
301
|
+
|
302
|
+
The Enumerable Body must respond to +each+.
|
303
|
+
It must only be called once.
|
304
|
+
It must not be called after being closed.
|
271
305
|
and must only yield String values.
|
272
306
|
|
273
307
|
The Body itself should not be an instance of String, as this will
|
274
308
|
break in Ruby 1.9.
|
275
309
|
|
276
|
-
|
277
|
-
|
278
|
-
|
310
|
+
Middleware must not call +each+ directly on the Body.
|
311
|
+
Instead, middleware can return a new Body that calls +each+ on the
|
312
|
+
original Body, yielding at least once per iteration.
|
279
313
|
|
280
|
-
If the Body responds to +
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
314
|
+
If the Body responds to +to_ary+, it must return an +Array+ whose
|
315
|
+
contents are identical to that produced by calling +each+.
|
316
|
+
Middleware may call +to_ary+ directly on the Body and return a new
|
317
|
+
Body in its place. In other words, middleware can only process the
|
318
|
+
Body directly if it responds to +to_ary+. If the Body responds to both
|
319
|
+
+to_ary+ and +close+, its implementation of +to_ary+ must call
|
320
|
+
+close+.
|
321
|
+
|
322
|
+
==== Streaming Body
|
323
|
+
|
324
|
+
The Streaming Body must respond to +call+.
|
325
|
+
It must only be called once.
|
326
|
+
It must not be called after being closed.
|
327
|
+
It takes a +stream+ argument.
|
328
|
+
|
329
|
+
The +stream+ argument must implement:
|
330
|
+
<tt>read, write, <<, flush, close, close_read, close_write, closed?</tt>
|
331
|
+
|
332
|
+
The semantics of these IO methods must be a best effort match to
|
333
|
+
those of a normal Ruby IO or Socket object, using standard arguments
|
334
|
+
and raising standard exceptions. Servers are encouraged to simply
|
335
|
+
pass on real IO objects, although it is recognized that this approach
|
336
|
+
is not directly compatible with HTTP/2.
|
285
337
|
|
286
|
-
The Body commonly is an Array of Strings, the application
|
287
|
-
instance itself, or a File-like object.
|
288
338
|
== Thanks
|
289
|
-
Some parts of this specification are adopted from
|
290
|
-
|
291
|
-
v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank
|
292
|
-
everyone involved in that effort.
|
339
|
+
Some parts of this specification are adopted from {PEP 333 – Python Web Server Gateway Interface v1.0}[https://peps.python.org/pep-0333/]
|
340
|
+
I'd like to thank everyone involved in that effort.
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../../constants'
|
4
|
+
|
3
5
|
module Rack
|
4
6
|
module Auth
|
5
7
|
# Rack::Auth::AbstractHandler implements common authentication functionality.
|
@@ -21,7 +23,7 @@ module Rack
|
|
21
23
|
return [ 401,
|
22
24
|
{ CONTENT_TYPE => 'text/plain',
|
23
25
|
CONTENT_LENGTH => '0',
|
24
|
-
'
|
26
|
+
'www-authenticate' => www_authenticate.to_s },
|
25
27
|
[]
|
26
28
|
]
|
27
29
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative '../../request'
|
4
|
+
|
3
5
|
module Rack
|
4
6
|
module Auth
|
5
7
|
class AbstractRequest
|
@@ -25,7 +27,7 @@ module Rack
|
|
25
27
|
end
|
26
28
|
|
27
29
|
def scheme
|
28
|
-
@scheme ||= parts.first
|
30
|
+
@scheme ||= parts.first&.downcase
|
29
31
|
end
|
30
32
|
|
31
33
|
def params
|
data/lib/rack/auth/basic.rb
CHANGED
data/lib/rack/auth/digest/md5.rb
CHANGED
@@ -1,131 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require_relative '../abstract/handler'
|
4
|
-
require_relative 'request'
|
5
|
-
require_relative 'params'
|
6
|
-
require_relative 'nonce'
|
7
|
-
require 'digest/md5'
|
8
|
-
|
9
|
-
module Rack
|
10
|
-
module Auth
|
11
|
-
module Digest
|
12
|
-
# Rack::Auth::Digest::MD5 implements the MD5 algorithm version of
|
13
|
-
# HTTP Digest Authentication, as per RFC 2617.
|
14
|
-
#
|
15
|
-
# Initialize with the [Rack] application that you want protecting,
|
16
|
-
# and a block that looks up a plaintext password for a given username.
|
17
|
-
#
|
18
|
-
# +opaque+ needs to be set to a constant base64/hexadecimal string.
|
19
|
-
#
|
20
|
-
class MD5 < AbstractHandler
|
21
|
-
|
22
|
-
attr_accessor :opaque
|
23
|
-
|
24
|
-
attr_writer :passwords_hashed
|
25
|
-
|
26
|
-
def initialize(app, realm = nil, opaque = nil, &authenticator)
|
27
|
-
@passwords_hashed = nil
|
28
|
-
if opaque.nil? and realm.respond_to? :values_at
|
29
|
-
realm, opaque, @passwords_hashed = realm.values_at :realm, :opaque, :passwords_hashed
|
30
|
-
end
|
31
|
-
super(app, realm, &authenticator)
|
32
|
-
@opaque = opaque
|
33
|
-
end
|
34
|
-
|
35
|
-
def passwords_hashed?
|
36
|
-
!!@passwords_hashed
|
37
|
-
end
|
38
|
-
|
39
|
-
def call(env)
|
40
|
-
auth = Request.new(env)
|
41
|
-
|
42
|
-
unless auth.provided?
|
43
|
-
return unauthorized
|
44
|
-
end
|
45
|
-
|
46
|
-
if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth)
|
47
|
-
return bad_request
|
48
|
-
end
|
49
|
-
|
50
|
-
if valid?(auth)
|
51
|
-
if auth.nonce.stale?
|
52
|
-
return unauthorized(challenge(stale: true))
|
53
|
-
else
|
54
|
-
env['REMOTE_USER'] = auth.username
|
55
|
-
|
56
|
-
return @app.call(env)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
unauthorized
|
61
|
-
end
|
62
|
-
|
63
|
-
|
64
|
-
private
|
65
|
-
|
66
|
-
QOP = 'auth'
|
67
|
-
|
68
|
-
def params(hash = {})
|
69
|
-
Params.new do |params|
|
70
|
-
params['realm'] = realm
|
71
|
-
params['nonce'] = Nonce.new.to_s
|
72
|
-
params['opaque'] = H(opaque)
|
73
|
-
params['qop'] = QOP
|
74
|
-
|
75
|
-
hash.each { |k, v| params[k] = v }
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def challenge(hash = {})
|
80
|
-
"Digest #{params(hash)}"
|
81
|
-
end
|
82
|
-
|
83
|
-
def valid?(auth)
|
84
|
-
valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth)
|
85
|
-
end
|
86
|
-
|
87
|
-
def valid_qop?(auth)
|
88
|
-
QOP == auth.qop
|
89
|
-
end
|
90
|
-
|
91
|
-
def valid_opaque?(auth)
|
92
|
-
H(opaque) == auth.opaque
|
93
|
-
end
|
94
|
-
|
95
|
-
def valid_nonce?(auth)
|
96
|
-
auth.nonce.valid?
|
97
|
-
end
|
98
|
-
|
99
|
-
def valid_digest?(auth)
|
100
|
-
pw = @authenticator.call(auth.username)
|
101
|
-
pw && Rack::Utils.secure_compare(digest(auth, pw), auth.response)
|
102
|
-
end
|
103
|
-
|
104
|
-
def md5(data)
|
105
|
-
::Digest::MD5.hexdigest(data)
|
106
|
-
end
|
107
|
-
|
108
|
-
alias :H :md5
|
109
|
-
|
110
|
-
def KD(secret, data)
|
111
|
-
H "#{secret}:#{data}"
|
112
|
-
end
|
113
|
-
|
114
|
-
def A1(auth, password)
|
115
|
-
"#{auth.username}:#{auth.realm}:#{password}"
|
116
|
-
end
|
117
|
-
|
118
|
-
def A2(auth)
|
119
|
-
"#{auth.method}:#{auth.uri}"
|
120
|
-
end
|
121
|
-
|
122
|
-
def digest(auth, password)
|
123
|
-
password_hash = passwords_hashed? ? password : H(A1(auth, password))
|
124
|
-
|
125
|
-
KD password_hash, "#{auth.nonce}:#{auth.nc}:#{auth.cnonce}:#{QOP}:#{H A2(auth)}"
|
126
|
-
end
|
127
|
-
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
1
|
+
require_relative '../digest'
|
@@ -1,54 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'digest/md5'
|
4
|
-
require 'base64'
|
5
|
-
|
6
|
-
module Rack
|
7
|
-
module Auth
|
8
|
-
module Digest
|
9
|
-
# Rack::Auth::Digest::Nonce is the default nonce generator for the
|
10
|
-
# Rack::Auth::Digest::MD5 authentication handler.
|
11
|
-
#
|
12
|
-
# +private_key+ needs to set to a constant string.
|
13
|
-
#
|
14
|
-
# +time_limit+ can be optionally set to an integer (number of seconds),
|
15
|
-
# to limit the validity of the generated nonces.
|
16
|
-
|
17
|
-
class Nonce
|
18
|
-
|
19
|
-
class << self
|
20
|
-
attr_accessor :private_key, :time_limit
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.parse(string)
|
24
|
-
new(*Base64.decode64(string).split(' ', 2))
|
25
|
-
end
|
26
|
-
|
27
|
-
def initialize(timestamp = Time.now, given_digest = nil)
|
28
|
-
@timestamp, @given_digest = timestamp.to_i, given_digest
|
29
|
-
end
|
30
|
-
|
31
|
-
def to_s
|
32
|
-
Base64.encode64("#{@timestamp} #{digest}").strip
|
33
|
-
end
|
34
|
-
|
35
|
-
def digest
|
36
|
-
::Digest::MD5.hexdigest("#{@timestamp}:#{self.class.private_key}")
|
37
|
-
end
|
38
|
-
|
39
|
-
def valid?
|
40
|
-
digest == @given_digest
|
41
|
-
end
|
42
|
-
|
43
|
-
def stale?
|
44
|
-
!self.class.time_limit.nil? && (Time.now.to_i - @timestamp) > self.class.time_limit
|
45
|
-
end
|
46
|
-
|
47
|
-
def fresh?
|
48
|
-
!stale?
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
1
|
+
require_relative '../digest'
|