diff options
Diffstat (limited to 'vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb')
-rw-r--r-- | vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb b/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb new file mode 100644 index 00000000000..7271621c564 --- /dev/null +++ b/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb @@ -0,0 +1,222 @@ +require 'omniauth' +require 'addressable/uri' + +module OmniAuth + module Strategies + class CAS3 + include OmniAuth::Strategy + + # Custom Exceptions + class MissingCASTicket < StandardError; end + class InvalidCASTicket < StandardError; end + + autoload :ServiceTicketValidator, 'omniauth/strategies/cas3/service_ticket_validator' + autoload :LogoutRequest, 'omniauth/strategies/cas3/logout_request' + + attr_accessor :raw_info + alias_method :user_info, :raw_info + + option :name, :cas3 # Required property by OmniAuth::Strategy + + option :host, nil + option :port, nil + option :path, nil + option :ssl, true + option :service_validate_url, '/p3/serviceValidate' + option :login_url, '/login' + option :logout_url, '/logout' + option :on_single_sign_out, Proc.new {} + # A Proc or lambda that returns a Hash of additional user info to be + # merged with the info returned by the CAS server. + # + # @param [Object] An instance of OmniAuth::Strategies::CAS for the current request + # @param [String] The user's Service Ticket value + # @param [Hash] The user info for the Service Ticket returned by the CAS server + # + # @return [Hash] Extra user info + option :fetch_raw_info, Proc.new { Hash.new } + # Make all the keys configurable with some defaults set here + option :uid_field, 'user' + option :name_key, 'name' + option :email_key, 'email' + option :nickname_key, 'user' + option :first_name_key, 'first_name' + option :last_name_key, 'last_name' + option :location_key, 'location' + option :image_key, 'image' + option :phone_key, 'phone' + + # As required by https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema + AuthHashSchemaKeys = %w{name email nickname first_name last_name location image phone} + info do + prune!({ + name: raw_info[options[:name_key].to_s], + email: raw_info[options[:email_key].to_s], + nickname: raw_info[options[:nickname_key].to_s], + first_name: raw_info[options[:first_name_key].to_s], + last_name: raw_info[options[:last_name_key].to_s], + location: raw_info[options[:location_key].to_s], + image: raw_info[options[:image_key].to_s], + phone: raw_info[options[:phone_key].to_s] + }) + end + + extra do + prune!( + raw_info.delete_if{ |k,v| AuthHashSchemaKeys.include?(k) } + ) + end + + uid do + raw_info[options[:uid_field].to_s] + end + + credentials do + prune!({ ticket: @ticket }) + end + + def callback_phase + if on_sso_path? + single_sign_out_phase + else + @ticket = request.params['ticket'] + return fail!(:no_ticket, MissingCASTicket.new('No CAS Ticket')) unless @ticket + fetch_raw_info(@ticket) + return fail!(:invalid_ticket, InvalidCASTicket.new('Invalid CAS Ticket')) if raw_info.empty? + super + end + end + + def request_phase + service_url = append_params(callback_url, return_url) + + [ + 302, + { + 'Location' => login_url(service_url), + 'Content-Type' => 'text/plain' + }, + ["You are being redirected to CAS for sign-in."] + ] + end + + def on_sso_path? + request.post? && request.params.has_key?('logoutRequest') + end + + def single_sign_out_phase + logout_request_service.new(self, request).call(options) + end + + # Build a CAS host with protocol and port + # + # + def cas_url + extract_url if options['url'] + validate_cas_setup + @cas_url ||= begin + uri = Addressable::URI.new + uri.host = options.host + uri.scheme = options.ssl ? 'https' : 'http' + uri.port = options.port + uri.path = options.path + uri.to_s + end + end + + def extract_url + url = Addressable::URI.parse(options.delete('url')) + options.merge!( + 'host' => url.host, + 'port' => url.port, + 'path' => url.path, + 'ssl' => url.scheme == 'https' + ) + end + + def validate_cas_setup + if options.host.nil? || options.login_url.nil? + raise ArgumentError.new(":host and :login_url MUST be provided") + end + end + + # Build a service-validation URL from +service+ and +ticket+. + # If +service+ has a ticket param, first remove it. URL-encode + # +service+ and add it and the +ticket+ as paraemters to the + # CAS serviceValidate URL. + # + # @param [String] service the service (a.k.a. return-to) URL + # @param [String] ticket the ticket to validate + # + # @return [String] a URL like `http://cas.mycompany.com/serviceValidate?service=...&ticket=...` + def service_validate_url(service_url, ticket) + service_url = Addressable::URI.parse(service_url) + service_url.query_values = service_url.query_values.tap { |qs| qs.delete('ticket') } + cas_url + append_params(options.service_validate_url, { + service: service_url.to_s, + ticket: ticket + }) + end + + # Build a CAS login URL from +service+. + # + # @param [String] service the service (a.k.a. return-to) URL + # + # @return [String] a URL like `http://cas.mycompany.com/login?service=...` + def login_url(service) + cas_url + append_params(options.login_url, { service: service }) + end + + # Adds URL-escaped +parameters+ to +base+. + # + # @param [String] base the base URL + # @param [String] params the parameters to append to the URL + # + # @return [String] the new joined URL. + def append_params(base, params) + params = params.each { |k,v| v = Rack::Utils.escape(v) } + Addressable::URI.parse(base).tap do |base_uri| + base_uri.query_values = (base_uri.query_values || {}).merge(params) + end.to_s + end + + # Validate the Service Ticket + # @return [Object] the validated Service Ticket + def validate_service_ticket(ticket) + ServiceTicketValidator.new(self, options, callback_url, ticket).call + end + + private + + def fetch_raw_info(ticket) + ticket_user_info = validate_service_ticket(ticket).user_info + custom_user_info = options.fetch_raw_info.call(self, options, ticket, ticket_user_info) + self.raw_info = ticket_user_info.merge(custom_user_info) + end + + # Deletes Hash pairs with `nil` values. + # From https://github.com/mkdynamic/omniauth-facebook/blob/972ed5e3456bcaed7df1f55efd7c05c216c8f48e/lib/omniauth/strategies/facebook.rb#L122-127 + def prune!(hash) + hash.delete_if do |_, value| + prune!(value) if value.is_a?(Hash) + value.nil? || (value.respond_to?(:empty?) && value.empty?) + end + end + + def return_url + # If the request already has a `url` parameter, then it will already be appended to the callback URL. + if request.params && request.params['url'] + {} + else + { url: request.referer } + end + end + + def logout_request_service + LogoutRequest + end + end + end +end + +OmniAuth.config.add_camelization 'cas3', 'CAS3' |