lib/omniauth/strategies/ldap/adaptor.rb in oa-enterprise-0.2.4 vs lib/omniauth/strategies/ldap/adaptor.rb in oa-enterprise-0.2.5

- old
+ new

@@ -7,277 +7,273 @@ module OmniAuth module Strategies class LDAP class Adaptor - class LdapError < StandardError; end - class ConfigurationError < StandardError; end - class AuthenticationError < StandardError; end - class ConnectionError < StandardError; end - + class LdapError < StandardError; end + class ConfigurationError < StandardError; end + class AuthenticationError < StandardError; end + class ConnectionError < StandardError; end + VALID_ADAPTER_CONFIGURATION_KEYS = [:host, :port, :method, :bind_dn, :password, :try_sasl, :sasl_mechanisms, :uid, :base, :allow_anonymous] - + MUST_HAVE_KEYS = [:host, :port, :method, :uid, :base] - + METHOD = { - :ssl => :simple_tls, - :tls => :start_tls, - :plain => nil - } - - attr_accessor :bind_dn, :password - attr_reader :connection, :uid, :base + :ssl => :simple_tls, + :tls => :start_tls, + :plain => nil, + } - def initialize(configuration={}) - @connection = nil - @disconnected = false - @bound = false - @configuration = configuration.dup - @configuration[:allow_anonymous] ||= false - @logger = @configuration.delete(:logger) - message = [] - MUST_HAVE_KEYS.each do |name| - message << name if configuration[name].nil? - end - raise ArgumentError.new(message.join(",") +" MUST be provided") unless message.empty? + attr_accessor :bind_dn, :password + attr_reader :connection, :uid, :base + + def initialize(configuration={}) + @connection = nil + @disconnected = false + @bound = false + @configuration = configuration.dup + @configuration[:allow_anonymous] ||= false + @logger = @configuration.delete(:logger) + message = [] + MUST_HAVE_KEYS.each do |name| + message << name if configuration[name].nil? + end + raise ArgumentError.new(message.join(",") +" MUST be provided") unless message.empty? VALID_ADAPTER_CONFIGURATION_KEYS.each do |name| instance_variable_set("@#{name}", configuration[name]) end + end - end - - def connect(options={}) - host = options[:host] || @host - method = ensure_method(options[:method] || @method || :plain) - port = options[:port] || @port || ensure_port(method) - @disconnected = false - @bound = false - @bind_tried = false + def connect(options={}) + host = options[:host] || @host + method = ensure_method(options[:method] || @method || :plain) + port = options[:port] || @port || ensure_port(method) + @disconnected = false + @bound = false + @bind_tried = false config = { :host => host, :port => port, } config[:encryption] = {:method => method} if method - + @connection, @uri, @with_start_tls = begin uri = construct_uri(host, port, method == :simple_tls) with_start_tls = method == :start_tls puts ({:uri => uri, :with_start_tls => with_start_tls}).inspect [Net::LDAP::Connection.new(config), uri, with_start_tls] rescue Net::LDAP::LdapError raise ConnectionError, $!.message end + end - end - - def unbind(options={}) - @connection.close # Net::LDAP doesn't implement unbind. - end - - def bind(options={}) - connect(options) unless connecting? - begin - @bind_tried = true + def unbind(options={}) + @connection.close # Net::LDAP doesn't implement unbind. + end - bind_dn = (options[:bind_dn] || @bind_dn).to_s - try_sasl = options.has_key?(:try_sasl) ? options[:try_sasl] : @try_sasl - if options.has_key?(:allow_anonymous) - allow_anonymous = options[:allow_anonymous] - else - allow_anonymous = @allow_anonymous + def bind(options={}) + connect(options) unless connecting? + begin + @bind_tried = true + + bind_dn = (options[:bind_dn] || @bind_dn).to_s + try_sasl = options.has_key?(:try_sasl) ? options[:try_sasl] : @try_sasl + if options.has_key?(:allow_anonymous) + allow_anonymous = options[:allow_anonymous] + else + allow_anonymous = @allow_anonymous + end + # Rough bind loop: + # Attempt 1: SASL if available + # Attempt 2: SIMPLE with credentials if password block + # Attempt 3: SIMPLE ANONYMOUS if 1 and 2 fail and allow anonymous is set to true + if try_sasl and sasl_bind(bind_dn, options) + puts "bound with sasl" + elsif simple_bind(bind_dn, options) + puts "bound with simple" + elsif allow_anonymous and bind_as_anonymous(options) + puts "bound as anonymous" + else + message = yield if block_given? + message ||= ('All authentication methods for %s exhausted.') % target + raise AuthenticationError, message + end + @bound = true + rescue Net::LDAP::LdapError + raise AuthenticationError, $!.message + end end - # Rough bind loop: - # Attempt 1: SASL if available - # Attempt 2: SIMPLE with credentials if password block - # Attempt 3: SIMPLE ANONYMOUS if 1 and 2 fail and allow anonymous is set to true - if try_sasl and sasl_bind(bind_dn, options) - puts "bound with sasl" - elsif simple_bind(bind_dn, options) - puts "bound with simple" - elsif allow_anonymous and bind_as_anonymous(options) - puts "bound as anonymous" - else - message = yield if block_given? - message ||= ('All authentication methods for %s exhausted.') % target - raise AuthenticationError, message - end - - @bound = true - rescue Net::LDAP::LdapError - raise AuthenticationError, $!.message - end - end - - def disconnect!(options={}) - unbind(options) - @connection = @uri = @with_start_tls = nil - @disconnected = true - end - - def rebind(options={}) - unbind(options) if bound? - connect(options) - end - - def connecting? - !@connection.nil? and !@disconnected - end - - def bound? - connecting? and @bound - end - - def search(options={}, &block) - base = options[:base] - filter = options[:filter] - limit = options[:limit] - - args = { - :base => @base, - :filter => filter, - :size => limit - } - - puts args.inspect + def disconnect!(options={}) + unbind(options) + @connection = @uri = @with_start_tls = nil + @disconnected = true + end + + def rebind(options={}) + unbind(options) if bound? + connect(options) + end + + def connecting? + !@connection.nil? and !@disconnected + end + + def bound? + connecting? and @bound + end + + def search(options={}, &block) + base = options[:base] + filter = options[:filter] + limit = options[:limit] + + args = { + :base => @base, + :filter => filter, + :size => limit + } + attributes = {} execute(:search, args) do |entry| entry.attribute_names.each do |name| attributes[name] = entry[name] end end attributes end - private - + private + def execute(method, *args, &block) - result = @connection.send(method, *args, &block) - message = nil + result = @connection.send(method, *args, &block) + message = nil if result.is_a?(Hash) message = result[:errorMessage] result = result[:resultCode] end unless result.zero? message = [Net::LDAP.result2string(result), message].compact.join(": ") raise LdapError, message end - end - - def ensure_port(method) - if method == :ssl - URI::LDAPS::DEFAULT_PORT - else - URI::LDAP::DEFAULT_PORT - end - end - - def prepare_connection(options) - end - - def ensure_method(method) - method ||= "plain" - normalized_method = method.to_s.downcase.to_sym - return METHOD[normalized_method] if METHOD.has_key?(normalized_method) - - available_methods = METHOD.keys.collect {|m| m.inspect}.join(", ") - format = "%s is not one of the available connect methods: %s" - raise ConfigurationError, format % [method.inspect, available_methods] - end - - def sasl_bind(bind_dn, options={}) - sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms - sasl_mechanisms.each do |mechanism| - begin - normalized_mechanism = mechanism.downcase.gsub(/-/, '_') - sasl_bind_setup = "sasl_bind_setup_#{normalized_mechanism}" - next unless respond_to?(sasl_bind_setup, true) - initial_credential, challenge_response = send(sasl_bind_setup, bind_dn, options) + end - args = { - :method => :sasl, - :initial_credential => initial_credential, - :mechanism => mechanism, - :challenge_response => challenge_response, - } - - info = { - :name => "bind: SASL", :dn => bind_dn, :mechanism => mechanism, - } - puts info.inspect + def ensure_port(method) + if method == :ssl + URI::LDAPS::DEFAULT_PORT + else + URI::LDAP::DEFAULT_PORT + end + end - execute(:bind, args) - return true - - rescue Exception => e - puts e.message + def prepare_connection(options) + end + + def ensure_method(method) + method ||= "plain" + normalized_method = method.to_s.downcase.to_sym + return METHOD[normalized_method] if METHOD.has_key?(normalized_method) + + available_methods = METHOD.keys.collect {|m| m.inspect}.join(", ") + format = "%s is not one of the available connect methods: %s" + raise ConfigurationError, format % [method.inspect, available_methods] + end + + def sasl_bind(bind_dn, options={}) + sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms + sasl_mechanisms.each do |mechanism| + begin + normalized_mechanism = mechanism.downcase.gsub(/-/, '_') + sasl_bind_setup = "sasl_bind_setup_#{normalized_mechanism}" + next unless respond_to?(sasl_bind_setup, true) + initial_credential, challenge_response = send(sasl_bind_setup, bind_dn, options) + + args = { + :method => :sasl, + :initial_credential => initial_credential, + :mechanism => mechanism, + :challenge_response => challenge_response, + } + + info = { + :name => "bind: SASL", :dn => bind_dn, :mechanism => mechanism, + } + + execute(:bind, args) + return true + + rescue Exception => e + puts e.message + end end + false + end + + def sasl_bind_setup_digest_md5(bind_dn, options) + initial_credential = "" + challenge_response = Proc.new do |cred| + pref = SASL::Preferences.new :digest_uri => "ldap/#{@host}", :username => bind_dn, :has_password? => true, :password => options[:password]||@password + sasl = SASL.new("DIGEST-MD5", pref) + response = sasl.receive("challenge", cred) + response[1] end + [initial_credential, challenge_response] + end - false - end + def sasl_bind_setup_gss_spnego(bind_dn, options) + puts options.inspect + user,psw = [bind_dn, options[:password]||@password] + raise LdapError.new( "invalid binding information" ) unless (user && psw) - def sasl_bind_setup_digest_md5(bind_dn, options) - initial_credential = "" - challenge_response = Proc.new do |cred| - pref = SASL::Preferences.new :digest_uri => "ldap/#{@host}", :username => bind_dn, :has_password? => true, :password => options[:password]||@password - sasl = SASL.new("DIGEST-MD5", pref) - response = sasl.receive("challenge", cred) - response[1] + nego = proc {|challenge| + t2_msg = Net::NTLM::Message.parse( challenge ) + user, domain = user.split('\\').reverse + t2_msg.target_name = Net::NTLM::encode_utf16le(domain) if domain + t3_msg = t2_msg.response( {:user => user, :password => psw}, {:ntlmv2 => true} ) + t3_msg.serialize + } + [Net::NTLM::Message::Type1.new.serialize, nego] end - [initial_credential, challenge_response] - end - def sasl_bind_setup_gss_spnego(bind_dn, options) - puts options.inspect - user,psw = [bind_dn, options[:password]||@password] - raise LdapError.new( "invalid binding information" ) unless (user && psw) + def simple_bind(bind_dn, options={}) + args = { + :method => :simple, + :username => bind_dn, + :password => (options[:password]||@password).to_s, + } + begin + raise AuthenticationError if args[:password] == "" + execute(:bind, args) + true + rescue Exception + false + end + end - nego = proc {|challenge| - t2_msg = Net::NTLM::Message.parse( challenge ) - user, domain = user.split('\\').reverse - t2_msg.target_name = Net::NTLM::encode_utf16le(domain) if domain - t3_msg = t2_msg.response( {:user => user, :password => psw}, {:ntlmv2 => true} ) - t3_msg.serialize - } - [Net::NTLM::Message::Type1.new.serialize, nego] - end - - def simple_bind(bind_dn, options={}) - args = { - :method => :simple, - :username => bind_dn, - :password => (options[:password]||@password).to_s, - } - begin - execute(:bind, args) + def bind_as_anonymous(options={}) + execute(:bind, {:method => :anonymous}) true - rescue Exception - false end - end - def bind_as_anonymous(options={}) - execute(:bind, {:method => :anonymous}) - true - end - def construct_uri(host, port, ssl) - protocol = ssl ? "ldaps" : "ldap" - URI.parse("#{protocol}://#{host}:#{port}").to_s - end - def target - return nil if @uri.nil? - if @with_start_tls - "#{@uri}(StartTLS)" - else - @uri + def construct_uri(host, port, ssl) + protocol = ssl ? "ldaps" : "ldap" + URI.parse("#{protocol}://#{host}:#{port}").to_s end - end + + def target + return nil if @uri.nil? + if @with_start_tls + "#{@uri}(StartTLS)" + else + @uri + end + end end end end end