redis-session-store 0.11.5 → 0.11.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -1
- data/AUTHORS.md +1 -0
- data/CHANGELOG.md +4 -0
- data/lib/redis-session-store.rb +28 -7
- data/redis-session-store.gemspec +1 -1
- data/spec/redis_session_store_spec.rb +93 -24
- data/spec/support.rb +24 -2
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d18e9f3e69775d747faf496a6dcd718aaae738759306fc85fb158e0670de9588
|
4
|
+
data.tar.gz: bd5457371142d59eb6b2009b6b3add32efcc57bb886ec6e05b415298031ad9d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69db8ffaf3ba8bfa9002f2355474b213ab9e7091338eeb63e3049ea31e575a461ed770e6024be89a925dccbaf8a1b4f62b261e5b927b30b1ef041f3e92d9f275
|
7
|
+
data.tar.gz: 3f4af5b4cd6b806c44407d2ad8fd536873c03fc72db4e20a386afaffc3a681a98ab7c7755b478c56d656c71439d1a44b651413d92ec01d3c6a60b4824d971c61
|
data/.rubocop.yml
CHANGED
data/AUTHORS.md
CHANGED
data/CHANGELOG.md
CHANGED
data/lib/redis-session-store.rb
CHANGED
@@ -3,7 +3,7 @@ require 'redis'
|
|
3
3
|
# Redis session storage for Rails, and for Rails only. Derived from
|
4
4
|
# the MemCacheStore code, simply dropping in Redis instead.
|
5
5
|
class RedisSessionStore < ActionDispatch::Session::AbstractSecureStore
|
6
|
-
VERSION = '0.11.
|
6
|
+
VERSION = '0.11.6'.freeze
|
7
7
|
# Rails 3.1 and beyond defines the constant elsewhere
|
8
8
|
unless defined?(ENV_SESSION_OPTIONS_KEY)
|
9
9
|
ENV_SESSION_OPTIONS_KEY = if Rack.release.split('.').first.to_i > 1
|
@@ -65,7 +65,7 @@ class RedisSessionStore < ActionDispatch::Session::AbstractSecureStore
|
|
65
65
|
|
66
66
|
!!(
|
67
67
|
value && !value.empty? &&
|
68
|
-
|
68
|
+
key_exists_with_fallback?(value)
|
69
69
|
)
|
70
70
|
rescue Errno::ECONNREFUSED, Redis::CannotConnectError => e
|
71
71
|
on_redis_down.call(e, env, value) if on_redis_down
|
@@ -73,6 +73,12 @@ class RedisSessionStore < ActionDispatch::Session::AbstractSecureStore
|
|
73
73
|
true
|
74
74
|
end
|
75
75
|
|
76
|
+
def key_exists_with_fallback?(value)
|
77
|
+
return false if private_session_id?(value.public_id)
|
78
|
+
|
79
|
+
key_exists?(value.private_id) || key_exists?(value.public_id)
|
80
|
+
end
|
81
|
+
|
76
82
|
def key_exists?(value)
|
77
83
|
if redis.respond_to?(:exists?)
|
78
84
|
# added in redis gem v4.2
|
@@ -83,6 +89,10 @@ class RedisSessionStore < ActionDispatch::Session::AbstractSecureStore
|
|
83
89
|
end
|
84
90
|
end
|
85
91
|
|
92
|
+
def private_session_id?(value)
|
93
|
+
value.match?(/\A\d+::/)
|
94
|
+
end
|
95
|
+
|
86
96
|
def verify_handlers!
|
87
97
|
%w(on_redis_down on_session_load_error).each do |h|
|
88
98
|
next unless (handler = public_send(h)) && !handler.respond_to?(:call)
|
@@ -100,13 +110,21 @@ class RedisSessionStore < ActionDispatch::Session::AbstractSecureStore
|
|
100
110
|
end
|
101
111
|
|
102
112
|
def get_session(env, sid)
|
103
|
-
sid && (session =
|
113
|
+
sid && (session = load_session_with_fallback(sid)) ? [sid, session] : session_default_values
|
104
114
|
rescue Errno::ECONNREFUSED, Redis::CannotConnectError => e
|
105
115
|
on_redis_down.call(e, env, sid) if on_redis_down
|
106
116
|
session_default_values
|
107
117
|
end
|
108
118
|
alias find_session get_session
|
109
119
|
|
120
|
+
def load_session_with_fallback(sid)
|
121
|
+
return nil if private_session_id?(sid.public_id)
|
122
|
+
|
123
|
+
load_session_from_redis(
|
124
|
+
key_exists?(sid.private_id) ? sid.private_id : sid.public_id
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
110
128
|
def load_session_from_redis(sid)
|
111
129
|
data = redis.get(prefixed(sid))
|
112
130
|
begin
|
@@ -126,9 +144,9 @@ class RedisSessionStore < ActionDispatch::Session::AbstractSecureStore
|
|
126
144
|
def set_session(env, sid, session_data, options = nil)
|
127
145
|
expiry = get_expiry(env, options)
|
128
146
|
if expiry
|
129
|
-
redis.setex(prefixed(sid), expiry, encode(session_data))
|
147
|
+
redis.setex(prefixed(sid.private_id), expiry, encode(session_data))
|
130
148
|
else
|
131
|
-
redis.set(prefixed(sid), encode(session_data))
|
149
|
+
redis.set(prefixed(sid.private_id), encode(session_data))
|
132
150
|
end
|
133
151
|
sid
|
134
152
|
rescue Errno::ECONNREFUSED, Redis::CannotConnectError => e
|
@@ -147,14 +165,17 @@ class RedisSessionStore < ActionDispatch::Session::AbstractSecureStore
|
|
147
165
|
end
|
148
166
|
|
149
167
|
def destroy_session(env, sid, options)
|
150
|
-
destroy_session_from_sid(sid, (options || {}).to_hash.merge(env: env))
|
168
|
+
destroy_session_from_sid(sid.public_id, (options || {}).to_hash.merge(env: env, drop: true))
|
169
|
+
destroy_session_from_sid(sid.private_id, (options || {}).to_hash.merge(env: env))
|
151
170
|
end
|
152
171
|
alias delete_session destroy_session
|
153
172
|
|
154
173
|
def destroy(env)
|
155
174
|
if env['rack.request.cookie_hash'] &&
|
156
175
|
(sid = env['rack.request.cookie_hash'][key])
|
157
|
-
|
176
|
+
sid = Rack::Session::SessionId.new(sid)
|
177
|
+
destroy_session_from_sid(sid.private_id, drop: true, env: env)
|
178
|
+
destroy_session_from_sid(sid.public_id, drop: true, env: env)
|
158
179
|
end
|
159
180
|
false
|
160
181
|
end
|
data/redis-session-store.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.version = File.read('lib/redis-session-store.rb')
|
16
16
|
.match(/^ VERSION = '(.*)'/)[1]
|
17
17
|
|
18
|
-
gem.add_runtime_dependency 'actionpack', '>=
|
18
|
+
gem.add_runtime_dependency 'actionpack', '>= 5.2.4.1', '< 9'
|
19
19
|
gem.add_runtime_dependency 'redis', '>= 3', '< 6'
|
20
20
|
|
21
21
|
gem.add_development_dependency 'fakeredis', '~> 0.8'
|
@@ -157,7 +157,7 @@ describe RedisSessionStore do
|
|
157
157
|
# https://github.com/rack/rack/blob/1.4.5/lib/rack/session/abstract/id.rb
|
158
158
|
|
159
159
|
let(:env) { double('env') }
|
160
|
-
let(:session_id) {
|
160
|
+
let(:session_id) { Rack::Session::SessionId.new('12 345') }
|
161
161
|
let(:session_data) { double('session_data') }
|
162
162
|
let(:options) { { expire_after: 123 } }
|
163
163
|
|
@@ -217,7 +217,8 @@ describe RedisSessionStore do
|
|
217
217
|
end
|
218
218
|
|
219
219
|
describe 'checking for session existence' do
|
220
|
-
let(:
|
220
|
+
let(:public_id) { 'foo' }
|
221
|
+
let(:session_id) { Rack::Session::SessionId.new(public_id) }
|
221
222
|
|
222
223
|
before do
|
223
224
|
allow(store).to receive(:current_session_id)
|
@@ -234,10 +235,9 @@ describe RedisSessionStore do
|
|
234
235
|
end
|
235
236
|
|
236
237
|
context 'when session id is empty string' do
|
237
|
-
let(:
|
238
|
+
let(:public_id) { '' }
|
238
239
|
|
239
240
|
it 'returns false' do
|
240
|
-
allow(store).to receive(:current_session_id).with(:env).and_return('')
|
241
241
|
expect(store.send(:session_exists?, :env)).to eq(false)
|
242
242
|
end
|
243
243
|
end
|
@@ -250,20 +250,46 @@ describe RedisSessionStore do
|
|
250
250
|
end
|
251
251
|
end
|
252
252
|
|
253
|
-
context 'when session id does not exist in redis' do
|
254
|
-
|
255
|
-
|
256
|
-
|
253
|
+
context 'when session private id does not exist in redis' do
|
254
|
+
context 'when session public id does not exist in redis' do
|
255
|
+
it 'returns false' do
|
256
|
+
expect(redis).to receive(:exists)
|
257
|
+
.with(session_id.private_id)
|
258
|
+
.and_return(false)
|
259
|
+
expect(redis).to receive(:exists).with('foo').and_return(false)
|
260
|
+
expect(store.send(:session_exists?, :env)).to eq(false)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
context 'when session public id exists in redis' do
|
265
|
+
it 'returns true' do
|
266
|
+
expect(redis).to receive(:exists)
|
267
|
+
.with(session_id.private_id)
|
268
|
+
.and_return(false)
|
269
|
+
expect(redis).to receive(:exists).with('foo').and_return(true)
|
270
|
+
expect(store.send(:session_exists?, :env)).to eq(true)
|
271
|
+
end
|
257
272
|
end
|
258
273
|
end
|
259
274
|
|
260
|
-
context 'when session id exists in redis' do
|
275
|
+
context 'when session private id exists in redis' do
|
261
276
|
it 'returns true' do
|
262
|
-
expect(redis).to receive(:exists)
|
277
|
+
expect(redis).to receive(:exists)
|
278
|
+
.with(session_id.private_id)
|
279
|
+
.and_return(true)
|
263
280
|
expect(store.send(:session_exists?, :env)).to eq(true)
|
264
281
|
end
|
265
282
|
end
|
266
283
|
|
284
|
+
context 'when session public id is formatted like a private id' do
|
285
|
+
let(:public_id) { Rack::Session::SessionId.new('foo').private_id }
|
286
|
+
|
287
|
+
it 'returns false' do
|
288
|
+
expect(redis).not_to receive(:exists)
|
289
|
+
expect(store.send(:session_exists?, :env)).to eq(false)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
267
293
|
context 'when redis is down' do
|
268
294
|
it 'returns true (fallback to old behavior)' do
|
269
295
|
allow(store).to receive(:redis).and_raise(Redis::CannotConnectError)
|
@@ -281,6 +307,7 @@ describe RedisSessionStore do
|
|
281
307
|
end
|
282
308
|
|
283
309
|
let(:fake_key) { 'thisisarediskey' }
|
310
|
+
let(:session_id) { Rack::Session::SessionId.new(fake_key) }
|
284
311
|
|
285
312
|
describe 'generate_sid' do
|
286
313
|
it 'generates a secure ID' do
|
@@ -289,13 +316,50 @@ describe RedisSessionStore do
|
|
289
316
|
end
|
290
317
|
end
|
291
318
|
|
292
|
-
|
293
|
-
redis
|
294
|
-
|
295
|
-
|
296
|
-
|
319
|
+
context 'when redis is up' do
|
320
|
+
let(:redis) { double('redis') }
|
321
|
+
let(:private_exists) { true }
|
322
|
+
|
323
|
+
before do
|
324
|
+
allow(store).to receive(:redis).and_return(redis)
|
325
|
+
allow(redis).to receive(:exists)
|
326
|
+
.with("#{options[:key_prefix]}#{session_id.private_id}")
|
327
|
+
.and_return(private_exists)
|
328
|
+
end
|
297
329
|
|
298
|
-
|
330
|
+
context 'when session private id exists in redis' do
|
331
|
+
it 'retrieves the prefixed private id from redis' do
|
332
|
+
expect(redis).to receive(:get).with("#{options[:key_prefix]}#{session_id.private_id}")
|
333
|
+
|
334
|
+
store.send(:get_session, double('env'), session_id)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context 'when session private id not found in redis' do
|
339
|
+
let(:private_exists) { false }
|
340
|
+
|
341
|
+
it 'retrieves the prefixed public id from redis' do
|
342
|
+
expect(redis).to receive(:get).with("#{options[:key_prefix]}#{fake_key}")
|
343
|
+
|
344
|
+
store.send(:get_session, double('env'), session_id)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
context 'when session id is formatted like a private id' do
|
349
|
+
let(:fake_key) { Rack::Session::SessionId.new('anykey').private_id }
|
350
|
+
let(:new_sid) { Rack::Session::SessionId.new('newid') }
|
351
|
+
|
352
|
+
before do
|
353
|
+
allow(store).to receive(:generate_sid).and_return(new_sid)
|
354
|
+
end
|
355
|
+
|
356
|
+
it 'returns a default new session' do
|
357
|
+
expect(redis).not_to receive(:exists)
|
358
|
+
expect(redis).not_to receive(:get)
|
359
|
+
expect(store.send(:get_session, double('env'), session_id))
|
360
|
+
.to eq([new_sid, {}])
|
361
|
+
end
|
362
|
+
end
|
299
363
|
end
|
300
364
|
|
301
365
|
context 'when redis is down' do
|
@@ -305,12 +369,12 @@ describe RedisSessionStore do
|
|
305
369
|
end
|
306
370
|
|
307
371
|
it 'returns an empty session hash' do
|
308
|
-
expect(store.send(:get_session, double('env'),
|
372
|
+
expect(store.send(:get_session, double('env'), session_id).last)
|
309
373
|
.to eq({})
|
310
374
|
end
|
311
375
|
|
312
376
|
it 'returns a newly generated sid' do
|
313
|
-
expect(store.send(:get_session, double('env'),
|
377
|
+
expect(store.send(:get_session, double('env'), session_id).first)
|
314
378
|
.to eq('foop')
|
315
379
|
end
|
316
380
|
|
@@ -319,7 +383,7 @@ describe RedisSessionStore do
|
|
319
383
|
|
320
384
|
it 'explodes' do
|
321
385
|
expect do
|
322
|
-
store.send(:get_session, double('env'),
|
386
|
+
store.send(:get_session, double('env'), session_id)
|
323
387
|
end.to raise_error(Redis::CannotConnectError)
|
324
388
|
end
|
325
389
|
end
|
@@ -331,6 +395,7 @@ describe RedisSessionStore do
|
|
331
395
|
let(:env) { { 'rack.request.cookie_hash' => cookie_hash } }
|
332
396
|
let(:cookie_hash) { double('cookie hash') }
|
333
397
|
let(:fake_key) { 'thisisarediskey' }
|
398
|
+
let(:session_id) { Rack::Session::SessionId.new(fake_key) }
|
334
399
|
|
335
400
|
before do
|
336
401
|
allow(cookie_hash).to receive(:[]).and_return(fake_key)
|
@@ -341,6 +406,8 @@ describe RedisSessionStore do
|
|
341
406
|
allow(store).to receive(:redis).and_return(redis)
|
342
407
|
expect(redis).to receive(:del)
|
343
408
|
.with("#{options[:key_prefix]}#{fake_key}")
|
409
|
+
expect(redis).to receive(:del)
|
410
|
+
.with("#{options[:key_prefix]}#{session_id.private_id}")
|
344
411
|
|
345
412
|
store.send(:destroy, env)
|
346
413
|
end
|
@@ -371,7 +438,8 @@ describe RedisSessionStore do
|
|
371
438
|
redis = double('redis', setnx: true)
|
372
439
|
allow(store).to receive(:redis).and_return(redis)
|
373
440
|
sid = store.send(:generate_sid)
|
374
|
-
expect(redis).to receive(:del).with("#{options[:key_prefix]}#{sid}")
|
441
|
+
expect(redis).to receive(:del).with("#{options[:key_prefix]}#{sid.public_id}")
|
442
|
+
expect(redis).to receive(:del).with("#{options[:key_prefix]}#{sid.private_id}")
|
375
443
|
|
376
444
|
store.send(:destroy_session, {}, sid, nil)
|
377
445
|
end
|
@@ -380,7 +448,7 @@ describe RedisSessionStore do
|
|
380
448
|
|
381
449
|
describe 'session encoding' do
|
382
450
|
let(:env) { double('env') }
|
383
|
-
let(:session_id) {
|
451
|
+
let(:session_id) { Rack::Session::SessionId.new('12 345') }
|
384
452
|
let(:session_data) { { 'some' => 'data' } }
|
385
453
|
let(:options) { {} }
|
386
454
|
let(:encoded_data) { Marshal.dump(session_data) }
|
@@ -393,11 +461,12 @@ describe RedisSessionStore do
|
|
393
461
|
|
394
462
|
shared_examples_for 'serializer' do
|
395
463
|
it 'encodes correctly' do
|
396
|
-
expect(redis).to receive(:set).with(
|
464
|
+
expect(redis).to receive(:set).with(session_id.private_id, expected_encoding)
|
397
465
|
store.send(:set_session, env, session_id, session_data, options)
|
398
466
|
end
|
399
467
|
|
400
468
|
it 'decodes correctly' do
|
469
|
+
allow(redis).to receive(:exists).with(session_id.private_id).and_return(true)
|
401
470
|
expect(store.send(:get_session, env, session_id))
|
402
471
|
.to eq([session_id, session_data])
|
403
472
|
end
|
@@ -548,7 +617,7 @@ describe RedisSessionStore do
|
|
548
617
|
describe 'setting the session' do
|
549
618
|
it 'allows changing the session' do
|
550
619
|
env = { 'rack.session.options' => {} }
|
551
|
-
sid = 1234
|
620
|
+
sid = Rack::Session::SessionId.new('1234')
|
552
621
|
allow(store).to receive(:redis).and_return(Redis.new)
|
553
622
|
data1 = { 'foo' => 'bar' }
|
554
623
|
store.send(:set_session, env, sid, data1)
|
@@ -560,7 +629,7 @@ describe RedisSessionStore do
|
|
560
629
|
|
561
630
|
it 'allows changing the session when the session has an expiry' do
|
562
631
|
env = { 'rack.session.options' => { expire_after: 60 } }
|
563
|
-
sid = 1234
|
632
|
+
sid = Rack::Session::SessionId.new('1234')
|
564
633
|
allow(store).to receive(:redis).and_return(Redis.new)
|
565
634
|
data1 = { 'foo' => 'bar' }
|
566
635
|
store.send(:set_session, env, sid, data1)
|
data/spec/support.rb
CHANGED
@@ -11,10 +11,32 @@ unless defined?(Rack::Session::SessionId)
|
|
11
11
|
module Rack
|
12
12
|
module Session
|
13
13
|
class SessionId
|
14
|
+
ID_VERSION = 2
|
15
|
+
|
14
16
|
attr_reader :public_id
|
15
17
|
|
16
|
-
def initialize(
|
17
|
-
@public_id
|
18
|
+
def initialize(public_id)
|
19
|
+
@public_id = public_id
|
20
|
+
end
|
21
|
+
|
22
|
+
alias to_s public_id
|
23
|
+
|
24
|
+
def empty?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
public_id.inspect
|
30
|
+
end
|
31
|
+
|
32
|
+
def private_id
|
33
|
+
"#{ID_VERSION}::#{hash_sid(public_id)}"
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def hash_sid(value)
|
39
|
+
"test_hash_from:#{value}"
|
18
40
|
end
|
19
41
|
end
|
20
42
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-session-store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mathias Meyer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -16,20 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.2.4.1
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '9'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 5.2.4.1
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '9'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: redis
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -197,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
197
197
|
- !ruby/object:Gem::Version
|
198
198
|
version: '0'
|
199
199
|
requirements: []
|
200
|
-
rubygems_version: 3.
|
200
|
+
rubygems_version: 3.5.23
|
201
201
|
signing_key:
|
202
202
|
specification_version: 4
|
203
203
|
summary: A drop-in replacement for e.g. MemCacheStore to store Rails sessions (and
|