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 Method Summary collapse
-
#call(env) ⇒ Object
Rack request chain.
-
#cookie_domain(env) ⇒ Object
Determine the domain name for which we should set the cookie.
-
#create_session(env) ⇒ Object
Ensure that the Rack environment contains a global session object; create a session if necessary.
-
#handle_error(activity, env, e) ⇒ Object
Handle exceptions that occur during app invocation.
-
#initialize(app, configuration, directory, &block) ⇒ Middleware
constructor
Make a new global session.
-
#perform_invalidation_callbacks(env, old_session, new_session) ⇒ Object
Perform callbacks to directory and/or local session informing them that this session has been invalidated.
-
#read_authorization_header(env) ⇒ Object
Read a global session from the HTTP Authorization header, if present.
-
#read_cookie(env) ⇒ Object
Read a global session from HTTP cookies, if present.
-
#renew_cookie(env) ⇒ Object
Renew the session ticket.
-
#update_cookie(env) ⇒ Object
Update the cookie jar with the revised ticket.
-
#wipe_cookie(env) ⇒ Object
Delete the global session cookie from the cookie jar.
Constructor Details
#initialize(app, configuration, directory, &block) ⇒ Middleware
Make a new global session.
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.
Parameters
app(Rack client): application to run configuration(String or Configuration): global_session configuration. If a string, is interpreted as a filename to load the config from. directory(String or Directory): Directory object that provides trust services to the global session implementation. If a string, is interpreted as a filesystem directory containing the public and private keys of authorities, from which default trust services will be initialized.
block: optional alternate ticket retrieval function
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 |
# File 'lib/global_session/rack.rb', line 54 def initialize(app, configuration, directory, &block) @app = app if configuration.instance_of?(String) @configuration = Configuration.new(configuration, ENV['RACK_ENV'] || 'development') else @configuration = configuration end begin klass_name = @configuration['directory'] || 'GlobalSession::Directory' #Constantize the type name that was given as a string parts = klass_name.split('::') namespace = Object namespace = namespace.const_get(parts.shift.to_sym) until parts.empty? directory_klass = namespace rescue Exception => e raise GlobalSession::ConfigurationError, "Invalid/unknown directory class name #{@configuration['directory']}" end if directory.instance_of?(String) @directory = directory_klass.new(@configuration, directory) else @directory = directory end @cookie_retrieval = block @cookie_name = @configuration['cookie']['name'] end |
Instance Method Details
#call(env) ⇒ Object
Rack request chain. Sets up the global session ticket from the environment and passes it up the chain.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/global_session/rack.rb', line 87 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.
Parameters
- env(Hash)
the Rack environment hash
292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/global_session/rack.rb', line 292 def (env) if @configuration['cookie'].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) ⇒ Object
Ensure that the Rack environment contains a global session object; create a session if necessary.
Parameters
env(Hash): Rack environment.
Return
- true
always returns true
177 178 179 180 181 |
# File 'lib/global_session/rack.rb', line 177 def create_session(env) env['global_session'] ||= @directory.create_session true end |
#handle_error(activity, env, e) ⇒ Object
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.
Parameters
activity(String): name of activity in which error happened env(Hash): Rack environment e(Exception): error that happened
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/global_session/rack.rb', line 254 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 raise e end end |
#perform_invalidation_callbacks(env, old_session, new_session) ⇒ Object
Perform callbacks to directory and/or local session informing them that this session has been invalidated.
Parameters
- env(Hash)
the rack environment
- old_session(GlobalSession)
the now-invalidated session
- new_session(GlobalSession)
the new session that will be sent to the client
278 279 280 281 282 283 284 |
# File 'lib/global_session/rack.rb', line 278 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) ⇒ Object
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.
Parameters
env(Hash): Rack environment.
Return
- result(true,false)
Returns true if the environment was populated, false otherwise
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/global_session/rack.rb', line 129 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) ⇒ Object
Read a global session from HTTP cookies, if present.
Parameters
env(Hash): Rack environment.
Return
- result(true,false)
Returns true if the environment was populated, false otherwise
157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/global_session/rack.rb', line 157 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) ⇒ Object
Renew the session ticket.
Parameters
env(Hash): Rack environment
187 188 189 190 191 192 193 194 195 |
# File 'lib/global_session/rack.rb', line 187 def (env) return unless @directory. 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 end |
#update_cookie(env) ⇒ Object
Update the cookie jar with the revised ticket.
Parameters
env(Hash): Rack environment
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/global_session/rack.rb', line 201 def (env) return unless @directory. return 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 rescue Exception => e (env) raise e end |
#wipe_cookie(env) ⇒ Object
Delete the global session cookie from the cookie jar.
Parameters
env(Hash): Rack environment
237 238 239 240 241 242 243 244 |
# File 'lib/global_session/rack.rb', line 237 def (env) return unless @directory. return if env['global_session.req.update'] == false env['rack.cookies'][@cookie_name] = {:value => nil, :domain => (env), :expires => Time.at(0)} end |