rack_global_session 0.2
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/.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
|