rack 2.0.7 → 2.0.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 116f4a51b2ae4bf5127f7609fe8bc0586647cd05
4
- data.tar.gz: aabc7fa3253cc8696e1d1f0e7b175656ef39b8a7
2
+ SHA256:
3
+ metadata.gz: e9142cfb8ba777286d8118d2b094a23c1fe4698b302c99966cb80670041c67f5
4
+ data.tar.gz: 341991ef42232bfecf702d98a17e671ffbbf6a95a8ff70bcca40f7c9aa9f5e85
5
5
  SHA512:
6
- metadata.gz: 1e2ecf2098113d2f435bd2e299cb207701960cd08225bd4bcc2953f46955f71225935c545c2efd35a0d288510435d56486ffc3e6c6cca9051695c70751447f85
7
- data.tar.gz: ff2e93c1e9989f628523a64693102ca05fdcd7749878710c2c4af44c84aa9eb1ac0af92817dbb251a5da929b09a35ea288ee20a0896d49771a41295917a18ca2
6
+ metadata.gz: 012e3ac8b25a2fa3c75e7cfbed5f5f4875010ecf96ee887aa9d4ed844badc614fa7decf04bfb889983004d0c310780763e8f9328c4dfb5243388a086f05ef059
7
+ data.tar.gz: 312252b7e153667c49c11fea9bdceaf679813d6e3176c6e8599fbe4741c326abc43510c3c0a48136a36d2027393e8286f11771a5c353c68269088a6f606b8372
data/SPEC CHANGED
@@ -60,8 +60,8 @@ below.
60
60
  the presence or absence of the
61
61
  appropriate HTTP header in the
62
62
  request. See
63
- {https://tools.ietf.org/html/rfc3875#section-4.1.18
64
- RFC3875 section 4.1.18} for
63
+ <a href="https://tools.ietf.org/html/rfc3875#section-4.1.18">
64
+ RFC3875 section 4.1.18</a> for
65
65
  specific behavior.
66
66
  In addition to this, the Rack environment must include these
67
67
  Rack-specific variables:
@@ -98,12 +98,13 @@ Rack-specific variables:
98
98
  Additional environment specifications have approved to
99
99
  standardized middleware APIs. None of these are required to
100
100
  be implemented by the server.
101
- <tt>rack.session</tt>:: A hash like interface for storing request session data.
101
+ <tt>rack.session</tt>:: A hash like interface for storing
102
+ request session data.
102
103
  The store must implement:
103
- store(key, value) (aliased as []=);
104
- fetch(key, default = nil) (aliased as []);
105
- delete(key);
106
- clear;
104
+ store(key, value) (aliased as []=);
105
+ fetch(key, default = nil) (aliased as []);
106
+ delete(key);
107
+ clear;
107
108
  <tt>rack.logger</tt>:: A common object interface for logging messages.
108
109
  The object must implement:
109
110
  info(message, &block)
@@ -18,7 +18,7 @@ module Rack
18
18
  VERSION.join(".")
19
19
  end
20
20
 
21
- RELEASE = "2.0.7"
21
+ RELEASE = "2.0.8"
22
22
 
23
23
  # Return the Rack release as a dotted string.
24
24
  def self.release
@@ -6,11 +6,38 @@ require 'time'
6
6
  require 'rack/request'
7
7
  require 'rack/response'
8
8
  require 'securerandom'
9
+ require 'digest/sha2'
9
10
 
10
11
  module Rack
11
12
 
12
13
  module Session
13
14
 
15
+ class SessionId
16
+ ID_VERSION = 2
17
+
18
+ attr_reader :public_id
19
+
20
+ def initialize(public_id)
21
+ @public_id = public_id
22
+ end
23
+
24
+ def private_id
25
+ "#{ID_VERSION}::#{hash_sid(public_id)}"
26
+ end
27
+
28
+ alias :cookie_value :public_id
29
+
30
+ def empty?; false; end
31
+ def to_s; raise; end
32
+ def inspect; public_id.inspect; end
33
+
34
+ private
35
+
36
+ def hash_sid(sid)
37
+ Digest::SHA256.hexdigest(sid)
38
+ end
39
+ end
40
+
14
41
  module Abstract
15
42
  # SessionHash is responsible to lazily load the session from store.
16
43
 
@@ -357,7 +384,7 @@ module Rack
357
384
  req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
358
385
  else
359
386
  cookie = Hash.new
360
- cookie[:value] = data
387
+ cookie[:value] = cookie_value(data)
361
388
  cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
362
389
  cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
363
390
  set_cookie(req, res, cookie.merge!(options))
@@ -365,6 +392,10 @@ module Rack
365
392
  end
366
393
  public :commit_session
367
394
 
395
+ def cookie_value(data)
396
+ data
397
+ end
398
+
368
399
  # Sets the cookie back to the client with session id. We skip the cookie
369
400
  # setting if the value didn't change (sid is the same) or expires was given.
370
401
 
@@ -406,6 +437,40 @@ module Rack
406
437
  end
407
438
  end
408
439
 
440
+ class PersistedSecure < Persisted
441
+ class SecureSessionHash < SessionHash
442
+ def [](key)
443
+ if key == "session_id"
444
+ load_for_read!
445
+ id.public_id
446
+ else
447
+ super
448
+ end
449
+ end
450
+ end
451
+
452
+ def generate_sid(*)
453
+ public_id = super
454
+
455
+ SessionId.new(public_id)
456
+ end
457
+
458
+ def extract_session_id(*)
459
+ public_id = super
460
+ public_id && SessionId.new(public_id)
461
+ end
462
+
463
+ private
464
+
465
+ def session_class
466
+ SecureSessionHash
467
+ end
468
+
469
+ def cookie_value(data)
470
+ data.cookie_value
471
+ end
472
+ end
473
+
409
474
  class ID < Persisted
410
475
  def self.inherited(klass)
411
476
  k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
@@ -45,7 +45,7 @@ module Rack
45
45
  # })
46
46
  #
47
47
 
48
- class Cookie < Abstract::Persisted
48
+ class Cookie < Abstract::PersistedSecure
49
49
  # Encode session cookies as Base64
50
50
  class Base64
51
51
  def encode(str)
@@ -153,6 +153,15 @@ module Rack
153
153
  data
154
154
  end
155
155
 
156
+ class SessionId < DelegateClass(Session::SessionId)
157
+ attr_reader :cookie_value
158
+
159
+ def initialize(session_id, cookie_value)
160
+ super(session_id)
161
+ @cookie_value = cookie_value
162
+ end
163
+ end
164
+
156
165
  def write_session(req, session_id, session, options)
157
166
  session = session.merge("session_id" => session_id)
158
167
  session_data = coder.encode(session)
@@ -165,7 +174,7 @@ module Rack
165
174
  req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
166
175
  nil
167
176
  else
168
- session_data
177
+ SessionId.new(session_id, session_data)
169
178
  end
170
179
  end
171
180
 
@@ -19,7 +19,7 @@ module Rack
19
19
  # Note that memcache does drop data before it may be listed to expire. For
20
20
  # a full description of behaviour, please see memcache's documentation.
21
21
 
22
- class Memcache < Abstract::ID
22
+ class Memcache < Abstract::PersistedSecure
23
23
  attr_reader :mutex, :pool
24
24
 
25
25
  DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
@@ -42,15 +42,15 @@ module Rack
42
42
  def generate_sid
43
43
  loop do
44
44
  sid = super
45
- break sid unless @pool.get(sid, true)
45
+ break sid unless @pool.get(sid.private_id, true)
46
46
  end
47
47
  end
48
48
 
49
- def get_session(env, sid)
50
- with_lock(env) do
51
- unless sid and session = @pool.get(sid)
49
+ def find_session(req, sid)
50
+ with_lock(req) do
51
+ unless sid and session = get_session_with_fallback(sid)
52
52
  sid, session = generate_sid, {}
53
- unless /^STORED/ =~ @pool.add(sid, session)
53
+ unless /^STORED/ =~ @pool.add(sid.private_id, session)
54
54
  raise "Session collision on '#{sid.inspect}'"
55
55
  end
56
56
  end
@@ -58,25 +58,26 @@ module Rack
58
58
  end
59
59
  end
60
60
 
61
- def set_session(env, session_id, new_session, options)
61
+ def write_session(req, session_id, new_session, options)
62
62
  expiry = options[:expire_after]
63
63
  expiry = expiry.nil? ? 0 : expiry + 1
64
64
 
65
- with_lock(env) do
66
- @pool.set session_id, new_session, expiry
65
+ with_lock(req) do
66
+ @pool.set session_id.private_id, new_session, expiry
67
67
  session_id
68
68
  end
69
69
  end
70
70
 
71
- def destroy_session(env, session_id, options)
72
- with_lock(env) do
73
- @pool.delete(session_id)
71
+ def delete_session(req, session_id, options)
72
+ with_lock(req) do
73
+ @pool.delete(session_id.public_id)
74
+ @pool.delete(session_id.private_id)
74
75
  generate_sid unless options[:drop]
75
76
  end
76
77
  end
77
78
 
78
- def with_lock(env)
79
- @mutex.lock if env[RACK_MULTITHREAD]
79
+ def with_lock(req)
80
+ @mutex.lock if req.multithread?
80
81
  yield
81
82
  rescue MemCache::MemCacheError, Errno::ECONNREFUSED
82
83
  if $VERBOSE
@@ -88,6 +89,11 @@ module Rack
88
89
  @mutex.unlock if @mutex.locked?
89
90
  end
90
91
 
92
+ private
93
+
94
+ def get_session_with_fallback(sid)
95
+ @pool.get(sid.private_id) || @pool.get(sid.public_id)
96
+ end
91
97
  end
92
98
  end
93
99
  end
@@ -24,7 +24,7 @@ module Rack
24
24
  # )
25
25
  # Rack::Handler::WEBrick.run sessioned
26
26
 
27
- class Pool < Abstract::Persisted
27
+ class Pool < Abstract::PersistedSecure
28
28
  attr_reader :mutex, :pool
29
29
  DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
30
30
 
@@ -37,15 +37,15 @@ module Rack
37
37
  def generate_sid
38
38
  loop do
39
39
  sid = super
40
- break sid unless @pool.key? sid
40
+ break sid unless @pool.key? sid.private_id
41
41
  end
42
42
  end
43
43
 
44
44
  def find_session(req, sid)
45
45
  with_lock(req) do
46
- unless sid and session = @pool[sid]
46
+ unless sid and session = get_session_with_fallback(sid)
47
47
  sid, session = generate_sid, {}
48
- @pool.store sid, session
48
+ @pool.store sid.private_id, session
49
49
  end
50
50
  [sid, session]
51
51
  end
@@ -53,14 +53,15 @@ module Rack
53
53
 
54
54
  def write_session(req, session_id, new_session, options)
55
55
  with_lock(req) do
56
- @pool.store session_id, new_session
56
+ @pool.store session_id.private_id, new_session
57
57
  session_id
58
58
  end
59
59
  end
60
60
 
61
61
  def delete_session(req, session_id, options)
62
62
  with_lock(req) do
63
- @pool.delete(session_id)
63
+ @pool.delete(session_id.public_id)
64
+ @pool.delete(session_id.private_id)
64
65
  generate_sid unless options[:drop]
65
66
  end
66
67
  end
@@ -71,6 +72,12 @@ module Rack
71
72
  ensure
72
73
  @mutex.unlock if @mutex.locked?
73
74
  end
75
+
76
+ private
77
+
78
+ def get_session_with_fallback(sid)
79
+ @pool[sid.private_id] || @pool[sid.public_id]
80
+ end
74
81
  end
75
82
  end
76
83
  end
@@ -226,15 +226,52 @@ begin
226
226
  req = Rack::MockRequest.new(pool)
227
227
 
228
228
  res0 = req.get("/")
229
- session_id = (cookie = res0["Set-Cookie"])[session_match, 1]
230
- ses0 = pool.pool.get(session_id, true)
229
+ session_id = Rack::Session::SessionId.new (cookie = res0["Set-Cookie"])[session_match, 1]
230
+ ses0 = pool.pool.get(session_id.private_id, true)
231
231
 
232
232
  req.get("/", "HTTP_COOKIE" => cookie)
233
- ses1 = pool.pool.get(session_id, true)
233
+ ses1 = pool.pool.get(session_id.private_id, true)
234
234
 
235
235
  ses1.wont_equal ses0
236
236
  end
237
237
 
238
+ it "can read the session with the legacy id" do
239
+ pool = Rack::Session::Memcache.new(incrementor)
240
+ req = Rack::MockRequest.new(pool)
241
+
242
+ res0 = req.get("/")
243
+ cookie = res0["Set-Cookie"]
244
+ session_id = Rack::Session::SessionId.new cookie[session_match, 1]
245
+ ses0 = pool.pool.get(session_id.private_id, true)
246
+ pool.pool.set(session_id.public_id, ses0, 0, true)
247
+ pool.pool.delete(session_id.private_id)
248
+
249
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
250
+ res1["Set-Cookie"].must_be_nil
251
+ res1.body.must_equal '{"counter"=>2}'
252
+ pool.pool.get(session_id.private_id, true).wont_be_nil
253
+ end
254
+
255
+ it "drops the session in the legacy id as well" do
256
+ pool = Rack::Session::Memcache.new(incrementor)
257
+ req = Rack::MockRequest.new(pool)
258
+ drop = Rack::Utils::Context.new(pool, drop_session)
259
+ dreq = Rack::MockRequest.new(drop)
260
+
261
+ res0 = req.get("/")
262
+ cookie = res0["Set-Cookie"]
263
+ session_id = Rack::Session::SessionId.new cookie[session_match, 1]
264
+ ses0 = pool.pool.get(session_id.private_id, true)
265
+ pool.pool.set(session_id.public_id, ses0, 0, true)
266
+ pool.pool.delete(session_id.private_id)
267
+
268
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
269
+ res2["Set-Cookie"].must_be_nil
270
+ res2.body.must_equal '{"counter"=>2}'
271
+ pool.pool.get(session_id.private_id, true).must_be_nil
272
+ pool.pool.get(session_id.public_id, true).must_be_nil
273
+ end
274
+
238
275
  # anyone know how to do this better?
239
276
  it "cleanly merges sessions when multithreaded" do
240
277
  skip unless $DEBUG
@@ -6,7 +6,7 @@ require 'rack/session/pool'
6
6
 
7
7
  describe Rack::Session::Pool do
8
8
  session_key = Rack::Session::Pool::DEFAULT_OPTIONS[:key]
9
- session_match = /#{session_key}=[0-9a-fA-F]+;/
9
+ session_match = /#{session_key}=([0-9a-fA-F]+);/
10
10
 
11
11
  incrementor = lambda do |env|
12
12
  env["rack.session"]["counter"] ||= 0
@@ -14,7 +14,7 @@ describe Rack::Session::Pool do
14
14
  Rack::Response.new(env["rack.session"].inspect).to_a
15
15
  end
16
16
 
17
- session_id = Rack::Lint.new(lambda do |env|
17
+ get_session_id = Rack::Lint.new(lambda do |env|
18
18
  Rack::Response.new(env["rack.session"].inspect).to_a
19
19
  end)
20
20
 
@@ -143,6 +143,43 @@ describe Rack::Session::Pool do
143
143
  pool.pool.size.must_equal 1
144
144
  end
145
145
 
146
+ it "can read the session with the legacy id" do
147
+ pool = Rack::Session::Pool.new(incrementor)
148
+ req = Rack::MockRequest.new(pool)
149
+
150
+ res0 = req.get("/")
151
+ cookie = res0["Set-Cookie"]
152
+ session_id = Rack::Session::SessionId.new cookie[session_match, 1]
153
+ ses0 = pool.pool[session_id.private_id]
154
+ pool.pool[session_id.public_id] = ses0
155
+ pool.pool.delete(session_id.private_id)
156
+
157
+ res1 = req.get("/", "HTTP_COOKIE" => cookie)
158
+ res1["Set-Cookie"].must_be_nil
159
+ res1.body.must_equal '{"counter"=>2}'
160
+ pool.pool[session_id.private_id].wont_be_nil
161
+ end
162
+
163
+ it "drops the session in the legacy id as well" do
164
+ pool = Rack::Session::Pool.new(incrementor)
165
+ req = Rack::MockRequest.new(pool)
166
+ drop = Rack::Utils::Context.new(pool, drop_session)
167
+ dreq = Rack::MockRequest.new(drop)
168
+
169
+ res0 = req.get("/")
170
+ cookie = res0["Set-Cookie"]
171
+ session_id = Rack::Session::SessionId.new cookie[session_match, 1]
172
+ ses0 = pool.pool[session_id.private_id]
173
+ pool.pool[session_id.public_id] = ses0
174
+ pool.pool.delete(session_id.private_id)
175
+
176
+ res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
177
+ res2["Set-Cookie"].must_be_nil
178
+ res2.body.must_equal '{"counter"=>2}'
179
+ pool.pool[session_id.private_id].must_be_nil
180
+ pool.pool[session_id.public_id].must_be_nil
181
+ end
182
+
146
183
  # anyone know how to do this better?
147
184
  it "should merge sessions when multithreaded" do
148
185
  unless $DEBUG
@@ -191,7 +228,7 @@ describe Rack::Session::Pool do
191
228
  end
192
229
 
193
230
  it "does not return a cookie if cookie was not written (only read)" do
194
- app = Rack::Session::Pool.new(session_id)
231
+ app = Rack::Session::Pool.new(get_session_id)
195
232
  res = Rack::MockRequest.new(app).get("/")
196
233
  res["Set-Cookie"].must_be_nil
197
234
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.7
4
+ version: 2.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leah Neukirchen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-02 00:00:00.000000000 Z
11
+ date: 2019-12-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -274,60 +274,59 @@ required_rubygems_version: !ruby/object:Gem::Requirement
274
274
  - !ruby/object:Gem::Version
275
275
  version: '0'
276
276
  requirements: []
277
- rubyforge_project:
278
- rubygems_version: 2.6.13
277
+ rubygems_version: 3.0.3
279
278
  signing_key:
280
279
  specification_version: 4
281
280
  summary: a modular Ruby webserver interface
282
281
  test_files:
283
- - test/spec_auth_basic.rb
284
- - test/spec_auth_digest.rb
285
- - test/spec_body_proxy.rb
286
- - test/spec_builder.rb
287
- - test/spec_cascade.rb
288
- - test/spec_cgi.rb
289
- - test/spec_chunked.rb
290
- - test/spec_common_logger.rb
291
- - test/spec_conditional_get.rb
292
- - test/spec_config.rb
293
- - test/spec_content_length.rb
294
- - test/spec_content_type.rb
282
+ - test/spec_multipart.rb
295
283
  - test/spec_deflater.rb
296
- - test/spec_directory.rb
284
+ - test/spec_static.rb
285
+ - test/spec_session_cookie.rb
286
+ - test/spec_session_pool.rb
297
287
  - test/spec_etag.rb
298
- - test/spec_events.rb
299
- - test/spec_fastcgi.rb
300
- - test/spec_file.rb
288
+ - test/spec_version.rb
301
289
  - test/spec_handler.rb
302
- - test/spec_head.rb
303
- - test/spec_lint.rb
304
- - test/spec_lobster.rb
305
- - test/spec_lock.rb
306
- - test/spec_logger.rb
307
- - test/spec_media_type.rb
308
- - test/spec_method_override.rb
290
+ - test/spec_thin.rb
291
+ - test/spec_session_abstract_id.rb
309
292
  - test/spec_mime.rb
310
- - test/spec_mock.rb
311
- - test/spec_multipart.rb
312
- - test/spec_null_logger.rb
313
293
  - test/spec_recursive.rb
294
+ - test/spec_null_logger.rb
295
+ - test/spec_media_type.rb
296
+ - test/spec_cgi.rb
297
+ - test/spec_method_override.rb
298
+ - test/spec_content_type.rb
299
+ - test/spec_session_abstract_session_hash.rb
314
300
  - test/spec_request.rb
315
- - test/spec_response.rb
316
- - test/spec_rewindable_input.rb
301
+ - test/spec_chunked.rb
302
+ - test/spec_show_exceptions.rb
317
303
  - test/spec_runtime.rb
304
+ - test/spec_fastcgi.rb
305
+ - test/spec_common_logger.rb
306
+ - test/spec_builder.rb
307
+ - test/spec_config.rb
308
+ - test/spec_utils.rb
318
309
  - test/spec_sendfile.rb
310
+ - test/spec_lobster.rb
311
+ - test/spec_lint.rb
312
+ - test/spec_conditional_get.rb
313
+ - test/spec_tempfile_reaper.rb
314
+ - test/spec_mock.rb
319
315
  - test/spec_server.rb
320
- - test/spec_session_abstract_id.rb
321
- - test/spec_session_abstract_session_hash.rb
322
- - test/spec_session_cookie.rb
323
- - test/spec_session_memcache.rb
324
- - test/spec_session_pool.rb
325
- - test/spec_show_exceptions.rb
316
+ - test/spec_directory.rb
317
+ - test/spec_webrick.rb
318
+ - test/spec_response.rb
319
+ - test/spec_file.rb
326
320
  - test/spec_show_status.rb
327
- - test/spec_static.rb
328
- - test/spec_tempfile_reaper.rb
329
- - test/spec_thin.rb
321
+ - test/spec_body_proxy.rb
322
+ - test/spec_logger.rb
323
+ - test/spec_auth_digest.rb
330
324
  - test/spec_urlmap.rb
331
- - test/spec_utils.rb
332
- - test/spec_version.rb
333
- - test/spec_webrick.rb
325
+ - test/spec_events.rb
326
+ - test/spec_cascade.rb
327
+ - test/spec_auth_basic.rb
328
+ - test/spec_head.rb
329
+ - test/spec_lock.rb
330
+ - test/spec_rewindable_input.rb
331
+ - test/spec_session_memcache.rb
332
+ - test/spec_content_length.rb