added user search and user info retrieve for LDAP strategy. updated the

reamde as well.
This commit is contained in:
Ping Yu 2010-10-02 21:58:14 -05:00
parent bfc4eb9c86
commit cd4af04646
3 changed files with 67 additions and 52 deletions

View File

@ -11,10 +11,10 @@ To get just enterprise functionality:
For the full auth suite:
gem install omniauth
CAS strategy
== Stand-Alone Example
CAS strategy
Use the strategy as a middleware in your application:
require 'omniauth/enterprise'
@ -42,13 +42,16 @@ If CAS is one of several authentication strategies, use the OmniAuth Builder:
LDAP strategy
use OmniAuth::Strategies::LDAP, :host => '10.101.10.1', :port => 389, :method => :plain, :try_sasl => true, :sasl_mechanisms => "GSS-SPNEGO"
use OmniAuth::Strategies::LDAP, :host => '10.101.10.1', :port => 389, :method => :plain, :base => 'dc=intridea, dc=com', :uid => 'sAMAccountName', :try_sasl => true, :sasl_mechanisms => "GSS-SPNEGO"
or
use OmniAuth::Builder do
provider :LDAP, :host => '10.101.10.1', :port => 389, :method => :plain, :try_sasl => true, :sasl_mechanisms => "GSS-SPNEGO"
provider :LDAP, :host => '10.101.10.1', :port => 389, :method => :plain, :base => 'dc=intridea, dc=com', :uid => 'sAMAccountName', :try_sasl => true, :sasl_mechanisms => "GSS-SPNEGO"
end
LDAP server's :host and :port are required, :method is also a required field, and allowed values are :plain, :ssl, and :tls.
:base is required, it is the distinguish name (DN) for your organization, all users should be searchable under this base.
:uid is required, it is the LDAP attribute name for the user name in the login form. typically AD would be 'sAMAccountName' or 'UniquePersonalIdentifier', while
OpenLDAP is 'uid'. You can also use 'dn', if your user choose the put in the dn in the login form (but usually is too long for user to remember or know).
:try_sasl and :sasl_mechanisms are optional, use it to initial SASL connection to server. mechanism supported are DIGEST-MD5 and GSS-SPNEGO.
Then simply direct users to '/auth/ldap' to have them authenticated via your company's LDAP server.

View File

@ -33,14 +33,17 @@ module OmniAuth
def perform
begin
@adaptor.bind(:bind_dn => request.POST['username'], :password => request.POST['password'])
rescue
@user_info = @adaptor.search(:filter => Net::LDAP::Filter.eq(@adaptor.uid, request.POST['username']),:limit => 1)
puts @user_info
request.POST['auth'] = auth_hash
@env['REQUEST_METHOD'] = 'GET'
@env['PATH_INFO'] = "#{OmniAuth.config.path_prefix}/#{name}/callback"
@app.call(@env)
rescue Exception => e
puts e.message
fail!(:invalid_credentials)
end
request.POST['auth'] = auth_hash
@env['REQUEST_METHOD'] = 'GET'
@env['PATH_INFO'] = "#{OmniAuth.config.path_prefix}/#{name}/callback"
@app.call(@env)
end
def callback_phase
@ -49,7 +52,8 @@ module OmniAuth
def auth_hash
OmniAuth::Utils.deep_merge(super, {
'uid' => request.POST['username']
'uid' => @user_info["dn"],
'user_info' => @user_info
})
end

View File

@ -1,3 +1,4 @@
#this code boughts pieces from activeldap and net-ldap
require 'rack'
require 'net/ldap'
require 'net/ntlm'
@ -11,15 +12,15 @@ module OmniAuth
class AuthenticationError < StandardError; end
class ConnectionError < StandardError; end
VALID_ADAPTER_CONFIGURATION_KEYS = [:host, :port, :method, :bind_dn, :password,
:try_sasl, :sasl_mechanisms, :sasl_quiet]
MUST_HAVE_KEYS = [:host, :port, :method]
:try_sasl, :sasl_mechanisms, :uid, :base]
MUST_HAVE_KEYS = [:host, :port, :method, :uid, :base]
METHOD = {
:ssl => :simple_tls,
:tls => :start_tls,
:plain => nil
}
attr_reader :bind_dn
attr_accessor :bind_dn, :password
attr_reader :connection, :uid, :base
def initialize(configuration={})
@connection = nil
@disconnected = false
@ -75,7 +76,6 @@ module OmniAuth
# 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 (or pwblock returns '')
if try_sasl and sasl_bind(bind_dn, options)
puts "bind with sasl"
elsif simple_bind(bind_dn, options)
@ -110,8 +110,27 @@ module OmniAuth
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
attributes = {}
execute(:search, args) do |entry|
puts entry.inspect
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)
@ -137,11 +156,6 @@ module OmniAuth
def prepare_connection(options)
end
def need_credential_sasl_mechanism?(mechanism)
not %(GSSAPI EXTERNAL ANONYMOUS).include?(mechanism)
end
def ensure_method(method)
method ||= "plain"
normalized_method = method.to_s.downcase.to_sym
@ -153,36 +167,30 @@ module OmniAuth
end
def sasl_bind(bind_dn, options={})
if options.has_key?(:sasl_quiet)
sasl_quiet = options[:sasl_quiet]
else
sasl_quiet = @sasl_quiet
end
sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms
begin
sasl_mechanisms.each do |mechanism|
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
return true if execute(:bind, args)
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
rescue Exception => e
puts e.message
false
end
false
end
@ -228,7 +236,7 @@ module OmniAuth
:maxbuf => "65536",
"digest-uri" => uri.inspect,
}
a1 = "#{bind_dn}:#{realm}:#{@password}"
a1 = "#{bind_dn}:#{realm}:#{options[:password]||@password}"
a1 = "#{Digest::MD5.digest(a1)}:#{nonce}:#{cnonce}"
ha1 = Digest::MD5.hexdigest(a1)
a2 = "AUTHENTICATE:#{uri}"
@ -244,7 +252,7 @@ module OmniAuth
end
def sasl_bind_setup_gss_spnego(bind_dn, options)
puts options.inspect
user,psw = [bind_dn, @password]
user,psw = [bind_dn, options[:password]||@password]
raise LdapError.new( "invalid binding information" ) unless (user && psw)
nego = proc {|challenge|
@ -261,7 +269,7 @@ module OmniAuth
args = {
:method => :simple,
:username => bind_dn,
:password => @password,
:password => options[:password]||@password,
}
execute(:bind, args)
true