redis-session-store 0.11.5 → 0.11.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|