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 |
# 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 # @deprecated require Directory object in v4 if klass.is_a?(Class) @directory = klass.new(@configuration, directory) elsif klass.is_a?(Directory) @directory = directory else raise GlobalSession::ConfigurationError, "Unsupported value for 'directory': expected Class or Directory, got #{klass.inspect}" end # Initialize the keystore @keystore = Keystore.new(@configuration) @cookie_retrieval = block @cookie_name = @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.
110 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 |
# File 'lib/global_session/rack.rb', line 110 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.
314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/global_session/rack.rb', line 314 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.
191 192 193 194 195 |
# File 'lib/global_session/rack.rb', line 191 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.
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/global_session/rack.rb', line 274 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.
301 302 303 304 305 306 307 |
# File 'lib/global_session/rack.rb', line 301 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.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/global_session/rack.rb', line 149 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.
174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/global_session/rack.rb', line 174 def (env) if @cookie_retrieval && ( = @cookie_retrieval.call(env)) env['global_session'] = @directory.load_session() true elsif env['rack.cookies'].has_key?(@cookie_name) env['global_session'] = @directory.load_session(env['rack.cookies'][@cookie_name]) true else false end end |
#renew_cookie(env) ⇒ true
Renew the session ticket.
201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/global_session/rack.rb', line 201 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.
217 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 |
# File 'lib/global_session/rack.rb', line 217 def (env) return true unless @configuration['authority'] 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'][@cookie_name] == value env['rack.cookies'][@cookie_name] = {:value => value, :domain => (env), :expires => expires, :httponly => true} 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.
255 256 257 258 259 260 261 262 263 264 |
# File 'lib/global_session/rack.rb', line 255 def (env) return unless @configuration['authority'] return if env['global_session.req.update'] == false env['rack.cookies'][@cookie_name] = {:value => nil, :domain => (env), :expires => Time.at(0)} true end |