lib/global_session/rack.rb in global_session-3.0.5 vs lib/global_session/rack.rb in global_session-3.1.0
- old
+ new
@@ -27,65 +27,88 @@
# Global session middleware. Note: this class relies on
# Rack::Cookies being used higher up in the chain.
class Middleware
LOCAL_SESSION_KEY = "rack.session".freeze
- # Make a new global session.
+ # @return [GlobalSession::Configuration]
+ attr_accessor :configuration
+
+ # @return [GlobalSession::Directory]
+ attr_accessor :directory
+
+ # 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.
#
- # === 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.
+ # @param [Configuration] configuration
+ # @param optional [String,Directory] directory the directory class name (DEPRECATED) or an actual instance of Directory
#
- # block: optional alternate ticket retrieval function
- def initialize(app, configuration, directory, &block)
+ # @yield if a block is provided, yields to the block to fetch session data from request state
+ # @yieldparam [Hash] env Rack request environment is passed as a yield parameter
+ 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
- klass_name = @configuration['directory'] || 'GlobalSession::Directory'
+ # v0.9.0 - v3.0.4: class name is the value of the 'directory' key
+ klass_name = @configuration['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
+ 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 #{@configuration['directory']}"
+ raise GlobalSession::ConfigurationError,
+ "Invalid/unknown directory class name: #{klass_name.inspect}"
end
- if directory.instance_of?(String)
- @directory = directory_klass.new(@configuration, directory)
- else
+ # 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']
+ @cookie_name = @configuration['cookie']['name']
end
# Rack request chain. Sets up the global session ticket from
# the environment and passes it up the chain.
+ #
+ # @return [Array] valid Rack response tuple e.g. [200, 'hello world']
+ # @param [Hash] env Rack request environment
def call(env)
env['rack.cookies'] = {} unless env['rack.cookies']
begin
err = nil
@@ -119,15 +142,12 @@
# 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
+ # @return [Boolean] true if the environment was populated, false otherwise
+ # @param [Hash] env Rack request environment
def read_authorization_header(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'
@@ -136,26 +156,23 @@
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.renew'] = false
env['global_session.req.update'] = false
- env['global_session'] = @directory.load_session(header_data.last)
+ env['global_session'] = @directory.load_session(header_data.last)
true
else
false
end
end
# 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
+ # @return [Boolean] true if the environment was populated, false otherwise
+ # @param [Hash] env Rack request environment
def read_cookie(env)
if @cookie_retrieval && (cookie = @cookie_retrieval.call(env))
env['global_session'] = @directory.load_session(cookie)
true
elsif env['rack.cookies'].has_key?(@cookie_name)
@@ -167,92 +184,95 @@
end
# 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
+ # @return [true] always returns true
+ # @param [Hash] env Rack request environment
def create_session(env)
env['global_session'] ||= @directory.create_session
true
end
# Renew the session ticket.
#
- # === Parameters
- # env(Hash): Rack environment
+ # @return [true] always returns true
+ # @param [Hash] env Rack request environment
def renew_cookie(env)
- return unless @directory.local_authority_name
+ 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'].expired_at < Time.at(Time.now.utc + 60 * renew.to_i)
env['global_session'].renew!
end
+
+ true
end
# Update the cookie jar with the revised ticket.
#
- # === Parameters
- # env(Hash): Rack environment
+ # @return [true] always returns true
+ # @param [Hash] env Rack request environment
def update_cookie(env)
- return unless @directory.local_authority_name
- return if env['global_session.req.update'] == false
+ 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
+ session = @directory.create_session
perform_invalidation_callbacks(env, old_session, session)
env['global_session'] = session
end
- value = session.to_s
+ 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 => cookie_domain(env),
- :expires => expires,
- :httponly=>true}
+ {:value => value,
+ :domain => cookie_domain(env),
+ :expires => expires,
+ :httponly => true}
end
else
# write an empty cookie
wipe_cookie(env)
end
+
+ true
rescue Exception => e
wipe_cookie(env)
raise e
end
# Delete the global session cookie from the cookie jar.
#
- # === Parameters
- # env(Hash): Rack environment
+ # @return [true] always returns true
+ # @param [Hash] env Rack request environment
def wipe_cookie(env)
- return unless @directory.local_authority_name
+ return unless @configuration['authority']
return if env['global_session.req.update'] == false
- env['rack.cookies'][@cookie_name] = {:value => nil,
- :domain => cookie_domain(env),
+ env['rack.cookies'][@cookie_name] = {:value => nil,
+ :domain => cookie_domain(env),
:expires => Time.at(0)}
+
+ true
end
# 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
+ # @return [true] always returns true
+ # @param [String] activity name of activity during which the error happened
+ # @param [Hash] env Rack request environment
+ # @param [Exception] e error that happened
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)
@@ -262,21 +282,24 @@
env['global_session.error'] = e
wipe_cookie(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 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
+ # @return [true] always returns true
+ # @param [Hash] env Rack request environment
+ # @param [GlobalSession::Session] old_session now-invalidated session
+ # @param [GlobalSession::Session] new_session new session that will be sent to the client
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
@@ -285,19 +308,18 @@
# 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
+ # @param [Hash] env Rack request environment
def cookie_domain(env)
- if @configuration['cookie'].key?('domain')
+ 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
+ parts = env['SERVER_NAME'].split('.')
+ parts = parts[1..-1] if parts.length > 2
domain = parts.join('.')
end
domain
end