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
- NUMERIC_HOST =
/^[0-9.]+$/.freeze
- 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.
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 105 106 |
# File 'lib/global_session/rack.rb', line 52 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) @cookie_retrieval = block @cookie_name = @configuration['cookie']['name'] end |
Instance Attribute Details
#configuration ⇒ GlobalSession::Configuration
35 36 37 |
# File 'lib/global_session/rack.rb', line 35 def configuration @configuration end |
#directory ⇒ GlobalSession::Directory
38 39 40 |
# File 'lib/global_session/rack.rb', line 38 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.
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 143 144 |
# File 'lib/global_session/rack.rb', line 113 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.
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/global_session/rack.rb', line 322 def (env) name = env['HTTP_X_FORWARDED_HOST'] || env['SERVER_NAME'] if @configuration['cookie'].has_key?('domain') # Use the explicitly provided domain name domain = @configuration['cookie']['domain'] elsif name =~ NUMERIC_HOST # Don't set a domain if the browser requested an IP-based host domain = nil else # Guess an appropriate domain for the cookie. Strip one level of # subdomain; leave SLDs unmolested; omit domain entirely for # one-component domains (e.g. localhost). parts = name.split('.') case parts.length when 0..1 domain = nil when 2 domain = parts.join('.') else domain = parts[1..-1].join('.') end end domain end |
#create_session(env) ⇒ true
Ensure that the Rack environment contains a global session object; create a session if necessary.
194 195 196 197 198 |
# File 'lib/global_session/rack.rb', line 194 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.
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
# File 'lib/global_session/rack.rb', line 282 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.
309 310 311 312 313 314 315 |
# File 'lib/global_session/rack.rb', line 309 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.
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/global_session/rack.rb', line 152 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.
177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/global_session/rack.rb', line 177 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.
204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/global_session/rack.rb', line 204 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.
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 254 255 256 257 |
# File 'lib/global_session/rack.rb', line 220 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'][@cookie_name] == value secure = (env['HTTP_X_FORWARDED_PROTO'] == 'https') || (env['rack.url_scheme'] == 'https') env['rack.cookies'][@cookie_name] = { :value => value, :domain => (env), :expires => expires, :httponly => true, :secure => secure, } 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.
263 264 265 266 267 268 269 270 271 272 |
# File 'lib/global_session/rack.rb', line 263 def (env) return unless @directory.keystore.private_key_name return if env['global_session.req.update'] == false env['rack.cookies'][@cookie_name] = {:value => nil, :domain => (env), :expires => Time.at(0)} true end |