Skip to content

Commit

Permalink
Merge branch 'master' of github.com:webagecorp/omniauth-ldap into web…
Browse files Browse the repository at this point in the history
…agecorp-master

Conflicts:
	README.md
	spec/omniauth/strategies/ldap_spec.rb
  • Loading branch information
Ping Yu committed Dec 13, 2013
2 parents efa1abc + 7f66cff commit 7aa1386
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 7 deletions.
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -13,6 +13,10 @@ Use the LDAP strategy as a middleware in your application:
:uid => 'sAMAccountName',
:name_proc => Proc.new {|name| name.gsub(/@.*$/,'')},
:bind_dn => 'default_bind_dn',
# Or, alternatively:
#:filter => '(&(uid=%{username})(memberOf=cn=myapp-users,ou=groups,dc=example,dc=com))'
: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 :title, :name_proc, :bind_dn, and :password.
Expand All @@ -29,6 +33,9 @@ Allowed values of :method are: :plain, :ssl, :tls.
: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'.

:filter is the LDAP filter used to search the user entry. It can be used in place of :uid for more flexibility.
`%{username}` will be replaced by the user name processed by :name_proc.

:name_proc allows you to match the user name entered with the format of the :uid attributes.
For example, value of 'sAMAccountName' in AD contains only the windows user name. If your user prefers using
email to login, a name_proc as above will trim the email string down to just the windows login name.
Expand Down
15 changes: 10 additions & 5 deletions lib/omniauth-ldap/adaptor.rb
Expand Up @@ -13,9 +13,10 @@ 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, :allow_anonymous]
VALID_ADAPTER_CONFIGURATION_KEYS = [:host, :port, :method, :bind_dn, :password, :try_sasl, :sasl_mechanisms, :uid, :base, :allow_anonymous, :filter]

MUST_HAVE_KEYS = [:host, :port, :method, :uid, :base]
# A list of needed keys. Possible alternatives are specified using sub-lists.
MUST_HAVE_KEYS = [:host, :port, :method, [:uid, :filter], :base]

METHOD = {
:ssl => :simple_tls,
Expand All @@ -24,11 +25,15 @@ class ConnectionError < StandardError; end
}

attr_accessor :bind_dn, :password
attr_reader :connection, :uid, :base, :auth
attr_reader :connection, :uid, :base, :auth, :filter
def self.validate(configuration={})
message = []
MUST_HAVE_KEYS.each do |name|
message << name if configuration[name].nil?
MUST_HAVE_KEYS.each do |names|
names = [names].flatten
missing_keys = names.select{|name| configuration[name].nil?}
if missing_keys == names
message << names.join(' or ')
end
end
raise ArgumentError.new(message.join(",") +" MUST be provided") unless message.empty?
end
Expand Down
10 changes: 9 additions & 1 deletion lib/omniauth/strategies/ldap.rb
Expand Up @@ -39,7 +39,7 @@ def callback_phase

return fail!(:missing_credentials) if missing_credentials?
begin
@ldap_user_info = @adaptor.bind_as(:filter => Net::LDAP::Filter.eq(@adaptor.uid, @options[:name_proc].call(request['username'])),:size => 1, :password => request['password'])
@ldap_user_info = @adaptor.bind_as(:filter => filter(@adaptor), :size => 1, :password => request['password'])
return fail!(:invalid_credentials) if !@ldap_user_info

@user_info = self.class.map_user(@@config, @ldap_user_info)
Expand All @@ -49,6 +49,14 @@ def callback_phase
end
end

def filter adaptor
if adaptor.filter and !adaptor.filter.empty?
Net::LDAP::Filter.construct(adaptor.filter % {username: @options[:name_proc].call(request['username'])})
else
Net::LDAP::Filter.eq(adaptor.uid, @options[:name_proc].call(request['username']))
end
end

uid {
@user_info["uid"]
}
Expand Down
26 changes: 25 additions & 1 deletion spec/omniauth/strategies/ldap_spec.rb
Expand Up @@ -50,6 +50,7 @@ class MyLdapProvider < OmniAuth::Strategies::LDAP; end
describe 'post /auth/ldap/callback' do
before(:each) do
@adaptor = double(OmniAuth::LDAP::Adaptor, {:uid => 'ping'})
@adaptor.stub(:filter)
OmniAuth::LDAP::Adaptor.stub(:new).and_return(@adaptor)
end

Expand Down Expand Up @@ -104,6 +105,17 @@ class MyLdapProvider < OmniAuth::Strategies::LDAP; end
last_response.should be_redirect
last_response.headers['Location'].should =~ %r{invalid_credentials}
end
context 'and filter is set' do
it 'should bind with filter' do
@adaptor.stub(:filter).and_return('uid=%{username}')
Net::LDAP::Filter.should_receive(:construct).with('uid=ping')
post('/auth/ldap/callback', {:username => 'ping', :password => 'password'})

last_response.should be_redirect
last_response.headers['Location'].should =~ %r{invalid_credentials}
end
end

end

context "and communication with LDAP server caused an exception" do
Expand All @@ -125,6 +137,7 @@ class MyLdapProvider < OmniAuth::Strategies::LDAP; end
let(:auth_hash){ last_request.env['omniauth.auth'] }

before(:each) do
@adaptor.stub(:filter)
@adaptor.stub(:bind_as).and_return(Net::LDAP::Entry.from_single_ldif_string(
%Q{dn: cn=ping, dc=intridea, dc=com
mail: ping@intridea.com
Expand All @@ -144,14 +157,25 @@ class MyLdapProvider < OmniAuth::Strategies::LDAP; end
description: omniauth-ldap
}
))
post('/auth/ldap/callback', {:username => 'ping', :password => 'password'})
end

it 'should not redirect to error page' do
post('/auth/ldap/callback', {:username => 'ping', :password => 'password'})
last_response.should_not be_redirect
end

context 'and filter is set' do
it 'should bind with filter' do
@adaptor.stub(:filter).and_return('uid=%{username}')
Net::LDAP::Filter.should_receive(:construct).with('uid=ping')
post('/auth/ldap/callback', {:username => 'ping', :password => 'password'})

last_response.should_not be_redirect
end
end

it 'should map user info to Auth Hash' do
post('/auth/ldap/callback', {:username => 'ping', :password => 'password'})
auth_hash.uid.should == 'cn=ping, dc=intridea, dc=com'
auth_hash.info.email.should == 'ping@intridea.com'
auth_hash.info.first_name.should == 'Ping'
Expand Down

0 comments on commit 7aa1386

Please sign in to comment.