rack_global_session 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +44 -0
- data/LICENSE +20 -0
- data/README.rdoc +66 -0
- data/Rakefile +93 -0
- data/lib/rack_global_session/version.rb +5 -0
- data/lib/rack_global_session.rb +154 -0
- data/rack_global_session.gemspec +57 -0
- data/spec/rack_global_session_spec.rb +200 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +128 -0
- metadata +226 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rack_global_session (0.2)
|
5
|
+
activesupport (~> 3.0.3)
|
6
|
+
has_global_session (~> 1.1.3)
|
7
|
+
i18n (~> 0.5.0)
|
8
|
+
rack (~> 1.2)
|
9
|
+
rack-contrib (~> 1.0.1)
|
10
|
+
tzinfo
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: http://rubygems.org/
|
14
|
+
specs:
|
15
|
+
activesupport (3.0.3)
|
16
|
+
flexmock (0.8.11)
|
17
|
+
has_global_session (1.1.3)
|
18
|
+
activesupport (>= 2.1.2)
|
19
|
+
json (>= 1.1.7)
|
20
|
+
uuidtools (>= 1.0.7)
|
21
|
+
i18n (0.5.0)
|
22
|
+
json (1.4.6)
|
23
|
+
rack (1.2.1)
|
24
|
+
rack-contrib (1.0.1)
|
25
|
+
rack (>= 0.9.1)
|
26
|
+
rspec (1.3.0)
|
27
|
+
rtags (0.97)
|
28
|
+
tzinfo (0.3.23)
|
29
|
+
uuidtools (2.1.1)
|
30
|
+
|
31
|
+
PLATFORMS
|
32
|
+
ruby
|
33
|
+
|
34
|
+
DEPENDENCIES
|
35
|
+
activesupport (~> 3.0.3)
|
36
|
+
flexmock (~> 0.8.11)
|
37
|
+
has_global_session (~> 1.1.3)
|
38
|
+
i18n (~> 0.5.0)
|
39
|
+
rack (~> 1.2)
|
40
|
+
rack-contrib (~> 1.0.1)
|
41
|
+
rack_global_session!
|
42
|
+
rspec (~> 1.3)
|
43
|
+
rtags (~> 0.97)
|
44
|
+
tzinfo
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 RightScale, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
'Software'), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
= rack_global_session
|
2
|
+
|
3
|
+
== DESCRIPTION
|
4
|
+
|
5
|
+
=== Synopsis
|
6
|
+
|
7
|
+
rack_global_session is a shim to make has_global_session work as Rack
|
8
|
+
middleware. rack_global_session requires that Rack::Cookies from
|
9
|
+
rack-contrib be loaded before it.
|
10
|
+
|
11
|
+
== USAGE
|
12
|
+
|
13
|
+
=== Simple Example
|
14
|
+
|
15
|
+
use Rack::Cookies
|
16
|
+
use Rack::GlobalSession, filename
|
17
|
+
|
18
|
+
=== Complex Example
|
19
|
+
|
20
|
+
use Rack::Cookies
|
21
|
+
use Rack::GlobalSession, filename, do |env|
|
22
|
+
auth = Rack::Auth::Basic::Request.new(env)
|
23
|
+
auth.provided? && auth.basic? && auth.credentials[1]
|
24
|
+
end
|
25
|
+
|
26
|
+
== INSTALLATION
|
27
|
+
|
28
|
+
This gem can be installed by entering the following at a command
|
29
|
+
prompt:
|
30
|
+
|
31
|
+
gem install rack_global_session
|
32
|
+
|
33
|
+
== TESTING
|
34
|
+
|
35
|
+
Install the following gems for testing:
|
36
|
+
- rspec
|
37
|
+
- flexmock
|
38
|
+
|
39
|
+
Then the build can be tested with
|
40
|
+
|
41
|
+
rake spec
|
42
|
+
|
43
|
+
== LICENSE
|
44
|
+
|
45
|
+
<b>RightScraper</b>
|
46
|
+
|
47
|
+
Copyright:: Copyright (c) 2010 RightScale, Inc.
|
48
|
+
|
49
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
50
|
+
a copy of this software and associated documentation files (the
|
51
|
+
'Software'), to deal in the Software without restriction, including
|
52
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
53
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
54
|
+
permit persons to whom the Software is furnished to do so, subject to
|
55
|
+
the following conditions:
|
56
|
+
|
57
|
+
The above copyright notice and this permission notice shall be
|
58
|
+
included in all copies or substantial portions of the Software.
|
59
|
+
|
60
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
61
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
62
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
63
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
64
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
65
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
66
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
#-- -*-ruby-*-
|
2
|
+
# Copyright: Copyright (c) 2010 RightScale, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# 'Software'), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'bundler'
|
26
|
+
require 'fileutils'
|
27
|
+
require 'rake'
|
28
|
+
require 'spec/rake/spectask'
|
29
|
+
require 'rake/rdoctask'
|
30
|
+
require 'rake/gempackagetask'
|
31
|
+
require 'rake/clean'
|
32
|
+
|
33
|
+
Bundler::GemHelper.install_tasks
|
34
|
+
|
35
|
+
# == Gem == #
|
36
|
+
|
37
|
+
gemtask = Rake::GemPackageTask.new(Gem::Specification.load("rack_global_session.gemspec")) do |package|
|
38
|
+
package.package_dir = ENV['PACKAGE_DIR'] || 'pkg'
|
39
|
+
package.need_zip = true
|
40
|
+
package.need_tar = true
|
41
|
+
end
|
42
|
+
|
43
|
+
directory gemtask.package_dir
|
44
|
+
|
45
|
+
CLEAN.include(gemtask.package_dir)
|
46
|
+
|
47
|
+
# == Unit Tests == #
|
48
|
+
|
49
|
+
task :specs => :spec
|
50
|
+
|
51
|
+
desc "Run unit tests"
|
52
|
+
Spec::Rake::SpecTask.new do |t|
|
53
|
+
t.spec_files = Dir['spec/**/*_spec.rb']
|
54
|
+
t.spec_opts = lambda do
|
55
|
+
IO.readlines(File.join(File.dirname(__FILE__), 'spec', 'spec.opts')).map {|l| l.chomp.split " "}.flatten
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "Run unit tests with RCov"
|
60
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
61
|
+
t.spec_files = Dir['spec/**/*_spec.rb']
|
62
|
+
t.rcov = true
|
63
|
+
t.rcov_opts = lambda do
|
64
|
+
IO.readlines(File.join(File.dirname(__FILE__), 'spec', 'rcov.opts')).map {|l| l.chomp.split " "}.flatten
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "Print Specdoc for unit tests"
|
69
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
70
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
71
|
+
t.spec_files = Dir['spec/**/*_spec.rb']
|
72
|
+
end
|
73
|
+
|
74
|
+
# == Documentation == #
|
75
|
+
|
76
|
+
desc "Generate API documentation to doc/rdocs/index.html"
|
77
|
+
Rake::RDocTask.new do |rd|
|
78
|
+
rd.rdoc_dir = 'doc/rdocs'
|
79
|
+
rd.main = 'README.rdoc'
|
80
|
+
rd.rdoc_files.include 'README.rdoc', "lib/**/*.rb"
|
81
|
+
|
82
|
+
rd.options << '--inline-source'
|
83
|
+
rd.options << '--line-numbers'
|
84
|
+
rd.options << '--all'
|
85
|
+
rd.options << '--fileboxes'
|
86
|
+
rd.options << '--diagram'
|
87
|
+
end
|
88
|
+
|
89
|
+
# == Emacs integration == #
|
90
|
+
desc "Rebuild TAGS file"
|
91
|
+
task :tags do
|
92
|
+
sh "rtags -R lib spec"
|
93
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright: Copyright (c) 2010 RightScale, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# 'Software'), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
require "has_global_session"
|
25
|
+
require "active_support"
|
26
|
+
require "active_support/time"
|
27
|
+
|
28
|
+
module Rack
|
29
|
+
# A port of has_global_session to Rack middleware.
|
30
|
+
module GlobalSessions
|
31
|
+
# Alias some of the HasGlobalSession classes for easy typing.
|
32
|
+
Configuration = HasGlobalSession::Configuration
|
33
|
+
Directory = HasGlobalSession::Directory
|
34
|
+
Session = HasGlobalSession::GlobalSession
|
35
|
+
# Global session middleware. Note: this class relies on
|
36
|
+
# Rack::Cookies being used higher up in the chain.
|
37
|
+
class GlobalSession
|
38
|
+
# Make a new global session.
|
39
|
+
#
|
40
|
+
# The optional block here controls an alternate ticket retrieval
|
41
|
+
# method. If no ticket is stored in the cookie jar, this
|
42
|
+
# function is called. If it returns a non-nil value, that value
|
43
|
+
# is the ticket.
|
44
|
+
#
|
45
|
+
# === Parameters
|
46
|
+
# app(Rack client): application to run
|
47
|
+
# configuration(String or HasGlobalSession::Configuration): has_global_session configuration.
|
48
|
+
# If a string, is interpreted as a
|
49
|
+
# filename to load the config from.
|
50
|
+
# block: optional alternate ticket retrieval function
|
51
|
+
def initialize(app, configuration, &block)
|
52
|
+
@app = app
|
53
|
+
if configuration.instance_of?(String)
|
54
|
+
@configuration = Configuration.new(configuration, ENV['RACK_ENV'] || 'development')
|
55
|
+
else
|
56
|
+
@configuration = configuration
|
57
|
+
end
|
58
|
+
@cookie_retrieval = block
|
59
|
+
@directory = Directory.new(@configuration, @configuration['directory'])
|
60
|
+
@cookie_name = @configuration['cookie']['name']
|
61
|
+
end
|
62
|
+
|
63
|
+
# Read a cookie from the Rack environment.
|
64
|
+
#
|
65
|
+
# === Parameters
|
66
|
+
# env(Hash): Rack environment.
|
67
|
+
def read_cookie(env)
|
68
|
+
begin
|
69
|
+
if env['rack.cookies'].key?(@cookie_name)
|
70
|
+
env['global_session'] = Session.new(@directory,
|
71
|
+
env['rack.cookies'][@cookie_name])
|
72
|
+
elsif @cookie_retrieval && cookie = @cookie_retrieval.call(env)
|
73
|
+
env['global_session'] = Session.new(@directory, cookie)
|
74
|
+
else
|
75
|
+
env['global_session'] = Session.new(@directory)
|
76
|
+
end
|
77
|
+
true
|
78
|
+
rescue Exception => e
|
79
|
+
# Reinitialize global session cookie
|
80
|
+
env['global_session'] = Session.new(@directory)
|
81
|
+
update_cookie(env)
|
82
|
+
raise e
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Renew the session ticket.
|
87
|
+
#
|
88
|
+
# === Parameters
|
89
|
+
# env(Hash): Rack environment
|
90
|
+
def renew_ticket(env)
|
91
|
+
if @configuration['renew'] && env['global_session'] &&
|
92
|
+
env['global_session'].directory.local_authority_name &&
|
93
|
+
env['global_session'].expired_at < renew.to_i.minutes.from_now.utc
|
94
|
+
env['global_session'].renew!
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Update the cookie jar with the revised ticket.
|
99
|
+
#
|
100
|
+
# === Parameters
|
101
|
+
# env(Hash): Rack environment
|
102
|
+
def update_cookie(env)
|
103
|
+
begin
|
104
|
+
domain = @configuration['cookie']['domain'] || ENV['SERVER_NAME']
|
105
|
+
if env['global_session'] && env['global_session'].valid?
|
106
|
+
value = env['global_session'].to_s
|
107
|
+
expires = @configuration['ephemeral'] ? nil : env['global_session'].expired_at
|
108
|
+
unless env['rack.cookies'].key?(@cookie_name) &&
|
109
|
+
env['rack.cookies'][@cookie_name] == value
|
110
|
+
env['rack.cookies'][@cookie_name] = {:value => value, :domain => domain, :expires => expires}
|
111
|
+
end
|
112
|
+
else
|
113
|
+
# write an empty cookie
|
114
|
+
env['rack.cookies'][@cookie_name] = {:value => nil, :domain => domain, :expires => Time.at(0)}
|
115
|
+
end
|
116
|
+
rescue Exception => e
|
117
|
+
wipe_cookie(env)
|
118
|
+
raise e
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Delete the ticket from the cookie jar.
|
123
|
+
#
|
124
|
+
# === Parameters
|
125
|
+
# env(Hash): Rack environment
|
126
|
+
def wipe_cookie(env)
|
127
|
+
domain = @configuration['cookie']['domain'] || ENV['SERVER_NAME']
|
128
|
+
env['rack.cookies'][@cookie_name] = {:value => nil, :domain => domain, :expires => Time.at(0)}
|
129
|
+
end
|
130
|
+
|
131
|
+
# Rack request chain. Sets up the global session ticket from
|
132
|
+
# the environment and passes it up the chain.
|
133
|
+
def call(env)
|
134
|
+
env['rack.cookies'] = {} unless env['rack.cookies']
|
135
|
+
begin
|
136
|
+
read_cookie(env)
|
137
|
+
renew_ticket(env)
|
138
|
+
tuple = @app.call(env)
|
139
|
+
rescue Exception => e
|
140
|
+
wipe_cookie(env)
|
141
|
+
$stderr.puts "Error while reading cookies: #{e.class} #{e} #{e.backtrace}"
|
142
|
+
if env['rack.logger']
|
143
|
+
env['rack.logger'].error("Error while reading cookies: #{e.class} #{e} #{e.backtrace}")
|
144
|
+
end
|
145
|
+
return [503, {'Content-Type' => 'text/plain'}, "Invalid cookie"]
|
146
|
+
else
|
147
|
+
update_cookie(env)
|
148
|
+
return tuple
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
GlobalSession = GlobalSessions::GlobalSession
|
154
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# -*-mode: ruby-mode; encoding: utf-8-*-
|
2
|
+
# Copyright: Copyright (c) 2010 RightScale, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# 'Software'), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
$:.push File.expand_path("../lib", __FILE__)
|
24
|
+
require "rack_global_session/version"
|
25
|
+
|
26
|
+
Gem::Specification.new do |spec|
|
27
|
+
spec.name = "rack_global_session"
|
28
|
+
spec.version = Rack::GlobalSessions::VERSION
|
29
|
+
spec.summary = "Add global session handling to Rack servers"
|
30
|
+
spec.description = <<EOS
|
31
|
+
A port of has_global_session to Rack middleware.
|
32
|
+
EOS
|
33
|
+
spec.authors = ['Graham Hughes']
|
34
|
+
spec.email = 'graham@rightscale.com'
|
35
|
+
spec.platform = Gem::Platform::RUBY
|
36
|
+
spec.has_rdoc = true
|
37
|
+
spec.rdoc_options = ["--main", "README.rdoc", "--title", "Rack::GlobalSession"]
|
38
|
+
spec.extra_rdoc_files = ["README.rdoc"]
|
39
|
+
spec.required_ruby_version = '>= 1.8.7'
|
40
|
+
spec.require_path = 'lib'
|
41
|
+
|
42
|
+
spec.add_dependency 'activesupport', '~> 3.0.3'
|
43
|
+
spec.add_dependency 'i18n', "~> 0.5.0"
|
44
|
+
spec.add_dependency 'tzinfo'
|
45
|
+
spec.add_dependency 'has_global_session', '~> 1.1.3'
|
46
|
+
spec.add_dependency 'rack', '~> 1.2'
|
47
|
+
spec.add_dependency 'rack-contrib', '~> 1.0.1'
|
48
|
+
|
49
|
+
spec.add_development_dependency 'rspec', "~> 1.3"
|
50
|
+
spec.add_development_dependency 'flexmock', "~> 0.8.11"
|
51
|
+
spec.add_development_dependency 'rtags', "~> 0.97"
|
52
|
+
|
53
|
+
spec.files = `git ls-files`.split("\n")
|
54
|
+
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
55
|
+
spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
56
|
+
spec.require_paths = ["lib"]
|
57
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright: Copyright (c) 2010 RightScale, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# 'Software'), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
|
25
|
+
require 'tmpdir'
|
26
|
+
require 'fileutils'
|
27
|
+
require 'stringio'
|
28
|
+
|
29
|
+
module Rack
|
30
|
+
describe GlobalSession do
|
31
|
+
it_should_behave_like 'Authentication'
|
32
|
+
|
33
|
+
before(:each) do
|
34
|
+
ENV['SERVER_NAME'] = "server"
|
35
|
+
ENV['RACK_ENV'] = "test"
|
36
|
+
end
|
37
|
+
|
38
|
+
before(:each) do
|
39
|
+
@olderr = $stderr
|
40
|
+
$stderr = StringIO.new
|
41
|
+
end
|
42
|
+
|
43
|
+
after(:each) do
|
44
|
+
$stderr = @olderr
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should create an empty session if none exists' do
|
48
|
+
token = Object.new
|
49
|
+
@app = Proc.new do |env|
|
50
|
+
env['global_session'].should_not be_nil
|
51
|
+
env['global_session'].should be_valid
|
52
|
+
token
|
53
|
+
end
|
54
|
+
environment = {}
|
55
|
+
Rack::GlobalSession.new(@app, @configuration).call(environment).should == token
|
56
|
+
environment['rack.cookies']["aCookie"][:value].should_not be_nil
|
57
|
+
environment['rack.cookies']["aCookie"][:domain].should == "example.com"
|
58
|
+
environment['rack.cookies']["aCookie"][:expires].should <= 10.minutes.from_now.utc
|
59
|
+
environment['rack.cookies']["aCookie"][:expires].should >= 9.minutes.from_now.utc
|
60
|
+
end
|
61
|
+
it 'should use SERVER_NAME if no domain is specified' do
|
62
|
+
@config_hash["common"]["cookie"].delete("domain")
|
63
|
+
dump_config(@config_hash)
|
64
|
+
environment = {}
|
65
|
+
Rack::GlobalSession.new(lambda {}, @configuration).call(environment)
|
66
|
+
environment['rack.cookies']["aCookie"][:domain].should == "server"
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should raise an error on ludicrously invalid cookies' do
|
70
|
+
environment = {"rack.cookies" => {
|
71
|
+
"aCookie" => "foo"
|
72
|
+
}}
|
73
|
+
key, hash, value = Rack::GlobalSession.new(lambda {}, @configuration).call(environment)
|
74
|
+
key.should == 503
|
75
|
+
hash.should == {'Content-Type' => 'text/plain'}
|
76
|
+
value.should == "Invalid cookie"
|
77
|
+
$stderr.string.should =~ /HasGlobalSession::MalformedCookie/
|
78
|
+
$stderr.string.should =~ /buffer error/
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should raise an error on well formed but invalid cookies' do
|
82
|
+
hash = {'id' => "root",
|
83
|
+
'tc' => Time.now, 'te' => Time.now,
|
84
|
+
'ds' => "not actually a signature",
|
85
|
+
'dx' => {"third" => 4},
|
86
|
+
's' => @first_key.private_encrypt("blah"),
|
87
|
+
'a' => "first",
|
88
|
+
}
|
89
|
+
json = HasGlobalSession::Encoding::JSON.dump(hash)
|
90
|
+
compressed = Zlib::Deflate.deflate(json, Zlib::BEST_COMPRESSION)
|
91
|
+
base64 = HasGlobalSession::Encoding::Base64Cookie.dump(compressed)
|
92
|
+
environment = {"rack.cookies" => {"aCookie" => base64}}
|
93
|
+
key, hash, value = Rack::GlobalSession.new(lambda {}, @configuration).call(environment)
|
94
|
+
key.should == 503
|
95
|
+
hash.should == {'Content-Type' => 'text/plain'}
|
96
|
+
value.should == "Invalid cookie"
|
97
|
+
$stderr.string.should =~ /OpenSSL::PKey::RSAError/
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'with a valid environment' do
|
101
|
+
before(:each) do
|
102
|
+
@environment = {}
|
103
|
+
Rack::GlobalSession.new(lambda {}, @configuration).call(@environment)
|
104
|
+
@environment['rack.cookies']['aCookie'] = @environment['rack.cookies']['aCookie'][:value]
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should be able to read cookies from random places' do
|
108
|
+
Rack::GlobalSession.new(lambda {}, @configuration) do |env|
|
109
|
+
env['foo']
|
110
|
+
end.call({'foo' => @environment['rack.cookies']['aCookie']})
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should read a valid cookie' do
|
114
|
+
Rack::GlobalSession.new(lambda {}, @configuration).call(@environment)
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'with an expired session' do
|
118
|
+
before(:each) do
|
119
|
+
Rack::GlobalSession.new(lambda {|e|
|
120
|
+
e['global_session'].instance_variable_set(:@expired_at,
|
121
|
+
1.minutes.from_now)},
|
122
|
+
@configuration).call(@environment)
|
123
|
+
@environment['rack.cookies']['aCookie'] = @environment['rack.cookies']['aCookie'][:value]
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should renew expired cookies when permitted' do
|
127
|
+
Rack::GlobalSession.new(lambda {|e| e['global_session'].renew!},
|
128
|
+
@configuration).call(@environment)
|
129
|
+
@environment['rack.cookies']["aCookie"][:expires].should <= 10.minutes.from_now.utc
|
130
|
+
@environment['rack.cookies']["aCookie"][:expires].should >= 9.minutes.from_now.utc
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should renew expired cookies implicitly' do
|
134
|
+
Rack::GlobalSession.new(lambda {|e| e['global_session'].expired_at.should >=
|
135
|
+
9.minutes.from_now.utc},
|
136
|
+
@configuration).call(@environment)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'when it is not an authority' do
|
141
|
+
before(:each) do
|
142
|
+
@config_hash["common"].delete("authority")
|
143
|
+
dump_config(@config_hash)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should not renew expired cookies' do
|
147
|
+
key, hash, value = Rack::GlobalSession.new(lambda {|e| e['global_session'].renew!},
|
148
|
+
@configuration).call(@environment)
|
149
|
+
key.should == 503
|
150
|
+
hash.should == {'Content-Type' => 'text/plain'}
|
151
|
+
value.should == "Invalid cookie"
|
152
|
+
$stderr.string.should =~ /HasGlobalSession::NoAuthority/
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should not attempt to update the cookie when it is not an authority' do
|
156
|
+
key, hash, value = Rack::GlobalSession.new(lambda {|e| e['global_session']['first'] = 4},
|
157
|
+
@configuration).call(@environment)
|
158
|
+
key.should == 503
|
159
|
+
hash.should == {'Content-Type' => 'text/plain'}
|
160
|
+
value.should == "Invalid cookie"
|
161
|
+
$stderr.string.should =~ /HasGlobalSession::NoAuthority/
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should update cookies correctly' do
|
166
|
+
Rack::GlobalSession.new(lambda { |env|
|
167
|
+
env['global_session']['first'] = "foo"
|
168
|
+
env['global_session']['second'] = "bar"
|
169
|
+
}, @configuration).call(@environment)
|
170
|
+
oldvalue = @environment['rack.cookies']['aCookie'][:value]
|
171
|
+
@environment['rack.cookies']['aCookie'] = @environment['rack.cookies']['aCookie'][:value]
|
172
|
+
Rack::GlobalSession.new(lambda { |env|
|
173
|
+
env['global_session']['first'].should == "foo"
|
174
|
+
env['global_session']['second'].should == "bar"
|
175
|
+
env['global_session']['first'] = "baz"
|
176
|
+
}, @configuration).call(@environment)
|
177
|
+
@environment['rack.cookies']['aCookie'][:value].should_not == oldvalue
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'should unconditionally wipe the cookie if an error occurs' do
|
181
|
+
@environment['rack.cookies']['aCookie'].should_not be_nil
|
182
|
+
key, hash, value = Rack::GlobalSession.new(lambda {raise "foo"}, @configuration).call(@environment)
|
183
|
+
key.should == 503
|
184
|
+
hash.should == {'Content-Type' => 'text/plain'}
|
185
|
+
value.should == "Invalid cookie"
|
186
|
+
@environment['rack.cookies']['aCookie'][:value].should be_nil
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'should refuse cookies from invalid certification authorities' do
|
190
|
+
@config_hash["common"]["trust"] = "second"
|
191
|
+
dump_config(@config_hash)
|
192
|
+
key, hash, value = Rack::GlobalSession.new(lambda {}, @configuration).call(@environment)
|
193
|
+
key.should == 503
|
194
|
+
hash.should == {'Content-Type' => 'text/plain'}
|
195
|
+
value.should == "Invalid cookie"
|
196
|
+
$stderr.string.should =~ /SecurityError/
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
data/spec/rcov.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--exclude "spec/*"~
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright: Copyright (c) 2010 RightScale, Inc.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# 'Software'), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
18
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
19
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
20
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
21
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
require "rubygems"
|
25
|
+
require 'bundler/setup'
|
26
|
+
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
|
27
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/rack_global_session")
|
28
|
+
require "spec"
|
29
|
+
require "flexmock"
|
30
|
+
require "tmpdir"
|
31
|
+
require "yaml"
|
32
|
+
|
33
|
+
Spec::Runner.configuration.mock_with :flexmock
|
34
|
+
|
35
|
+
module Rack::GlobalSessions
|
36
|
+
# Helper class for managing OpenSSL keys.
|
37
|
+
class KeyFactory
|
38
|
+
# Make a new keystore, including a temporary directory.
|
39
|
+
def initialize
|
40
|
+
@keystore = Dir.mktmpdir
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return the directory all keys are stored in.
|
44
|
+
#
|
45
|
+
# === Returns
|
46
|
+
# keystore(String): directory where all keys are stored
|
47
|
+
def dir
|
48
|
+
@keystore
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create a new OpenSSL key and write it to the temporary directory.
|
52
|
+
#
|
53
|
+
# === Parameters
|
54
|
+
# name(String): name of key
|
55
|
+
# write_private(Boolean): whether to write the private key to the directory
|
56
|
+
#
|
57
|
+
# === Returns
|
58
|
+
# new_key(OpenSSL::PKey::RSA): key generated
|
59
|
+
def create(name, write_private)
|
60
|
+
new_key = OpenSSL::PKey::RSA.generate(1024)
|
61
|
+
new_public = new_key.public_key.to_pem
|
62
|
+
new_private = new_key.to_pem
|
63
|
+
File.open(File.join(@keystore, "#{name}.pub"), 'w') { |f| f.puts new_public }
|
64
|
+
File.open(File.join(@keystore, "#{name}.key"), 'w') { |f| f.puts new_key } if write_private
|
65
|
+
new_key
|
66
|
+
end
|
67
|
+
|
68
|
+
# Remove all keys in the key store.
|
69
|
+
def reset()
|
70
|
+
Dir[File.join(@keystore, "*")].each { |f| FileUtils.remove_entry_secure f }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Tear down the keystore.
|
74
|
+
def destroy()
|
75
|
+
FileUtils.remove_entry_secure(@keystore)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
module SpecHelpers
|
80
|
+
shared_examples_for "Authentication" do
|
81
|
+
before(:all) do
|
82
|
+
@factory = KeyFactory.new
|
83
|
+
end
|
84
|
+
|
85
|
+
before(:each) do
|
86
|
+
@first_key = @factory.create("first", true)
|
87
|
+
@second_key = @factory.create("second", false)
|
88
|
+
@config_hash = {
|
89
|
+
"common" => {
|
90
|
+
"attributes" => {
|
91
|
+
"signed" => ["first", "second"],
|
92
|
+
"insecure" => ["third"]
|
93
|
+
},
|
94
|
+
"authority" => "first",
|
95
|
+
"trust" => "first",
|
96
|
+
"timeout" => 10,
|
97
|
+
"directory" => @factory.dir,
|
98
|
+
"cookie" => {
|
99
|
+
"name" => "aCookie",
|
100
|
+
"domain" => "example.com",
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
@config_file = File.join(@factory.dir, "config")
|
105
|
+
dump_config(@config_hash)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Dump configuration for has_global_session to the config file.
|
109
|
+
#
|
110
|
+
# === Parameters
|
111
|
+
# hash(Hash): has_global_session configuration
|
112
|
+
def dump_config(hash)
|
113
|
+
File.open(@config_file, "w") do |file|
|
114
|
+
file << YAML.dump(hash)
|
115
|
+
end
|
116
|
+
@configuration = Configuration.new(@config_file, "test")
|
117
|
+
end
|
118
|
+
|
119
|
+
after(:all) do
|
120
|
+
@factory.destroy
|
121
|
+
end
|
122
|
+
|
123
|
+
after(:each) do
|
124
|
+
@factory.reset
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
metadata
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack_global_session
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: "0.2"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Graham Hughes
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-11-30 00:00:00 -08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: activesupport
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 1
|
29
|
+
segments:
|
30
|
+
- 3
|
31
|
+
- 0
|
32
|
+
- 3
|
33
|
+
version: 3.0.3
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: i18n
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 11
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
- 5
|
48
|
+
- 0
|
49
|
+
version: 0.5.0
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: tzinfo
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
type: :runtime
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: has_global_session
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ~>
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 21
|
75
|
+
segments:
|
76
|
+
- 1
|
77
|
+
- 1
|
78
|
+
- 3
|
79
|
+
version: 1.1.3
|
80
|
+
type: :runtime
|
81
|
+
version_requirements: *id004
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rack
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 11
|
91
|
+
segments:
|
92
|
+
- 1
|
93
|
+
- 2
|
94
|
+
version: "1.2"
|
95
|
+
type: :runtime
|
96
|
+
version_requirements: *id005
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rack-contrib
|
99
|
+
prerelease: false
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ~>
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
hash: 21
|
106
|
+
segments:
|
107
|
+
- 1
|
108
|
+
- 0
|
109
|
+
- 1
|
110
|
+
version: 1.0.1
|
111
|
+
type: :runtime
|
112
|
+
version_requirements: *id006
|
113
|
+
- !ruby/object:Gem::Dependency
|
114
|
+
name: rspec
|
115
|
+
prerelease: false
|
116
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ~>
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
hash: 9
|
122
|
+
segments:
|
123
|
+
- 1
|
124
|
+
- 3
|
125
|
+
version: "1.3"
|
126
|
+
type: :development
|
127
|
+
version_requirements: *id007
|
128
|
+
- !ruby/object:Gem::Dependency
|
129
|
+
name: flexmock
|
130
|
+
prerelease: false
|
131
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
133
|
+
requirements:
|
134
|
+
- - ~>
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
hash: 41
|
137
|
+
segments:
|
138
|
+
- 0
|
139
|
+
- 8
|
140
|
+
- 11
|
141
|
+
version: 0.8.11
|
142
|
+
type: :development
|
143
|
+
version_requirements: *id008
|
144
|
+
- !ruby/object:Gem::Dependency
|
145
|
+
name: rtags
|
146
|
+
prerelease: false
|
147
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ~>
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
hash: 201
|
153
|
+
segments:
|
154
|
+
- 0
|
155
|
+
- 97
|
156
|
+
version: "0.97"
|
157
|
+
type: :development
|
158
|
+
version_requirements: *id009
|
159
|
+
description: |
|
160
|
+
A port of has_global_session to Rack middleware.
|
161
|
+
|
162
|
+
email: graham@rightscale.com
|
163
|
+
executables: []
|
164
|
+
|
165
|
+
extensions: []
|
166
|
+
|
167
|
+
extra_rdoc_files:
|
168
|
+
- README.rdoc
|
169
|
+
files:
|
170
|
+
- .gitignore
|
171
|
+
- Gemfile
|
172
|
+
- Gemfile.lock
|
173
|
+
- LICENSE
|
174
|
+
- README.rdoc
|
175
|
+
- Rakefile
|
176
|
+
- lib/rack_global_session.rb
|
177
|
+
- lib/rack_global_session/version.rb
|
178
|
+
- rack_global_session.gemspec
|
179
|
+
- spec/rack_global_session_spec.rb
|
180
|
+
- spec/rcov.opts
|
181
|
+
- spec/spec.opts
|
182
|
+
- spec/spec_helper.rb
|
183
|
+
has_rdoc: true
|
184
|
+
homepage:
|
185
|
+
licenses: []
|
186
|
+
|
187
|
+
post_install_message:
|
188
|
+
rdoc_options:
|
189
|
+
- --main
|
190
|
+
- README.rdoc
|
191
|
+
- --title
|
192
|
+
- Rack::GlobalSession
|
193
|
+
require_paths:
|
194
|
+
- lib
|
195
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
196
|
+
none: false
|
197
|
+
requirements:
|
198
|
+
- - ">="
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
hash: 57
|
201
|
+
segments:
|
202
|
+
- 1
|
203
|
+
- 8
|
204
|
+
- 7
|
205
|
+
version: 1.8.7
|
206
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
207
|
+
none: false
|
208
|
+
requirements:
|
209
|
+
- - ">="
|
210
|
+
- !ruby/object:Gem::Version
|
211
|
+
hash: 3
|
212
|
+
segments:
|
213
|
+
- 0
|
214
|
+
version: "0"
|
215
|
+
requirements: []
|
216
|
+
|
217
|
+
rubyforge_project:
|
218
|
+
rubygems_version: 1.3.7
|
219
|
+
signing_key:
|
220
|
+
specification_version: 3
|
221
|
+
summary: Add global session handling to Rack servers
|
222
|
+
test_files:
|
223
|
+
- spec/rack_global_session_spec.rb
|
224
|
+
- spec/rcov.opts
|
225
|
+
- spec/spec.opts
|
226
|
+
- spec/spec_helper.rb
|