class CASClient::Client
The client brokers all HTTP transactions with the CAS
server.
Attributes
Public Class Methods
# File lib/casclient/client.rb, line 11 def initialize(conf = nil) configure(conf) if conf end
Public Instance Methods
# File lib/casclient/client.rb, line 224 def add_service_to_login_url(service_url) uri = URI.parse(login_url) uri.query = (uri.query ? uri.query + "&" : "") + "service=#{CGI.escape(service_url)}" uri.to_s end
Returns true if the configured CAS
server is up and responding; false otherwise.
# File lib/casclient/client.rb, line 137 def cas_server_is_up? uri = URI.parse(login_url) log.debug "Checking if CAS server at URI '#{uri}' is up..." https = https_connection(uri) begin raw_res = https.start do |conn| conn.get("#{uri.path}?#{uri.query}") end rescue Errno::ECONNREFUSED => e log.warn "CAS server did not respond! (#{e.inspect})" return false end log.debug "CAS server responded with #{raw_res.inspect}:\n#{raw_res.body}" return raw_res.kind_of?(Net::HTTPSuccess) end
# File lib/casclient/client.rb, line 15 def configure(conf) #TODO: raise error if conf contains unrecognized cas options (this would help detect user typos in the config) raise ArgumentError, "Missing :cas_base_url parameter!" unless conf[:cas_base_url] if conf.has_key?("encode_extra_attributes_as") unless (conf[:encode_extra_attributes_as] == :json || conf[:encode_extra_attributes_as] == :yaml) raise ArgumentError, "Unkown Value for :encode_extra_attributes_as parameter! Allowed options are json or yaml - #{conf[:encode_extra_attributes_as]}" end end @cas_base_url = conf[:cas_base_url].gsub(/\/$/, '') @cas_destination_logout_param_name = conf[:cas_destination_logout_param_name] @login_url = conf[:login_url] @logout_url = conf[:logout_url] @validate_url = conf[:validate_url] @proxy_url = conf[:proxy_url] @service_url = conf[:service_url] @force_ssl_verification = conf[:force_ssl_verification] @proxy_callback_url = conf[:proxy_callback_url] #proxy server settings @proxy_host = conf[:proxy_host] @proxy_port = conf[:proxy_port] @username_session_key = conf[:username_session_key] || :cas_user @extra_attributes_session_key = conf[:extra_attributes_session_key] || :cas_extra_attributes @ticket_store_class = case conf[:ticket_store] when :local_dir_ticket_store, nil CASClient::Tickets::Storage::LocalDirTicketStore when :active_record_ticket_store ::ACTIVE_RECORD_TICKET_STORE else conf[:ticket_store] end @ticket_store = @ticket_store_class.new conf[:ticket_store_config] raise CASException, "The Ticket Store is not a subclass of AbstractTicketStore, it is a #{@ticket_store_class}" unless @ticket_store.kind_of? CASClient::Tickets::Storage::AbstractTicketStore @log = CASClient::LoggerWrapper.new @log.set_real_logger(conf[:logger]) if conf[:logger] @ticket_store.log = @log @conf_options = conf end
Requests a login using the given credentials for the given service; returns a LoginResponse
object.
# File lib/casclient/client.rb, line 160 def login_to_service(credentials, service) lt = request_login_ticket data = credentials.merge( :lt => lt, :service => service ) res = submit_data_to_cas(login_url, data) response = CASClient::LoginResponse.new(res) if response.is_success? log.info("Login was successful for ticket: #{response.ticket.inspect}.") end return response end
# File lib/casclient/client.rb, line 64 def login_url @login_url || (cas_base_url + "/login") end
Returns the CAS
server’s logout url.
If a logout_url
has not been explicitly configured, the default is cas_base_url
+ “/logout”.
- destination_url
-
Set this if you want the user to be able to immediately log back in. Generally you’ll want to use something like
request.referer
. Note that the above behaviour describes RubyCAS-Server – otherCAS
server implementations might use this parameter differently (or not at all). - follow_url
-
This satisfies section 2.3.1 of the
CAS
protocol spec. See www.ja-sig.org/products/cas/overview/protocol
# File lib/casclient/client.rb, line 85 def logout_url(destination_url = nil, follow_url = nil, service_url = nil) url = @logout_url || (cas_base_url + "/logout") uri = URI.parse(url) service_url = (service_url if service_url) || @service_url h = uri.query ? query_to_hash(uri.query) : {} if destination_url # if present, remove the 'ticket' parameter from the destination_url duri = URI.parse(destination_url) dh = duri.query ? query_to_hash(duri.query) : {} dh.delete('ticket') duri.query = hash_to_query(dh) destination_url = duri.to_s.gsub(/\?$/, '') h[cas_destination_logout_param_name] = destination_url if destination_url h['gateway'] = 'true' elsif follow_url h['url'] = follow_url if follow_url h['service'] = service_url else h['service'] = service_url end uri.query = hash_to_query(h) uri.to_s end
# File lib/casclient/client.rb, line 110 def proxy_url @proxy_url || (cas_base_url + "/proxy") end
Requests a login ticket from the CAS
server for use in a login request; returns a LoginTicket object.
This only works with RubyCAS-Server, since obtaining login tickets in this manner is not part of the official CAS
spec.
# File lib/casclient/client.rb, line 183 def request_login_ticket uri = URI.parse(login_url+'Ticket') https = https_connection(uri) res = https.post(uri.path, ';') raise CASException, res.body unless res.kind_of? Net::HTTPSuccess res.body.strip end
Requests a proxy ticket from the CAS
server for the given service using the given pgt (proxy granting ticket); returns a ProxyTicket
object.
The pgt required to request a proxy ticket is obtained as part of a ValidationResponse
.
# File lib/casclient/client.rb, line 199 def request_proxy_ticket(pgt, target_service) uri = URI.parse(proxy_url) h = uri.query ? query_to_hash(uri.query) : {} h['pgt'] = pgt.ticket h['targetService'] = target_service uri.query = hash_to_query(h) response = request_cas_response(uri, ProxyResponse) pt = ProxyTicket.new(response.proxy_ticket, target_service) pt.success = response.is_success? pt.failure_code = response.failure_code pt.failure_message = response.failure_message return pt end
# File lib/casclient/client.rb, line 216 def retrieve_proxy_granting_ticket(pgt_iou) pgt = @ticket_store.retrieve_pgt(pgt_iou) raise CASException, "Couldn't find pgt for pgt_iou #{pgt_iou}" unless pgt ProxyGrantingTicket.new(pgt, pgt_iou) end
# File lib/casclient/client.rb, line 114 def validate_service_ticket(st) uri = URI.parse(validate_url) h = uri.query ? query_to_hash(uri.query) : {} h['service'] = st.service h['ticket'] = st.ticket h['renew'] = "1" if st.renew h['pgtUrl'] = proxy_callback_url if proxy_callback_url uri.query = hash_to_query(h) response = request_cas_response(uri, ValidationResponse) st.user = response.user st.extra_attributes = response.extra_attributes st.pgt_iou = response.pgt_iou st.success = response.is_success? st.failure_code = response.failure_code st.failure_message = response.failure_message return st end
# File lib/casclient/client.rb, line 68 def validate_url @validate_url || (cas_base_url + "/proxyValidate") end
Private Instance Methods
# File lib/casclient/client.rb, line 281 def hash_to_query(hash) pairs = [] hash.each do |k, vals| vals = [vals] unless vals.kind_of? Array vals.each {|v| pairs << (v.nil? ? CGI.escape(k) : "#{CGI.escape(k)}=#{CGI.escape(v)}")} end pairs.join("&") end
# File lib/casclient/client.rb, line 232 def https_connection(uri) https = Net::HTTP::Proxy(proxy_host, proxy_port).new(uri.host, uri.port) https.use_ssl = (uri.scheme == 'https') https.verify_mode = (@force_ssl_verification ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE) https end
# File lib/casclient/client.rb, line 277 def query_to_hash(query) CGI.parse(query) end
Fetches a CAS
response of the given type from the given URI. Type should be either ValidationResponse
or ProxyResponse
.
# File lib/casclient/client.rb, line 241 def request_cas_response(uri, type, options={}) log.debug "Requesting CAS response for URI #{uri}" uri = URI.parse(uri) unless uri.kind_of? URI https = https_connection(uri) begin raw_res = https.start do |conn| conn.get("#{uri.path}?#{uri.query}") end rescue Errno::ECONNREFUSED => e log.error "CAS server did not respond! (#{e.inspect})" raise "The CAS authentication server at #{uri} is not responding!" end # We accept responses of type 422 since RubyCAS-Server generates these # in response to requests from the client that are processable but contain # invalid CAS data (for example an invalid service ticket). if raw_res.kind_of?(Net::HTTPSuccess) || raw_res.code.to_i == 422 log.debug "CAS server responded with #{raw_res.inspect}:\n#{raw_res.body}" else log.error "CAS server responded with an error! (#{raw_res.inspect})" raise "The CAS authentication server at #{uri} responded with an error (#{raw_res.inspect})!" end type.new(raw_res.body, @conf_options) end
Submits some data to the given URI and returns a Net::HTTPResponse.
# File lib/casclient/client.rb, line 269 def submit_data_to_cas(uri, data) uri = URI.parse(uri) unless uri.kind_of? URI req = Net::HTTP::Post.new(uri.path) req.set_form_data(data, ';') https = https_connection(uri) https.start {|conn| conn.request(req) } end