couchrest_session_store 0.2.0 → 0.2.1
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.
- data/.travis.yml +2 -0
- data/README.md +7 -1
- data/couchrest_session_store.gemspec +1 -1
- data/design/Session.json +8 -0
- data/lib/couchrest/session/document.rb +13 -3
- data/lib/couchrest/session/store.rb +26 -7
- data/test/session_store_test.rb +72 -15
- data/test/setup_couch.sh +7 -0
- metadata +4 -2
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,13 @@
|
|
2
2
|
|
3
3
|
A simple session store based on CouchRest Model.
|
4
4
|
|
5
|
-
|
5
|
+
|
6
|
+
## Setup ##
|
7
|
+
|
8
|
+
CouchRest::Session::Store will automatically pick up the config/couch.yml file for CouchRest Model.
|
9
|
+
Cleaning up sessions requires a design document in the sessions database that enables querying by expiry. See the design directory for an example and test/setup_couch.sh for a script that puts the document on the couch for our tests.
|
10
|
+
|
11
|
+
|
6
12
|
|
7
13
|
## Options ##
|
8
14
|
|
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.files = `git ls-files`.split("\n")
|
15
15
|
gem.name = "couchrest_session_store"
|
16
16
|
gem.require_paths = ["lib"]
|
17
|
-
gem.version = '0.2.
|
17
|
+
gem.version = '0.2.1'
|
18
18
|
|
19
19
|
gem.add_dependency "couchrest"
|
20
20
|
gem.add_dependency "couchrest_model"
|
data/design/Session.json
ADDED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'couchrest/session/utility'
|
2
|
+
require 'time'
|
2
3
|
|
3
4
|
class CouchRest::Session::Document
|
4
5
|
include CouchRest::Session::Utility
|
@@ -13,12 +14,21 @@ class CouchRest::Session::Document
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
def self.build(sid, session, options)
|
17
|
+
def self.build(sid, session, options = {})
|
17
18
|
self.new(CouchRest::Document.new({"_id" => sid})).tap do |session_doc|
|
18
19
|
session_doc.update session, options
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
23
|
+
def self.build_or_update(sid, session, options = {})
|
24
|
+
options[:marshal_data] = true if options[:marshal_data].nil?
|
25
|
+
doc = self.load(sid)
|
26
|
+
doc.update(session, options)
|
27
|
+
return doc
|
28
|
+
rescue RestClient::ResourceNotFound
|
29
|
+
self.build(sid, session, options)
|
30
|
+
end
|
31
|
+
|
22
32
|
def load(sid)
|
23
33
|
@doc = database.get(sid)
|
24
34
|
end
|
@@ -62,11 +72,11 @@ class CouchRest::Session::Document
|
|
62
72
|
|
63
73
|
def expiry_from_options(options)
|
64
74
|
expire_after = options[:expire_after]
|
65
|
-
expire_after && (Time.now + expire_after)
|
75
|
+
expire_after && (Time.now + expire_after).utc
|
66
76
|
end
|
67
77
|
|
68
78
|
def expires
|
69
|
-
doc["expires"] && Time.
|
79
|
+
doc["expires"] && Time.iso8601(doc["expires"])
|
70
80
|
end
|
71
81
|
|
72
82
|
def doc
|
@@ -21,6 +21,29 @@ class CouchRest::Session::Store < ActionDispatch::Session::AbstractStore
|
|
21
21
|
use_database @options[:database] || "sessions"
|
22
22
|
end
|
23
23
|
|
24
|
+
def cleanup(rows)
|
25
|
+
rows.each do |row|
|
26
|
+
doc = CouchRest::Session::Document.load(row['id'])
|
27
|
+
doc.delete
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def expired
|
32
|
+
find_by_expires startkey: 1,
|
33
|
+
endkey: Time.now.utc.iso8601
|
34
|
+
end
|
35
|
+
|
36
|
+
def never_expiring
|
37
|
+
find_by_expires endkey: 1
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_by_expires(options = {})
|
41
|
+
options[:reduce] ||= false
|
42
|
+
design = self.class.database.get '_design/Session'
|
43
|
+
response = design.view :by_expires, options
|
44
|
+
response['rows']
|
45
|
+
end
|
46
|
+
|
24
47
|
private
|
25
48
|
|
26
49
|
def get_session(env, sid)
|
@@ -35,6 +58,7 @@ class CouchRest::Session::Store < ActionDispatch::Session::AbstractStore
|
|
35
58
|
end
|
36
59
|
|
37
60
|
def set_session(env, sid, session, options)
|
61
|
+
raise RestClient::ResourceNotFound if /^_design\/(.*)/ =~ sid
|
38
62
|
doc = build_or_update_doc(sid, session, options)
|
39
63
|
doc.save
|
40
64
|
return sid
|
@@ -56,12 +80,7 @@ class CouchRest::Session::Store < ActionDispatch::Session::AbstractStore
|
|
56
80
|
end
|
57
81
|
|
58
82
|
def build_or_update_doc(sid, session, options)
|
59
|
-
|
60
|
-
doc = secure_get(sid)
|
61
|
-
doc.update(session, options)
|
62
|
-
return doc
|
63
|
-
rescue RestClient::ResourceNotFound
|
64
|
-
CouchRest::Session::Document.build(sid, session, options)
|
83
|
+
CouchRest::Session::Document.build_or_update(sid, session, options)
|
65
84
|
end
|
66
85
|
|
67
86
|
# prevent access to design docs
|
@@ -71,5 +90,5 @@ class CouchRest::Session::Store < ActionDispatch::Session::AbstractStore
|
|
71
90
|
raise RestClient::ResourceNotFound if /^_design\/(.*)/ =~ sid
|
72
91
|
CouchRest::Session::Document.load(sid)
|
73
92
|
end
|
74
|
-
end
|
75
93
|
|
94
|
+
end
|
data/test/session_store_test.rb
CHANGED
@@ -9,21 +9,27 @@ class SessionStoreTest < MiniTest::Test
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_normal_session_flow
|
12
|
-
sid, session =
|
13
|
-
store_session(sid, session)
|
12
|
+
sid, session = never_expiring_session
|
14
13
|
assert_equal [sid, session], store.send(:get_session, env, sid)
|
15
14
|
store.send :destroy_session, env, sid, {}
|
16
15
|
end
|
17
16
|
|
18
17
|
def test_updating_session
|
19
|
-
sid, session =
|
20
|
-
store_session(sid, session)
|
18
|
+
sid, session = never_expiring_session
|
21
19
|
session[:bla] = "blub"
|
22
20
|
store.send :set_session, env, sid, session, {}
|
23
21
|
assert_equal [sid, session], store.send(:get_session, env, sid)
|
24
22
|
store.send :destroy_session, env, sid, {}
|
25
23
|
end
|
26
24
|
|
25
|
+
def test_prevent_access_to_design_docs
|
26
|
+
sid = '_design/bla'
|
27
|
+
session = {views: 'my hacked view'}
|
28
|
+
assert_raises RestClient::ResourceNotFound do
|
29
|
+
store_session(sid, session)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
27
33
|
def test_unmarshalled_session_flow
|
28
34
|
sid, session = init_session
|
29
35
|
store_session sid, session, :marshal_data => false
|
@@ -42,16 +48,14 @@ class SessionStoreTest < MiniTest::Test
|
|
42
48
|
end
|
43
49
|
|
44
50
|
def test_logout_in_between
|
45
|
-
sid, session =
|
46
|
-
store_session(sid, session)
|
51
|
+
sid, session = never_expiring_session
|
47
52
|
store.send :destroy_session, env, sid, {}
|
48
53
|
other_sid, other_session = store.send(:get_session, env, sid)
|
49
54
|
assert_equal Hash.new, other_session
|
50
55
|
end
|
51
56
|
|
52
57
|
def test_can_logout_twice
|
53
|
-
sid, session =
|
54
|
-
store_session(sid, session)
|
58
|
+
sid, session = never_expiring_session
|
55
59
|
store.send :destroy_session, env, sid, {}
|
56
60
|
store.send :destroy_session, env, sid, {}
|
57
61
|
other_sid, other_session = store.send(:get_session, env, sid)
|
@@ -59,8 +63,7 @@ class SessionStoreTest < MiniTest::Test
|
|
59
63
|
end
|
60
64
|
|
61
65
|
def test_stored_and_not_expired_yet
|
62
|
-
sid, session =
|
63
|
-
store_session(sid, session, expire_after: 300)
|
66
|
+
sid, session = expiring_session
|
64
67
|
doc = CouchRest::Session::Document.load(sid)
|
65
68
|
expires = doc.send :expires
|
66
69
|
assert expires
|
@@ -71,17 +74,44 @@ class SessionStoreTest < MiniTest::Test
|
|
71
74
|
end
|
72
75
|
|
73
76
|
def test_stored_but_expired
|
74
|
-
sid, session =
|
75
|
-
store_session(sid, session, expire_after: 300)
|
76
|
-
CouchTester.new.update(sid, "expires" => Time.now - 2.minutes)
|
77
|
+
sid, session = expired_session
|
77
78
|
other_sid, other_session = store.send(:get_session, env, sid)
|
78
79
|
assert_equal Hash.new, other_session, "session should have expired"
|
79
80
|
assert other_sid != sid
|
80
81
|
end
|
81
82
|
|
83
|
+
def test_find_expired_sessions
|
84
|
+
expired, expiring, never_expiring = seed_sessions
|
85
|
+
expired_session_ids = store.expired.map {|row| row['id']}
|
86
|
+
assert expired_session_ids.include?(expired)
|
87
|
+
assert !expired_session_ids.include?(expiring)
|
88
|
+
assert !expired_session_ids.include?(never_expiring)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_find_never_expiring_sessions
|
92
|
+
expired, expiring, never_expiring = seed_sessions
|
93
|
+
never_expiring_session_ids = store.never_expiring.map {|row| row['id']}
|
94
|
+
assert never_expiring_session_ids.include?(never_expiring)
|
95
|
+
assert !never_expiring_session_ids.include?(expiring)
|
96
|
+
assert !never_expiring_session_ids.include?(expired)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_cleanup_expired_sessions
|
100
|
+
sid, session = expired_session
|
101
|
+
store.cleanup(store.expired)
|
102
|
+
assert_raises RestClient::ResourceNotFound do
|
103
|
+
CouchTester.new.get(sid)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_keep_fresh_during_cleanup
|
108
|
+
sid, session = expiring_session
|
109
|
+
store.cleanup(store.expired)
|
110
|
+
assert_equal [sid, session], store.send(:get_session, env, sid)
|
111
|
+
end
|
112
|
+
|
82
113
|
def test_store_without_expiry
|
83
|
-
sid, session =
|
84
|
-
store_session(sid, session)
|
114
|
+
sid, session = never_expiring_session
|
85
115
|
couch = CouchTester.new
|
86
116
|
assert_nil couch.get(sid)["expires"]
|
87
117
|
assert_equal [sid, session], store.send(:get_session, env, sid)
|
@@ -99,6 +129,25 @@ class SessionStoreTest < MiniTest::Test
|
|
99
129
|
env ||= settings
|
100
130
|
end
|
101
131
|
|
132
|
+
# returns the session ids of an expired, and expiring and a never
|
133
|
+
# expiring session
|
134
|
+
def seed_sessions
|
135
|
+
[expired_session, expiring_session, never_expiring_session].map(&:first)
|
136
|
+
end
|
137
|
+
|
138
|
+
def never_expiring_session
|
139
|
+
store_session *init_session
|
140
|
+
end
|
141
|
+
|
142
|
+
def expiring_session
|
143
|
+
sid, session = init_session
|
144
|
+
store_session(sid, session, expire_after: 300)
|
145
|
+
end
|
146
|
+
|
147
|
+
def expired_session
|
148
|
+
expire_session *expiring_session
|
149
|
+
end
|
150
|
+
|
102
151
|
def init_session
|
103
152
|
sid, session = store.send :get_session, env, nil
|
104
153
|
session[:key] = "stub"
|
@@ -107,5 +156,13 @@ class SessionStoreTest < MiniTest::Test
|
|
107
156
|
|
108
157
|
def store_session(sid, session, options = {})
|
109
158
|
store.send :set_session, env, sid, session, options
|
159
|
+
return sid, session
|
110
160
|
end
|
161
|
+
|
162
|
+
def expire_session(sid, session)
|
163
|
+
CouchTester.new.update sid,
|
164
|
+
"expires" => (Time.now - 10.minutes).utc.iso8601
|
165
|
+
return sid, session
|
166
|
+
end
|
167
|
+
|
111
168
|
end
|
data/test/setup_couch.sh
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: couchrest_session_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-11-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: couchrest
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- README.md
|
104
104
|
- Rakefile
|
105
105
|
- couchrest_session_store.gemspec
|
106
|
+
- design/Session.json
|
106
107
|
- lib/couchrest/session.rb
|
107
108
|
- lib/couchrest/session/document.rb
|
108
109
|
- lib/couchrest/session/store.rb
|
@@ -110,6 +111,7 @@ files:
|
|
110
111
|
- lib/couchrest_session_store.rb
|
111
112
|
- test/couch_tester.rb
|
112
113
|
- test/session_store_test.rb
|
114
|
+
- test/setup_couch.sh
|
113
115
|
- test/test_clock.rb
|
114
116
|
- test/test_helper.rb
|
115
117
|
homepage: http://github.com/azul/couchrest_session_store
|