From 0ed8037344866cb38864ace68f293cbcf89dfc7c Mon Sep 17 00:00:00 2001 From: Ping Yu Date: Sun, 2 Jan 2011 11:55:11 -0600 Subject: [PATCH] adding user lookup before the first, and use the DN from the lookup result in the final binding. --- oa-enterprise/README.rdoc | 10 +- oa-enterprise/lib/omniauth/strategies/ldap.rb | 17 +-- .../lib/omniauth/strategies/ldap/adaptor.rb | 133 +++++++++--------- 3 files changed, 84 insertions(+), 76 deletions(-) diff --git a/oa-enterprise/README.rdoc b/oa-enterprise/README.rdoc index f3330be..2854bdc 100644 --- a/oa-enterprise/README.rdoc +++ b/oa-enterprise/README.rdoc @@ -36,10 +36,18 @@ Use the LDAP strategy as a middleware in your applicaiton: :base => 'dc=intridea, dc=com', :uid => 'sAMAccountName', :name_proc => Proc.new {|name| name.gsub(/@.*$/,'')} + :bind_dn => 'default_bind_dn' + :password => 'password' -All of the listed options are required, with the exception of :name_proc. +All of the listed options are required, with the exception of :name_proc, :bind_dn, and :password Allowed values of :method are: :plain, :ssl, :tls. +:bind_dn and :password are used to perform the initial binding if user lookup is +needed. If the user lookup returns result, the DN attribute from the result set is used +to perform the final binding. This is needed only when the LDAP server requires +DN to be used for binding and you may only want user to using email or username +in the login form. + :uid is the LDAP attribute name for the user name in the login form. typically AD would be 'sAMAccountName' or 'UserPrincipalName', while OpenLDAP is 'uid'. You can also use 'dn', if your user choose the put in the dn in the login form diff --git a/oa-enterprise/lib/omniauth/strategies/ldap.rb b/oa-enterprise/lib/omniauth/strategies/ldap.rb index 7b293f3..647e837 100644 --- a/oa-enterprise/lib/omniauth/strategies/ldap.rb +++ b/oa-enterprise/lib/omniauth/strategies/ldap.rb @@ -52,15 +52,16 @@ module OmniAuth def perform begin - bind_dn = "#{@adaptor.uid}=#{request.POST['username']}" - bind_dn << ",#{@adaptor.base}" unless @adaptor.base == '' + @ldap_user_info = {} + (@adaptor.bind unless @adaptor.bound?) rescue puts "failed to bind with the default credentials" + @ldap_user_info = @adaptor.search(:filter => Net::LDAP::Filter.eq(@adaptor.uid, @name_proc.call(request.POST['username'])),:limit => 1) if @adaptor.bound? + bind_dn = request.POST['username'] + bind_dn = @ldap_user_info[:dn].to_a.first if @ldap_user_info[:dn] + @adaptor.bind(:bind_dn => bind_dn, :password => request.POST['password']) + @ldap_user_info = @adaptor.search(:filter => Net::LDAP::Filter.eq(@adaptor.uid, @name_proc.call(request.POST['username'])),:limit => 1) if @ldap_user_info.empty? + @user_info = self.class.map_user(@@config, @ldap_user_info) - @adaptor.bind(:bind_dn => bind_dn, :password => request.POST['password']) - @ldap_user_info = @adaptor.search(:filter => Net::LDAP::Filter.eq(@adaptor.uid, @name_proc.call(request.POST['username'])),:limit => 1) - @user_info = self.class.map_user(@@config, @ldap_user_info) - - @env['omniauth.auth'] = auth_hash - #@env['REQUEST_METHOD'] = 'GET' + @env['omniauth.auth'] = auth_hash @env['PATH_INFO'] = "#{OmniAuth.config.path_prefix}/#{name}/callback" call_app! diff --git a/oa-enterprise/lib/omniauth/strategies/ldap/adaptor.rb b/oa-enterprise/lib/omniauth/strategies/ldap/adaptor.rb index 21f0164..bf0bbca 100644 --- a/oa-enterprise/lib/omniauth/strategies/ldap/adaptor.rb +++ b/oa-enterprise/lib/omniauth/strategies/ldap/adaptor.rb @@ -25,7 +25,7 @@ module OmniAuth :plain => nil } - attr_accessor :bind_dn, :password + attr_accessor :bind_dn, :password attr_reader :connection, :uid, :base def initialize(configuration={}) @@ -46,9 +46,8 @@ module OmniAuth def connect(options={}) host = options[:host] || @host - method = options[:method] || @method || :plain + method = ensure_method(options[:method] || @method || :plain) port = options[:port] || @port || ensure_port(method) - method = ensure_method(method) @disconnected = false @bound = false @bind_tried = false @@ -179,65 +178,65 @@ module OmniAuth 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 + + 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 + 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 + execute(:bind, args) + return true + + rescue Exception => e + puts e.message 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 - 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) + false + 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] + 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 = { @@ -249,19 +248,19 @@ module OmniAuth true end - def construct_uri(host, port, ssl) - protocol = ssl ? "ldaps" : "ldap" - URI.parse("#{protocol}://#{host}:#{port}").to_s + 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 - - def target - return nil if @uri.nil? - if @with_start_tls - "#{@uri}(StartTLS)" - else - @uri - end - end + end end end end