Class: GlobalSession::Rack::Middleware
- Inherits:
-
Object
- Object
- GlobalSession::Rack::Middleware
- Defined in:
- lib/global_session/rack.rb
Overview
Global session middleware. Note: this class relies on Rack::Cookies being used higher up in the chain.
Constant Summary collapse
- LOCAL_SESSION_KEY =
"rack.session".freeze
Instance Attribute Summary collapse
Instance Method Summary collapse
-
#call(env) ⇒ Array
Rack request chain.
-
#cookie_domain(env) ⇒ Object
Determine the domain name for which we should set the cookie.
-
#create_session(env) ⇒ true
Ensure that the Rack environment contains a global session object; create a session if necessary.
-
#handle_error(activity, env, e) ⇒ true
Handle exceptions that occur during app invocation.
-
#initialize(app, configuration, directory = nil) {|env| ... } ⇒ Middleware
constructor
Make a new global session middleware.
-
#perform_invalidation_callbacks(env, old_session, new_session) ⇒ true
Perform callbacks to directory and/or local session informing them that this session has been invalidated.
-
#read_authorization_header(env) ⇒ Boolean
Read a global session from the HTTP Authorization header, if present.
-
#read_cookie(env) ⇒ Boolean
Read a global session from HTTP cookies, if present.
-
#renew_cookie(env) ⇒ true
Renew the session ticket.
-
#update_cookie(env) ⇒ true
Update the cookie jar with the revised ticket.
-
#wipe_cookie(env) ⇒ true
Delete the global session cookie from the cookie jar.
Constructor Details
#initialize(app, configuration, directory = nil) {|env| ... } ⇒ Middleware
Make a new global session middleware.
The optional block here controls an alternate ticket retrieval method. If no ticket is stored in the cookie jar, this function is called. If it returns a non-nil value, that value is the ticket.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/global_session/rack.rb', line 50 def initialize(app, configuration, directory=nil, &block) @app = app # Initialize shared configuration # @deprecated require Configuration object in v4 if configuration.instance_of?(String) @configuration = Configuration.new(configuration, ENV['RACK_ENV'] || 'development') else @configuration = configuration end klass = nil begin # v0.9.0 - v3.0.4: class name is the value of the 'directory' key klass_name = @configuration['directory'] case klass_name when Hash # v3.0.5 and beyond: class name is in 'class' subkey klass_name = klass_name['class'] when NilClass # the eternal default, if the class name is not provided klass_name = 'GlobalSession::Directory' end if klass_name.is_a?(String) # for apps klass = klass_name.to_const else # for specs that need to directly inject a class/object klass = klass_name end rescue Exception => e raise GlobalSession::ConfigurationError, "Invalid/unknown directory class name: #{klass_name.inspect}" end # Initialize the directory object if directory.is_a?(Directory) # In v4-style initialization, the directory is always passed in @directory = directory elsif klass.is_a?(Class) # @deprecated v3-style initialization where the config file names the directory class @directory = klass.new(@configuration, directory) else raise GlobalSession::ConfigurationError, "Cannot determine directory class/instance; method parameter is a #{directory.class.name} and configuration parameter is #{klass.class.name}" end # Initialize the keystore @keystore = Keystore.new(@configuration) = block = @configuration['cookie']['name'] end |
Instance Attribute Details
#configuration ⇒ GlobalSession::Configuration
33 34 35 |
# File 'lib/global_session/rack.rb', line 33 def configuration @configuration end |
#directory ⇒ GlobalSession::Directory
36 37 38 |
# File 'lib/global_session/rack.rb', line 36 def directory @directory end |
Instance Method Details
#call(env) ⇒ Array
Rack request chain. Sets up the global session ticket from the environment and passes it up the chain.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/global_session/rack.rb', line 111 def call(env) env['rack.cookies'] = {} unless env['rack.cookies'] begin err = nil (env) || (env) || create_session(env) rescue Exception => read_err err = read_err # Catch "double whammy" errors begin env['global_session'] = @directory.create_session rescue Exception => create_err err = create_err end handle_error('reading session cookie', env, err) end tuple = nil begin tuple = @app.call(env) rescue Exception => read_err handle_error('processing request', env, read_err) return tuple else (env) (env) return tuple end end |
#cookie_domain(env) ⇒ Object
Determine the domain name for which we should set the cookie. Uses the domain specified in the configuration if one is found; otherwise, uses the SERVER_NAME from the request but strips off the first component if the domain name contains more than two components.
318 319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/global_session/rack.rb', line 318 def (env) if @configuration['cookie'].has_key?('domain') # Use the explicitly provided domain name domain = @configuration['cookie']['domain'] else # Use the server name, but strip off the most specific component parts = env['SERVER_NAME'].split('.') parts = parts[1..-1] if parts.length > 2 domain = parts.join('.') end domain end |
#create_session(env) ⇒ true
Ensure that the Rack environment contains a global session object; create a session if necessary.
192 193 194 195 196 |
# File 'lib/global_session/rack.rb', line 192 def create_session(env) env['global_session'] ||= @directory.create_session true end |
#handle_error(activity, env, e) ⇒ true
Handle exceptions that occur during app invocation. This will either save the error in the Rack environment or raise it, depending on the type of error. The error may also be logged.
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/global_session/rack.rb', line 278 def handle_error(activity, env, e) if env['rack.logger'] msg = "#{e.class} while #{activity}: #{e}" msg += " #{e.backtrace}" unless e.is_a?(ExpiredSession) env['rack.logger'].error(msg) end if e.is_a?(ClientError) || e.is_a?(SecurityError) env['global_session.error'] = e (env) elsif e.is_a? ConfigurationError env['global_session.error'] = e else # Don't intercept errors unless they're GlobalSession-related raise e end true end |
#perform_invalidation_callbacks(env, old_session, new_session) ⇒ true
Perform callbacks to directory and/or local session informing them that this session has been invalidated.
305 306 307 308 309 310 311 |
# File 'lib/global_session/rack.rb', line 305 def perform_invalidation_callbacks(env, old_session, new_session) if (local_session = env[LOCAL_SESSION_KEY]) && local_session.respond_to?(:rename!) local_session.rename!(old_session, new_session) end true end |
#read_authorization_header(env) ⇒ Boolean
Read a global session from the HTTP Authorization header, if present. If an authorization header was found, also disable global session cookie update and renewal by setting the corresponding keys of the Rack environment.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/global_session/rack.rb', line 150 def (env) if env.has_key? 'X-HTTP_AUTHORIZATION' # RFC2617 style (preferred by OAuth 2.0 spec) header_data = env['X-HTTP_AUTHORIZATION'].to_s.split elsif env.has_key? 'HTTP_AUTHORIZATION' # Fallback style (generally when no load balancer is present, e.g. dev/test) header_data = env['HTTP_AUTHORIZATION'].to_s.split else header_data = nil end if header_data && header_data.size == 2 && header_data.first.downcase == 'bearer' env['global_session.req.renew'] = false env['global_session.req.update'] = false env['global_session'] = @directory.load_session(header_data.last) true else false end end |
#read_cookie(env) ⇒ Boolean
Read a global session from HTTP cookies, if present.
175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/global_session/rack.rb', line 175 def (env) if && ( = .call(env)) env['global_session'] = @directory.load_session() true elsif env['rack.cookies'].has_key?() env['global_session'] = @directory.load_session(env['rack.cookies'][]) true else false end end |
#renew_cookie(env) ⇒ true
Renew the session ticket.
202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/global_session/rack.rb', line 202 def (env) return unless @configuration['authority'] return if env['global_session.req.renew'] == false if (renew = @configuration['renew']) && env['global_session'] && env['global_session'].expired_at < Time.at(Time.now.utc + 60 * renew.to_i) env['global_session'].renew! end true end |
#update_cookie(env) ⇒ true
Update the cookie jar with the revised ticket.
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/global_session/rack.rb', line 218 def (env) return true unless @directory.keystore.private_key_name return true if env['global_session.req.update'] == false session = env['global_session'] if session unless session.valid? old_session = session session = @directory.create_session perform_invalidation_callbacks(env, old_session, session) env['global_session'] = session end value = session.to_s expires = @configuration['ephemeral'] ? nil : session.expired_at unless env['rack.cookies'][] == value env['rack.cookies'][] = { :value => value, :domain => (env), :expires => expires, :httponly => true, :secure => (env['rack.url_scheme'] == 'https'), } end else # write an empty cookie (env) end true rescue Exception => e (env) raise e end |
#wipe_cookie(env) ⇒ true
Delete the global session cookie from the cookie jar.
259 260 261 262 263 264 265 266 267 268 |
# File 'lib/global_session/rack.rb', line 259 def (env) return unless @directory.keystore.private_key_name return if env['global_session.req.update'] == false env['rack.cookies'][] = {:value => nil, :domain => (env), :expires => Time.at(0)} true end |