lib/omniauth/strategies/ldap/adaptor.rb in oa-enterprise-0.1.6 vs lib/omniauth/strategies/ldap/adaptor.rb in oa-enterprise-0.2.0.beta1

- old
+ new

@@ -1,247 +1,268 @@ #this code boughts pieces from activeldap and net-ldap + require 'rack' require 'net/ldap' require 'net/ntlm' require 'uri' + module OmniAuth module Strategies class LDAP class Adaptor - 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] - 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 - def initialize(configuration={}) - @connection = nil - @disconnected = false - @bound = false - @configuration = configuration.dup - @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 + 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] + + 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 + + def initialize(configuration={}) + @connection = nil + @disconnected = false + @bound = false + @configuration = configuration.dup + @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 - def connect(options={}) - host = options[:host] || @host - method = options[:method] || @method || :plain - port = options[:port] || @port || ensure_port(method) - method = ensure_method(method) - @disconnected = false - @bound = false - @bind_tried = false + def connect(options={}) + host = options[:host] || @host + method = options[:method] || @method || :plain + port = options[:port] || @port || ensure_port(method) + method = ensure_method(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 + + @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 unbind(options={}) + @connection.close # Net::LDAP doesn't implement unbind. + end - def bind(options={}) - connect(options) unless connecting? - begin - @bind_tried = true + 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 - bind_dn = (options[:bind_dn] || @bind_dn).to_s - try_sasl = options.has_key?(:try_sasl) ? options[:try_sasl] : @try_sasl - - # Rough bind loop: - # Attempt 1: SASL if available - # Attempt 2: SIMPLE with credentials if password block - if try_sasl and sasl_bind(bind_dn, options) - puts "bind with sasl" - elsif simple_bind(bind_dn, options) - puts "bind with simple" - 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 + # Rough bind loop: + # Attempt 1: SASL if available + # Attempt 2: SIMPLE with credentials if password block + if try_sasl and sasl_bind(bind_dn, options) + puts "bind with sasl" + elsif simple_bind(bind_dn, options) + puts "bind with simple" + 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 disconnect!(options={}) + unbind(options) + @connection = @uri = @with_start_tls = nil + @disconnected = true + end - def rebind(options={}) - unbind(options) if bound? - connect(options) - end + def rebind(options={}) + unbind(options) if bound? + connect(options) + end - def connecting? - !@connection.nil? and !@disconnected - end + def connecting? + !@connection.nil? and !@disconnected + end - def bound? - connecting? and @bound - end + def bound? + connecting? and @bound + end - def search(options={}, &block) - base = options[:base] - filter = options[:filter] - limit = options[:limit] + def search(options={}, &block) + base = options[:base] + filter = options[:filter] + limit = options[:limit] - args = { - :base => @base, - :filter => filter, - :size => limit - } - puts args.inspect + args = { + :base => @base, + :filter => filter, + :size => limit + } + + puts args.inspect + attributes = {} execute(:search, args) do |entry| entry.attribute_names.each do |name| attributes[name] = entry[name] end - end - attributes - end - private - def execute(method, *args, &block) - 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 + end + attributes + end + + private + + def execute(method, *args, &block) + 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 + def ensure_port(method) + if method == :ssl + URI::LDAPS::DEFAULT_PORT + else + URI::LDAP::DEFAULT_PORT end + end - def prepare_connection(options) - 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 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, - } - puts info.inspect - 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] + 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, + } + puts info.inspect + + execute(:bind, args) + return true + + rescue Exception => e + puts e.message + end + end + + false 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) - 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 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 + + 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) + + 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, - } - execute(:bind, args) - true - end + def simple_bind(bind_dn, options={}) + args = { + :method => :simple, + :username => bind_dn, + :password => options[:password]||@password, + } + execute(:bind, args) + 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 - end - 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 + end + end end end end -end \ No newline at end of file +end