require 'els_token/module_inheritable_attributes' require 'els_token/els_identity' require 'net/http' require 'uri' module ElsToken def self.included(base) base.extend ClassMethods base.send :include, ElsToken::ModuleInheritableAttributes base.send :mattr_inheritable, :els_options base.instance_variable_set("@els_options", {}) end module ClassMethods # els_config expects a hash with environmental # parameters including the els gateway and expected # cookie name (when used in a Rack environment) # An optional fake identity can be supplied which # will override any active authentication. This can # be especially useful during automated testing. # The fake ID can take any of the ElsIdentity properties # # A typical setup would initialize a options hash to # include the following # # faker: # name: neilcuk # employee_number: 09095 # roles: # - App Admins # - Domain Users # uri: https://els-admin.corp.aol.com:443/opensso/identity # cookie: iPlanetDirectoryPro # cert: /path/to/certs # # Do not include the faker object in your production # configuration :) # # only the uri option is required if you are not worried # about cookies and do not plan on using them. If you want # to include a certificate for interacting with the ELS # server then you can specify a file or directory to find # the cert. By default Certificate validiation is off! # def els_config(options = {}) unless options["uri"] raise "I need a uri to authenticate against" unless options["faker"] end els_options.merge!(options) end def els_uri(uri = nil) return els_options["uri"] unless uri els_options["uri"] = uri end def els_cookie_name(cookie_name = nil) return els_options["cookie"] unless cookie_name els_options["cookie_name"] = uri end def els_faker(faker = {}) els_options["faker"] = faker end def els_options @els_options end # authenticates against ELS and returns the user token def authenticate(username,password,options={}) begin response = els_http_request("/authenticate", {"uri"=>"realm=aolcorporate","username"=>"#{username}","password"=>"#{password}"}, options) if response.code.eql? "200" # return the token response.body.chomp.sub(/token\.id=/,"") else raise response.error! end rescue Net::HTTPExceptions => e1 raise e1, "token retrieval failed for #{username}" rescue Exception => e # Do not expect these. Wrapping the exception so # as to not reveal the passed in password puts e.backtrace raise e, "unable to fetch token for #{username}" end end # passes a token to els to see if it is still valid def is_token_valid?(token, options={}) response = els_http_request("/isTokenValid",{"tokenid"=>"#{token}"},options) if (response.code.eql? "200") && (response.body.chomp.eql? "boolean=true") true else false end end # obtain a friendly ElsIdentity object by passing # in a token def get_token_identity(token,options={}) ElsIdentity.new(get_raw_token_identity(token,options)) end # get_token_identity wraps the ELS identity response # in a nice, friendly, object. If you don't like that object # or need the raw data, then use this. def get_raw_token_identity(token,options={}) response = els_http_request("/attributes",{"subjectid"=>"#{token}"},options) if response.code.eql? "200" response.body else response.error! end end # When used inside a rack environment # will attempt to retrieve the user token # from the session cookie and return a full # identity. This is pretty much a convenience # method that chains is_cookie_token_valid? # then get_token_identity def get_identity(token, options ={}) options = els_options.dup.merge(options) return fake_id(options) if options.has_key?('faker') begin if is_token_valid?(token, options) get_token_identity(token, options) else raise "token is invalid" end rescue Exception => e raise e end end private def els_http_request(url_base_extension, request_body={}, options) options = els_options.dup.merge(options) uri = URI.parse(options['uri'] + url_base_extension) # uri.query=query_string http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true # Use a known certificate if supplied if rootca = options['cert'] if File.exist? rootca http.ca_file = rootca elsif Dir.exist? rootca http.ca.path = rootca else raise "${rootca} cannot be found" end http.verify_mode = OpenSSL::SSL::VERIFY_PEER http.verify_depth = 5 else http.verify_mode = OpenSSL::SSL::VERIFY_NONE end request = Net::HTTP::Post.new(uri.request_uri) request.set_form_data(request_body) http.request(request) end def fake_it? els_options.has_key? 'faker' end def fake_id(options ={}) options = els_options.dup.merge(options) id = ElsIdentity.new id.instance_variable_set("@roles",options['faker']['roles']) id.instance_variable_set("@mail",options['faker']['mail']) id.instance_variable_set("@last_name",options['faker']['last_name']) id.instance_variable_set("@first_name",options['faker']['first_name']) id.instance_variable_set("@uac",options['faker']['uac']) id.instance_variable_set("@dn",options['faker']['dn']) id.instance_variable_set("@common_name",options['faker']['common_name']) id.instance_variable_set("@employee_number",options['faker']['employee_number']) id.instance_variable_set("@display_name",options['faker']['display_name']) id.instance_variable_set("@token_id",options['faker']['token_id']) id.instance_variable_set("@user_status",options['faker']['user_status']) id end end # Instance methods class Runner include ElsToken end def authenticate(username,password) Runner.authenticate(username,password,self.class.els_options) end def is_token_valid?(token) Runner.is_token_valid?(token,self.class.els_options) end def get_token_identity(token) Runner.get_token_identity(token,self.class.els_options) end def get_raw_token_identity(token) Runner.get_raw_token_identity(token,self.class.els_options) end def get_identity token = cookies[self.class.els_options['cookie']] Runner.get_identity(token,self.class.els_options) end # extract the token from a cookie # This method expects a hash called cookies # to be present. It will look for a cookie with # the key of the cookie value in the config hash def is_cookie_token_valid? return true if self.class.els_options.has_key? 'faker' raise "No cookies instance found" if cookies.nil? token = cookies[self.class.els_options['cookie']] if token.nil? || !Runner.is_token_valid?(token,self.class.els_options) false else true end end # How about a few class methods of our own? def self.authenticate(username,password,options) Runner.authenticate(username,password,options) end def self.is_token_valid?(token, options) Runner.is_token_valid?(token,options) end def self.get_identity(token, options) Runner.get_identity(token,options) end end