From 9d632a84fdafa132c291e51c4db56271853c70d3 Mon Sep 17 00:00:00 2001 From: Rashit Azizbaev Date: Wed, 4 Jul 2012 02:07:15 +0400 Subject: [PATCH 01/18] Added Net::LDAP::Entry object to strategies test --- spec/omniauth/strategies/ldap_spec.rb | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/spec/omniauth/strategies/ldap_spec.rb b/spec/omniauth/strategies/ldap_spec.rb index 2a39f79..2090c48 100644 --- a/spec/omniauth/strategies/ldap_spec.rb +++ b/spec/omniauth/strategies/ldap_spec.rb @@ -75,10 +75,25 @@ def session context 'success' do let(:auth_hash){ last_request.env['omniauth.auth'] } before(:each) do - @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'], - :jpegphoto => ['http://www.intridea.com/ping.jpg'], :description => ['omniauth-ldap']}) + @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 +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 +jpegphoto: http://www.intridea.com/ping.jpg +description: omniauth-ldap +} + )) post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) end From b524feb63048591a20de0f70c2d597f0b9d6a156 Mon Sep 17 00:00:00 2001 From: Rashit Azizbaev Date: Wed, 4 Jul 2012 02:11:21 +0400 Subject: [PATCH 02/18] Added respond_to? check to user mapping function Net::LDAP::Entry returns empty array if key not exists, so we should check for key with respond_to? --- lib/omniauth/strategies/ldap.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/omniauth/strategies/ldap.rb b/lib/omniauth/strategies/ldap.rb index 45fb1d1..ac30e4c 100644 --- a/lib/omniauth/strategies/ldap.rb +++ b/lib/omniauth/strategies/ldap.rb @@ -65,14 +65,14 @@ def self.map_user(mapper, object) mapper.each do |key, value| case value when String - user[key] = object[value.downcase.to_sym].first if object[value.downcase.to_sym] + user[key] = object[value.downcase.to_sym].first if object.respond_to? value.downcase.to_sym when Array - value.each {|v| (user[key] = object[v.downcase.to_sym].first; break;) if object[v.downcase.to_sym]} + value.each {|v| (user[key] = object[v.downcase.to_sym].first; break;) if object.respond_to? v.downcase.to_sym} when Hash value.map do |key1, value1| pattern = key1.dup value1.each_with_index do |v,i| - part = ''; v.collect(&:downcase).collect(&:to_sym).each {|v1| (part = object[v1].first; break;) if object[v1]} + part = ''; v.collect(&:downcase).collect(&:to_sym).each {|v1| (part = object[v1].first; break;) if object.respond_to? v1} pattern.gsub!("%#{i}",part||'') end user[key] = pattern From 77eb81a45e9194eb8634815f8cd636cac015e25a Mon Sep 17 00:00:00 2001 From: Dmitriy Dzema Date: Thu, 27 Sep 2012 17:31:23 +0800 Subject: [PATCH 03/18] Use fail!(:missing_credentials) instead of raising custom exception When exception is raised from middleware host application can not handle it. This is why OmniAuth uses #failure! which sends message to application so it can do something about it --- lib/omniauth/strategies/ldap.rb | 3 +-- spec/omniauth/strategies/ldap_spec.rb | 15 +++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/omniauth/strategies/ldap.rb b/lib/omniauth/strategies/ldap.rb index 45fb1d1..d79da3e 100644 --- a/lib/omniauth/strategies/ldap.rb +++ b/lib/omniauth/strategies/ldap.rb @@ -3,7 +3,6 @@ module OmniAuth module Strategies class LDAP - class MissingCredentialsError < StandardError; end include OmniAuth::Strategy @@config = { 'name' => 'cn', @@ -38,7 +37,7 @@ def request_phase def callback_phase @adaptor = OmniAuth::LDAP::Adaptor.new @options - raise MissingCredentialsError.new("Missing login credentials") if request['username'].nil? || request['password'].nil? + return fail!(:missing_credentials) if request['username'].nil? || request['password'].nil? 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/strategies/ldap_spec.rb b/spec/omniauth/strategies/ldap_spec.rb index 2a39f79..787c9d9 100644 --- a/spec/omniauth/strategies/ldap_spec.rb +++ b/spec/omniauth/strategies/ldap_spec.rb @@ -56,10 +56,13 @@ def session before(:each) do @adaptor.stub(:bind_as).and_return(false) end - it 'should raise MissingCredentialsError' do - lambda{post('/auth/ldap/callback', {})}.should raise_error OmniAuth::Strategies::LDAP::MissingCredentialsError + it 'should redirect to error page when username is not present' do + post('/auth/ldap/callback', {}) + + last_response.should be_redirect + last_response.headers['Location'].should =~ %r{missing_credentials} end - it 'should redirect to error page' do + it 'should redirect to error page' do post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) last_response.should be_redirect last_response.headers['Location'].should =~ %r{invalid_credentials} @@ -81,9 +84,9 @@ def session :jpegphoto => ['http://www.intridea.com/ping.jpg'], :description => ['omniauth-ldap']}) post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) end - - it 'should raise MissingCredentialsError' do - should_not raise_error OmniAuth::Strategies::LDAP::MissingCredentialsError + + it 'should not redirect to error page' do + last_response.should_not be_redirect end it 'should map user info' do auth_hash.uid.should == 'cn=ping, dc=intridea, dc=com' From 9a73d14c5f81c118f3f4c521052c03ebfe588ad3 Mon Sep 17 00:00:00 2001 From: Dmitriy Dzema Date: Thu, 27 Sep 2012 17:41:53 +0800 Subject: [PATCH 04/18] Unify formatting in spec files, remove trailing whitespace --- spec/omniauth-ldap/adaptor_spec.rb | 39 +++++++++++++++------------ spec/omniauth/strategies/ldap_spec.rb | 34 +++++++++++++---------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/spec/omniauth-ldap/adaptor_spec.rb b/spec/omniauth-ldap/adaptor_spec.rb index b205c57..e6a304f 100644 --- a/spec/omniauth-ldap/adaptor_spec.rb +++ b/spec/omniauth-ldap/adaptor_spec.rb @@ -2,13 +2,13 @@ describe "OmniAuth::LDAP::Adaptor" do describe 'initialize' do - it 'should throw exception when must have field is not set' do #[:host, :port, :method, :bind_dn] - lambda { OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'plain'})}.should raise_error(ArgumentError) + lambda { OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'plain'})}.should raise_error(ArgumentError) end + it 'should throw exception when method is not supported' do - lambda { OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'myplain', uid: 'uid', port: 389, base: 'dc=com'})}.should raise_error(OmniAuth::LDAP::Adaptor::ConfigurationError) + lambda { OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'myplain', uid: 'uid', port: 389, base: 'dc=com'})}.should raise_error(OmniAuth::LDAP::Adaptor::ConfigurationError) end it 'should setup ldap connection with anonymous' do @@ -17,54 +17,59 @@ adaptor.connection.host.should == '192.168.1.145' adaptor.connection.port.should == 389 adaptor.connection.base.should == 'dc=intridea, dc=com' - adaptor.connection.instance_variable_get('@auth').should == {:method => :anonymous, :username => nil, :password => nil} + adaptor.connection.instance_variable_get('@auth').should == {:method => :anonymous, :username => nil, :password => nil} end + it 'should setup ldap connection with simple' do adaptor = OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'plain', base: 'dc=intridea, dc=com', port: 389, uid: 'sAMAccountName', bind_dn: 'bind_dn', password: 'password'}) adaptor.connection.should_not == nil adaptor.connection.host.should == '192.168.1.145' adaptor.connection.port.should == 389 adaptor.connection.base.should == 'dc=intridea, dc=com' - adaptor.connection.instance_variable_get('@auth').should == {:method => :simple, :username => 'bind_dn', :password => 'password'} - end + adaptor.connection.instance_variable_get('@auth').should == {:method => :simple, :username => 'bind_dn', :password => 'password'} + end + it 'should setup ldap connection with sasl-md5' do adaptor = OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'plain', base: 'dc=intridea, dc=com', port: 389, uid: 'sAMAccountName', try_sasl: true, sasl_mechanisms: ["DIGEST-MD5"], bind_dn: 'bind_dn', password: 'password'}) adaptor.connection.should_not == nil adaptor.connection.host.should == '192.168.1.145' adaptor.connection.port.should == 389 adaptor.connection.base.should == 'dc=intridea, dc=com' - adaptor.connection.instance_variable_get('@auth')[:method].should == :sasl - adaptor.connection.instance_variable_get('@auth')[:mechanism].should == 'DIGEST-MD5' - adaptor.connection.instance_variable_get('@auth')[:initial_credential].should == '' - adaptor.connection.instance_variable_get('@auth')[:challenge_response].should_not be_nil + adaptor.connection.instance_variable_get('@auth')[:method].should == :sasl + adaptor.connection.instance_variable_get('@auth')[:mechanism].should == 'DIGEST-MD5' + adaptor.connection.instance_variable_get('@auth')[:initial_credential].should == '' + adaptor.connection.instance_variable_get('@auth')[:challenge_response].should_not be_nil end + it 'should setup ldap connection with sasl-gss' do adaptor = OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'plain', base: 'dc=intridea, dc=com', port: 389, uid: 'sAMAccountName', try_sasl: true, sasl_mechanisms: ["GSS-SPNEGO"], bind_dn: 'bind_dn', password: 'password'}) adaptor.connection.should_not == nil adaptor.connection.host.should == '192.168.1.145' adaptor.connection.port.should == 389 adaptor.connection.base.should == 'dc=intridea, dc=com' - adaptor.connection.instance_variable_get('@auth')[:method].should == :sasl - adaptor.connection.instance_variable_get('@auth')[:mechanism].should == 'GSS-SPNEGO' - adaptor.connection.instance_variable_get('@auth')[:initial_credential].should =~ /^NTLMSSP/ - adaptor.connection.instance_variable_get('@auth')[:challenge_response].should_not be_nil + adaptor.connection.instance_variable_get('@auth')[:method].should == :sasl + adaptor.connection.instance_variable_get('@auth')[:mechanism].should == 'GSS-SPNEGO' + adaptor.connection.instance_variable_get('@auth')[:initial_credential].should =~ /^NTLMSSP/ + adaptor.connection.instance_variable_get('@auth')[:challenge_response].should_not be_nil end end - + describe 'bind_as' do let(:args) { {:filter => Net::LDAP::Filter.eq('sAMAccountName', 'username'), :password => 'password', :size => 1} } let(:rs) { Struct.new(:dn).new('new dn') } + it 'should bind simple' do adaptor = OmniAuth::LDAP::Adaptor.new({host: "192.168.1.126", method: 'plain', base: 'dc=score, dc=local', port: 389, uid: 'sAMAccountName', bind_dn: 'bind_dn', password: 'password'}) adaptor.connection.should_receive(:open).and_yield(adaptor.connection) - adaptor.connection.should_receive(:search).with(args).and_return([rs]) + adaptor.connection.should_receive(:search).with(args).and_return([rs]) adaptor.connection.should_receive(:bind).with({:username => 'new dn', :password => args[:password], :method => :simple}).and_return(true) adaptor.bind_as(args).should == rs end + it 'should bind sasl' do adaptor = OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'plain', base: 'dc=intridea, dc=com', port: 389, uid: 'sAMAccountName', try_sasl: true, sasl_mechanisms: ["GSS-SPNEGO"], bind_dn: 'bind_dn', password: 'password'}) adaptor.connection.should_receive(:open).and_yield(adaptor.connection) - adaptor.connection.should_receive(:search).with(args).and_return([rs]) + adaptor.connection.should_receive(:search).with(args).and_return([rs]) adaptor.connection.should_receive(:bind).and_return(true) adaptor.bind_as(args).should == rs end diff --git a/spec/omniauth/strategies/ldap_spec.rb b/spec/omniauth/strategies/ldap_spec.rb index 787c9d9..22a4a69 100644 --- a/spec/omniauth/strategies/ldap_spec.rb +++ b/spec/omniauth/strategies/ldap_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' describe "OmniAuth::Strategies::LDAP" do - # :title => "My LDAP", + # :title => "My LDAP", # :host => '10.101.10.1', # :port => 389, # :method => :plain, @@ -8,12 +8,13 @@ # :uid => 'sAMAccountName', # :name_proc => Proc.new {|name| name.gsub(/@.*$/,'')} # :bind_dn => 'default_bind_dn' - # :password => 'password' - class MyLdapProvider < OmniAuth::Strategies::LDAP; end + # :password => 'password' + class MyLdapProvider < OmniAuth::Strategies::LDAP; end + def app Rack::Builder.new { use OmniAuth::Test::PhonySession - use MyLdapProvider, :name => 'ldap', :title => 'MyLdap Form', :host => '192.168.1.145', :base => 'dc=score, dc=local', :name_proc => Proc.new {|name| name.gsub(/@.*$/,'')} + use MyLdapProvider, :name => 'ldap', :title => 'MyLdap Form', :host => '192.168.1.145', :base => 'dc=score, dc=local', :name_proc => Proc.new {|name| name.gsub(/@.*$/,'')} run lambda { |env| [404, {'Content-Type' => 'text/plain'}, [env.key?('omniauth.auth').to_s]] } }.to_app end @@ -44,7 +45,6 @@ def session it 'should have a label of the form title' do last_response.body.scan('MyLdap Form').size.should > 1 end - end describe 'post /auth/ldap/callback' do @@ -52,42 +52,48 @@ def session @adaptor = mock(OmniAuth::LDAP::Adaptor, {:uid => 'ping'}) OmniAuth::LDAP::Adaptor.stub(:new).and_return(@adaptor) end + context 'failure' do - before(:each) do - @adaptor.stub(:bind_as).and_return(false) - end + before(:each) do + @adaptor.stub(:bind_as).and_return(false) + end + it 'should redirect to error page when username is not present' do post('/auth/ldap/callback', {}) last_response.should be_redirect last_response.headers['Location'].should =~ %r{missing_credentials} end + it 'should redirect to error page' do post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) last_response.should be_redirect last_response.headers['Location'].should =~ %r{invalid_credentials} end - it 'should redirect to error page when there is exception' do + + it 'should redirect to error page when there is exception' do @adaptor.stub(:bind_as).and_throw(Exception.new('connection_error')) post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) last_response.should be_redirect last_response.headers['Location'].should =~ %r{ldap_error} end end - + context 'success' do let(:auth_hash){ last_request.env['omniauth.auth'] } + before(:each) do - @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'], - :jpegphoto => ['http://www.intridea.com/ping.jpg'], :description => ['omniauth-ldap']}) + @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'], + :jpegphoto => ['http://www.intridea.com/ping.jpg'], :description => ['omniauth-ldap']}) post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) end it 'should not redirect to error page' do last_response.should_not be_redirect end + it 'should map user info' do auth_hash.uid.should == 'cn=ping, dc=intridea, dc=com' auth_hash.info.email.should == 'ping@intridea.com' From e2664b7b7e6b69769c0252a47cb6db82708963e0 Mon Sep 17 00:00:00 2001 From: Dmitriy Dzema Date: Thu, 27 Sep 2012 17:44:37 +0800 Subject: [PATCH 05/18] Use #let instead of defineing methods in specs --- spec/omniauth/strategies/ldap_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/omniauth/strategies/ldap_spec.rb b/spec/omniauth/strategies/ldap_spec.rb index 22a4a69..97bda81 100644 --- a/spec/omniauth/strategies/ldap_spec.rb +++ b/spec/omniauth/strategies/ldap_spec.rb @@ -11,7 +11,7 @@ # :password => 'password' class MyLdapProvider < OmniAuth::Strategies::LDAP; end - def app + let(:app) do Rack::Builder.new { use OmniAuth::Test::PhonySession use MyLdapProvider, :name => 'ldap', :title => 'MyLdap Form', :host => '192.168.1.145', :base => 'dc=score, dc=local', :name_proc => Proc.new {|name| name.gsub(/@.*$/,'')} @@ -19,7 +19,7 @@ def app }.to_app end - def session + let(:session) do last_request.env['rack.session'] end From 78632d25f2d0182851f3f285589740815250b1ce Mon Sep 17 00:00:00 2001 From: Dmitriy Dzema Date: Thu, 27 Sep 2012 17:59:49 +0800 Subject: [PATCH 06/18] Rewrite LDAP strategy spec to specify conditions for each test case --- spec/omniauth/strategies/ldap_spec.rb | 77 ++++++++++++++++++++------- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/spec/omniauth/strategies/ldap_spec.rb b/spec/omniauth/strategies/ldap_spec.rb index 97bda81..192d9ef 100644 --- a/spec/omniauth/strategies/ldap_spec.rb +++ b/spec/omniauth/strategies/ldap_spec.rb @@ -58,24 +58,55 @@ class MyLdapProvider < OmniAuth::Strategies::LDAP; end @adaptor.stub(:bind_as).and_return(false) end - it 'should redirect to error page when username is not present' do - post('/auth/ldap/callback', {}) + context "when username is not preset" do + it 'should redirect to error page' do + post('/auth/ldap/callback', {}) - last_response.should be_redirect - last_response.headers['Location'].should =~ %r{missing_credentials} + last_response.should be_redirect + last_response.headers['Location'].should =~ %r{missing_credentials} + end end - it 'should redirect to error page' do - post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) - last_response.should be_redirect - last_response.headers['Location'].should =~ %r{invalid_credentials} + context "when password is not preset" do + it 'should redirect to error page' do + post('/auth/ldap/callback', {:username => "ping"}) + + last_response.should be_redirect + last_response.headers['Location'].should =~ %r{missing_credentials} + end end - it 'should redirect to error page when there is exception' do - @adaptor.stub(:bind_as).and_throw(Exception.new('connection_error')) - post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) - last_response.should be_redirect - last_response.headers['Location'].should =~ %r{ldap_error} + context "when password is empty" do + it 'should redirect to error page' do + post('/auth/ldap/callback', {:username => "ping", :password => ""}) + + last_response.should be_redirect + last_response.headers['Location'].should =~ %r{missing_credentials} + end + end + + context "when username and password are present" do + context "and bind on LDAP server failed" do + it 'should redirect to error page' do + post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) + + last_response.should be_redirect + last_response.headers['Location'].should =~ %r{invalid_credentials} + end + end + + context "and communication with LDAP server caused an exception" do + before :each do + @adaptor.stub(:bind_as).and_throw(Exception.new('connection_error')) + end + + it 'should redirect to error page' do + post('/auth/ldap/callback', {:username => "ping", :password => "password"}) + + last_response.should be_redirect + last_response.headers['Location'].should =~ %r{ldap_error} + end + end end end @@ -83,10 +114,20 @@ class MyLdapProvider < OmniAuth::Strategies::LDAP; end let(:auth_hash){ last_request.env['omniauth.auth'] } before(:each) do - @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'], - :jpegphoto => ['http://www.intridea.com/ping.jpg'], :description => ['omniauth-ldap']}) + @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'], + :jpegphoto => ['http://www.intridea.com/ping.jpg'], + :description => ['omniauth-ldap']}) + post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) end @@ -94,7 +135,7 @@ class MyLdapProvider < OmniAuth::Strategies::LDAP; end last_response.should_not be_redirect end - it 'should map user info' do + it 'should map user info to Auth Hash' do 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' From df7a8d04478794c3d90a784819fcfefe30e50180 Mon Sep 17 00:00:00 2001 From: Dmitriy Dzema Date: Thu, 27 Sep 2012 18:12:56 +0800 Subject: [PATCH 07/18] Add cases when username is empty to strategy spec --- spec/omniauth/strategies/ldap_spec.rb | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/spec/omniauth/strategies/ldap_spec.rb b/spec/omniauth/strategies/ldap_spec.rb index 192d9ef..caea7dd 100644 --- a/spec/omniauth/strategies/ldap_spec.rb +++ b/spec/omniauth/strategies/ldap_spec.rb @@ -67,21 +67,32 @@ class MyLdapProvider < OmniAuth::Strategies::LDAP; end end end - context "when password is not preset" do + context "when username is empty" do it 'should redirect to error page' do - post('/auth/ldap/callback', {:username => "ping"}) + post('/auth/ldap/callback', {:username => ""}) last_response.should be_redirect last_response.headers['Location'].should =~ %r{missing_credentials} end end - context "when password is empty" do - it 'should redirect to error page' do - post('/auth/ldap/callback', {:username => "ping", :password => ""}) + context "when username is present" do + context "and password is not preset" do + it 'should redirect to error page' do + post('/auth/ldap/callback', {:username => "ping"}) - last_response.should be_redirect - last_response.headers['Location'].should =~ %r{missing_credentials} + last_response.should be_redirect + last_response.headers['Location'].should =~ %r{missing_credentials} + end + end + + context "and password is empty" do + it 'should redirect to error page' do + post('/auth/ldap/callback', {:username => "ping", :password => ""}) + + last_response.should be_redirect + last_response.headers['Location'].should =~ %r{missing_credentials} + end end end From 987a81f774a925bbcbd263ba0223d68909578e42 Mon Sep 17 00:00:00 2001 From: Dmitriy Dzema Date: Thu, 27 Sep 2012 18:14:30 +0800 Subject: [PATCH 08/18] Do not allow to sign in using blank username or blank password AD allows bind with with correct username and empty password so application will think user successfully authenticated which is a serious mistake --- lib/omniauth/strategies/ldap.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/omniauth/strategies/ldap.rb b/lib/omniauth/strategies/ldap.rb index d79da3e..2ba577a 100644 --- a/lib/omniauth/strategies/ldap.rb +++ b/lib/omniauth/strategies/ldap.rb @@ -37,7 +37,7 @@ def request_phase def callback_phase @adaptor = OmniAuth::LDAP::Adaptor.new @options - return fail!(:missing_credentials) if request['username'].nil? || request['password'].nil? + 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']) return fail!(:invalid_credentials) if !@ldap_user_info @@ -80,6 +80,12 @@ def self.map_user(mapper, object) end user end + + protected + + def missing_credentials? + request['username'].nil? or request['username'].empty? or request['password'].nil? or request['password'].empty? + end # missing_credentials? end end end From fa90d493703b8c040bfaf3fc1f2c4f902449ee22 Mon Sep 17 00:00:00 2001 From: Samuel de Framond Date: Tue, 16 Oct 2012 12:07:49 +0800 Subject: [PATCH 09/18] Add the possibility to specify a custom LDAP filter --- lib/omniauth-ldap/adaptor.rb | 15 ++-- lib/omniauth/strategies/ldap.rb | 10 ++- spec/omniauth/strategies/ldap_spec.rb | 106 ++++++++++++++------------ 3 files changed, 75 insertions(+), 56 deletions(-) diff --git a/lib/omniauth-ldap/adaptor.rb b/lib/omniauth-ldap/adaptor.rb index 5c769b2..69f0411 100644 --- a/lib/omniauth-ldap/adaptor.rb +++ b/lib/omniauth-ldap/adaptor.rb @@ -14,9 +14,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, @@ -25,11 +26,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 diff --git a/lib/omniauth/strategies/ldap.rb b/lib/omniauth/strategies/ldap.rb index 45fb1d1..dc9c822 100644 --- a/lib/omniauth/strategies/ldap.rb +++ b/lib/omniauth/strategies/ldap.rb @@ -40,7 +40,7 @@ def callback_phase raise MissingCredentialsError.new("Missing login credentials") if request['username'].nil? || request['password'].nil? 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) @@ -50,6 +50,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"] } diff --git a/spec/omniauth/strategies/ldap_spec.rb b/spec/omniauth/strategies/ldap_spec.rb index 2a39f79..6ed3df0 100644 --- a/spec/omniauth/strategies/ldap_spec.rb +++ b/spec/omniauth/strategies/ldap_spec.rb @@ -48,56 +48,62 @@ def session end describe 'post /auth/ldap/callback' do - before(:each) do - @adaptor = mock(OmniAuth::LDAP::Adaptor, {:uid => 'ping'}) - OmniAuth::LDAP::Adaptor.stub(:new).and_return(@adaptor) - end - context 'failure' do - before(:each) do - @adaptor.stub(:bind_as).and_return(false) - end - it 'should raise MissingCredentialsError' do - lambda{post('/auth/ldap/callback', {})}.should raise_error OmniAuth::Strategies::LDAP::MissingCredentialsError - end - it 'should redirect to error page' do - post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) - last_response.should be_redirect - last_response.headers['Location'].should =~ %r{invalid_credentials} - end - it 'should redirect to error page when there is exception' do - @adaptor.stub(:bind_as).and_throw(Exception.new('connection_error')) - post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) - last_response.should be_redirect - last_response.headers['Location'].should =~ %r{ldap_error} - end - end - - context 'success' do - let(:auth_hash){ last_request.env['omniauth.auth'] } - before(:each) do - @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'], - :jpegphoto => ['http://www.intridea.com/ping.jpg'], :description => ['omniauth-ldap']}) - post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) - end - - it 'should raise MissingCredentialsError' do - should_not raise_error OmniAuth::Strategies::LDAP::MissingCredentialsError - end - it 'should map user info' do - 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' - auth_hash.info.last_name.should == 'Yu' - auth_hash.info.phone.should == '555-555-5555' - auth_hash.info.mobile.should == '444-444-4444' - auth_hash.info.nickname.should == 'ping' - auth_hash.info.title.should == 'dev' - auth_hash.info.location.should == 'k street, Washington, DC, U.S.A 20001' - auth_hash.info.url.should == 'www.intridea.com' - auth_hash.info.image.should == 'http://www.intridea.com/ping.jpg' - auth_hash.info.description.should == 'omniauth-ldap' + {:filter => '(ping=%{username})', :uid => 'ping'}.each_pair do |key, value| + context "when using :#{key}" do + before(:each) do + mocked_methods = {:filter => nil, :uid => nil} + mocked_methods[key] = value + @adaptor = mock(OmniAuth::LDAP::Adaptor, mocked_methods) + OmniAuth::LDAP::Adaptor.stub(:new).and_return(@adaptor) + end + context 'failure' do + before(:each) do + @adaptor.stub(:bind_as).and_return(false) + end + it 'should raise MissingCredentialsError' do + lambda{post('/auth/ldap/callback', {})}.should raise_error OmniAuth::Strategies::LDAP::MissingCredentialsError + end + it 'should redirect to error page' do + post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) + last_response.should be_redirect + last_response.headers['Location'].should =~ %r{invalid_credentials} + end + it 'should redirect to error page when there is exception' do + @adaptor.stub(:bind_as).and_throw(Exception.new('connection_error')) + post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) + last_response.should be_redirect + last_response.headers['Location'].should =~ %r{ldap_error} + end + end + + context 'success' do + let(:auth_hash){ last_request.env['omniauth.auth'] } + before(:each) do + @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'], + :jpegphoto => ['http://www.intridea.com/ping.jpg'], :description => ['omniauth-ldap']}) + post('/auth/ldap/callback', {:username => 'ping', :password => 'password'}) + end + + it 'should raise MissingCredentialsError' do + should_not raise_error OmniAuth::Strategies::LDAP::MissingCredentialsError + end + it 'should map user info' do + 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' + auth_hash.info.last_name.should == 'Yu' + auth_hash.info.phone.should == '555-555-5555' + auth_hash.info.mobile.should == '444-444-4444' + auth_hash.info.nickname.should == 'ping' + auth_hash.info.title.should == 'dev' + auth_hash.info.location.should == 'k street, Washington, DC, U.S.A 20001' + auth_hash.info.url.should == 'www.intridea.com' + auth_hash.info.image.should == 'http://www.intridea.com/ping.jpg' + auth_hash.info.description.should == 'omniauth-ldap' + end + end end end end From 7f66cffb4049954d3e1877b6c4afc547ce526736 Mon Sep 17 00:00:00 2001 From: Samuel de Framond Date: Tue, 16 Oct 2012 15:09:13 +0800 Subject: [PATCH 10/18] Add some ducomentation --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 60d7e77..fb660e7 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Use the LDAP strategy as a middleware in your application: :method => :plain, :base => 'dc=intridea, dc=com', :uid => 'sAMAccountName', + # 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' @@ -29,6 +31,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. From 77e842e9bf1eadcad034139456baeb48d6cdcc54 Mon Sep 17 00:00:00 2001 From: Ping Yu Date: Wed, 23 Jan 2013 08:44:53 -0600 Subject: [PATCH 11/18] increased the version --- lib/omniauth-ldap/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/omniauth-ldap/version.rb b/lib/omniauth-ldap/version.rb index 628e132..1531127 100644 --- a/lib/omniauth-ldap/version.rb +++ b/lib/omniauth-ldap/version.rb @@ -1,5 +1,5 @@ module OmniAuth module LDAP - VERSION = "1.0.2" + VERSION = "1.0.3" end end From 2c0cc458f08f162477d938b7591137e63ffd1e3f Mon Sep 17 00:00:00 2001 From: Jordi Massaguer Pla Date: Tue, 5 Feb 2013 13:25:42 +0100 Subject: [PATCH 12/18] add license information to gemspec This way it can be used with rubygems.org API --- omniauth-ldap.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/omniauth-ldap.gemspec b/omniauth-ldap.gemspec index 71e44d5..ecea3a0 100644 --- a/omniauth-ldap.gemspec +++ b/omniauth-ldap.gemspec @@ -7,6 +7,7 @@ Gem::Specification.new do |gem| gem.description = %q{A LDAP strategy for OmniAuth.} gem.summary = %q{A LDAP strategy for OmniAuth.} gem.homepage = "https://github.com/intridea/omniauth-ldap" + gem.license = "MIT" gem.add_runtime_dependency 'omniauth', '~> 1.0' gem.add_runtime_dependency 'net-ldap', '~> 0.2.2' From fe216a1a962ca14446223a5a371e1d710cdbcad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Ho=CC=88ltje?= Date: Wed, 13 Feb 2013 15:33:27 -0500 Subject: [PATCH 13/18] gitignore Gemfile.lock The Gemfile.lock shouldn't be included in a gem-based repository because it may artificially lock obsolete gems. An example of this is rb-fsevent which at the version it was locked at is incompatable with OS X 10.8 If something does need the version locked, specify it in the .gemspec or Gemfile itself. --- .gitignore | 1 + Gemfile.lock | 79 ---------------------------------------------------- 2 files changed, 1 insertion(+), 79 deletions(-) delete mode 100644 Gemfile.lock diff --git a/.gitignore b/.gitignore index bf84190..3b653ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .project coverage +/Gemfile.lock diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 6b0d47f..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,79 +0,0 @@ -PATH - remote: . - specs: - omniauth-ldap (1.0.2) - net-ldap (~> 0.2.2) - omniauth (~> 1.0) - pyu-ruby-sasl (~> 0.0.3.1) - rubyntlm (~> 0.1.1) - -GEM - remote: http://rubygems.org/ - specs: - archive-tar-minitar (0.5.2) - columnize (0.3.4) - diff-lcs (1.1.3) - ffi (1.0.9) - growl (1.0.3) - guard (0.8.8) - thor (~> 0.14.6) - guard-bundler (0.1.3) - bundler (>= 1.0.0) - guard (>= 0.2.2) - guard-rspec (0.5.0) - guard (>= 0.8.4) - hashie (1.2.0) - libnotify (0.5.7) - ffi (= 1.0.9) - linecache19 (0.5.12) - ruby_core_source (>= 0.1.4) - multi_json (1.0.3) - net-ldap (0.2.2) - omniauth (1.0.1) - hashie (~> 1.2) - rack - pyu-ruby-sasl (0.0.3.3) - rack (1.3.5) - rack-test (0.6.1) - rack (>= 1.0) - rb-fsevent (0.4.3.1) - rspec (2.7.0) - rspec-core (~> 2.7.0) - rspec-expectations (~> 2.7.0) - rspec-mocks (~> 2.7.0) - rspec-core (2.7.1) - rspec-expectations (2.7.0) - diff-lcs (~> 1.1.2) - rspec-mocks (2.7.0) - ruby-debug-base19 (0.11.25) - columnize (>= 0.3.1) - linecache19 (>= 0.5.11) - ruby_core_source (>= 0.1.4) - ruby-debug19 (0.11.6) - columnize (>= 0.3.1) - linecache19 (>= 0.5.11) - ruby-debug-base19 (>= 0.11.19) - ruby_core_source (0.1.5) - archive-tar-minitar (>= 0.5.2) - rubyntlm (0.1.1) - simplecov (0.5.4) - multi_json (~> 1.0.3) - simplecov-html (~> 0.5.3) - simplecov-html (0.5.3) - thor (0.14.6) - -PLATFORMS - ruby - -DEPENDENCIES - growl - guard - guard-bundler - guard-rspec - libnotify - omniauth-ldap! - rack-test - rb-fsevent - rspec (~> 2.7) - ruby-debug19 - simplecov From 5cd27716467a34ca22b00941f71827275f141f92 Mon Sep 17 00:00:00 2001 From: Leonardo Andres Garcia Crespo Date: Wed, 27 Feb 2013 15:56:18 -0300 Subject: [PATCH 14/18] Update net-ldap --- omniauth-ldap.gemspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/omniauth-ldap.gemspec b/omniauth-ldap.gemspec index 71e44d5..55d7bac 100644 --- a/omniauth-ldap.gemspec +++ b/omniauth-ldap.gemspec @@ -9,15 +9,15 @@ Gem::Specification.new do |gem| gem.homepage = "https://github.com/intridea/omniauth-ldap" gem.add_runtime_dependency 'omniauth', '~> 1.0' - gem.add_runtime_dependency 'net-ldap', '~> 0.2.2' + gem.add_runtime_dependency 'net-ldap', '~> 0.3.1' gem.add_runtime_dependency 'pyu-ruby-sasl', '~> 0.0.3.1' - gem.add_runtime_dependency 'rubyntlm', '~> 0.1.1' + gem.add_runtime_dependency 'rubyntlm', '~> 0.1.1' gem.add_development_dependency 'rspec', '~> 2.7' gem.add_development_dependency 'simplecov' gem.add_development_dependency 'rack-test' gem.add_development_dependency 'libnotify' gem.add_development_dependency 'ruby-debug19' - + gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } gem.files = `git ls-files`.split("\n") gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") From 6e9537b626558eecb1d3eff70937d0155480534a Mon Sep 17 00:00:00 2001 From: "Robert C. Sheets" Date: Mon, 4 Mar 2013 18:53:10 -0800 Subject: [PATCH 15/18] Add two missing commas to example code --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 60d7e77..7e5335a 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ Use the LDAP strategy as a middleware in your application: :method => :plain, :base => 'dc=intridea, dc=com', :uid => 'sAMAccountName', - :name_proc => Proc.new {|name| name.gsub(/@.*$/,'')} - :bind_dn => 'default_bind_dn' + :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. From 256c3d47b75aef7473ae376fc1f3c8e5fb5bdc3c Mon Sep 17 00:00:00 2001 From: Ping Yu Date: Tue, 10 Dec 2013 18:29:04 -0600 Subject: [PATCH 16/18] removed the rspec deprecation. --- spec/omniauth/strategies/ldap_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/omniauth/strategies/ldap_spec.rb b/spec/omniauth/strategies/ldap_spec.rb index cef890b..393dc75 100644 --- a/spec/omniauth/strategies/ldap_spec.rb +++ b/spec/omniauth/strategies/ldap_spec.rb @@ -49,7 +49,7 @@ class MyLdapProvider < OmniAuth::Strategies::LDAP; end describe 'post /auth/ldap/callback' do before(:each) do - @adaptor = mock(OmniAuth::LDAP::Adaptor, {:uid => 'ping'}) + @adaptor = double(OmniAuth::LDAP::Adaptor, {:uid => 'ping'}) OmniAuth::LDAP::Adaptor.stub(:new).and_return(@adaptor) end From 22118934196502e0dd0ff68378a0adf161dfd703 Mon Sep 17 00:00:00 2001 From: Ping Yu Date: Tue, 10 Dec 2013 18:42:51 -0600 Subject: [PATCH 17/18] removed the @url variable and construct_uri method --- lib/omniauth-ldap/adaptor.rb | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/omniauth-ldap/adaptor.rb b/lib/omniauth-ldap/adaptor.rb index 5c769b2..5e15340 100644 --- a/lib/omniauth-ldap/adaptor.rb +++ b/lib/omniauth-ldap/adaptor.rb @@ -3,7 +3,6 @@ require 'rack' require 'net/ldap' require 'net/ntlm' -require 'uri' require 'sasl' require 'kconv' module OmniAuth @@ -48,11 +47,9 @@ 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, :username => @bind_dn, @@ -61,11 +58,11 @@ def initialize(configuration={}) config[:auth] = @auth @connection = Net::LDAP.new(config) end - + #:base => "dc=yourcompany, dc=com", # :filter => "(mail=#{user})", # :password => psw - def bind_as(args = {}) + def bind_as(args = {}) result = false @connection.open do |me| rs = me.search args @@ -140,10 +137,6 @@ def sasl_bind_setup_gss_spnego(options) [Net::NTLM::Message::Type1.new.serialize, nego] end - def construct_uri(host, port, ssl) - protocol = ssl ? "ldaps" : "ldap" - URI.parse("#{protocol}://#{host}:#{port}").to_s - end end end end From 5ef14bb033c24240d04152a05470d0266036a351 Mon Sep 17 00:00:00 2001 From: Ping Yu Date: Tue, 10 Dec 2013 18:52:54 -0600 Subject: [PATCH 18/18] increased the version to 1.0.4 --- lib/omniauth-ldap/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/omniauth-ldap/version.rb b/lib/omniauth-ldap/version.rb index 1531127..d5adc48 100644 --- a/lib/omniauth-ldap/version.rb +++ b/lib/omniauth-ldap/version.rb @@ -1,5 +1,5 @@ module OmniAuth module LDAP - VERSION = "1.0.3" + VERSION = "1.0.4" end end