Skip to content

Sync with intridea/omniauth-ldap #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from Feb 1, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9d632a8
Added Net::LDAP::Entry object to strategies test
syndicut Jul 3, 2012
b524feb
Added respond_to? check to user mapping function
syndicut Jul 3, 2012
77eb81a
Use fail!(:missing_credentials) instead of raising custom exception
Sep 27, 2012
9a73d14
Unify formatting in spec files, remove trailing whitespace
Sep 27, 2012
e2664b7
Use #let instead of defineing methods in specs
Sep 27, 2012
78632d2
Rewrite LDAP strategy spec to specify conditions for each test case
Sep 27, 2012
df7a8d0
Add cases when username is empty to strategy spec
Sep 27, 2012
987a81f
Do not allow to sign in using blank username or blank password
Sep 27, 2012
fa90d49
Add the possibility to specify a custom LDAP filter
sdeframond Oct 16, 2012
7f66cff
Add some ducomentation
sdeframond Oct 16, 2012
ef89fe7
Merge pull request #23 from DimaD/master
Jan 23, 2013
77e842e
increased the version
Jan 23, 2013
2c0cc45
add license information to gemspec
jordimassaguerpla Feb 5, 2013
fe216a1
gitignore Gemfile.lock
Feb 13, 2013
5cd2771
Update net-ldap
leoasis Feb 27, 2013
6e9537b
Add two missing commas to example code
rcsheets Mar 5, 2013
b4af89c
Merge branch 'map-userinfo-fix' of github.com:syndicut/omniauth-ldap …
Dec 11, 2013
256c3d4
removed the rspec deprecation.
Dec 11, 2013
a47a72a
Merge pull request #25 from jordimassaguerpla/master
Dec 11, 2013
62bc4f0
Merge pull request #28 from docwhat/gemfile_lock
Dec 11, 2013
232e088
Merge pull request #30 from leoasis/master
Dec 11, 2013
2211893
removed the @url variable and construct_uri method
Dec 11, 2013
120487f
Merge pull request #32 from rcsheets/master
Dec 11, 2013
5ef14bb
increased the version to 1.0.4
Dec 11, 2013
efa1abc
Merge branch 'master' of github.com:intridea/omniauth-ldap
Dec 11, 2013
7aa1386
Merge branch 'master' of github.com:webagecorp/omniauth-ldap into web…
Dec 13, 2013
76d7754
Merge branch 'master' of https://github.com/intridea/omniauth-ldap
Jan 31, 2014
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ branches:
only:
- 'master'
rvm:
- 1.9.2
- 2.0.0
script: "bundle exec rspec spec"
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
gitlab_omniauth-ldap (1.0.2)
gitlab_omniauth-ldap (1.0.4)
net-ldap (~> 0.3.1)
omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.1)
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ 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',
# 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'
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
1 change: 1 addition & 0 deletions gitlab_omniauth-ldap.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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/gitlabhq/omniauth-ldap"
gem.license = "MIT"

gem.add_runtime_dependency 'omniauth', '~> 1.0'
gem.add_runtime_dependency 'net-ldap', '~> 0.3.1'
Expand Down
21 changes: 10 additions & 11 deletions lib/omniauth-ldap/adaptor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
require 'rack'
require 'net/ldap'
require 'net/ntlm'
require 'uri'
require 'sasl'
require 'kconv'
module OmniAuth
Expand All @@ -14,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 @@ -25,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 All @@ -48,7 +52,6 @@ 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)

Expand Down Expand Up @@ -140,10 +143,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
2 changes: 1 addition & 1 deletion lib/omniauth-ldap/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module OmniAuth
module LDAP
VERSION = "1.0.3"
VERSION = "1.0.4"
end
end
29 changes: 19 additions & 10 deletions lib/omniauth/strategies/ldap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
module OmniAuth
module Strategies
class LDAP
class MissingCredentialsError < StandardError; end
include OmniAuth::Strategy

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a good idea @randx?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, it looked for a minute like we were no longer raising an error for missing credentials. I see it uses the fail! method now.

@@config = {
'name' => 'cn',
Expand Down Expand Up @@ -38,14 +37,10 @@ def request_phase
def callback_phase
@adaptor = OmniAuth::LDAP::Adaptor.new @options

return fail!(:missing_credentials) if missing_credentials?
begin
# GITLAB security patch
# Dont allow blank password for ldap auth
if request['username'].nil? || request['username'].empty? || request['password'].nil? || request['password'].empty?
raise MissingCredentialsError.new("Missing login credentials")
end
@ldap_user_info = @adaptor.bind_as(:filter => filter(@adaptor), :size => 1, :password => request['password'])

@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

@user_info = self.class.map_user(@@config, @ldap_user_info)
Expand All @@ -55,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"]
}
Expand All @@ -70,14 +73,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
Expand All @@ -86,6 +89,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
Expand Down
39 changes: 22 additions & 17 deletions spec/omniauth-ldap/adaptor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading