couchrest_session_store 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|