adding user lookup before the first, and use the DN from

the lookup result in the final binding.
This commit is contained in:
Ping Yu 2011-01-02 11:55:11 -06:00
parent 61eb50782b
commit 0ed8037344
3 changed files with 84 additions and 76 deletions

View File

@ -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

View File

@ -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!

View File

@ -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