has_global_session 0.8.0 → 0.8.1
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/{README → README.rdoc} +88 -51
- data/has_global_session.gemspec +3 -3
- data/lib/generators/global_session_authority/global_session_authority_generator.rb +32 -0
- data/lib/generators/global_session_config/global_session_config_generator.rb +1 -1
- data/lib/generators/global_session_config/templates/global_session.yml.erb +18 -6
- data/lib/has_global_session/configuration.rb +20 -12
- data/lib/has_global_session/global_session.rb +29 -12
- data/lib/has_global_session/integrated_session.rb +3 -0
- data/rails/action_controller_instance_methods.rb +38 -1
- data/rails/init.rb +15 -12
- metadata +31 -10
data/{README → README.rdoc}
RENAMED
@@ -5,104 +5,121 @@ session state in a cryptographically secure way, facilitating single sign-on
|
|
5
5
|
and enabling easier development of large-scale distributed applications that
|
6
6
|
make use of architectural strategies such as sharding or separation of concerns.
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
In other words: it lets semi-related Web apps share selected bits of session
|
9
|
+
state.
|
10
|
+
|
11
|
+
This plugin does not provide a complete solution for identity management. In
|
12
|
+
particular, it does not provide any of the following:
|
10
13
|
|
11
14
|
* <b>federation</b> -- aka cross-domain single sign-on -- use OpenID for that.
|
12
15
|
|
13
|
-
* <b>authentication</b> -- the application
|
16
|
+
* <b>authentication</b> -- the application must authenticate the user.
|
17
|
+
|
18
|
+
* <b>authorization</b> -- the application is responsible for using the contents
|
19
|
+
of the global session to make authorization decisions.
|
14
20
|
|
15
21
|
* <b>secrecy</b> -- global session attributes can be signed but never encrypted;
|
16
|
-
protect against third-party snooping using SSL
|
17
|
-
users to see their session state, put it in a database
|
22
|
+
protect against third-party snooping using SSL. Group secrecy is expensive;
|
23
|
+
if you don't want your users to see their session state, put it in a database,
|
24
|
+
or in an encrypted local session cookie.
|
18
25
|
|
19
26
|
* <b>replication</b> -- the authentication authorities must have some way to
|
20
|
-
share information about the database of users
|
27
|
+
share information about the database of users in order to authenticate
|
28
|
+
them and place identifying information into the global session.
|
21
29
|
|
22
30
|
* <b>single sign-out</b> -- the authorities must have some way to broadcast a
|
23
31
|
notification when sessions are invalidated; they can override the default
|
24
32
|
Directory implementation to do realtime revocation checking.
|
25
33
|
|
26
|
-
|
34
|
+
= Example
|
35
|
+
|
36
|
+
1) Create a basic config file and edit it to suit your needs:
|
37
|
+
$ script/generate global_session_config mycoolapp.com
|
38
|
+
|
39
|
+
2) Create an authentication authority:
|
40
|
+
$ script/generate global_session_authority mycoolapp
|
41
|
+
|
42
|
+
3) Declare that some or all of your controllers will use the global session:
|
43
|
+
class ApplicationController < ActionController::Base
|
44
|
+
has_global_session
|
45
|
+
end
|
46
|
+
|
47
|
+
4) Make use of the global session hash in your controllers:
|
48
|
+
global_session['user'] = @user.id
|
49
|
+
...
|
50
|
+
@current_user = User.find(global_session['user'])
|
51
|
+
|
52
|
+
5) For easier programming, enable seamless integration with the local session:
|
53
|
+
(in global_session.yml)
|
54
|
+
common:
|
55
|
+
integrated: true
|
56
|
+
|
57
|
+
(in your controllers)
|
58
|
+
session['user'] = @user.id #goes to the global session
|
59
|
+
session['local_thingie'] = @thingie.id #goes to the local session
|
60
|
+
|
61
|
+
= Global Session Contents
|
27
62
|
|
28
63
|
Global session state is stored as a cookie in the user's browser. The cookie
|
29
64
|
is a Zlib-compressed JSON dictionary containing the following stuff:
|
30
|
-
* session metadata (UUID, created-at, expires-at,
|
65
|
+
* session metadata (UUID, created-at, expires-at, signing-authority)
|
31
66
|
* signed session attributes (e.g. the authenticated user ID)
|
32
67
|
* insecure session attributes (e.g. the last-visited URL)
|
33
68
|
* a cryptographic signature of the metadata and signed attributes
|
34
69
|
|
35
70
|
The global session is unserialized and its signature is verified whenever
|
36
|
-
a controller asks for
|
37
|
-
whenever
|
71
|
+
a controller asks for one of its attributes. The cookie's value is updated
|
72
|
+
whenever attributes change. As an optimization, the signature is only
|
38
73
|
recomputed when the metadata or signed attributes have changed; insecure
|
39
74
|
attributes can change "for free."
|
40
75
|
|
41
76
|
Because the security properties of attributes can vary, HasGlobalSession
|
42
77
|
requires all _possible_ attributes to be declared up-front in the config
|
43
78
|
file. The 'attributes' section of the config file defines the _schema_
|
44
|
-
for the global session: which attributes can be used,
|
45
|
-
|
79
|
+
for the global session: which attributes can be used, which can be trusted
|
80
|
+
to make authorization decisions (because they are signed), and are insecure
|
81
|
+
and therefore act only as "hints" about the session.
|
46
82
|
|
47
83
|
Since the session is serialized as JSON, only a limited range of object
|
48
84
|
types can be stored in it: strings, numbers, lists, hashes and other Ruby
|
49
85
|
primitives. Ruby booleans (true/false) do not translate well into JSON
|
50
86
|
and should be avoided.
|
51
87
|
|
52
|
-
= Example
|
53
|
-
|
54
|
-
1) Create a basic config file and edit it to suit your needs:
|
55
|
-
$ script/generate global_session_config mycoolapp.com
|
56
|
-
|
57
|
-
2) Create an authentication authority:
|
58
|
-
$ script/generate global_session_authority mycoolapp
|
59
|
-
|
60
|
-
3) Declare that some or all of your application's controllers will have access
|
61
|
-
to the global session:
|
62
|
-
class ApplicationController < ActionController::Base
|
63
|
-
has_global_session
|
64
|
-
end
|
65
|
-
|
66
|
-
4) Make use of the global session hash in your controllers:
|
67
|
-
global_session['user'] = @user.id
|
68
|
-
...
|
69
|
-
@current_user = User.find(global_session['user'])
|
70
|
-
|
71
88
|
= Detailed Information
|
72
89
|
|
73
90
|
== Global Session Domain
|
74
91
|
|
75
92
|
We refer to collection of _all_ Web application instances capable of using the
|
76
93
|
global session as the "domain." The global session domain may consist of any
|
77
|
-
number of distinct
|
78
|
-
The
|
94
|
+
number of distinct nodes, possibly hidden behind load balancers or proxies.
|
95
|
+
The nodes within the domain may all be running the same Rails application,
|
79
96
|
or they may be running different codebases that represent different parts of
|
80
97
|
a distributed application. (They may also be using app frameworks other than
|
81
98
|
Rails.)
|
82
99
|
|
83
|
-
The only constraint imposed by HasGlobalSession is that all
|
100
|
+
The only constraint imposed by HasGlobalSession is that all nodes within the
|
84
101
|
domain must have end-user-facing URLs within the same second-level DNS domain.
|
85
102
|
This is due to limitations imposed by the HTTP cookie mechanism: for privacy
|
86
|
-
reasons, cookies will only be sent to
|
87
|
-
|
103
|
+
reasons, cookies will only be sent to nodes within the same domain as the
|
104
|
+
node that first created them.
|
88
105
|
|
89
106
|
For example, in my HasGlobalSession configuration file I might specify that my
|
90
|
-
cookie's domain is "example.com". My app
|
107
|
+
cookie's domain is "example.com". My app nodes at app1.example.com and
|
91
108
|
app2.example.com would be part of the global session domain, but my business
|
92
|
-
partner's application at
|
109
|
+
partner's application at app3.partner.com could not participate.
|
93
110
|
|
94
111
|
== Authorities and Relying Parties
|
95
112
|
|
96
|
-
A
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
113
|
+
A node that can create or update the global session is said to be an "authority"
|
114
|
+
(because it's trusted by other parties to make assertions about global session
|
115
|
+
state). An application that can read the global session is said to be a "relying
|
116
|
+
party." In practice, every application is a relying party, but not all of them
|
117
|
+
need to be authorities.
|
101
118
|
|
102
119
|
There is an RSA key pair associated with each authority. The authority's
|
103
120
|
public key is distribued to all relying parties, but the private key must
|
104
121
|
remain a secret to that authority (which may consist of many individual
|
105
|
-
|
122
|
+
nodes).
|
106
123
|
|
107
124
|
This system allows for significant flexibility when configuring a distributed
|
108
125
|
app's global session. There must be at least one authority, but for many apps
|
@@ -117,7 +134,7 @@ Here are some reasons you might consider dividing your systems into different
|
|
117
134
|
authorities:
|
118
135
|
* beta/staging system vs. production system
|
119
136
|
* system hosted by a third party vs. system hosted internally
|
120
|
-
* e-commerce
|
137
|
+
* e-commerce node vs. storefront node
|
121
138
|
|
122
139
|
== The Directory
|
123
140
|
|
@@ -131,21 +148,41 @@ signing session attributes.
|
|
131
148
|
|
132
149
|
The Directory implementation included with HasGlobalSession uses the filesystem
|
133
150
|
as the backing store for its key pairs. Its #initialize method accepts a
|
134
|
-
filesystem path that will be searched for
|
135
|
-
|
151
|
+
filesystem path that will be searched for files containing PEM-encoded public
|
152
|
+
and private keys (the same format used by OpenSSH). This simple Directory
|
153
|
+
implementation relies on the following conventions:
|
154
|
+
* Public keys have a *.pub extension.
|
155
|
+
* Private keys have a *.key extension.
|
156
|
+
* If a node is an authority, then one (and *only* one) *.key file should exist.
|
157
|
+
* The local node's authority name is inferred from the name of the private key
|
158
|
+
file.
|
136
159
|
|
137
160
|
When used with a Rails app, HasGlobalSession expects to find its keystore in
|
138
161
|
config/authorities. You can use the global_session generator to create new key
|
139
|
-
pairs. Remember never to check a *.key file into a public repository!! (
|
140
|
-
|
162
|
+
pairs. Remember never to check a *.key file into a public repository!! (*.pub
|
163
|
+
files can be checked into source control and distributed freely.)
|
141
164
|
|
142
165
|
If you wish all of the systems to stop trusting an authority, simply delete
|
143
|
-
its public key from config/authorities.
|
166
|
+
its public key from config/authorities and re-deploy your app.
|
167
|
+
|
168
|
+
=== Implementing Your Own Directory Provider
|
169
|
+
|
170
|
+
To replace or enhance the built-in Directory, simply create a new class that
|
171
|
+
extends Directory and put the class somewhere in your app (the lib directory
|
172
|
+
is a good choice). In the HasGlobalSession configuration file, specify the
|
173
|
+
class name of the directory under the 'common' section, like so:
|
174
|
+
common:
|
175
|
+
integrated: true
|
176
|
+
directory: MyCoolDirectory
|
144
177
|
|
145
178
|
= To-Do
|
146
179
|
|
147
180
|
* Configurable session expiry
|
181
|
+
|
148
182
|
* Option to auto-renew session
|
149
|
-
|
183
|
+
|
184
|
+
* Option for non-sticky global session
|
185
|
+
(e.g. cookie expires at close of browser session, not at global session
|
186
|
+
expiration!)
|
150
187
|
|
151
188
|
Copyright (c) 2010 Tony Spataro <code@tracker.xeger.net>, released under the MIT license
|
data/has_global_session.gemspec
CHANGED
@@ -7,7 +7,7 @@ spec = Gem::Specification.new do |s|
|
|
7
7
|
s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
|
8
8
|
|
9
9
|
s.name = 'has_global_session'
|
10
|
-
s.version = '0.8.
|
10
|
+
s.version = '0.8.1'
|
11
11
|
s.date = '2010-06-01'
|
12
12
|
|
13
13
|
s.authors = ['Tony Spataro']
|
@@ -17,10 +17,10 @@ spec = Gem::Specification.new do |s|
|
|
17
17
|
s.summary = %q{Secure single-domain session sharing plugin for Rails.}
|
18
18
|
s.description = %q{This Rails plugin allows several Rails web apps that share the same back-end user database to share session state in a cryptographically secure way, facilitating single sign-on in a distributed web app. It only provides session sharing and does not concern itself with authentication or replication of the user database.}
|
19
19
|
|
20
|
-
s.add_runtime_dependency('uuidtools', [">=
|
20
|
+
s.add_runtime_dependency('uuidtools', [">= 1.0.7"])
|
21
21
|
|
22
22
|
basedir = File.dirname(__FILE__)
|
23
|
-
candidates = ['has_global_session.gemspec', 'init.rb', 'MIT-LICENSE', 'README'] +
|
23
|
+
candidates = ['has_global_session.gemspec', 'init.rb', 'MIT-LICENSE', 'README.rdoc'] +
|
24
24
|
Dir['lib/**/*'] +
|
25
25
|
Dir['rails/**/*']
|
26
26
|
s.files = candidates.sort
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class GlobalSessionAuthorityGenerator < Rails::Generator::Base
|
2
|
+
def initialize(runtime_args, runtime_options = {})
|
3
|
+
super
|
4
|
+
|
5
|
+
@app_name = File.basename(RAILS_ROOT)
|
6
|
+
@auth_name = args.shift
|
7
|
+
raise ArgumentError, "Must specify name for global session authority, e.g. 'mycoolapp'" unless @auth_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def manifest
|
11
|
+
record do |m|
|
12
|
+
new_key = OpenSSL::PKey::RSA.generate( 1024 )
|
13
|
+
new_public = new_key.public_key.to_pem
|
14
|
+
new_private = new_key.to_pem
|
15
|
+
|
16
|
+
dest_dir = File.join(RAILS_ROOT, 'config', 'authorities')
|
17
|
+
FileUtils.mkdir_p(dest_dir)
|
18
|
+
|
19
|
+
File.open(File.join(dest_dir, @auth_name + ".pub"), 'w') do |f|
|
20
|
+
f.puts new_public
|
21
|
+
end
|
22
|
+
|
23
|
+
File.open(File.join(dest_dir, @auth_name + ".key"), 'w') do |f|
|
24
|
+
f.puts new_private
|
25
|
+
end
|
26
|
+
|
27
|
+
puts "***"
|
28
|
+
puts "*** Don't forget to delete config/authorities/#{@auth_name}.key"
|
29
|
+
puts "***"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -10,7 +10,7 @@ class GlobalSessionConfigGenerator < Rails::Generator::Base
|
|
10
10
|
def manifest
|
11
11
|
record do |m|
|
12
12
|
|
13
|
-
m.template '
|
13
|
+
m.template 'global_session.yml.erb',
|
14
14
|
'config/global_session.yml',
|
15
15
|
:assigns=>{:app_name=>@app_name,
|
16
16
|
:app_domain=>@app_domain}
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# Common settings of the global session (that apply to all Rails environments)
|
2
|
-
# are listed here. These may be overidden in the environment-specific section
|
3
|
-
# but it seldom makes sense to do so.
|
2
|
+
# are listed here. These may be overidden in the environment-specific section.
|
4
3
|
common:
|
5
4
|
attributes:
|
6
5
|
# Signed attributes of the global session
|
@@ -14,17 +13,30 @@ common:
|
|
14
13
|
# global attributes always taking precedence over local attributes.
|
15
14
|
integrated: true
|
16
15
|
|
17
|
-
|
16
|
+
# Test/spec runs
|
17
|
+
test:
|
18
|
+
timeout: 900
|
18
19
|
cookie:
|
19
|
-
name: __<%= app_name %>
|
20
|
+
name: __<%= app_name %>_test
|
20
21
|
domain: localhost
|
22
|
+
trust:
|
23
|
+
- test
|
21
24
|
|
22
|
-
|
25
|
+
# Development mode
|
26
|
+
development:
|
27
|
+
timeout: 3600
|
23
28
|
cookie:
|
24
|
-
name: __<%= app_name %>
|
29
|
+
name: __<%= app_name %>_development
|
25
30
|
domain: localhost
|
31
|
+
trust:
|
32
|
+
- development
|
33
|
+
- production
|
26
34
|
|
35
|
+
# Production mode
|
27
36
|
production:
|
37
|
+
timeout: 3600
|
28
38
|
cookie:
|
29
39
|
name: __<%= app_name %>_global
|
30
40
|
domain: <%= app_domain %>
|
41
|
+
trust:
|
42
|
+
- production
|
@@ -4,23 +4,13 @@ module HasGlobalSession
|
|
4
4
|
mattr_accessor :environment
|
5
5
|
|
6
6
|
def self.[](key)
|
7
|
-
|
8
|
-
raise MissingConfiguration, "config_file is nil; cannot read configuration" unless config_file
|
9
|
-
raise MissingConfiguration, "environment is nil; must be specified" unless environment
|
10
|
-
@config = YAML.load(File.read(config_file))
|
11
|
-
validate
|
12
|
-
end
|
13
|
-
if @config.has_key?(environment) && @config[environment].has_key?(key)
|
14
|
-
return @config[environment][key]
|
15
|
-
else
|
16
|
-
@config['common'][key]
|
17
|
-
end
|
7
|
+
get(key, true)
|
18
8
|
end
|
19
9
|
|
20
10
|
def self.validate
|
21
11
|
['attributes/signed', 'integrated', 'cookie/name', 'cookie/domain'].each do |path|
|
22
12
|
elements = path.split '/'
|
23
|
-
object =
|
13
|
+
object = get(elements.shift, false)
|
24
14
|
elements.each do |element|
|
25
15
|
object = object[element]
|
26
16
|
if object.nil?
|
@@ -30,5 +20,23 @@ module HasGlobalSession
|
|
30
20
|
end
|
31
21
|
end
|
32
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def self.get(key, validated)
|
26
|
+
unless @config
|
27
|
+
raise MissingConfiguration, "config_file is nil; cannot read configuration" unless config_file
|
28
|
+
raise MissingConfiguration, "environment is nil; must be specified" unless environment
|
29
|
+
@config = YAML.load(File.read(config_file))
|
30
|
+
raise TypeError, "#{config_file} must contain a Hash!" unless Hash === @config
|
31
|
+
validate if validated
|
32
|
+
end
|
33
|
+
if @config.has_key?(environment) &&
|
34
|
+
@config[environment].respond_to?(:has_key?) &&
|
35
|
+
@config[environment].has_key?(key)
|
36
|
+
return @config[environment][key]
|
37
|
+
else
|
38
|
+
@config['common'][key]
|
39
|
+
end
|
40
|
+
end
|
33
41
|
end
|
34
42
|
end
|
@@ -36,6 +36,12 @@ module HasGlobalSession
|
|
36
36
|
raise SecurityError, "Signature mismatch on global session cookie; tampering suspected"
|
37
37
|
end
|
38
38
|
|
39
|
+
unless Configuration['trust'].blank? ||
|
40
|
+
@authority = @directory.my_authority_name ||
|
41
|
+
Configuration['trust'].include?(@authority)
|
42
|
+
raise SecurityError, "Global sessions created by #{@authority} are not trusted"
|
43
|
+
end
|
44
|
+
|
39
45
|
if expired? || @directory.invalidated_session?(@id)
|
40
46
|
raise ExpiredSession, "Global session cookie has expired"
|
41
47
|
end
|
@@ -43,25 +49,25 @@ module HasGlobalSession
|
|
43
49
|
else
|
44
50
|
@signed = {}
|
45
51
|
@insecure = {}
|
46
|
-
@id =
|
52
|
+
@id = UUID.timestamp_create.to_s
|
47
53
|
@created_at = Time.now.utc
|
48
|
-
@expires_at = 2.hours.from_now.utc #TODO configurable
|
49
54
|
@authority = @directory.my_authority_name
|
50
|
-
|
55
|
+
renew!
|
51
56
|
end
|
52
57
|
end
|
53
58
|
|
54
|
-
def supports_key?(key)
|
55
|
-
@schema_signed.include?(key) || @schema_insecure.include?(key)
|
56
|
-
end
|
57
|
-
|
58
59
|
def expired?
|
59
60
|
(@expires_at <= Time.now)
|
60
61
|
end
|
61
62
|
|
62
63
|
def expire!
|
63
64
|
@expires_at = Time.at(0)
|
64
|
-
@
|
65
|
+
@dirty = true
|
66
|
+
end
|
67
|
+
|
68
|
+
def renew!
|
69
|
+
@expires_at = Configuration['timeout'].to_i.minutes.from_now.utc || 1.hours.from_now.utc
|
70
|
+
@dirty = true
|
65
71
|
end
|
66
72
|
|
67
73
|
def to_s
|
@@ -69,7 +75,7 @@ module HasGlobalSession
|
|
69
75
|
'tc'=>@created_at.to_i, 'te'=>@expires_at.to_i,
|
70
76
|
'ds'=>@signed, 'dx'=>@insecure}
|
71
77
|
|
72
|
-
if @signature && !@
|
78
|
+
if @signature && !@dirty
|
73
79
|
#use cached signature unless we've changed secure state
|
74
80
|
authority = @authority
|
75
81
|
signature = @signature
|
@@ -82,23 +88,34 @@ module HasGlobalSession
|
|
82
88
|
|
83
89
|
hash['s'] = signature
|
84
90
|
hash['a'] = authority
|
85
|
-
json = ActiveSupport::JSON.encode(hash)
|
91
|
+
json = hash.to_json #ActiveSupport::JSON.encode(hash) -- why does this expect Data sometimes?!
|
86
92
|
zbin = Zlib::Deflate.deflate(json, Zlib::BEST_COMPRESSION)
|
87
93
|
return Base64.encode64(zbin)
|
88
94
|
end
|
89
95
|
|
96
|
+
def supports_key?(key)
|
97
|
+
@schema_signed.include?(key) || @schema_insecure.include?(key)
|
98
|
+
end
|
99
|
+
|
90
100
|
def [](key)
|
91
101
|
@signed[key] || @insecure[key]
|
92
102
|
end
|
93
103
|
|
94
104
|
def []=(key, value)
|
105
|
+
case value
|
106
|
+
when String, Numeric, Array
|
107
|
+
#no-op
|
108
|
+
else
|
109
|
+
raise TypeError, "Cannot store values of type #{value.class.name} reliably"
|
110
|
+
end
|
111
|
+
|
95
112
|
if @schema_signed.include?(key)
|
96
113
|
unless @directory.my_private_key && @directory.my_authority_name
|
97
114
|
raise StandardError, 'Cannot change secure session attributes; we are not an authority'
|
98
115
|
end
|
99
116
|
|
100
117
|
@signed[key] = value
|
101
|
-
@
|
118
|
+
@dirty = true
|
102
119
|
elsif @schema_insecure.include?(key)
|
103
120
|
@insecure[key] = value
|
104
121
|
else
|
@@ -109,7 +126,7 @@ module HasGlobalSession
|
|
109
126
|
def has_key?(key)
|
110
127
|
@signed.has_key(key) || @insecure.has_key?(key)
|
111
128
|
end
|
112
|
-
|
129
|
+
|
113
130
|
def keys
|
114
131
|
@signed.keys + @insecure.keys
|
115
132
|
end
|
@@ -6,6 +6,7 @@ module HasGlobalSession
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def [](key)
|
9
|
+
key = key.to_s
|
9
10
|
if @global_session.supports_key?(key)
|
10
11
|
@global_session[key]
|
11
12
|
else
|
@@ -14,6 +15,7 @@ module HasGlobalSession
|
|
14
15
|
end
|
15
16
|
|
16
17
|
def []=(key, value)
|
18
|
+
key = key.to_s
|
17
19
|
if @global_session.supports_key?(key)
|
18
20
|
@global_session[key] = value
|
19
21
|
else
|
@@ -22,6 +24,7 @@ module HasGlobalSession
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def has_key?(key)
|
27
|
+
key = key.to_s
|
25
28
|
@global_session.has_key(key) || @local_session.has_key?(key)
|
26
29
|
end
|
27
30
|
|
@@ -4,8 +4,14 @@ module HasGlobalSession
|
|
4
4
|
return @global_session if @global_session
|
5
5
|
|
6
6
|
begin
|
7
|
+
if (klass = Configuration['directory'])
|
8
|
+
klass = klass.constantize
|
9
|
+
else
|
10
|
+
klass = Directory
|
11
|
+
end
|
12
|
+
|
13
|
+
directory = klass.new(File.join(RAILS_ROOT, 'config', 'authorities'))
|
7
14
|
cookie = cookies[Configuration['cookie']['name']]
|
8
|
-
directory = Directory.new(File.join(RAILS_ROOT, 'config', 'authorities'))
|
9
15
|
|
10
16
|
begin
|
11
17
|
#unserialize the global session from the cookie, or
|
@@ -44,5 +50,36 @@ module HasGlobalSession
|
|
44
50
|
cookies[Configuration['cookie']['name']] = options
|
45
51
|
end
|
46
52
|
end
|
53
|
+
|
54
|
+
def log_processing
|
55
|
+
if logger && logger.info?
|
56
|
+
log_processing_for_request_id
|
57
|
+
log_processing_for_parameters
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def log_processing_for_request_id()
|
62
|
+
if global_session && global_session.id
|
63
|
+
session_id = global_session.id + " (#{session[:session_id]})"
|
64
|
+
elsif session[:session_id]
|
65
|
+
session_id = session[:session_id]
|
66
|
+
elsif request.session_options[:id]
|
67
|
+
session_id = request.session_options[:id]
|
68
|
+
end
|
69
|
+
|
70
|
+
request_id = "\n\nProcessing #{self.class.name}\##{action_name} "
|
71
|
+
request_id << "to #{params[:format]} " if params[:format]
|
72
|
+
request_id << "(for #{request_origin.split[0]}) [#{request.method.to_s.upcase}]"
|
73
|
+
request_id << "\n Session ID: #{session_id}" if session_id
|
74
|
+
|
75
|
+
logger.info(request_id)
|
76
|
+
end
|
77
|
+
|
78
|
+
def log_processing_for_parameters
|
79
|
+
parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
|
80
|
+
parameters = parameters.except!(:controller, :action, :format, :_method)
|
81
|
+
|
82
|
+
logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
|
83
|
+
end
|
47
84
|
end
|
48
85
|
end
|
data/rails/init.rb
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
basedir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
2
2
|
require File.join(basedir, 'lib', 'has_global_session')
|
3
3
|
|
4
|
-
|
5
|
-
# and operating environment.
|
6
|
-
HasGlobalSession::Configuration.config_file =
|
7
|
-
File.join(RAILS_ROOT, 'config', 'global_session.yml')
|
8
|
-
HasGlobalSession::Configuration.environment = RAILS_ENV
|
4
|
+
config_file = File.join(RAILS_ROOT, 'config', 'global_session.yml')
|
9
5
|
|
10
|
-
|
6
|
+
if File.exist?(config_file)
|
7
|
+
# Tie the Configuration module to Rails' filesystem structure
|
8
|
+
# and operating environment.
|
9
|
+
HasGlobalSession::Configuration.config_file = config_file
|
10
|
+
HasGlobalSession::Configuration.environment = RAILS_ENV
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
require File.join(basedir, 'rails', 'action_controller_instance_methods')
|
13
|
+
|
14
|
+
# Enable ActionController integration.
|
15
|
+
class ActionController::Base
|
16
|
+
def self.has_global_session
|
17
|
+
include HasGlobalSession::ActionControllerInstanceMethods
|
18
|
+
after_filter :global_session_update_cookie
|
19
|
+
end
|
17
20
|
end
|
18
|
-
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: has_global_session
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 61
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 8
|
9
|
+
- 1
|
10
|
+
version: 0.8.1
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Tony Spataro
|
@@ -14,14 +20,20 @@ default_executable:
|
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
22
|
name: uuidtools
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
20
26
|
requirements:
|
21
27
|
- - ">="
|
22
28
|
- !ruby/object:Gem::Version
|
23
|
-
|
24
|
-
|
29
|
+
hash: 25
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
- 7
|
34
|
+
version: 1.0.7
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
25
37
|
description: This Rails plugin allows several Rails web apps that share the same back-end user database to share session state in a cryptographically secure way, facilitating single sign-on in a distributed web app. It only provides session sharing and does not concern itself with authentication or replication of the user database.
|
26
38
|
email: code@tracker.xeger.net
|
27
39
|
executables: []
|
@@ -32,9 +44,10 @@ extra_rdoc_files: []
|
|
32
44
|
|
33
45
|
files:
|
34
46
|
- MIT-LICENSE
|
35
|
-
- README
|
47
|
+
- README.rdoc
|
36
48
|
- has_global_session.gemspec
|
37
49
|
- init.rb
|
50
|
+
- lib/generators/global_session_authority/global_session_authority_generator.rb
|
38
51
|
- lib/generators/global_session_config/USAGE
|
39
52
|
- lib/generators/global_session_config/global_session_config_generator.rb
|
40
53
|
- lib/generators/global_session_config/templates/global_session.yml.erb
|
@@ -55,21 +68,29 @@ rdoc_options: []
|
|
55
68
|
require_paths:
|
56
69
|
- lib
|
57
70
|
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
58
72
|
requirements:
|
59
73
|
- - ">="
|
60
74
|
- !ruby/object:Gem::Version
|
75
|
+
hash: 57
|
76
|
+
segments:
|
77
|
+
- 1
|
78
|
+
- 8
|
79
|
+
- 7
|
61
80
|
version: 1.8.7
|
62
|
-
version:
|
63
81
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
64
83
|
requirements:
|
65
84
|
- - ">="
|
66
85
|
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
67
89
|
version: "0"
|
68
|
-
version:
|
69
90
|
requirements: []
|
70
91
|
|
71
92
|
rubyforge_project:
|
72
|
-
rubygems_version: 1.3.
|
93
|
+
rubygems_version: 1.3.7
|
73
94
|
signing_key:
|
74
95
|
specification_version: 3
|
75
96
|
summary: Secure single-domain session sharing plugin for Rails.
|