module CASClient module Frameworks module Rails class Filter cattr_accessor :config, :log, :client # These are initialized when you call configure. @@config = nil @@client = nil @@log = nil def self.use_gatewaying? @@config[:use_gatewaying] end def self.filter(controller) raise "Cannot use the CASClient filter because it has not yet been configured." if config.nil? case RequestHandler.determine_response(condition, use_gatewaying?) when :single_sign_out controller.send(:render, :text => "CAS Single-Sign-Out request intercepted.") return false when :allow return true when :to_login redirect_to_cas_for_authentication(controller) return false when :validation_failed redirect_to_cas_for_authentication(controller) return false end end def self.configure(config) @@config = config @@config[:logger] = RAILS_DEFAULT_LOGGER unless @@config[:logger] @@client = CASClient::Client.new(config) @@log = client.log end # Returns the login URL for the current controller. # Useful when you want to provide a "Login" link in a GatewayFilter'ed # action. def self.login_url(controller) service_url = read_service_url(controller) url = client.add_service_to_login_url(service_url) log.debug("Generated login url: #{url}") return url end # Clears the given controller's local Rails session, does some local # CAS cleanup, and redirects to the CAS logout page. Additionally, the # request.referer value from the controller instance # is passed to the CAS server as a 'destination' parameter. This # allows RubyCAS server to provide a follow-up login page allowing # the user to log back in to the service they just logged out from # using a different username and password. Other CAS server # implemenations may use this 'destination' parameter in different # ways. # If given, the optional service URL overrides # request.referer. def self.logout(controller, service = nil) referer = service || controller.request.referer st = controller.session[:cas_last_valid_ticket] delete_service_session_lookup(st) if st controller.send(:reset_session) controller.send(:redirect_to, client.logout_url(referer)) end def self.redirect_to_cas_for_authentication(controller) redirect_url = login_url(controller) if use_gatewaying? controller.session[:cas_sent_to_gateway] = true redirect_url << "&gateway=true" else controller.session[:cas_sent_to_gateway] = false end if controller.session[:previous_redirect_to_cas] && controller.session[:previous_redirect_to_cas] > (Time.now - 1.second) log.warn("Previous redirect to the CAS server was less than a second ago. The client at #{controller.request.remote_ip.inspect} may be stuck in a redirection loop!") controller.session[:cas_validation_retry_count] ||= 0 if controller.session[:cas_validation_retry_count] > 3 log.error("Redirection loop intercepted. Client at #{controller.request.remote_ip.inspect} will be redirected back to login page and forced to renew authentication.") redirect_url += "&renew=1&redirection_loop_intercepted=1" end controller.session[:cas_validation_retry_count] += 1 else controller.session[:cas_validation_retry_count] = 0 end controller.session[:previous_redirect_to_cas] = Time.now log.debug("Redirecting to #{redirect_url.inspect}") controller.send(:redirect_to, redirect_url) end end class GatewayFilter < Filter def self.use_gatewaying? return true unless @@config[:use_gatewaying] == false end end end end end