has_global_session 0.9.5 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -176,10 +176,4 @@ class name of the directory under the 'common' section, like so:
176
176
  integrated: true
177
177
  directory: MyCoolDirectory
178
178
 
179
- = To-Do
180
-
181
- * Option to auto-renew session
182
-
183
- * Implement single sign-out via redirect
184
-
185
179
  Copyright (c) 2010 Tony Spataro <code@tracker.xeger.net>, released under the MIT license
@@ -7,8 +7,8 @@ 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.9.5'
11
- s.date = '2010-07-20'
10
+ s.version = '1.0'
11
+ s.date = '2010-07-27'
12
12
 
13
13
  s.authors = ['Tony Spataro']
14
14
  s.email = 'code@tracker.xeger.net'
@@ -1,16 +1,98 @@
1
1
  module HasGlobalSession
2
+ # Central point of access for HasGlobalSession configuration information. This is
3
+ # mostly a very thin wrapper around the serialized hash written to the YAML config
4
+ # file.
5
+ #
6
+ # The configuration is stored as a set of nested hashes and accessed by the code
7
+ # using hash lookup; for example, we might ask for +Configuration['cookie']['domain']+
8
+ # if we wanted to know which domain the cookie should be set for.
9
+ #
10
+ # The following settings are supported:
11
+ # * attributes
12
+ # * signed
13
+ # * insecure
14
+ # * integrated
15
+ # * ephemeral
16
+ # * timeout
17
+ # * renew
18
+ # * authority
19
+ # * trust
20
+ # * directory
21
+ # * cookie
22
+ # * name
23
+ # * domain
24
+ #
25
+ # === Environment-Specific Settings
26
+ # The top level of keys in the configuration hash are special; they provide different
27
+ # sections of settings that apply in different environments. For instance, a Rails
28
+ # application might have one set of settings that apply in the development environment;
29
+ # these would appear under +Configuration['development']+. Another set of settings would
30
+ # apply in the production environment and would appear under +Configuration['production']+.
31
+ #
32
+ # === Common Settings
33
+ # In addition to having one section for each operating environment, the configuration
34
+ # file can specify a 'common' section for settings that apply
35
+ #
36
+ # === Lookup Mechanism
37
+ # When the code asks for +Configuration['foo']+, we first check whether the current
38
+ # environment's config section has a value for foo. If one is found, we return that.
39
+ #
40
+ # If no environment-specific setting is found, we check the 'common' section and return
41
+ # the value found there.
42
+ #
43
+ # === Config File Location
44
+ # The name and location of the config file depend on the Web framework with which
45
+ # you are integrating; see HasGlobalSession::Rails for more information.
46
+ #
2
47
  module Configuration
48
+ # Reader for the environment module-attribute.
49
+ #
50
+ # === Return
51
+ # env(String):: The current configuration environment
3
52
  def self.environment; @environment; end
53
+
54
+ # Writer for the environment module-attribute.
55
+ #
56
+ # === Parameters
57
+ # value(String):: Configuration environment from which settings should be read
58
+ #
59
+ # === Return
60
+ # env(String):: The new configuration environment
4
61
  def self.environment=(value); @environment = value; end
5
62
 
63
+ # Reader for the config_file module-attribute.
64
+ #
65
+ # === Return
66
+ # file(String):: Absolute path to configuration file
6
67
  def self.config_file; @config_file; end
68
+
69
+ # Writer for the config_file module-attribute.
70
+ #
71
+ # === Parameters
72
+ # value(String):: Absolute path to configuration file
73
+ #
74
+ # === Return
75
+ # env(String):: The new path to the configuration file
7
76
  def self.config_file=(value); @config_file= value; end
8
77
 
78
+ # Reader for configuration elements. The reader first checks
79
+ # the current environment's settings section for the named
80
+ # value; if not found, it checks the common settings section.
81
+ #
82
+ # === Parameters
83
+ # name(Type):: Description
84
+ #
85
+ # === Return
86
+ # name(Type):: Description
87
+ #
88
+ # === Raise
89
+ # MissingConfiguration:: if config file location is unset, environment is unset, or config file is missing
90
+ # TypeError:: if config file does not contain a YAML-serialized Hash
9
91
  def self.[](key)
10
92
  get(key, true)
11
93
  end
12
94
 
13
- def self.validate
95
+ def self.validate # :nodoc
14
96
  ['attributes/signed', 'integrated', 'cookie/name', 'timeout'].each do |path|
15
97
  elements = path.split '/'
16
98
  object = get(elements.shift, false)
@@ -25,7 +107,8 @@ module HasGlobalSession
25
107
  end
26
108
 
27
109
  private
28
- def self.get(key, validated)
110
+
111
+ def self.get(key, validated) # :nodoc
29
112
  unless @config
30
113
  raise MissingConfiguration, "config_file is nil; cannot read configuration" unless config_file
31
114
  raise MissingConfiguration, "environment is nil; must be specified" unless environment
@@ -1,7 +1,43 @@
1
1
  module HasGlobalSession
2
+ # The global session directory, which provides some lookup and decision services
3
+ # to instances of GlobalSession.
4
+ #
5
+ # The default implementation is simplistic, but should be suitable for most applications.
6
+ # Directory is designed to be specialized via subclassing. To override the behavior to
7
+ # suit your needs, simply create a subclass of Directory and add a configuration file
8
+ # setting to specify the class name of your implementation:
9
+ #
10
+ # common:
11
+ # directory: MyCoolDirectory
12
+ #
13
+ #
14
+ # === The Authority Keystore
15
+ # Directory uses a filesystem directory as a backing store for RSA
16
+ # public keys of global session authorities. The directory should
17
+ # contain one or more +*.pub+ files containing OpenSSH-format public
18
+ # RSA keys. The name of the pub file determines the name of the
19
+ # authority it represents.
20
+ #
21
+ # === The Local Authority
22
+ # Directory will infer the name of the local authority (if any) by
23
+ # looking for a private-key file in the keystore. If a +*.key+ file
24
+ # is found, then its name is taken to be the name of the local
25
+ # authority and all GlobalSessions created will be signed by that
26
+ # authority's private key.
27
+ #
28
+ # If more than one key file is found, Directory will raise an error
29
+ # at initialization time.
30
+ #
2
31
  class Directory
3
32
  attr_reader :authorities, :private_key, :local_authority_name
4
33
 
34
+ # Create a new Directory.
35
+ #
36
+ # === Parameters
37
+ # keystore_directory(String):: Absolute path to authority keystore
38
+ #
39
+ # ===Raise
40
+ # ConfigurationError:: if too many or too few keys are found, or if *.key/*.pub files are malformatted
5
41
  def initialize(keystore_directory)
6
42
  certs = Dir[File.join(keystore_directory, '*.pub')]
7
43
  keys = Dir[File.join(keystore_directory, '*.key')]
@@ -24,14 +60,41 @@ module HasGlobalSession
24
60
  end
25
61
  end
26
62
 
63
+ # Determine whether this system trusts a particular authority based on
64
+ # the trust settings specified in Configuration.
65
+ #
66
+ # === Parameters
67
+ # authority(String):: The name of the authority
68
+ #
69
+ # === Return
70
+ # trusted(true|false):: whether the local system trusts sessions signed by the specified authority
27
71
  def trusted_authority?(authority)
28
72
  Configuration['trust'].include?(authority)
29
73
  end
30
74
 
75
+ # Determine whether the given session UUID is valid. The default implementation only considers
76
+ # a session to be invalid if its expired_at timestamp is in the past. Custom implementations
77
+ # might want to consider other factors, such as whether the user has signed out of this node
78
+ # or another node (perhaps using some sort of centralized lookup or single sign-out mechanism).
79
+ #
80
+ # === Parameters
81
+ # uuid(String):: Global session UUID
82
+ # expired_at(Time):: When the session expired (or will expire)
83
+ #
84
+ # === Return
85
+ # valid(true|false):: whether the specified session is valid
31
86
  def valid_session?(uuid, expired_at)
32
87
  expired_at > Time.now
33
88
  end
34
89
 
90
+ # Callback used by GlobalSession objects to report when the application code calls
91
+ # #invalidate! on them. The default implementation of this method does nothing.
92
+ #
93
+ # uuid(String):: Global session UUID
94
+ # expired_at(Time):: When the session expired
95
+ #
96
+ # === Return
97
+ # true:: Always returns true
35
98
  def report_invalid_session(uuid, expired_at)
36
99
  true
37
100
  end
@@ -1,10 +1,29 @@
1
1
  module HasGlobalSession
2
+ # Various encoding (not encryption!) techniques used by the global session plugin.
3
+ #
2
4
  module Encoding
5
+ # JSON serializer, used to serialize Hash objects in a form suitable
6
+ # for stuffing into a cookie.
7
+ #
3
8
  class JSON
9
+ # Unserialize JSON to Hash.
10
+ #
11
+ # === Parameters
12
+ # json(String):: A well-formed JSON document
13
+ #
14
+ # === Return
15
+ # value(Hash):: An unserialized Ruby Hash
4
16
  def self.load(json)
5
17
  ::JSON.load(json)
6
18
  end
7
19
 
20
+ # Serialize Hash to JSON document.
21
+ #
22
+ # === Parameters
23
+ # value(Hash):: The hash to be serialized
24
+ #
25
+ # === Return
26
+ # json(String):: A JSON-serialized representation of +value+
8
27
  def self.dump(object)
9
28
  return object.to_json
10
29
  end
@@ -21,11 +40,25 @@ module HasGlobalSession
21
40
  # this scheme preserves the '=' padding characters due to limitations of
22
41
  # Ruby's built-in base64 encoding routines.
23
42
  class Base64Cookie
43
+ # Decode a B64cookie-encoded string.
44
+ #
45
+ # === Parameters
46
+ # encoded(String):: The encoded string
47
+ #
48
+ # === Return
49
+ # decoded(String):: The decoded result, which may contain nonprintable bytes
24
50
  def self.load(string)
25
51
  tr = string.tr('-_', '+/')
26
52
  return tr.unpack('m')[0]
27
53
  end
28
54
 
55
+ # Encode a Ruby (ASCII or binary) string.
56
+ #
57
+ # === Parameters
58
+ # decoded(String):: The raw string to be encoded
59
+ #
60
+ # === Return
61
+ # encoded(String):: The B64cookie-encoded result.
29
62
  def self.dump(object)
30
63
  raw = [object].pack('m')
31
64
  raw.tr!('+/', '-_')
@@ -6,15 +6,34 @@ require 'zlib'
6
6
  require 'uuidtools'
7
7
 
8
8
  module HasGlobalSession
9
+ # Ladies and gentlemen: the one and only, star of the show, GLOBAL SESSION!
10
+ #
11
+ # GlobalSession is designed to act as much like a Hash as possible. You can use
12
+ # most of the methods you would use with Hash: [], has_key?, each, etc. It has a
13
+ # few additional methods that are specific to itself, mostly involving whether
14
+ # it's expired, valid, supports a certain key, etc.
15
+ #
9
16
  class GlobalSession
10
17
  attr_reader :id, :authority, :created_at, :expired_at, :directory
11
18
 
19
+ # Create a new global session object.
20
+ #
21
+ # === Parameters
22
+ # directory(Directory):: directory implementation that the session should use for various operations
23
+ # cookie(String):: Optional, serialized global session cookie. If none is supplied, a new session is created.
24
+ # valid_signature_digest(String):: Optional, already-trusted signature. If supplied, the expensive RSA-verify operation will be skipped if the cookie's signature matches the value supplied.
25
+ #
26
+ # ===Raise
27
+ # InvalidSession:: if the session contained in the cookie has been invalidated
28
+ # ExpiredSession:: if the session contained in the cookie has expired
29
+ # MalformedCookie:: if the cookie was corrupt or malformed
30
+ # SecurityError:: if signature is invalid or cookie is not signed by a trusted authority
12
31
  def initialize(directory, cookie=nil, valid_signature_digest=nil)
13
32
  @schema_signed = Set.new((Configuration['attributes']['signed']))
14
33
  @schema_insecure = Set.new((Configuration['attributes']['insecure']))
15
34
  @directory = directory
16
35
 
17
- if cookie
36
+ if cookie && !cookie.empty?
18
37
  load_from_cookie(cookie, valid_signature_digest)
19
38
  elsif @directory.local_authority_name
20
39
  create_from_scratch
@@ -23,10 +42,21 @@ module HasGlobalSession
23
42
  end
24
43
  end
25
44
 
45
+ # Determine whether the session is valid. This method simply delegates to the
46
+ # directory associated with this session.
47
+ #
48
+ # === Return
49
+ # valid(true|false):: True if the session is valid, false otherwise
26
50
  def valid?
27
51
  @directory.valid_session?(@id, @expired_at)
28
52
  end
29
53
 
54
+ # Serialize the session to a form suitable for use with HTTP cookies. If any
55
+ # secure attributes have changed since the session was instantiated, compute
56
+ # a fresh RSA signature.
57
+ #
58
+ # === Return
59
+ # cookie(String):: The B64cookie-encoded Zlib-compressed JSON-serialized global session hash
30
60
  def to_s
31
61
  if @cookie && !@dirty_insecure && !@dirty_secure
32
62
  #use cached cookie if nothing has changed
@@ -57,35 +87,84 @@ module HasGlobalSession
57
87
  return Encoding::Base64Cookie.dump(zbin)
58
88
  end
59
89
 
90
+ # Determine whether the global session schema allows a given key to be placed
91
+ # in the global session.
92
+ #
93
+ # === Parameters
94
+ # key(String):: The name of the key
95
+ #
96
+ # === Return
97
+ # supported(true|false):: Whether the specified key is supported
60
98
  def supports_key?(key)
61
99
  @schema_signed.include?(key) || @schema_insecure.include?(key)
62
100
  end
63
101
 
102
+ # Determine whether this session contains a value with the specified key.
103
+ #
104
+ # === Parameters
105
+ # key(String):: The name of the key
106
+ #
107
+ # === Return
108
+ # contained(true|false):: Whether the session currently has a value for the specified key.
64
109
  def has_key?(key)
65
110
  @signed.has_key(key) || @insecure.has_key?(key)
66
111
  end
67
112
 
68
- def signature_digest
69
- @signature ? digest(@signature) : nil
70
- end
71
-
113
+ # Return the keys that are currently present in the global session.
114
+ #
115
+ # === Return
116
+ # keys(Array):: List of keys contained in the global session
72
117
  def keys
73
118
  @signed.keys + @insecure.keys
74
119
  end
75
120
 
121
+ # Return the values that are currently present in the global session.
122
+ #
123
+ # === Return
124
+ # values(Array):: List of values contained in the global session
76
125
  def values
77
126
  @signed.values + @insecure.values
78
127
  end
79
128
 
80
- def each_pair(&block)
129
+ # Iterate over each key/value pair
130
+ #
131
+ # === Block
132
+ # An iterator which will be called with each key/value pair
133
+ #
134
+ # === Return
135
+ # Returns the value of the last expression evaluated by the block
136
+ def each_pair(&block) # :yields: |key, value|
81
137
  @signed.each_pair(&block)
82
138
  @insecure.each_pair(&block)
83
139
  end
84
140
 
141
+ # Lookup a value by its key.
142
+ #
143
+ # === Parameters
144
+ # key(String):: the key
145
+ #
146
+ # === Return
147
+ # value(Object):: The value associated with +key+, or nil if +key+ is not present
85
148
  def [](key)
86
149
  @signed[key] || @insecure[key]
87
150
  end
88
151
 
152
+ # Set a value in the global session hash. If the supplied key is denoted as
153
+ # secure by the global session schema, causes a new signature to be computed
154
+ # when the session is next serialized.
155
+ #
156
+ # === Parameters
157
+ # key(String):: The key to set
158
+ # value(Object):: The value to set
159
+ #
160
+ # === Return
161
+ # value(Object):: Always returns the value that was set
162
+ #
163
+ # ===Raise
164
+ # InvalidSession:: if the session has been invalidated (and therefore can't be written to)
165
+ # ArgumentError:: if the configuration doesn't define the specified key as part of the global session
166
+ # NoAuthority:: if the specified key is secure and the local node is not an authority
167
+ # UnserializableType:: if the specified value can't be serialized as JSON
89
168
  def []=(key, value)
90
169
  raise InvalidSession unless valid?
91
170
 
@@ -102,36 +181,58 @@ module HasGlobalSession
102
181
  else
103
182
  raise ArgumentError, "Attribute '#{key}' is not specified in global session configuration"
104
183
  end
184
+
185
+ return value
105
186
  end
106
187
 
188
+ # Invalidate this session by reporting its UUID to the Directory.
189
+ #
190
+ # === Return
191
+ # unknown(Object):: Returns whatever the Directory returns
107
192
  def invalidate!
108
193
  @directory.report_invalid_session(@id, @expired_at)
109
194
  end
110
195
 
196
+ # Renews this global session, changing its expiry timestamp into the future.
197
+ # Causes a new signature will be computed when the session is next serialized.
198
+ #
199
+ # === Return
200
+ # true:: Always returns true
111
201
  def renew!
112
202
  authority_check
113
203
  @expired_at = Configuration['timeout'].to_i.minutes.from_now.utc
114
204
  @dirty_secure = true
115
205
  end
116
206
 
207
+ # Return the SHA1 hash of the most recently-computed RSA signature of this session.
208
+ # This isn't really intended for the end user; it exists so the Web framework integration
209
+ # code can optimize request speed by caching the most recently verified signature in the
210
+ # local session and avoid re-verifying it on every request.
211
+ #
212
+ # === Return
213
+ # digest(String):: SHA1 hex-digest of most-recently-computed signature
214
+ def signature_digest
215
+ @signature ? digest(@signature) : nil
216
+ end
217
+
117
218
  private
118
219
 
119
- def authority_check
220
+ def authority_check # :nodoc:
120
221
  unless @directory.local_authority_name
121
222
  raise NoAuthority, 'Cannot change secure session attributes; we are not an authority'
122
223
  end
123
224
  end
124
225
 
125
- def canonical_digest(input)
226
+ def canonical_digest(input) # :nodoc:
126
227
  canonical = Encoding::JSON.dump(canonicalize(input))
127
228
  return digest(canonical)
128
229
  end
129
230
 
130
- def digest(input)
231
+ def digest(input) # :nodoc:
131
232
  return Digest::SHA1.new().update(input).hexdigest
132
233
  end
133
234
 
134
- def canonicalize(input)
235
+ def canonicalize(input) # :nodoc:
135
236
  case input
136
237
  when Hash
137
238
  output = Array.new
@@ -150,10 +251,16 @@ module HasGlobalSession
150
251
  return output
151
252
  end
152
253
 
153
- def load_from_cookie(cookie, valid_signature_digest)
154
- zbin = Encoding::Base64Cookie.load(cookie)
155
- json = Zlib::Inflate.inflate(zbin)
156
- hash = Encoding::JSON.load(json)
254
+ def load_from_cookie(cookie, valid_signature_digest) # :nodoc:
255
+ begin
256
+ zbin = Encoding::Base64Cookie.load(cookie)
257
+ json = Zlib::Inflate.inflate(zbin)
258
+ hash = Encoding::JSON.load(json)
259
+ rescue Exception => e
260
+ mc = MalformedCookie.new("Caused by #{e.class.name}: #{e.message}")
261
+ mc.set_backtrace(e.backtrace)
262
+ raise mc
263
+ end
157
264
 
158
265
  id = hash['id']
159
266
  authority = hash['a']
@@ -200,7 +307,7 @@ module HasGlobalSession
200
307
  @cookie = cookie
201
308
  end
202
309
 
203
- def create_from_scratch
310
+ def create_from_scratch # :nodoc:
204
311
  authority_check
205
312
 
206
313
  @signed = {}
@@ -219,7 +326,7 @@ module HasGlobalSession
219
326
  renew!
220
327
  end
221
328
 
222
- def create_invalid
329
+ def create_invalid # :nodoc:
223
330
  @id = nil
224
331
  @created_at = Time.now.utc
225
332
  @expired_at = created_at
@@ -1,12 +1,42 @@
1
1
  module HasGlobalSession
2
+ # Helper class that enables the end user to treat the global and local session as if
3
+ # they were the same object. This is accomplished by implementing approximately the
4
+ # same interface as a Hash, and dispatching to one or the other session object depending
5
+ # on various factors.
6
+ #
7
+ # This class isn't intended to be used directly by the end user. Instead, set integrated: true
8
+ # in the configuration file and the Web framework integration code will manage an integrated
9
+ # session object for you, as well as overriding the framework's default session accessor to
10
+ # return an integrated session instead.
11
+ #
12
+ # When using an integrated session, you can always get to the underlying objects by
13
+ # using the #local and #global readers of this class.
14
+ #
2
15
  class IntegratedSession
3
- attr_reader :local, :global
16
+ # Return the local-session objects, whose type may vary depending on the Web framework.
17
+ attr_reader :local
18
+
19
+ # Return the global-session object.
20
+ attr_reader :global
4
21
 
22
+ # Construct a new integrated session.
23
+ #
24
+ # === Parameters
25
+ # local(Object):: Local session that acts like a Hash
26
+ # global(GlobalSession):: GlobalSession
5
27
  def initialize(local, global)
6
28
  @local = local
7
29
  @global = global
8
30
  end
9
31
 
32
+ # Retrieve a value from the global session if the supplied key is supported by
33
+ # the global session, else retrieve it from the local session.
34
+ #
35
+ # === Parameters
36
+ # key(String):: the key
37
+ #
38
+ # === Return
39
+ # value(Object):: The value associated with +key+, or nil if +key+ is not present
10
40
  def [](key)
11
41
  key = key.to_s
12
42
  if @global.supports_key?(key)
@@ -16,6 +46,15 @@ module HasGlobalSession
16
46
  end
17
47
  end
18
48
 
49
+ # Set a value in the global session (if the supplied key is supported) or the local
50
+ # session otherwise.
51
+ #
52
+ # === Parameters
53
+ # key(String):: The key to set
54
+ # value(Object):: The value to set
55
+ #
56
+ # === Return
57
+ # value(Object):: Always returns the value that was set
19
58
  def []=(key, value)
20
59
  key = key.to_s
21
60
  if @global.supports_key?(key)
@@ -23,34 +62,48 @@ module HasGlobalSession
23
62
  else
24
63
  @local[key] = value
25
64
  end
65
+
66
+ return value
26
67
  end
27
68
 
69
+ # Determine whether the global or local session contains a value with the specified key.
70
+ #
71
+ # === Parameters
72
+ # key(String):: The name of the key
73
+ #
74
+ # === Return
75
+ # contained(true|false):: Whether the session currently has a value for the specified key.
28
76
  def has_key?(key)
29
77
  key = key.to_s
30
- @global.has_key(key) || @local.has_key?(key)
78
+ @global.has_key?(key) || @local.has_key?(key)
31
79
  end
32
80
 
81
+ # Return the keys that are currently present in either the global or local session.
82
+ #
83
+ # === Return
84
+ # keys(Array):: List of keys contained in the global or local session.
33
85
  def keys
34
86
  @global.keys + @local.keys
35
87
  end
36
88
 
89
+ # Return the values that are currently present in the global or local session.
90
+ #
91
+ # === Return
92
+ # values(Array):: List of values contained in the global or local session.
37
93
  def values
38
94
  @global.values + @local.values
39
95
  end
40
96
 
97
+ # Iterate over each key/value pair in both the global and local session.
98
+ #
99
+ # === Block
100
+ # An iterator which will be called with each key/value pair
101
+ #
102
+ # === Return
103
+ # Returns the value of the last expression evaluated by the block
41
104
  def each_pair(&block)
42
105
  @global.each_pair(&block)
43
106
  @local.each_pair(&block)
44
107
  end
45
-
46
- def method_missing(meth, *args)
47
- if @global.respond_to?(meth)
48
- return @global.send(meth, *args)
49
- elsif @local.respond_to?(meth)
50
- return @local.send(meth, *args)
51
- else
52
- super
53
- end
54
- end
55
108
  end
56
109
  end
@@ -1,17 +1,39 @@
1
1
  module HasGlobalSession
2
+ # Rails integration for HasGlobalSession.
3
+ #
4
+ # The configuration file for Rails apps is located in +config/global_session.yml+ and a generator
5
+ # (global_session_config) is available for creating a sensible default.
6
+ #
7
+ # There is also a generator (global_session_authority) for creating authority keypairs.
8
+ #
9
+ # The main integration touchpoint for Rails is the module ActionControllerInstanceMethods,
10
+ # which gets mixed into ActionController::Base. This is where all of the magic happens..
11
+ #
2
12
  module Rails
13
+ # Module that is mixed into ActionController-derived classes when the class method
14
+ # +has_global_session+ is called.
15
+ #
3
16
  module ActionControllerInstanceMethods
4
- def self.included(base)
17
+ def self.included(base) # :nodoc:
5
18
  base.alias_method_chain :session, :global_session
6
19
  base.before_filter :global_session_read_cookie
7
20
  base.before_filter :global_session_auto_renew
8
21
  base.after_filter :global_session_update_cookie
9
22
  end
10
23
 
24
+ # Global session reader.
25
+ #
26
+ # === Return
27
+ # session(GlobalSession):: the global session associated with the current request, nil if none
11
28
  def global_session
12
29
  @global_session
13
30
  end
14
31
 
32
+ # Aliased version of ActionController::Base#session which will return the integrated
33
+ # global-and-local session object (IntegratedSession).
34
+ #
35
+ # === Return
36
+ # session(IntegratedSession):: the integrated session
15
37
  def session_with_global_session
16
38
  if Configuration['integrated'] && @global_session
17
39
  unless @integrated_session &&
@@ -27,6 +49,11 @@ module HasGlobalSession
27
49
  end
28
50
  end
29
51
 
52
+ # Before-filter to read the global session cookie and construct the GlobalSession object
53
+ # for this controller instance.
54
+ #
55
+ # === Return
56
+ # true:: Always returns true
30
57
  def global_session_read_cookie
31
58
  directory = global_session_create_directory
32
59
  cookie_name = Configuration['cookie']['name']
@@ -56,15 +83,27 @@ module HasGlobalSession
56
83
  end
57
84
  end
58
85
 
86
+ # Before-filter to renew the global session if it will be expiring soon.
87
+ #
88
+ # === Return
89
+ # true:: Always returns true
59
90
  def global_session_auto_renew
60
91
  #Auto-renew session if needed
61
92
  renew = Configuration['renew']
62
- if @global_session.directory.local_authority_name && renew &&
93
+ if @global_session &&
94
+ renew &&
95
+ @global_session.directory.local_authority_name &&
63
96
  @global_session.expired_at < renew.to_i.minutes.from_now.utc
64
97
  @global_session.renew!
65
- end
98
+ end
99
+
100
+ return true
66
101
  end
67
102
 
103
+ # After-filter to write any pending changes to the global session cookie.
104
+ #
105
+ # === Return
106
+ # true:: Always returns true
68
107
  def global_session_update_cookie
69
108
  name = Configuration['cookie']['name']
70
109
  domain = Configuration['cookie']['domain'] || request.env['SERVER_NAME']
@@ -90,6 +129,15 @@ module HasGlobalSession
90
129
  end
91
130
  end
92
131
 
132
+ # Override for the ActionController method of the same name that logs
133
+ # information about the request. Our version logs the global session ID
134
+ # instead of the local session ID.
135
+ #
136
+ # === Parameters
137
+ # name(Type):: Description
138
+ #
139
+ # === Return
140
+ # name(Type):: Description
93
141
  def log_processing
94
142
  if logger && logger.info?
95
143
  log_processing_for_request_id
@@ -97,7 +145,7 @@ module HasGlobalSession
97
145
  end
98
146
  end
99
147
 
100
- def log_processing_for_request_id
148
+ def log_processing_for_request_id # :nodoc:
101
149
  if global_session && global_session.id
102
150
  session_id = global_session.id + " (#{session[:session_id]})"
103
151
  elsif session[:session_id]
@@ -114,7 +162,7 @@ module HasGlobalSession
114
162
  logger.info(request_id)
115
163
  end
116
164
 
117
- def log_processing_for_parameters
165
+ def log_processing_for_parameters # :nodoc:
118
166
  parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
119
167
  parameters = parameters.except!(:controller, :action, :format, :_method)
120
168
 
@@ -123,7 +171,7 @@ module HasGlobalSession
123
171
 
124
172
  private
125
173
 
126
- def global_session_create_directory
174
+ def global_session_create_directory # :nodoc:
127
175
  if (klass = Configuration['directory'])
128
176
  klass = klass.constantize
129
177
  else
@@ -1,9 +1,46 @@
1
1
  module HasGlobalSession
2
+ # Indicates that the global session configuration file is missing from disk.
3
+ #
2
4
  class MissingConfiguration < Exception; end
5
+
6
+ # Indicates that the global session configuration file is missing elements or is
7
+ # malformatted.
8
+ #
3
9
  class ConfigurationError < Exception; end
10
+
11
+ # Indicates that a client submitted a request with a valid session cookie, but the
12
+ # session ID was reported as invalid by the Directory.
13
+ #
14
+ # See Directory#valid_session? for more information.
15
+ #
4
16
  class InvalidSession < Exception; end
17
+
18
+ # Indicates that a client submitted a request with a valid session cookie, but the
19
+ # session has expired.
20
+ #
5
21
  class ExpiredSession < Exception; end
22
+
23
+ # Indicates that a client submitted a request with a session cookie that could not
24
+ # be decoded or decompressed.
25
+ #
26
+ class MalformedCookie < Exception; end
27
+
28
+ # Indicates that application code tried to put an unserializable object into the glboal
29
+ # session hash. Because the global session is serialized as JSON and not all Ruby types
30
+ # can be easily round-tripped to JSON and back without data loss, we constrain the types
31
+ # that can be serialized.
32
+ #
33
+ # See HasGlobalSession::Encoding::JSON for more information on serializable types.
34
+ #
6
35
  class UnserializableType < Exception; end
36
+
37
+ # Indicates that the application code tried to write a secure session attribute or
38
+ # renew the global session. Both of these operations require a local authority
39
+ # because they require a new signature to be computed on the global session.
40
+ #
41
+ # See HasGlobalSession::Configuration and HasGlobalSession::Directory for more
42
+ # information.
43
+ #
7
44
  class NoAuthority < Exception; end
8
45
  end
9
46
 
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_global_session
3
3
  version: !ruby/object:Gem::Version
4
- hash: 49
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
+ - 1
7
8
  - 0
8
- - 9
9
- - 5
10
- version: 0.9.5
9
+ version: "1.0"
11
10
  platform: ruby
12
11
  authors:
13
12
  - Tony Spataro
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-07-20 00:00:00 -07:00
17
+ date: 2010-07-27 00:00:00 -07:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency