Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
adding user lookup before the first, and use the DN from
the lookup result in the final binding.
  • Loading branch information
Ping Yu committed Jan 2, 2011
1 parent 61eb507 commit 0ed8037
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 75 deletions.
10 changes: 9 additions & 1 deletion oa-enterprise/README.rdoc
Expand Up @@ -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
Expand Down
17 changes: 9 additions & 8 deletions oa-enterprise/lib/omniauth/strategies/ldap.rb
Expand Up @@ -52,15 +52,16 @@ def get_credentials

def perform
begin
bind_dn = "#{@adaptor.uid}=#{request.POST['username']}"
bind_dn << ",#{@adaptor.base}" unless @adaptor.base == ''

This comment has been minimized.

Copy link
@holman

holman Jan 2, 2011

Contributor

Doesn't this break binding against the :base parameter?

This comment has been minimized.

Copy link
@pyu10055

pyu10055 Jan 2, 2011

Contributor

usually combining the uid and the base would not be in the correct form of DN. For example if 'uid' is 'email'. DN becomes 'email=abc@abcd.com, dc=abc, dc=com', which is the same as DN. uid should be used to simplify user's input and allow user lookup, and the base should be use for search. Using the previous example: search email=abc@abcd.com under 'dc=abc, dc=com' tree.
basically, these two line make guess of the dn, but it will not be correct for most of time. Since we are doing user dn lookup in the follow code, these two line are not needed anymore.

@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!
Expand Down
131 changes: 65 additions & 66 deletions oa-enterprise/lib/omniauth/strategies/ldap/adaptor.rb
Expand Up @@ -25,7 +25,7 @@ class ConnectionError < StandardError; end
:plain => nil
}

attr_accessor :bind_dn, :password
attr_accessor :bind_dn, :password
attr_reader :connection, :uid, :base

def initialize(configuration={})
Expand All @@ -46,9 +46,8 @@ def initialize(configuration={})

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
Expand Down Expand Up @@ -179,65 +178,65 @@ def ensure_method(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

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
end

false
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]
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)
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
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 = {
Expand All @@ -249,19 +248,19 @@ def simple_bind(bind_dn, options={})
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
Expand Down

0 comments on commit 0ed8037

Please sign in to comment.