rack 3.0.10 → 3.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,256 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'abstract/handler'
4
- require_relative 'abstract/request'
5
- require 'digest/md5'
6
- require 'base64'
7
-
8
- module Rack
9
- warn "Rack::Auth::Digest is deprecated and will be removed in Rack 3.1", uplevel: 1
10
-
11
- module Auth
12
- module Digest
13
- # Rack::Auth::Digest::Nonce is the default nonce generator for the
14
- # Rack::Auth::Digest::MD5 authentication handler.
15
- #
16
- # +private_key+ needs to set to a constant string.
17
- #
18
- # +time_limit+ can be optionally set to an integer (number of seconds),
19
- # to limit the validity of the generated nonces.
20
-
21
- class Nonce
22
-
23
- class << self
24
- attr_accessor :private_key, :time_limit
25
- end
26
-
27
- def self.parse(string)
28
- new(*Base64.decode64(string).split(' ', 2))
29
- end
30
-
31
- def initialize(timestamp = Time.now, given_digest = nil)
32
- @timestamp, @given_digest = timestamp.to_i, given_digest
33
- end
34
-
35
- def to_s
36
- Base64.encode64("#{@timestamp} #{digest}").strip
37
- end
38
-
39
- def digest
40
- ::Digest::MD5.hexdigest("#{@timestamp}:#{self.class.private_key}")
41
- end
42
-
43
- def valid?
44
- digest == @given_digest
45
- end
46
-
47
- def stale?
48
- !self.class.time_limit.nil? && (Time.now.to_i - @timestamp) > self.class.time_limit
49
- end
50
-
51
- def fresh?
52
- !stale?
53
- end
54
-
55
- end
56
-
57
- class Params < Hash
58
-
59
- def self.parse(str)
60
- Params[*split_header_value(str).map do |param|
61
- k, v = param.split('=', 2)
62
- [k, dequote(v)]
63
- end.flatten]
64
- end
65
-
66
- def self.dequote(str) # From WEBrick::HTTPUtils
67
- ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
68
- ret.gsub!(/\\(.)/, "\\1")
69
- ret
70
- end
71
-
72
- def self.split_header_value(str)
73
- str.scan(/\w+\=(?:"[^\"]+"|[^,]+)/n)
74
- end
75
-
76
- def initialize
77
- super()
78
-
79
- yield self if block_given?
80
- end
81
-
82
- def [](k)
83
- super k.to_s
84
- end
85
-
86
- def []=(k, v)
87
- super k.to_s, v.to_s
88
- end
89
-
90
- UNQUOTED = ['nc', 'stale']
91
-
92
- def to_s
93
- map do |k, v|
94
- "#{k}=#{(UNQUOTED.include?(k) ? v.to_s : quote(v))}"
95
- end.join(', ')
96
- end
97
-
98
- def quote(str) # From WEBrick::HTTPUtils
99
- '"' + str.gsub(/[\\\"]/o, "\\\1") + '"'
100
- end
101
-
102
- end
103
-
104
- class Request < Auth::AbstractRequest
105
- def method
106
- @env[RACK_METHODOVERRIDE_ORIGINAL_METHOD] || @env[REQUEST_METHOD]
107
- end
108
-
109
- def digest?
110
- "digest" == scheme
111
- end
112
-
113
- def correct_uri?
114
- request.fullpath == uri
115
- end
116
-
117
- def nonce
118
- @nonce ||= Nonce.parse(params['nonce'])
119
- end
120
-
121
- def params
122
- @params ||= Params.parse(parts.last)
123
- end
124
-
125
- def respond_to?(sym, *)
126
- super or params.has_key? sym.to_s
127
- end
128
-
129
- def method_missing(sym, *args)
130
- return super unless params.has_key?(key = sym.to_s)
131
- return params[key] if args.size == 0
132
- raise ArgumentError, "wrong number of arguments (#{args.size} for 0)"
133
- end
134
- end
135
-
136
- # Rack::Auth::Digest::MD5 implements the MD5 algorithm version of
137
- # HTTP Digest Authentication, as per RFC 2617.
138
- #
139
- # Initialize with the [Rack] application that you want protecting,
140
- # and a block that looks up a plaintext password for a given username.
141
- #
142
- # +opaque+ needs to be set to a constant base64/hexadecimal string.
143
- #
144
- class MD5 < AbstractHandler
145
-
146
- attr_accessor :opaque
147
-
148
- attr_writer :passwords_hashed
149
-
150
- def initialize(app, realm = nil, opaque = nil, &authenticator)
151
- @passwords_hashed = nil
152
- if opaque.nil? and realm.respond_to? :values_at
153
- realm, opaque, @passwords_hashed = realm.values_at :realm, :opaque, :passwords_hashed
154
- end
155
- super(app, realm, &authenticator)
156
- @opaque = opaque
157
- end
158
-
159
- def passwords_hashed?
160
- !!@passwords_hashed
161
- end
162
-
163
- def call(env)
164
- auth = Request.new(env)
165
-
166
- unless auth.provided?
167
- return unauthorized
168
- end
169
-
170
- if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth)
171
- return bad_request
172
- end
173
-
174
- if valid?(auth)
175
- if auth.nonce.stale?
176
- return unauthorized(challenge(stale: true))
177
- else
178
- env['REMOTE_USER'] = auth.username
179
-
180
- return @app.call(env)
181
- end
182
- end
183
-
184
- unauthorized
185
- end
186
-
187
-
188
- private
189
-
190
- QOP = 'auth'
191
-
192
- def params(hash = {})
193
- Params.new do |params|
194
- params['realm'] = realm
195
- params['nonce'] = Nonce.new.to_s
196
- params['opaque'] = H(opaque)
197
- params['qop'] = QOP
198
-
199
- hash.each { |k, v| params[k] = v }
200
- end
201
- end
202
-
203
- def challenge(hash = {})
204
- "Digest #{params(hash)}"
205
- end
206
-
207
- def valid?(auth)
208
- valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth)
209
- end
210
-
211
- def valid_qop?(auth)
212
- QOP == auth.qop
213
- end
214
-
215
- def valid_opaque?(auth)
216
- H(opaque) == auth.opaque
217
- end
218
-
219
- def valid_nonce?(auth)
220
- auth.nonce.valid?
221
- end
222
-
223
- def valid_digest?(auth)
224
- pw = @authenticator.call(auth.username)
225
- pw && Rack::Utils.secure_compare(digest(auth, pw), auth.response)
226
- end
227
-
228
- def md5(data)
229
- ::Digest::MD5.hexdigest(data)
230
- end
231
-
232
- alias :H :md5
233
-
234
- def KD(secret, data)
235
- H "#{secret}:#{data}"
236
- end
237
-
238
- def A1(auth, password)
239
- "#{auth.username}:#{auth.realm}:#{password}"
240
- end
241
-
242
- def A2(auth)
243
- "#{auth.method}:#{auth.uri}"
244
- end
245
-
246
- def digest(auth, password)
247
- password_hash = passwords_hashed? ? password : H(A1(auth, password))
248
-
249
- KD password_hash, "#{auth.nonce}:#{auth.nc}:#{auth.cnonce}:#{QOP}:#{H A2(auth)}"
250
- end
251
-
252
- end
253
- end
254
- end
255
- end
256
-
data/lib/rack/chunked.rb DELETED
@@ -1,120 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'constants'
4
- require_relative 'utils'
5
-
6
- module Rack
7
- warn "Rack::Chunked is deprecated and will be removed in Rack 3.1", uplevel: 1
8
-
9
- # Middleware that applies chunked transfer encoding to response bodies
10
- # when the response does not include a content-length header.
11
- #
12
- # This supports the trailer response header to allow the use of trailing
13
- # headers in the chunked encoding. However, using this requires you manually
14
- # specify a response body that supports a +trailers+ method. Example:
15
- #
16
- # [200, { 'trailer' => 'expires'}, ["Hello", "World"]]
17
- # # error raised
18
- #
19
- # body = ["Hello", "World"]
20
- # def body.trailers
21
- # { 'expires' => Time.now.to_s }
22
- # end
23
- # [200, { 'trailer' => 'expires'}, body]
24
- # # No exception raised
25
- class Chunked
26
- include Rack::Utils
27
-
28
- # A body wrapper that emits chunked responses.
29
- class Body
30
- TERM = "\r\n"
31
- TAIL = "0#{TERM}"
32
-
33
- # Store the response body to be chunked.
34
- def initialize(body)
35
- @body = body
36
- end
37
-
38
- # For each element yielded by the response body, yield
39
- # the element in chunked encoding.
40
- def each(&block)
41
- term = TERM
42
- @body.each do |chunk|
43
- size = chunk.bytesize
44
- next if size == 0
45
-
46
- yield [size.to_s(16), term, chunk.b, term].join
47
- end
48
- yield TAIL
49
- yield_trailers(&block)
50
- yield term
51
- end
52
-
53
- # Close the response body if the response body supports it.
54
- def close
55
- @body.close if @body.respond_to?(:close)
56
- end
57
-
58
- private
59
-
60
- # Do nothing as this class does not support trailer headers.
61
- def yield_trailers
62
- end
63
- end
64
-
65
- # A body wrapper that emits chunked responses and also supports
66
- # sending Trailer headers. Note that the response body provided to
67
- # initialize must have a +trailers+ method that returns a hash
68
- # of trailer headers, and the rack response itself should have a
69
- # Trailer header listing the headers that the +trailers+ method
70
- # will return.
71
- class TrailerBody < Body
72
- private
73
-
74
- # Yield strings for each trailer header.
75
- def yield_trailers
76
- @body.trailers.each_pair do |k, v|
77
- yield "#{k}: #{v}\r\n"
78
- end
79
- end
80
- end
81
-
82
- def initialize(app)
83
- @app = app
84
- end
85
-
86
- # Whether the HTTP version supports chunked encoding (HTTP 1.1 does).
87
- def chunkable_version?(ver)
88
- case ver
89
- # pre-HTTP/1.0 (informally "HTTP/0.9") HTTP requests did not have
90
- # a version (nor response headers)
91
- when 'HTTP/1.0', nil, 'HTTP/0.9'
92
- false
93
- else
94
- true
95
- end
96
- end
97
-
98
- # If the rack app returns a response that should have a body,
99
- # but does not have content-length or transfer-encoding headers,
100
- # modify the response to use chunked transfer-encoding.
101
- def call(env)
102
- status, headers, body = response = @app.call(env)
103
-
104
- if chunkable_version?(env[SERVER_PROTOCOL]) &&
105
- !STATUS_WITH_NO_ENTITY_BODY.key?(status.to_i) &&
106
- !headers[CONTENT_LENGTH] &&
107
- !headers[TRANSFER_ENCODING]
108
-
109
- headers[TRANSFER_ENCODING] = 'chunked'
110
- if headers['trailer']
111
- response[2] = TrailerBody.new(body)
112
- else
113
- response[2] = Body.new(body)
114
- end
115
- end
116
-
117
- response
118
- end
119
- end
120
- end
data/lib/rack/file.rb DELETED
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'files'
4
-
5
- module Rack
6
- warn "Rack::File is deprecated and will be removed in Rack 3.1", uplevel: 1
7
-
8
- File = Files
9
- end