riak-sessions 0.8.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|