Skip to content

Commit 5656da8

Browse files
author
Ping Yu
committed
init check in. rewrote the ldap strategy based on the omniauth 1.0
spec. Added spec test for ldap strategy
0 parents  commit 5656da8

File tree

13 files changed

+582
-0
lines changed

13 files changed

+582
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.project
2+
coverage

Gemfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
source 'http://rubygems.org'
2+
3+
gemspec
4+
5+
group :development, :test do
6+
gem 'guard'
7+
gem 'guard-rspec'
8+
gem 'guard-bundler'
9+
gem 'growl'
10+
gem 'rb-fsevent'
11+
end

Gemfile.lock

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
PATH
2+
remote: .
3+
specs:
4+
omniauth-ldap (1.0.0.beta1)
5+
net-ldap (~> 0.2.2)
6+
omniauth (~> 1.0.0.beta1)
7+
pyu-ruby-sasl (~> 0.0.3.1)
8+
rubyntlm (~> 0.1.1)
9+
10+
GEM
11+
remote: http://rubygems.org/
12+
specs:
13+
archive-tar-minitar (0.5.2)
14+
columnize (0.3.4)
15+
diff-lcs (1.1.3)
16+
ffi (1.0.9)
17+
growl (1.0.3)
18+
guard (0.8.8)
19+
thor (~> 0.14.6)
20+
guard-bundler (0.1.3)
21+
bundler (>= 1.0.0)
22+
guard (>= 0.2.2)
23+
guard-rspec (0.5.0)
24+
guard (>= 0.8.4)
25+
hashie (1.2.0)
26+
libnotify (0.5.7)
27+
ffi (= 1.0.9)
28+
linecache19 (0.5.12)
29+
ruby_core_source (>= 0.1.4)
30+
multi_json (1.0.3)
31+
net-ldap (0.2.2)
32+
omniauth (1.0.0.beta1)
33+
hashie
34+
rack
35+
pyu-ruby-sasl (0.0.3.3)
36+
rack (1.3.5)
37+
rack-test (0.6.1)
38+
rack (>= 1.0)
39+
rb-fsevent (0.4.3.1)
40+
rspec (2.7.0)
41+
rspec-core (~> 2.7.0)
42+
rspec-expectations (~> 2.7.0)
43+
rspec-mocks (~> 2.7.0)
44+
rspec-core (2.7.1)
45+
rspec-expectations (2.7.0)
46+
diff-lcs (~> 1.1.2)
47+
rspec-mocks (2.7.0)
48+
ruby-debug-base19 (0.11.25)
49+
columnize (>= 0.3.1)
50+
linecache19 (>= 0.5.11)
51+
ruby_core_source (>= 0.1.4)
52+
ruby-debug19 (0.11.6)
53+
columnize (>= 0.3.1)
54+
linecache19 (>= 0.5.11)
55+
ruby-debug-base19 (>= 0.11.19)
56+
ruby_core_source (0.1.5)
57+
archive-tar-minitar (>= 0.5.2)
58+
rubyntlm (0.1.1)
59+
simplecov (0.5.4)
60+
multi_json (~> 1.0.3)
61+
simplecov-html (~> 0.5.3)
62+
simplecov-html (0.5.3)
63+
thor (0.14.6)
64+
65+
PLATFORMS
66+
ruby
67+
68+
DEPENDENCIES
69+
growl
70+
guard
71+
guard-bundler
72+
guard-rspec
73+
libnotify
74+
omniauth-ldap!
75+
rack-test
76+
rb-fsevent
77+
rspec (~> 2.6)
78+
ruby-debug19
79+
simplecov

Guardfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
guard 'rspec', :version => 2 do
2+
watch(%r{^spec/.+_spec\.rb$})
3+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4+
watch('spec/spec_helper.rb') { "spec" }
5+
end
6+
7+
8+
guard 'bundler' do
9+
watch('Gemfile')
10+
watch(/^.+\.gemspec/)
11+
end

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# OmniAuth LDAP
2+
3+
**Note:** This gem is designed to work with the in-beta OmniAuth 1.0
4+
library. It will not be officially released on RubyGems.org until
5+
OmniAuth 1.0 is released.
6+
7+
== LDAP
8+
9+
Use the LDAP strategy as a middleware in your application:
10+
11+
use OmniAuth::Strategies::LDAP,
12+
:title => "My LDAP",
13+
:host => '10.101.10.1',
14+
:port => 389,
15+
:method => :plain,
16+
:base => 'dc=intridea, dc=com',
17+
:uid => 'sAMAccountName',
18+
:name_proc => Proc.new {|name| name.gsub(/@.*$/,'')}
19+
:bind_dn => 'default_bind_dn'
20+
:password => 'password'
21+
22+
All of the listed options are required, with the exception of :name_proc, :bind_dn, and :password.
23+
Allowed values of :method are: :plain, :ssl, :tls.
24+
25+
:bind_dn and :password is the default credentials to perform user lookup.
26+
most LDAP servers require that you supply a complete DN as a binding-credential, along with an authenticator
27+
such as a password. But for many applications, you often don’t have a full DN to identify the user.
28+
You usually get a simple identifier like a username or an email address, along with a password.
29+
Since many LDAP servers don't allow anonymous access, search function will require a bound connection,
30+
:bind_dn and :password will be required for searching on the username or email to retrieve the DN attribute
31+
for the user. If the LDAP server allows anonymous access, you don't need to provide these two parameters.
32+
33+
:uid is the LDAP attribute name for the user name in the login form.
34+
typically AD would be 'sAMAccountName' or 'UserPrincipalName', while OpenLDAP is 'uid'.
35+
36+
:name_proc allows you to match the user name entered with the format of the :uid attributes.
37+
For example, value of 'sAMAccountName' in AD contains only the windows user name. If your user prefers using
38+
email to login, a name_proc as above will trim the email string down to just the windows login name.
39+
In summary, use :name_proc to fill the gap between the submitted username and LDAP uid attribute value.
40+
41+
:try_sasl and :sasl_mechanisms are optional. :try_sasl [true | false], :sasl_mechanisms ['DIGEST-MD5' | 'GSS-SPNEGO']
42+
Use them to initialize a SASL connection to server. If you are not familiar with these authentication methods,
43+
please just avoid them.
44+
45+
Direct users to '/auth/ldap' to have them authenticated via your company's LDAP server.
46+
47+
48+
## License
49+
50+
Copyright (C) 2011 by Ping Yu and Intridea, Inc.
51+
52+
Permission is hereby granted, free of charge, to any person obtaining a copy
53+
of this software and associated documentation files (the "Software"), to deal
54+
in the Software without restriction, including without limitation the rights
55+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
56+
copies of the Software, and to permit persons to whom the Software is
57+
furnished to do so, subject to the following conditions:
58+
59+
The above copyright notice and this permission notice shall be included in
60+
all copies or substantial portions of the Software.
61+
62+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
63+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
64+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
65+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
66+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
67+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
68+
THE SOFTWARE.

Rakefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env rake
2+
require "bundler/gem_tasks"
3+
require 'rspec/core/rake_task'
4+
5+
desc 'Default: run specs.'
6+
task :default => :spec
7+
8+
desc "Run specs"
9+
RSpec::Core::RakeTask.new

lib/omniauth-ldap.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
require "omniauth-ldap/version"
2+
require "omniauth-ldap/adaptor"
3+
require 'omniauth/strategies/ldap'
4+

lib/omniauth-ldap/adaptor.rb

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#this code borrowed pieces from activeldap and net-ldap
2+
3+
require 'rack'
4+
require 'net/ldap'
5+
require 'net/ntlm'
6+
require 'uri'
7+
8+
module OmniAuth
9+
module LDAP
10+
class Adaptor
11+
class LdapError < StandardError; end
12+
class ConfigurationError < StandardError; end
13+
class AuthenticationError < StandardError; end
14+
class ConnectionError < StandardError; end
15+
16+
VALID_ADAPTER_CONFIGURATION_KEYS = [:host, :port, :method, :bind_dn, :password, :try_sasl, :sasl_mechanisms, :uid, :base, :allow_anonymous]
17+
18+
MUST_HAVE_KEYS = [:host, :port, :method, :uid, :base]
19+
20+
METHOD = {
21+
:ssl => :simple_tls,
22+
:tls => :start_tls,
23+
:plain => nil,
24+
}
25+
26+
attr_accessor :bind_dn, :password
27+
attr_reader :connection, :uid, :base
28+
29+
def initialize(configuration={})
30+
@disconnected = false
31+
@bound = false
32+
@configuration = configuration.dup
33+
@configuration[:allow_anonymous] ||= false
34+
@logger = @configuration.delete(:logger)
35+
message = []
36+
MUST_HAVE_KEYS.each do |name|
37+
message << name if configuration[name].nil?
38+
end
39+
raise ArgumentError.new(message.join(",") +" MUST be provided") unless message.empty?
40+
VALID_ADAPTER_CONFIGURATION_KEYS.each do |name|
41+
instance_variable_set("@#{name}", configuration[name])
42+
end
43+
44+
method = ensure_method(@method)
45+
config = {
46+
:host => @host,
47+
:port => @port,
48+
:encryption => method
49+
}
50+
@uri = construct_uri(@host, @port, @method != :plain)
51+
52+
@bind_method = @try_sasl ? "sasl" : @allow_anonymous ? 'anonymous' : 'simple'
53+
@bind_method = 'anonymous' unless @bind_dn && @password
54+
55+
@auth = sasl_auths.first if @bind_method == 'sasl'
56+
@bind_method = 'simple' unless @auth
57+
@auth ||= { :method => @bind_method,
58+
:username => @bind_dn,
59+
:password => @passowrd
60+
}
61+
config[:auth] = @auth
62+
@connection = Net::LDAP.new(config)
63+
end
64+
65+
#:base => "dc=yourcompany, dc=com",
66+
# :filter => "(mail=#{user})",
67+
# :password => psw
68+
def bind_as(args = {})
69+
result = false
70+
@connection.open { |me|
71+
rs = search args
72+
if rs and rs.first and dn = rs.first.dn
73+
password = args[:password]
74+
method = args[:method]
75+
password = password.call if password.respond_to?(:call)
76+
if method == 'sasl'
77+
result = rs if bind(sasl_auths(args))
78+
else
79+
result = rs if bind(:method => :simple, :username => dn,
80+
:password => password)
81+
end
82+
end
83+
}
84+
result
85+
end
86+
87+
private
88+
89+
def ensure_port(method)
90+
if method == :ssl
91+
URI::LDAPS::DEFAULT_PORT
92+
else
93+
URI::LDAP::DEFAULT_PORT
94+
end
95+
end
96+
97+
def ensure_method(method)
98+
method ||= "plain"
99+
normalized_method = method.to_s.downcase.to_sym
100+
return METHOD[normalized_method] if METHOD.has_key?(normalized_method)
101+
102+
available_methods = METHOD.keys.collect {|m| m.inspect}.join(", ")
103+
format = "%s is not one of the available connect methods: %s"
104+
raise ConfigurationError, format % [method.inspect, available_methods]
105+
end
106+
107+
def sasl_auths(options={})
108+
auths = []
109+
sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms
110+
sasl_mechanisms.each do |mechanism|
111+
normalized_mechanism = mechanism.downcase.gsub(/-/, '_')
112+
sasl_bind_setup = "sasl_bind_setup_#{normalized_mechanism}"
113+
next unless respond_to?(sasl_bind_setup, true)
114+
initial_credential, challenge_response = send(sasl_bind_setup, options)
115+
116+
auths << {
117+
:method => :sasl,
118+
:initial_credential => initial_credential,
119+
:mechanism => mechanism,
120+
:challenge_response => challenge_response,
121+
}
122+
end
123+
end
124+
125+
def sasl_bind_setup_digest_md5(options)
126+
bind_dn = options[:username]
127+
initial_credential = ""
128+
challenge_response = Proc.new do |cred|
129+
pref = SASL::Preferences.new :digest_uri => "ldap/#{@host}", :username => bind_dn, :has_password? => true, :password => options[:password]||@password
130+
sasl = SASL.new("DIGEST-MD5", pref)
131+
response = sasl.receive("challenge", cred)
132+
response[1]
133+
end
134+
[initial_credential, challenge_response]
135+
end
136+
137+
def sasl_bind_setup_gss_spnego(options)
138+
bind_dn = options[:username]
139+
psw = [bind_dn, options[:password]||@password]
140+
raise LdapError.new( "invalid binding information" ) unless (bind_dn && psw)
141+
142+
nego = proc {|challenge|
143+
t2_msg = Net::NTLM::Message.parse( challenge )
144+
bind_dn, domain = bind_dn.split('\\').reverse
145+
t2_msg.target_name = Net::NTLM::encode_utf16le(domain) if domain
146+
t3_msg = t2_msg.response( {:user => bind_dn, :password => psw}, {:ntlmv2 => true} )
147+
t3_msg.serialize
148+
}
149+
[Net::NTLM::Message::Type1.new.serialize, nego]
150+
end
151+
152+
def construct_uri(host, port, ssl)
153+
protocol = ssl ? "ldaps" : "ldap"
154+
URI.parse("#{protocol}://#{host}:#{port}").to_s
155+
end
156+
end
157+
end
158+
end

lib/omniauth-ldap/version.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module OmniAuth
2+
module LDAP
3+
VERSION = "1.0.0.beta1"
4+
end
5+
end

0 commit comments

Comments
 (0)