diff --git a/README.md b/README.md index 60d7e77..f1e601f 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,10 @@ Allowed values of :method are: :plain, :ssl, :tls. Use them to initialize a SASL connection to server. If you are not familiar with these authentication methods, please just avoid them. +:use_user_credential Allow you to use current login user's info as LDAP credential. true/false. + If you don't have a default credential (:bind_dn and :password) for ldap config, + You can fake it by using the current login user's name and password. + Direct users to '/auth/ldap' to have them authenticated via your company's LDAP server. diff --git a/lib/omniauth-ldap/adaptor.rb b/lib/omniauth-ldap/adaptor.rb index 5c769b2..421fe9e 100644 --- a/lib/omniauth-ldap/adaptor.rb +++ b/lib/omniauth-ldap/adaptor.rb @@ -14,7 +14,7 @@ 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, :use_user_credential] MUST_HAVE_KEYS = [:host, :port, :method, :uid, :base] @@ -25,6 +25,7 @@ class ConnectionError < StandardError; end } attr_accessor :bind_dn, :password + attr_accessor :use_user_credential attr_reader :connection, :uid, :base, :auth def self.validate(configuration={}) message = [] @@ -41,6 +42,11 @@ def initialize(configuration={}) VALID_ADAPTER_CONFIGURATION_KEYS.each do |name| instance_variable_set("@#{name}", @configuration[name]) end + @uri = construct_uri(@host, @port, @method != :plain) # seems not used anywhere. + reset_connection + end + + def reset_connection method = ensure_method(@method) config = { :host => @host, @@ -48,19 +54,24 @@ def initialize(configuration={}) :encryption => method, :base => @base } - @uri = construct_uri(@host, @port, @method != :plain) - @bind_method = @try_sasl ? :sasl : (@allow_anonymous||!@bind_dn||!@password ? :anonymous : :simple) - - @auth = sasl_auths({:username => @bind_dn, :password => @password}).first if @bind_method == :sasl - @auth ||= { :method => @bind_method, + if @bind_method == :sasl + @auth = sasl_auths({:username => @bind_dn, :password => @password}).first if @bind_method == :sasl + else + @auth = { :method => @bind_method, :username => @bind_dn, :password => @password } + end config[:auth] = @auth @connection = Net::LDAP.new(config) end + + # convert 'dc=intridea, dc=com' to 'intridea.com' + def base_to_host(str) + str.split(',').map{|x| x.split('=').last}.join('.') + end #:base => "dc=yourcompany, dc=com", # :filter => "(mail=#{user})", diff --git a/lib/omniauth/strategies/ldap.rb b/lib/omniauth/strategies/ldap.rb index 45fb1d1..a178b28 100644 --- a/lib/omniauth/strategies/ldap.rb +++ b/lib/omniauth/strategies/ldap.rb @@ -39,6 +39,13 @@ def callback_phase @adaptor = OmniAuth::LDAP::Adaptor.new @options raise MissingCredentialsError.new("Missing login credentials") if request['username'].nil? || request['password'].nil? + + if @adaptor.use_user_credential + @adaptor.bind_dn = request['username'] + '@' + @adaptor.base_to_host(@adaptor.base) + @adaptor.password = request['password'] + @adaptor.reset_connection + end + 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']) return fail!(:invalid_credentials) if !@ldap_user_info diff --git a/spec/omniauth-ldap/adaptor_spec.rb b/spec/omniauth-ldap/adaptor_spec.rb index b205c57..96dce1b 100644 --- a/spec/omniauth-ldap/adaptor_spec.rb +++ b/spec/omniauth-ldap/adaptor_spec.rb @@ -49,6 +49,14 @@ adaptor.connection.instance_variable_get('@auth')[:initial_credential].should =~ /^NTLMSSP/ adaptor.connection.instance_variable_get('@auth')[:challenge_response].should_not be_nil end + it 'should reset connection' do + adaptor = OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'plain', base: 'dc=intridea, dc=com', port: 389, uid: 'sAMAccountName'}) + adaptor.connection.instance_variable_get('@auth').should == {:method => :anonymous, :username => nil, :password => nil} + adaptor.bind_dn ='someone' + adaptor.password='secret' + adaptor.reset_connection + adaptor.connection.instance_variable_get('@auth').should == {:method => :simple, :username => 'someone', :password => 'secret'} + end end describe 'bind_as' do diff --git a/spec/omniauth/strategies/ldap_spec.rb b/spec/omniauth/strategies/ldap_spec.rb index 2a39f79..116020d 100644 --- a/spec/omniauth/strategies/ldap_spec.rb +++ b/spec/omniauth/strategies/ldap_spec.rb @@ -55,6 +55,7 @@ def session context 'failure' do before(:each) do @adaptor.stub(:bind_as).and_return(false) + @adaptor.stub(:use_user_credential).and_return(false) end it 'should raise MissingCredentialsError' do lambda{post('/auth/ldap/callback', {})}.should raise_error OmniAuth::Strategies::LDAP::MissingCredentialsError @@ -75,6 +76,7 @@ def session context 'success' do let(:auth_hash){ last_request.env['omniauth.auth'] } before(:each) do + @adaptor.stub(:use_user_credential).and_return(false) @adaptor.stub(:bind_as).and_return({:dn => ['cn=ping, dc=intridea, dc=com'], :mail => ['ping@intridea.com'], :givenname => ['Ping'], :sn => ['Yu'], :telephonenumber => ['555-555-5555'], :mobile => ['444-444-4444'], :uid => ['ping'], :title => ['dev'], :address =>[ 'k street'], :l => ['Washington'], :st => ['DC'], :co => ["U.S.A"], :postofficebox => ['20001'], :wwwhomepage => ['www.intridea.com'], @@ -100,5 +102,23 @@ def session auth_hash.info.description.should == 'omniauth-ldap' end end + + context 'use user credential' do + let(:auth_hash){ last_request.env['omniauth.auth'] } + before(:each) do + @adaptor = mock(OmniAuth::LDAP::Adaptor, {:uid => 'sAMAccountName', + :base => 'dc=intridea, dc=com', + :use_user_credential => true}) + OmniAuth::LDAP::Adaptor.stub(:new).and_return(@adaptor) + end + + it 'should reset connection' do + @adaptor.stub(:base_to_host).and_return('intridea.com') + @adaptor.should_receive(:bind_dn=).with('ping@intridea.com') + @adaptor.should_receive(:password=).with('password') + @adaptor.should_receive(:reset_connection) + post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) + end + end end end