riak-sessions 0.8.0.beta2
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/Rakefile +68 -0
- data/lib/riak/session_store.rb +96 -0
- data/lib/riak-sessions.rb +31 -0
- data/lib/ripple/session_store.rb +81 -0
- data/spec/fixtures/session_autoload_test/session_autoload_test/foo.rb +10 -0
- data/spec/riak_session_store_spec.rb +150 -0
- data/spec/ripple_session_store_spec.rb +140 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/ripple_session_support.rb +82 -0
- metadata +197 -0
data/Rakefile
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
|
4
|
+
version = File.read('../VERSION').strip
|
5
|
+
|
6
|
+
gemspec = Gem::Specification.new do |gem|
|
7
|
+
gem.name = "riak-sessions"
|
8
|
+
gem.summary = %Q{riak-sessions is a session store backed by Riak, the distributed database by Basho.}
|
9
|
+
gem.description = %Q{riak-sessions is a session store backed by Riak, the distributed database by Basho. It includes session implementations for both Rack and Rails 3.}
|
10
|
+
gem.version = version
|
11
|
+
gem.email = "seancribbs@gmail.com"
|
12
|
+
gem.homepage = "http://seancribbs.github.com/ripple"
|
13
|
+
gem.authors = ["Sean Cribbs"]
|
14
|
+
gem.add_development_dependency "rspec", "~>2.0.0.beta.18"
|
15
|
+
gem.add_development_dependency "rspec-rails", "~>2.0.0.beta.18"
|
16
|
+
gem.add_development_dependency "yajl-ruby"
|
17
|
+
gem.add_development_dependency "rails", "~>3.0.0"
|
18
|
+
gem.add_development_dependency "curb", ">0.6"
|
19
|
+
gem.add_development_dependency "rake"
|
20
|
+
gem.add_dependency "riak-client", "~>#{version}"
|
21
|
+
gem.add_dependency "rack", ">=1.0"
|
22
|
+
|
23
|
+
files = FileList["**/*"]
|
24
|
+
files.exclude /\.DS_Store/
|
25
|
+
files.exclude /\#/
|
26
|
+
files.exclude /~/
|
27
|
+
files.exclude /\.swp/
|
28
|
+
files.exclude '**/._*'
|
29
|
+
files.exclude '**/*.orig'
|
30
|
+
files.exclude '**/*.rej'
|
31
|
+
files.exclude /^pkg/
|
32
|
+
files.exclude 'riak-sessions.gemspec'
|
33
|
+
files.exclude 'Gemfile'
|
34
|
+
files.exclude 'Gemfile.lock'
|
35
|
+
|
36
|
+
gem.files = files.to_a
|
37
|
+
|
38
|
+
gem.test_files = FileList["spec/**/*.rb"].to_a
|
39
|
+
end
|
40
|
+
|
41
|
+
# Gem packaging tasks
|
42
|
+
Rake::GemPackageTask.new(gemspec) do |pkg|
|
43
|
+
pkg.need_zip = false
|
44
|
+
pkg.need_tar = false
|
45
|
+
end
|
46
|
+
|
47
|
+
task :gem => :gemspec
|
48
|
+
|
49
|
+
desc %{Build the gemspec file.}
|
50
|
+
task :gemspec do
|
51
|
+
gemspec.validate
|
52
|
+
File.open("#{gemspec.name}.gemspec", 'w'){|f| f.write gemspec.to_ruby }
|
53
|
+
end
|
54
|
+
|
55
|
+
desc %{Release the gem to RubyGems.org}
|
56
|
+
task :release => :gem do
|
57
|
+
system "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
|
58
|
+
end
|
59
|
+
|
60
|
+
require 'rspec/core'
|
61
|
+
require 'rspec/core/rake_task'
|
62
|
+
|
63
|
+
desc "Run Specs"
|
64
|
+
Rspec::Core::RakeTask.new(:spec) do |spec|
|
65
|
+
spec.pattern = "spec/**/*_spec.rb"
|
66
|
+
end
|
67
|
+
|
68
|
+
task :default => :spec
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
require 'riak'
|
15
|
+
require 'rack/session/abstract/id'
|
16
|
+
|
17
|
+
module Riak
|
18
|
+
# Lets you store web application session data in Riak.
|
19
|
+
# Useful for those cases where you need more than the 4K that
|
20
|
+
# cookies provide.
|
21
|
+
#
|
22
|
+
# Usage (Rack builder):
|
23
|
+
# use Riak::SessionStore
|
24
|
+
#
|
25
|
+
# Usage (Rails):
|
26
|
+
# config.middleware.use Riak::SessionStore
|
27
|
+
#
|
28
|
+
# For configuration options, see #initialize.
|
29
|
+
class SessionStore < Rack::Session::Abstract::ID
|
30
|
+
DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS.merge \
|
31
|
+
:host => "127.0.0.1",
|
32
|
+
:port => 8098,
|
33
|
+
:bucket => "_sessions",
|
34
|
+
:r => 1,
|
35
|
+
:w => 1,
|
36
|
+
:dw => 0,
|
37
|
+
:rw => 1,
|
38
|
+
:n_val => 2,
|
39
|
+
:last_write_wins => false
|
40
|
+
|
41
|
+
|
42
|
+
attr_reader :bucket
|
43
|
+
|
44
|
+
# Creates a new Riak::SessionStore middleware
|
45
|
+
# @param app the Rack application
|
46
|
+
# @param [Hash] options configuration options
|
47
|
+
# @option
|
48
|
+
# @see Rack::Session::Abstract::ID#initialize
|
49
|
+
def initialize(app, options={})
|
50
|
+
super
|
51
|
+
@client = Riak::Client.new(default_options.slice(:host,:port))
|
52
|
+
# @client.http.send(:curl).verbose = true
|
53
|
+
@bucket = @client.bucket(default_options[:bucket]).tap do |b|
|
54
|
+
new_props = {}
|
55
|
+
[:r,:w,:dw,:rw,:n_val].each do |q|
|
56
|
+
new_props[q.to_s] = default_options[q] unless b.send(q) == default_options[q]
|
57
|
+
end
|
58
|
+
b.props = new_props unless new_props.blank?
|
59
|
+
end
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_sid
|
64
|
+
loop do
|
65
|
+
sid = super
|
66
|
+
break sid unless @bucket.exists?(sid)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def get_session(env, session_id)
|
72
|
+
unless session_id && robject = (bucket.get(session_id) rescue nil)
|
73
|
+
session_id, robject = generate_sid, bucket.new
|
74
|
+
robject.key = session_id
|
75
|
+
robject.data = {}
|
76
|
+
robject.store
|
77
|
+
end
|
78
|
+
[session_id, robject.data]
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_session(env, session_id, session, options)
|
82
|
+
if options[:renew] or options[:drop]
|
83
|
+
bucket.delete(session_id)
|
84
|
+
return false if options[:drop]
|
85
|
+
session_id = generate_sid
|
86
|
+
end
|
87
|
+
robject = bucket.get_or_new(session_id)
|
88
|
+
robject.data = session
|
89
|
+
robject.store
|
90
|
+
session_id
|
91
|
+
rescue Riak::FailedRequest
|
92
|
+
env['rack.errors'].puts $!.inspect
|
93
|
+
false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
require 'riak'
|
15
|
+
require 'rack'
|
16
|
+
|
17
|
+
module Riak
|
18
|
+
autoload :SessionStore, "riak/session_store"
|
19
|
+
end
|
20
|
+
|
21
|
+
if defined?(ActionDispatch)
|
22
|
+
module Ripple
|
23
|
+
autoload :SessionStore, "ripple/session_store"
|
24
|
+
end
|
25
|
+
|
26
|
+
module ActionDispatch
|
27
|
+
module Session
|
28
|
+
autoload :RiakStore, "ripple/session_store"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
begin
|
15
|
+
require 'action_dispatch/middleware/session/abstract_store'
|
16
|
+
rescue LoadError, NameError
|
17
|
+
# TODO i18n this message
|
18
|
+
$stderr.puts "Ripple::SessionStore requires ActionPack >= 3.0.0"
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
|
22
|
+
module Ripple
|
23
|
+
class SessionStore < ActionDispatch::Session::AbstractStore
|
24
|
+
def initialize(app, options={})
|
25
|
+
super
|
26
|
+
@default_options = {
|
27
|
+
:bucket => "_sessions",
|
28
|
+
:r => 1,
|
29
|
+
:w => 1,
|
30
|
+
:dw => 0,
|
31
|
+
:rw => 1,
|
32
|
+
:n_val => 2,
|
33
|
+
:last_write_wins => false,
|
34
|
+
:host => "127.0.0.1",
|
35
|
+
:port => 8098
|
36
|
+
}.merge(@default_options)
|
37
|
+
@client = Riak::Client.new(@default_options.slice(:host,:port,:client_id))
|
38
|
+
@bucket = @client.bucket(@default_options[:bucket])
|
39
|
+
set_bucket_defaults
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def get_session(env, sid)
|
44
|
+
sid ||= generate_sid
|
45
|
+
session = {}
|
46
|
+
begin
|
47
|
+
session = @bucket.get(sid).data
|
48
|
+
rescue Riak::FailedRequest => fr
|
49
|
+
raise fr unless fr.code.to_i == 404
|
50
|
+
end
|
51
|
+
[sid, session]
|
52
|
+
end
|
53
|
+
|
54
|
+
def set_session(env, sid, session_data)
|
55
|
+
robject = @bucket.get_or_new(sid)
|
56
|
+
robject.content_type = "application/x-ruby-marshal"
|
57
|
+
robject.data = session_data
|
58
|
+
robject.store
|
59
|
+
sid
|
60
|
+
rescue Riak::FailedRequest
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
def destroy(env)
|
65
|
+
if sid = current_session_id(env)
|
66
|
+
@bucket.delete(sid)
|
67
|
+
end
|
68
|
+
rescue Riak::FailedRequest
|
69
|
+
false
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_bucket_defaults
|
73
|
+
bucket_opts = @default_options.slice(:r,:w,:dw,:rw,:n_val,:last_write_wins).stringify_keys
|
74
|
+
new_props = bucket_opts.inject({}) do |hash,(k,v)|
|
75
|
+
hash[k] = v unless @bucket.props[k] == v
|
76
|
+
hash
|
77
|
+
end
|
78
|
+
@bucket.props = new_props unless new_props.empty?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
require File.expand_path("../spec_helper", __FILE__)
|
15
|
+
require 'rack/mock'
|
16
|
+
|
17
|
+
describe Riak::SessionStore do
|
18
|
+
session_key = Riak::SessionStore::DEFAULT_OPTIONS[:key]
|
19
|
+
session_match = /#{session_key}=([0-9a-fA-F]+);/
|
20
|
+
incrementor = lambda do |env|
|
21
|
+
env["rack.session"]["counter"] ||= 0
|
22
|
+
env["rack.session"]["counter"] += 1
|
23
|
+
Rack::Response.new(env["rack.session"].inspect).to_a
|
24
|
+
end
|
25
|
+
drop_session = proc do |env|
|
26
|
+
env['rack.session.options'][:drop] = true
|
27
|
+
incrementor.call(env)
|
28
|
+
end
|
29
|
+
renew_session = proc do |env|
|
30
|
+
env['rack.session.options'][:renew] = true
|
31
|
+
incrementor.call(env)
|
32
|
+
end
|
33
|
+
defer_session = proc do |env|
|
34
|
+
env['rack.session.options'][:defer] = true
|
35
|
+
incrementor.call(env)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "creates a new cookie" do
|
39
|
+
pool = Riak::SessionStore.new(incrementor)
|
40
|
+
res = Rack::MockRequest.new(pool).get("/")
|
41
|
+
res["Set-Cookie"].should include("#{session_key}=")
|
42
|
+
res.body.should == '{"counter"=>1}'
|
43
|
+
end
|
44
|
+
|
45
|
+
it "determines session from a cookie" do
|
46
|
+
pool = Riak::SessionStore.new(incrementor)
|
47
|
+
req = Rack::MockRequest.new(pool)
|
48
|
+
res = req.get("/")
|
49
|
+
cookie = res["Set-Cookie"]
|
50
|
+
req.get("/", "HTTP_COOKIE" => cookie).
|
51
|
+
body.should == '{"counter"=>2}'
|
52
|
+
req.get("/", "HTTP_COOKIE" => cookie).
|
53
|
+
body.should == '{"counter"=>3}'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "survives nonexistant cookies" do
|
57
|
+
bad_cookie = "rack.session=blarghfasel"
|
58
|
+
pool = Riak::SessionStore.new(incrementor)
|
59
|
+
res = Rack::MockRequest.new(pool).
|
60
|
+
get("/", "HTTP_COOKIE" => bad_cookie)
|
61
|
+
res.body.should == '{"counter"=>1}'
|
62
|
+
cookie = res["Set-Cookie"][session_match]
|
63
|
+
cookie.should_not match(/#{bad_cookie}/)
|
64
|
+
end
|
65
|
+
|
66
|
+
# it "maintains freshness" do
|
67
|
+
# pool = Riak::SessionStore.new(incrementor, :expire_after => 3)
|
68
|
+
# res = Rack::MockRequest.new(pool).get('/')
|
69
|
+
# res.body.should include '"counter"=>1'
|
70
|
+
# cookie = res["Set-Cookie"]
|
71
|
+
# res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
|
72
|
+
# res["Set-Cookie"].should == cookie
|
73
|
+
# res.body.should include '"counter"=>2'
|
74
|
+
# puts 'Sleeping to expire session' if $DEBUG
|
75
|
+
# sleep 4
|
76
|
+
# res = Rack::MockRequest.new(pool).get('/', "HTTP_COOKIE" => cookie)
|
77
|
+
# res["Set-Cookie"].should_not == cookie
|
78
|
+
# res.body.should include '"counter"=>1'
|
79
|
+
# end
|
80
|
+
|
81
|
+
it "deletes cookies with :drop option" do
|
82
|
+
pool = Riak::SessionStore.new(incrementor)
|
83
|
+
req = Rack::MockRequest.new(pool)
|
84
|
+
drop = Rack::Utils::Context.new(pool, drop_session)
|
85
|
+
dreq = Rack::MockRequest.new(drop)
|
86
|
+
|
87
|
+
res0 = req.get("/")
|
88
|
+
session = (cookie = res0["Set-Cookie"])[session_match]
|
89
|
+
res0.body.should == '{"counter"=>1}'
|
90
|
+
|
91
|
+
res1 = req.get("/", "HTTP_COOKIE" => cookie)
|
92
|
+
res1["Set-Cookie"][session_match].should == session
|
93
|
+
res1.body.should == '{"counter"=>2}'
|
94
|
+
|
95
|
+
res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
|
96
|
+
res2["Set-Cookie"].should == nil
|
97
|
+
res2.body.should == '{"counter"=>3}'
|
98
|
+
|
99
|
+
res3 = req.get("/", "HTTP_COOKIE" => cookie)
|
100
|
+
res3["Set-Cookie"][session_match].should_not == session
|
101
|
+
res3.body.should == '{"counter"=>1}'
|
102
|
+
end
|
103
|
+
it "provides new session id with :renew option" do
|
104
|
+
pool = Riak::SessionStore.new(incrementor)
|
105
|
+
req = Rack::MockRequest.new(pool)
|
106
|
+
renew = Rack::Utils::Context.new(pool, renew_session)
|
107
|
+
rreq = Rack::MockRequest.new(renew)
|
108
|
+
|
109
|
+
res0 = req.get("/")
|
110
|
+
session = (cookie = res0["Set-Cookie"])[session_match]
|
111
|
+
res0.body.should == '{"counter"=>1}'
|
112
|
+
|
113
|
+
res1 = req.get("/", "HTTP_COOKIE" => cookie)
|
114
|
+
res1["Set-Cookie"][session_match].should == session
|
115
|
+
res1.body.should == '{"counter"=>2}'
|
116
|
+
|
117
|
+
res2 = rreq.get("/", "HTTP_COOKIE" => cookie)
|
118
|
+
new_cookie = res2["Set-Cookie"]
|
119
|
+
new_session = new_cookie[session_match]
|
120
|
+
new_session.should_not == session
|
121
|
+
res2.body.should == '{"counter"=>3}'
|
122
|
+
|
123
|
+
res3 = req.get("/", "HTTP_COOKIE" => new_cookie)
|
124
|
+
res3["Set-Cookie"][session_match].should == new_session
|
125
|
+
res3.body.should == '{"counter"=>4}'
|
126
|
+
end
|
127
|
+
|
128
|
+
it "omits cookie with :defer option" do
|
129
|
+
pool = Riak::SessionStore.new(incrementor)
|
130
|
+
req = Rack::MockRequest.new(pool)
|
131
|
+
defer = Rack::Utils::Context.new(pool, defer_session)
|
132
|
+
dreq = Rack::MockRequest.new(defer)
|
133
|
+
|
134
|
+
res0 = req.get("/")
|
135
|
+
session = (cookie = res0["Set-Cookie"])[session_match]
|
136
|
+
res0.body.should == '{"counter"=>1}'
|
137
|
+
|
138
|
+
res1 = req.get("/", "HTTP_COOKIE" => cookie)
|
139
|
+
res1["Set-Cookie"][session_match].should == session
|
140
|
+
res1.body.should == '{"counter"=>2}'
|
141
|
+
|
142
|
+
res2 = dreq.get("/", "HTTP_COOKIE" => cookie)
|
143
|
+
res2["Set-Cookie"].should == nil
|
144
|
+
res2.body.should == '{"counter"=>3}'
|
145
|
+
|
146
|
+
res3 = req.get("/", "HTTP_COOKIE" => cookie)
|
147
|
+
res3["Set-Cookie"][session_match].should == session
|
148
|
+
res3.body.should == '{"counter"=>4}'
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
require File.expand_path("../spec_helper", __FILE__)
|
15
|
+
require 'ripple/session_store'
|
16
|
+
|
17
|
+
describe "Ripple::SessionStore" do
|
18
|
+
include RSpec::Rails::RequestExampleGroup
|
19
|
+
include Ripple::SessionStoreTest
|
20
|
+
hooks[:before][:each].pop # Remove the router junk
|
21
|
+
|
22
|
+
before :each do
|
23
|
+
@app = build_app do |middleware|
|
24
|
+
middleware.use Ripple::SessionStore, :key => '_session_id'
|
25
|
+
middleware.delete "ActionDispatch::ShowExceptions"
|
26
|
+
end
|
27
|
+
@app.routes.draw do
|
28
|
+
match ':action', :to => ::Ripple::SessionStoreTest::TestController
|
29
|
+
end
|
30
|
+
::Ripple::SessionStoreTest::TestController.send(:include, @app.routes.url_helpers)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should set and get a session value" do
|
34
|
+
get '/set_session_value'
|
35
|
+
response.should be_success
|
36
|
+
cookies['_session_id'].should be
|
37
|
+
|
38
|
+
get '/get_session_value'
|
39
|
+
response.should be_success
|
40
|
+
'foo: "bar"'.should == response.body
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should get nothing from a new session" do
|
44
|
+
get '/get_session_value'
|
45
|
+
response.should be_success
|
46
|
+
'foo: nil'.should == response.body
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should get an empty session after reset" do
|
50
|
+
get '/set_session_value'
|
51
|
+
response.should be_success
|
52
|
+
cookies['_session_id'].should be
|
53
|
+
session_cookie = cookies.send(:hash_for)['_session_id']
|
54
|
+
|
55
|
+
get '/call_reset_session'
|
56
|
+
response.should be_success
|
57
|
+
[].should_not == headers['Set-Cookie']
|
58
|
+
|
59
|
+
cookies << session_cookie # replace our new session_id with our old, pre-reset session_id
|
60
|
+
|
61
|
+
get '/get_session_value'
|
62
|
+
response.should be_success
|
63
|
+
'foo: nil'.should == response.body
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should not create a session unless writing to it" do
|
67
|
+
get '/get_session_value'
|
68
|
+
response.should be_success
|
69
|
+
'foo: nil'.should == response.body
|
70
|
+
cookies['_session_id'].should be_nil
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should set a value in the new session after reset" do
|
74
|
+
get '/set_session_value'
|
75
|
+
response.should be_success
|
76
|
+
cookies['_session_id'].should be
|
77
|
+
session_id = cookies['_session_id']
|
78
|
+
|
79
|
+
get '/call_reset_session'
|
80
|
+
response.should be_success
|
81
|
+
[].should_not == headers['Set-Cookie']
|
82
|
+
|
83
|
+
get '/get_session_value'
|
84
|
+
response.should be_success
|
85
|
+
'foo: nil'.should == response.body
|
86
|
+
|
87
|
+
get '/get_session_id'
|
88
|
+
response.should be_success
|
89
|
+
session_id.should_not == response.body
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should get the session id when the session exists" do
|
93
|
+
get '/set_session_value'
|
94
|
+
response.should be_success
|
95
|
+
cookies['_session_id'].should be
|
96
|
+
session_id = cookies['_session_id']
|
97
|
+
|
98
|
+
get '/get_session_id'
|
99
|
+
response.should be_success
|
100
|
+
session_id.should == response.body
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should deserialize an unloaded class" do
|
104
|
+
with_autoload_path "session_autoload_test" do
|
105
|
+
get '/set_serialized_session_value'
|
106
|
+
response.should be_success
|
107
|
+
cookies['_session_id'].should be
|
108
|
+
end
|
109
|
+
with_autoload_path "session_autoload_test" do
|
110
|
+
get '/get_session_id'
|
111
|
+
response.should be_success
|
112
|
+
end
|
113
|
+
with_autoload_path "session_autoload_test" do
|
114
|
+
get '/get_session_value'
|
115
|
+
response.should be_success
|
116
|
+
'foo: #<SessionAutoloadTest::Foo bar:"baz">'.should == response.body
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should not send the session cookie again if the ID already exists" do
|
121
|
+
get '/set_session_value'
|
122
|
+
response.should be_success
|
123
|
+
cookies['_session_id'].should be
|
124
|
+
|
125
|
+
get '/get_session_value'
|
126
|
+
response.should be_success
|
127
|
+
headers['Set-Cookie'].should be_nil
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should prevent session fixation" do
|
131
|
+
get '/set_session_value'
|
132
|
+
response.should be_success
|
133
|
+
cookies['_session_id'].should be
|
134
|
+
|
135
|
+
get '/get_session_value'
|
136
|
+
response.should be_success
|
137
|
+
headers['Set-Cookie'].should be_nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
16
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'riak-client','lib'))
|
17
|
+
|
18
|
+
require 'rubygems' # Use the gems path only for the spec suite
|
19
|
+
require 'riak'
|
20
|
+
%w{rails action_pack action_dispatch action_controller action_view}.each {|f| require f }
|
21
|
+
require 'riak-sessions'
|
22
|
+
require 'rspec/rails'
|
23
|
+
require 'rspec/autorun'
|
24
|
+
|
25
|
+
Dir[File.join(File.dirname(__FILE__), "support", "*.rb")].each {|f| require f }
|
26
|
+
|
27
|
+
Rspec.configure do |config|
|
28
|
+
config.mock_with :rspec
|
29
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'rspec/rails/example/request_example_group'
|
2
|
+
|
3
|
+
class RoutedRackApp
|
4
|
+
attr_reader :routes
|
5
|
+
|
6
|
+
def initialize(routes, &blk)
|
7
|
+
@routes = routes
|
8
|
+
@stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@stack.call(env)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Ripple
|
17
|
+
module SessionStoreTest
|
18
|
+
class TestController < ActionController::Base
|
19
|
+
def no_session_access
|
20
|
+
head :ok
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_session_value
|
24
|
+
session[:foo] = "bar"
|
25
|
+
head :ok
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_serialized_session_value
|
29
|
+
session[:foo] = SessionAutoloadTest::Foo.new
|
30
|
+
head :ok
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_session_value
|
34
|
+
render :text => "foo: #{session[:foo].inspect}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_session_id
|
38
|
+
render :text => "#{request.session_options[:id]}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def call_reset_session
|
42
|
+
session[:bar]
|
43
|
+
reset_session
|
44
|
+
session[:bar] = "baz"
|
45
|
+
head :ok
|
46
|
+
end
|
47
|
+
|
48
|
+
def rescue_action(e) raise end
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_app(routes = nil)
|
52
|
+
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
|
53
|
+
middleware.use "ActionDispatch::ShowExceptions"
|
54
|
+
middleware.use "ActionDispatch::Callbacks"
|
55
|
+
middleware.use "ActionDispatch::ParamsParser"
|
56
|
+
middleware.use "ActionDispatch::Cookies"
|
57
|
+
middleware.use "ActionDispatch::Flash"
|
58
|
+
middleware.use "ActionDispatch::Head"
|
59
|
+
yield(middleware) if block_given?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def app
|
64
|
+
@app || super
|
65
|
+
end
|
66
|
+
|
67
|
+
def with_autoload_path(path)
|
68
|
+
path = File.join(File.dirname(__FILE__),"..","fixtures", path)
|
69
|
+
if ActiveSupport::Dependencies.autoload_paths.include?(path)
|
70
|
+
yield
|
71
|
+
else
|
72
|
+
begin
|
73
|
+
ActiveSupport::Dependencies.autoload_paths << path
|
74
|
+
yield
|
75
|
+
ensure
|
76
|
+
ActiveSupport::Dependencies.autoload_paths.reject! {|p| p == path}
|
77
|
+
ActiveSupport::Dependencies.clear
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
metadata
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: riak-sessions
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: true
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 8
|
8
|
+
- 0
|
9
|
+
- beta2
|
10
|
+
version: 0.8.0.beta2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Sean Cribbs
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-30 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
- 0
|
32
|
+
- 0
|
33
|
+
- beta
|
34
|
+
- 18
|
35
|
+
version: 2.0.0.beta.18
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rspec-rails
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
segments:
|
47
|
+
- 2
|
48
|
+
- 0
|
49
|
+
- 0
|
50
|
+
- beta
|
51
|
+
- 18
|
52
|
+
version: 2.0.0.beta.18
|
53
|
+
type: :development
|
54
|
+
version_requirements: *id002
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yajl-ruby
|
57
|
+
prerelease: false
|
58
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rails
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
segments:
|
77
|
+
- 3
|
78
|
+
- 0
|
79
|
+
- 0
|
80
|
+
version: 3.0.0
|
81
|
+
type: :development
|
82
|
+
version_requirements: *id004
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: curb
|
85
|
+
prerelease: false
|
86
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">"
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
- 6
|
94
|
+
version: "0.6"
|
95
|
+
type: :development
|
96
|
+
version_requirements: *id005
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
prerelease: false
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
segments:
|
106
|
+
- 0
|
107
|
+
version: "0"
|
108
|
+
type: :development
|
109
|
+
version_requirements: *id006
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: riak-client
|
112
|
+
prerelease: false
|
113
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ~>
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
- 8
|
121
|
+
- 0
|
122
|
+
- beta2
|
123
|
+
version: 0.8.0.beta2
|
124
|
+
type: :runtime
|
125
|
+
version_requirements: *id007
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: rack
|
128
|
+
prerelease: false
|
129
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
segments:
|
135
|
+
- 1
|
136
|
+
- 0
|
137
|
+
version: "1.0"
|
138
|
+
type: :runtime
|
139
|
+
version_requirements: *id008
|
140
|
+
description: riak-sessions is a session store backed by Riak, the distributed database by Basho. It includes session implementations for both Rack and Rails 3.
|
141
|
+
email: seancribbs@gmail.com
|
142
|
+
executables: []
|
143
|
+
|
144
|
+
extensions: []
|
145
|
+
|
146
|
+
extra_rdoc_files: []
|
147
|
+
|
148
|
+
files:
|
149
|
+
- lib/riak/session_store.rb
|
150
|
+
- lib/riak-sessions.rb
|
151
|
+
- lib/ripple/session_store.rb
|
152
|
+
- Rakefile
|
153
|
+
- spec/fixtures/session_autoload_test/session_autoload_test/foo.rb
|
154
|
+
- spec/riak_session_store_spec.rb
|
155
|
+
- spec/ripple_session_store_spec.rb
|
156
|
+
- spec/spec_helper.rb
|
157
|
+
- spec/support/ripple_session_support.rb
|
158
|
+
has_rdoc: true
|
159
|
+
homepage: http://seancribbs.github.com/ripple
|
160
|
+
licenses: []
|
161
|
+
|
162
|
+
post_install_message:
|
163
|
+
rdoc_options: []
|
164
|
+
|
165
|
+
require_paths:
|
166
|
+
- lib
|
167
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
168
|
+
none: false
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
segments:
|
173
|
+
- 0
|
174
|
+
version: "0"
|
175
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
176
|
+
none: false
|
177
|
+
requirements:
|
178
|
+
- - ">"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
segments:
|
181
|
+
- 1
|
182
|
+
- 3
|
183
|
+
- 1
|
184
|
+
version: 1.3.1
|
185
|
+
requirements: []
|
186
|
+
|
187
|
+
rubyforge_project:
|
188
|
+
rubygems_version: 1.3.7
|
189
|
+
signing_key:
|
190
|
+
specification_version: 3
|
191
|
+
summary: riak-sessions is a session store backed by Riak, the distributed database by Basho.
|
192
|
+
test_files:
|
193
|
+
- spec/fixtures/session_autoload_test/session_autoload_test/foo.rb
|
194
|
+
- spec/riak_session_store_spec.rb
|
195
|
+
- spec/ripple_session_store_spec.rb
|
196
|
+
- spec/spec_helper.rb
|
197
|
+
- spec/support/ripple_session_support.rb
|