rack 1.6.11 → 1.6.12
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 +4 -4
- data/lib/rack.rb +1 -1
- data/lib/rack/session/abstract/id.rb +79 -3
- data/lib/rack/session/cookie.rb +11 -2
- data/lib/rack/session/memcache.rb +12 -6
- data/lib/rack/session/pool.rb +13 -6
- data/rack.gemspec +1 -1
- data/test/spec_session_abstract_id.rb +1 -1
- data/test/spec_session_memcache.rb +40 -3
- data/test/spec_session_pool.rb +40 -3
- data/test/spec_showexceptions.rb +1 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f95a801d50bb534494f0a7c7bbddb845d4e3448e65041642e6dea7102ca1c3d
|
4
|
+
data.tar.gz: 41df2cb86bafdb3ea49aa84382700982451396babe777cd33331b8cf0373a413
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af0085562b2835171e9fd23d746c91c036d2031966f16f81c7c16cc82410d5414f9215b3e40a89021b5dfd1c1c76344afd22a966cbba4fc8d22ba61ae49b22cf
|
7
|
+
data.tar.gz: e992b79e53e683527b06d7129ff07faa0c1b0754287b3f485673de13f231a83628168dde620e89a897bb4fd407d5c0c6fc329962c62b8c845fb54a2ff1359d29
|
data/lib/rack.rb
CHANGED
@@ -9,11 +9,38 @@ begin
|
|
9
9
|
rescue LoadError
|
10
10
|
# We just won't get securerandom
|
11
11
|
end
|
12
|
+
require "digest/sha2"
|
12
13
|
|
13
14
|
module Rack
|
14
15
|
|
15
16
|
module Session
|
16
17
|
|
18
|
+
class SessionId
|
19
|
+
ID_VERSION = 2
|
20
|
+
|
21
|
+
attr_reader :public_id
|
22
|
+
|
23
|
+
def initialize(public_id)
|
24
|
+
@public_id = public_id
|
25
|
+
end
|
26
|
+
|
27
|
+
def private_id
|
28
|
+
"#{ID_VERSION}::#{hash_sid(public_id)}"
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :cookie_value :public_id
|
32
|
+
|
33
|
+
def empty?; false; end
|
34
|
+
def to_s; raise; end
|
35
|
+
def inspect; public_id.inspect; end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def hash_sid(sid)
|
40
|
+
Digest::SHA256.hexdigest(sid)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
17
44
|
module Abstract
|
18
45
|
ENV_SESSION_KEY = 'rack.session'.freeze
|
19
46
|
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
|
@@ -191,7 +218,7 @@ module Rack
|
|
191
218
|
# Not included by default; you must require 'rack/session/abstract/id'
|
192
219
|
# to use.
|
193
220
|
|
194
|
-
class
|
221
|
+
class Persisted
|
195
222
|
DEFAULT_OPTIONS = {
|
196
223
|
:key => 'rack.session',
|
197
224
|
:path => '/',
|
@@ -342,10 +369,10 @@ module Rack
|
|
342
369
|
if not data = set_session(env, session_id, session_data, options)
|
343
370
|
env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
344
371
|
elsif options[:defer] and not options[:renew]
|
345
|
-
env["rack.errors"].puts("Deferring cookie for #{session_id}") if $VERBOSE
|
372
|
+
env["rack.errors"].puts("Deferring cookie for #{session_id.public_id}") if $VERBOSE
|
346
373
|
else
|
347
374
|
cookie = Hash.new
|
348
|
-
cookie[:value] = data
|
375
|
+
cookie[:value] = cookie_value(data)
|
349
376
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
350
377
|
cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
|
351
378
|
set_cookie(env, headers, cookie.merge!(options))
|
@@ -354,6 +381,10 @@ module Rack
|
|
354
381
|
[status, headers, body]
|
355
382
|
end
|
356
383
|
|
384
|
+
def cookie_value(data)
|
385
|
+
data
|
386
|
+
end
|
387
|
+
|
357
388
|
# Sets the cookie back to the client with session id. We skip the cookie
|
358
389
|
# setting if the value didn't change (sid is the same) or expires was given.
|
359
390
|
|
@@ -394,6 +425,51 @@ module Rack
|
|
394
425
|
raise '#destroy_session not implemented'
|
395
426
|
end
|
396
427
|
end
|
428
|
+
|
429
|
+
class PersistedSecure < Persisted
|
430
|
+
class SecureSessionHash < SessionHash
|
431
|
+
def [](key)
|
432
|
+
if key == "session_id"
|
433
|
+
load_for_read!
|
434
|
+
id.public_id
|
435
|
+
else
|
436
|
+
super
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
def generate_sid(*)
|
442
|
+
public_id = super
|
443
|
+
|
444
|
+
SessionId.new(public_id)
|
445
|
+
end
|
446
|
+
|
447
|
+
def extract_session_id(*)
|
448
|
+
public_id = super
|
449
|
+
public_id && SessionId.new(public_id)
|
450
|
+
end
|
451
|
+
|
452
|
+
private
|
453
|
+
|
454
|
+
def session_class
|
455
|
+
SecureSessionHash
|
456
|
+
end
|
457
|
+
|
458
|
+
def cookie_value(data)
|
459
|
+
data.cookie_value
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
class ID < Persisted
|
464
|
+
def self.inherited(klass)
|
465
|
+
k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
|
466
|
+
unless k.instance_variable_defined?(:"@_rack_warned")
|
467
|
+
warn "#{klass} is inheriting from #{ID}. Inheriting from #{ID} is deprecated, please inherit from #{Persisted} instead" if $VERBOSE
|
468
|
+
k.instance_variable_set(:"@_rack_warned", true)
|
469
|
+
end
|
470
|
+
super
|
471
|
+
end
|
472
|
+
end
|
397
473
|
end
|
398
474
|
end
|
399
475
|
end
|
data/lib/rack/session/cookie.rb
CHANGED
@@ -44,7 +44,7 @@ module Rack
|
|
44
44
|
# })
|
45
45
|
#
|
46
46
|
|
47
|
-
class Cookie < Abstract::
|
47
|
+
class Cookie < Abstract::PersistedSecure
|
48
48
|
# Encode session cookies as Base64
|
49
49
|
class Base64
|
50
50
|
def encode(str)
|
@@ -151,6 +151,15 @@ module Rack
|
|
151
151
|
data
|
152
152
|
end
|
153
153
|
|
154
|
+
class SessionId < DelegateClass(Session::SessionId)
|
155
|
+
attr_reader :cookie_value
|
156
|
+
|
157
|
+
def initialize(session_id, cookie_value)
|
158
|
+
super(session_id)
|
159
|
+
@cookie_value = cookie_value
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
154
163
|
def set_session(env, session_id, session, options)
|
155
164
|
session = session.merge("session_id" => session_id)
|
156
165
|
session_data = coder.encode(session)
|
@@ -163,7 +172,7 @@ module Rack
|
|
163
172
|
env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
|
164
173
|
nil
|
165
174
|
else
|
166
|
-
session_data
|
175
|
+
SessionId.new(session_id, session_data)
|
167
176
|
end
|
168
177
|
end
|
169
178
|
|
@@ -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::
|
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
49
|
def get_session(env, sid)
|
50
50
|
with_lock(env) do
|
51
|
-
unless sid and session =
|
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
|
@@ -63,14 +63,15 @@ module Rack
|
|
63
63
|
expiry = expiry.nil? ? 0 : expiry + 1
|
64
64
|
|
65
65
|
with_lock(env) do
|
66
|
-
@pool.set session_id, new_session, expiry
|
66
|
+
@pool.set session_id.private_id, new_session, expiry
|
67
67
|
session_id
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
71
|
def destroy_session(env, session_id, options)
|
72
72
|
with_lock(env) do
|
73
|
-
@pool.delete(session_id)
|
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
|
@@ -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
|
data/lib/rack/session/pool.rb
CHANGED
@@ -24,7 +24,7 @@ module Rack
|
|
24
24
|
# )
|
25
25
|
# Rack::Handler::WEBrick.run sessioned
|
26
26
|
|
27
|
-
class Pool < Abstract::
|
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 get_session(env, sid)
|
45
45
|
with_lock(env) do
|
46
|
-
unless sid and session =
|
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 set_session(env, session_id, new_session, options)
|
55
55
|
with_lock(env) 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 destroy_session(env, session_id, options)
|
62
62
|
with_lock(env) 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
|
data/rack.gemspec
CHANGED
@@ -225,15 +225,52 @@ begin
|
|
225
225
|
req = Rack::MockRequest.new(pool)
|
226
226
|
|
227
227
|
res0 = req.get("/")
|
228
|
-
session_id = (cookie = res0["Set-Cookie"])[session_match, 1]
|
229
|
-
ses0 = pool.pool.get(session_id, true)
|
228
|
+
session_id = Rack::Session::SessionId.new (cookie = res0["Set-Cookie"])[session_match, 1]
|
229
|
+
ses0 = pool.pool.get(session_id.private_id, true)
|
230
230
|
|
231
231
|
req.get("/", "HTTP_COOKIE" => cookie)
|
232
|
-
ses1 = pool.pool.get(session_id, true)
|
232
|
+
ses1 = pool.pool.get(session_id.private_id, true)
|
233
233
|
|
234
234
|
ses1.should.not.equal ses0
|
235
235
|
end
|
236
236
|
|
237
|
+
it "can read the session with the legacy id" do
|
238
|
+
pool = Rack::Session::Memcache.new(incrementor)
|
239
|
+
req = Rack::MockRequest.new(pool)
|
240
|
+
|
241
|
+
res0 = req.get("/")
|
242
|
+
cookie = res0["Set-Cookie"]
|
243
|
+
session_id = Rack::Session::SessionId.new cookie[session_match, 1]
|
244
|
+
ses0 = pool.pool.get(session_id.private_id, true)
|
245
|
+
pool.pool.set(session_id.public_id, ses0, 0, true)
|
246
|
+
pool.pool.delete(session_id.private_id)
|
247
|
+
|
248
|
+
res1 = req.get("/", "HTTP_COOKIE" => cookie)
|
249
|
+
res1["Set-Cookie"].should.be.nil
|
250
|
+
res1.body.should.equal '{"counter"=>2}'
|
251
|
+
pool.pool.get(session_id.private_id, true).should.not.be.nil
|
252
|
+
end
|
253
|
+
|
254
|
+
it "drops the session in the legacy id as well" do
|
255
|
+
pool = Rack::Session::Memcache.new(incrementor)
|
256
|
+
req = Rack::MockRequest.new(pool)
|
257
|
+
drop = Rack::Utils::Context.new(pool, drop_session)
|
258
|
+
dreq = Rack::MockRequest.new(drop)
|
259
|
+
|
260
|
+
res0 = req.get("/")
|
261
|
+
cookie = res0["Set-Cookie"]
|
262
|
+
session_id = Rack::Session::SessionId.new cookie[session_match, 1]
|
263
|
+
ses0 = pool.pool.get(session_id.private_id, true)
|
264
|
+
pool.pool.set(session_id.public_id, ses0, 0, true)
|
265
|
+
pool.pool.delete(session_id.private_id)
|
266
|
+
|
267
|
+
res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
|
268
|
+
res2["Set-Cookie"].should.be.nil
|
269
|
+
res2.body.should.equal '{"counter"=>2}'
|
270
|
+
pool.pool.get(session_id.private_id, true).should.be.nil
|
271
|
+
pool.pool.get(session_id.public_id, true).should.be.nil
|
272
|
+
end
|
273
|
+
|
237
274
|
# anyone know how to do this better?
|
238
275
|
it "cleanly merges sessions when multithreaded" do
|
239
276
|
unless $DEBUG
|
data/test/spec_session_pool.rb
CHANGED
@@ -5,7 +5,7 @@ require 'rack/session/pool'
|
|
5
5
|
|
6
6
|
describe Rack::Session::Pool do
|
7
7
|
session_key = Rack::Session::Pool::DEFAULT_OPTIONS[:key]
|
8
|
-
session_match = /#{session_key}=[0-9a-fA-F]
|
8
|
+
session_match = /#{session_key}=([0-9a-fA-F]+);/
|
9
9
|
|
10
10
|
incrementor = lambda do |env|
|
11
11
|
env["rack.session"]["counter"] ||= 0
|
@@ -13,7 +13,7 @@ describe Rack::Session::Pool do
|
|
13
13
|
Rack::Response.new(env["rack.session"].inspect).to_a
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
get_session_id = Rack::Lint.new(lambda do |env|
|
17
17
|
Rack::Response.new(env["rack.session"].inspect).to_a
|
18
18
|
end)
|
19
19
|
|
@@ -142,6 +142,43 @@ describe Rack::Session::Pool do
|
|
142
142
|
pool.pool.size.should.equal 1
|
143
143
|
end
|
144
144
|
|
145
|
+
it "can read the session with the legacy id" do
|
146
|
+
pool = Rack::Session::Pool.new(incrementor)
|
147
|
+
req = Rack::MockRequest.new(pool)
|
148
|
+
|
149
|
+
res0 = req.get("/")
|
150
|
+
cookie = res0["Set-Cookie"]
|
151
|
+
session_id = Rack::Session::SessionId.new cookie[session_match, 1]
|
152
|
+
ses0 = pool.pool[session_id.private_id]
|
153
|
+
pool.pool[session_id.public_id] = ses0
|
154
|
+
pool.pool.delete(session_id.private_id)
|
155
|
+
|
156
|
+
res1 = req.get("/", "HTTP_COOKIE" => cookie)
|
157
|
+
res1["Set-Cookie"].should.be.nil
|
158
|
+
res1.body.should.equal '{"counter"=>2}'
|
159
|
+
pool.pool[session_id.private_id].should.not.be.nil
|
160
|
+
end
|
161
|
+
|
162
|
+
it "drops the session in the legacy id as well" do
|
163
|
+
pool = Rack::Session::Pool.new(incrementor)
|
164
|
+
req = Rack::MockRequest.new(pool)
|
165
|
+
drop = Rack::Utils::Context.new(pool, drop_session)
|
166
|
+
dreq = Rack::MockRequest.new(drop)
|
167
|
+
|
168
|
+
res0 = req.get("/")
|
169
|
+
cookie = res0["Set-Cookie"]
|
170
|
+
session_id = Rack::Session::SessionId.new cookie[session_match, 1]
|
171
|
+
ses0 = pool.pool[session_id.private_id]
|
172
|
+
pool.pool[session_id.public_id] = ses0
|
173
|
+
pool.pool.delete(session_id.private_id)
|
174
|
+
|
175
|
+
res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
|
176
|
+
res2["Set-Cookie"].should.be.nil
|
177
|
+
res2.body.should.equal '{"counter"=>2}'
|
178
|
+
pool.pool[session_id.private_id].should.be.nil
|
179
|
+
pool.pool[session_id.public_id].should.be.nil
|
180
|
+
end
|
181
|
+
|
145
182
|
# anyone know how to do this better?
|
146
183
|
it "should merge sessions when multithreaded" do
|
147
184
|
unless $DEBUG
|
@@ -190,7 +227,7 @@ describe Rack::Session::Pool do
|
|
190
227
|
end
|
191
228
|
|
192
229
|
it "does not return a cookie if cookie was not written (only read)" do
|
193
|
-
app = Rack::Session::Pool.new(
|
230
|
+
app = Rack::Session::Pool.new(get_session_id)
|
194
231
|
res = Rack::MockRequest.new(app).get("/")
|
195
232
|
res["Set-Cookie"].should.be.nil
|
196
233
|
end
|
data/test/spec_showexceptions.rb
CHANGED
@@ -92,7 +92,7 @@ describe Rack::ShowExceptions do
|
|
92
92
|
[{ "HTTP_ACCEPT" => "text/foo" }, true],
|
93
93
|
[{ "HTTP_ACCEPT" => "text/html" }, false]
|
94
94
|
].each do |env, expected|
|
95
|
-
|
95
|
+
expected.should.equal exc.prefers_plaintext?(env)
|
96
96
|
end
|
97
97
|
end
|
98
98
|
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: 1.6.
|
4
|
+
version: 1.6.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Neukirchen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bacon
|
@@ -255,8 +255,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
255
255
|
- !ruby/object:Gem::Version
|
256
256
|
version: '0'
|
257
257
|
requirements: []
|
258
|
-
|
259
|
-
rubygems_version: 2.7.6
|
258
|
+
rubygems_version: 3.0.3
|
260
259
|
signing_key:
|
261
260
|
specification_version: 4
|
262
261
|
summary: a modular Ruby webserver interface
|