omniauth-ldap2 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 37117fed333df2253ff06ce7800decc9b1626a6b
4
+ data.tar.gz: d934972fa031cc1facd90686888e86d4cf277c70
5
+ SHA512:
6
+ metadata.gz: fd88f413e0a42b3841e997830220b869e3f9e46984e603f9a41c57601d6e60eb7f8ddcf0f8eaed2222ec72a73396c0551ef1426661ca31f58dae099e4b625503
7
+ data.tar.gz: d39124abda5ad9430fb504bf0c8ae35b300de39b5b51b83439d43967fb1d8ebbd399b3eaf9092a417cfb33d4038a5770138a0766e85e88a2ad06b997756d4dd3
@@ -0,0 +1,3 @@
1
+ .project
2
+ coverage
3
+ /Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile ADDED
@@ -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
@@ -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
@@ -0,0 +1,71 @@
1
+ # OmniAuth LDAP
2
+
3
+ == LDAP
4
+
5
+ Use the LDAP strategy as a middleware in your application:
6
+
7
+ use OmniAuth::Strategies::LDAP,
8
+ :title => "My LDAP",
9
+ :host => '10.101.10.1',
10
+ :port => 389,
11
+ :method => :plain,
12
+ :base => 'dc=intridea, dc=com',
13
+ :uid => 'sAMAccountName',
14
+ :name_proc => Proc.new {|name| name.gsub(/@.*$/,'')},
15
+ :bind_dn => 'default_bind_dn',
16
+ # Or, alternatively:
17
+ #:filter => '(&(uid=%{username})(memberOf=cn=myapp-users,ou=groups,dc=example,dc=com))'
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 :title, :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
+ :filter is the LDAP filter used to search the user entry. It can be used in place of :uid for more flexibility.
37
+ `%{username}` will be replaced by the user name processed by :name_proc.
38
+
39
+ :name_proc allows you to match the user name entered with the format of the :uid attributes.
40
+ For example, value of 'sAMAccountName' in AD contains only the windows user name. If your user prefers using
41
+ email to login, a name_proc as above will trim the email string down to just the windows login name.
42
+ In summary, use :name_proc to fill the gap between the submitted username and LDAP uid attribute value.
43
+
44
+ :try_sasl and :sasl_mechanisms are optional. :try_sasl [true | false], :sasl_mechanisms ['DIGEST-MD5' | 'GSS-SPNEGO']
45
+ Use them to initialize a SASL connection to server. If you are not familiar with these authentication methods,
46
+ please just avoid them.
47
+
48
+ Direct users to '/auth/ldap' to have them authenticated via your company's LDAP server.
49
+
50
+
51
+ ## License
52
+
53
+ Copyright (C) 2011 by Ping Yu and Intridea, Inc.
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining a copy
56
+ of this software and associated documentation files (the "Software"), to deal
57
+ in the Software without restriction, including without limitation the rights
58
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
59
+ copies of the Software, and to permit persons to whom the Software is
60
+ furnished to do so, subject to the following conditions:
61
+
62
+ The above copyright notice and this permission notice shall be included in
63
+ all copies or substantial portions of the Software.
64
+
65
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
66
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
67
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
68
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
69
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
70
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
71
+ THE SOFTWARE.
@@ -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
@@ -0,0 +1,4 @@
1
+ require "omniauth-ldap/version"
2
+ require "omniauth-ldap/adaptor"
3
+ require 'omniauth/strategies/ldap'
4
+
@@ -0,0 +1,147 @@
1
+ #this code borrowed pieces from activeldap and net-ldap
2
+
3
+ require 'rack'
4
+ require 'net/ldap'
5
+ require 'net/ntlm'
6
+ require 'sasl'
7
+ require 'kconv'
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, :filter]
17
+
18
+ # A list of needed keys. Possible alternatives are specified using sub-lists.
19
+ MUST_HAVE_KEYS = [:host, :port, :method, [:uid, :filter], :base]
20
+
21
+ METHOD = {
22
+ :ssl => :simple_tls,
23
+ :tls => :start_tls,
24
+ :plain => nil,
25
+ }
26
+
27
+ attr_accessor :bind_dn, :password
28
+ attr_reader :connection, :uid, :base, :auth, :filter
29
+ def self.validate(configuration={})
30
+ message = []
31
+ MUST_HAVE_KEYS.each do |names|
32
+ names = [names].flatten
33
+ missing_keys = names.select{|name| configuration[name].nil?}
34
+ if missing_keys == names
35
+ message << names.join(' or ')
36
+ end
37
+ end
38
+ raise ArgumentError.new(message.join(",") +" MUST be provided") unless message.empty?
39
+ end
40
+ def initialize(configuration={})
41
+ Adaptor.validate(configuration)
42
+ @configuration = configuration.dup
43
+ @configuration[:allow_anonymous] ||= false
44
+ @logger = @configuration.delete(:logger)
45
+ VALID_ADAPTER_CONFIGURATION_KEYS.each do |name|
46
+ instance_variable_set("@#{name}", @configuration[name])
47
+ end
48
+ method = ensure_method(@method)
49
+ config = {
50
+ :host => @host,
51
+ :port => @port,
52
+ :encryption => method,
53
+ :base => @base
54
+ }
55
+ @bind_method = @try_sasl ? :sasl : (@allow_anonymous||!@bind_dn||!@password ? :anonymous : :simple)
56
+
57
+
58
+ @auth = sasl_auths({:username => @bind_dn, :password => @password}).first if @bind_method == :sasl
59
+ @auth ||= { :method => @bind_method,
60
+ :username => @bind_dn,
61
+ :password => @password
62
+ }
63
+ config[:auth] = @auth
64
+ @connection = Net::LDAP.new(config)
65
+ end
66
+
67
+ #:base => "dc=yourcompany, dc=com",
68
+ # :filter => "(mail=#{user})",
69
+ # :password => psw
70
+ def bind_as(args = {})
71
+ result = false
72
+ @connection.open do |me|
73
+ rs = me.search args
74
+ if rs and rs.first and dn = rs.first.dn
75
+ password = args[:password]
76
+ method = args[:method] || @method
77
+ password = password.call if password.respond_to?(:call)
78
+ if method == 'sasl'
79
+ result = rs.first if me.bind(sasl_auths({:username => dn, :password => password}).first)
80
+ else
81
+ result = rs.first if me.bind(:method => :simple, :username => dn,
82
+ :password => password)
83
+ end
84
+ end
85
+ end
86
+ result
87
+ end
88
+
89
+ private
90
+ def ensure_method(method)
91
+ method ||= "plain"
92
+ normalized_method = method.to_s.downcase.to_sym
93
+ return METHOD[normalized_method] if METHOD.has_key?(normalized_method)
94
+
95
+ available_methods = METHOD.keys.collect {|m| m.inspect}.join(", ")
96
+ format = "%s is not one of the available connect methods: %s"
97
+ raise ConfigurationError, format % [method.inspect, available_methods]
98
+ end
99
+
100
+ def sasl_auths(options={})
101
+ auths = []
102
+ sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms
103
+ sasl_mechanisms.each do |mechanism|
104
+ normalized_mechanism = mechanism.downcase.gsub(/-/, '_')
105
+ sasl_bind_setup = "sasl_bind_setup_#{normalized_mechanism}"
106
+ next unless respond_to?(sasl_bind_setup, true)
107
+ initial_credential, challenge_response = send(sasl_bind_setup, options)
108
+ auths << {
109
+ :method => :sasl,
110
+ :initial_credential => initial_credential,
111
+ :mechanism => mechanism,
112
+ :challenge_response => challenge_response
113
+ }
114
+ end
115
+ auths
116
+ end
117
+
118
+ def sasl_bind_setup_digest_md5(options)
119
+ bind_dn = options[:username]
120
+ initial_credential = ""
121
+ challenge_response = Proc.new do |cred|
122
+ pref = SASL::Preferences.new :digest_uri => "ldap/#{@host}", :username => bind_dn, :has_password? => true, :password => options[:password]
123
+ sasl = SASL.new("DIGEST-MD5", pref)
124
+ response = sasl.receive("challenge", cred)
125
+ response[1]
126
+ end
127
+ [initial_credential, challenge_response]
128
+ end
129
+
130
+ def sasl_bind_setup_gss_spnego(options)
131
+ bind_dn = options[:username]
132
+ psw = options[:password]
133
+ raise LdapError.new( "invalid binding information" ) unless (bind_dn && psw)
134
+
135
+ nego = proc {|challenge|
136
+ t2_msg = Net::NTLM::Message.parse( challenge )
137
+ bind_dn, domain = bind_dn.split('\\').reverse
138
+ t2_msg.target_name = Net::NTLM::encode_utf16le(domain) if domain
139
+ t3_msg = t2_msg.response( {:user => bind_dn, :password => psw}, {:ntlmv2 => true} )
140
+ t3_msg.serialize
141
+ }
142
+ [Net::NTLM::Message::Type1.new.serialize, nego]
143
+ end
144
+
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,5 @@
1
+ module OmniAuth
2
+ module LDAP
3
+ VERSION = "1.0.4"
4
+ end
5
+ end
@@ -0,0 +1,101 @@
1
+ require 'omniauth'
2
+
3
+ module OmniAuth
4
+ module Strategies
5
+ class LDAP
6
+ include OmniAuth::Strategy
7
+ @@config = {
8
+ 'name' => 'cn',
9
+ 'first_name' => 'givenName',
10
+ 'last_name' => 'sn',
11
+ 'email' => ['mail', "email", 'userPrincipalName'],
12
+ 'phone' => ['telephoneNumber', 'homePhone', 'facsimileTelephoneNumber'],
13
+ 'mobile' => ['mobile', 'mobileTelephoneNumber'],
14
+ 'nickname' => ['uid', 'userid', 'sAMAccountName'],
15
+ 'title' => 'title',
16
+ 'location' => {"%0, %1, %2, %3 %4" => [['address', 'postalAddress', 'homePostalAddress', 'street', 'streetAddress'], ['l'], ['st'],['co'],['postOfficeBox']]},
17
+ 'uid' => 'dn',
18
+ 'url' => ['wwwhomepage'],
19
+ 'image' => 'jpegPhoto',
20
+ 'description' => 'description'
21
+ }
22
+ option :title, "LDAP Authentication" #default title for authentication form
23
+ option :port, 389
24
+ option :method, :plain
25
+ option :uid, 'sAMAccountName'
26
+ option :name_proc, lambda {|n| n}
27
+
28
+ def request_phase
29
+ OmniAuth::LDAP::Adaptor.validate @options
30
+ f = OmniAuth::Form.new(:title => (options[:title] || "LDAP Authentication"), :url => callback_path)
31
+ f.text_field 'Login', 'username'
32
+ f.password_field 'Password', 'password'
33
+ f.button "Sign In"
34
+ f.to_response
35
+ end
36
+
37
+ def callback_phase
38
+ @adaptor = OmniAuth::LDAP::Adaptor.new @options
39
+
40
+ return fail!(:missing_credentials) if missing_credentials?
41
+ begin
42
+ @ldap_user_info = @adaptor.bind_as(:filter => filter(@adaptor), :size => 1, :password => request['password'])
43
+ return fail!(:invalid_credentials) if !@ldap_user_info
44
+
45
+ @user_info = self.class.map_user(@@config, @ldap_user_info)
46
+ super
47
+ rescue Exception => e
48
+ return fail!(:ldap_error, e)
49
+ end
50
+ end
51
+
52
+ def filter adaptor
53
+ if adaptor.filter and !adaptor.filter.empty?
54
+ Net::LDAP::Filter.construct(adaptor.filter % {username: @options[:name_proc].call(request['username'])})
55
+ else
56
+ Net::LDAP::Filter.eq(adaptor.uid, @options[:name_proc].call(request['username']))
57
+ end
58
+ end
59
+
60
+ uid {
61
+ @user_info["uid"]
62
+ }
63
+ info {
64
+ @user_info
65
+ }
66
+ extra {
67
+ { :raw_info => @ldap_user_info }
68
+ }
69
+
70
+ def self.map_user(mapper, object)
71
+ user = {}
72
+ mapper.each do |key, value|
73
+ case value
74
+ when String
75
+ user[key] = object[value.downcase.to_sym].first if object.respond_to? value.downcase.to_sym
76
+ when Array
77
+ value.each {|v| (user[key] = object[v.downcase.to_sym].first; break;) if object.respond_to? v.downcase.to_sym}
78
+ when Hash
79
+ value.map do |key1, value1|
80
+ pattern = key1.dup
81
+ value1.each_with_index do |v,i|
82
+ part = ''; v.collect(&:downcase).collect(&:to_sym).each {|v1| (part = object[v1].first; break;) if object.respond_to? v1}
83
+ pattern.gsub!("%#{i}",part||'')
84
+ end
85
+ user[key] = pattern
86
+ end
87
+ end
88
+ end
89
+ user
90
+ end
91
+
92
+ protected
93
+
94
+ def missing_credentials?
95
+ request['username'].nil? or request['username'].empty? or request['password'].nil? or request['password'].empty?
96
+ end # missing_credentials?
97
+ end
98
+ end
99
+ end
100
+
101
+ OmniAuth.config.add_camelization 'ldap', 'LDAP'
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/omniauth-ldap/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Ping Yu"]
6
+ gem.email = ["ping@intridea.com"]
7
+ gem.description = %q{A LDAP strategy for OmniAuth.}
8
+ gem.summary = %q{A LDAP strategy for OmniAuth.}
9
+ gem.homepage = "https://github.com/intridea/omniauth-ldap"
10
+ gem.license = "MIT"
11
+
12
+ gem.add_runtime_dependency 'omniauth'
13
+ gem.add_runtime_dependency 'net-ldap'
14
+ gem.add_runtime_dependency 'pyu-ruby-sasl'
15
+ gem.add_runtime_dependency 'rubyntlm'
16
+ gem.add_development_dependency 'rspec'
17
+ gem.add_development_dependency 'simplecov'
18
+ gem.add_development_dependency 'rack-test'
19
+ gem.add_development_dependency 'libnotify'
20
+ gem.add_development_dependency 'ruby-debug19'
21
+
22
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ gem.files = `git ls-files`.split("\n")
24
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ gem.name = "omniauth-ldap2"
26
+ gem.require_paths = ["lib"]
27
+ gem.version = OmniAuth::LDAP::VERSION
28
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+ describe "OmniAuth::LDAP::Adaptor" do
3
+
4
+ describe 'initialize' do
5
+ it 'should throw exception when must have field is not set' do
6
+ #[:host, :port, :method, :bind_dn]
7
+ lambda { OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'plain'})}.should raise_error(ArgumentError)
8
+ end
9
+
10
+ it 'should throw exception when method is not supported' do
11
+ 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)
12
+ end
13
+
14
+ it 'should setup ldap connection with anonymous' do
15
+ adaptor = OmniAuth::LDAP::Adaptor.new({host: "192.168.1.145", method: 'plain', base: 'dc=intridea, dc=com', port: 389, uid: 'sAMAccountName'})
16
+ adaptor.connection.should_not == nil
17
+ adaptor.connection.host.should == '192.168.1.145'
18
+ adaptor.connection.port.should == 389
19
+ adaptor.connection.base.should == 'dc=intridea, dc=com'
20
+ adaptor.connection.instance_variable_get('@auth').should == {:method => :anonymous, :username => nil, :password => nil}
21
+ end
22
+
23
+ it 'should setup ldap connection with simple' do
24
+ 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'})
25
+ adaptor.connection.should_not == nil
26
+ adaptor.connection.host.should == '192.168.1.145'
27
+ adaptor.connection.port.should == 389
28
+ adaptor.connection.base.should == 'dc=intridea, dc=com'
29
+ adaptor.connection.instance_variable_get('@auth').should == {:method => :simple, :username => 'bind_dn', :password => 'password'}
30
+ end
31
+
32
+ it 'should setup ldap connection with sasl-md5' do
33
+ 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'})
34
+ adaptor.connection.should_not == nil
35
+ adaptor.connection.host.should == '192.168.1.145'
36
+ adaptor.connection.port.should == 389
37
+ adaptor.connection.base.should == 'dc=intridea, dc=com'
38
+ adaptor.connection.instance_variable_get('@auth')[:method].should == :sasl
39
+ adaptor.connection.instance_variable_get('@auth')[:mechanism].should == 'DIGEST-MD5'
40
+ adaptor.connection.instance_variable_get('@auth')[:initial_credential].should == ''
41
+ adaptor.connection.instance_variable_get('@auth')[:challenge_response].should_not be_nil
42
+ end
43
+
44
+ it 'should setup ldap connection with sasl-gss' do
45
+ 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'})
46
+ adaptor.connection.should_not == nil
47
+ adaptor.connection.host.should == '192.168.1.145'
48
+ adaptor.connection.port.should == 389
49
+ adaptor.connection.base.should == 'dc=intridea, dc=com'
50
+ adaptor.connection.instance_variable_get('@auth')[:method].should == :sasl
51
+ adaptor.connection.instance_variable_get('@auth')[:mechanism].should == 'GSS-SPNEGO'
52
+ adaptor.connection.instance_variable_get('@auth')[:initial_credential].should =~ /^NTLMSSP/
53
+ adaptor.connection.instance_variable_get('@auth')[:challenge_response].should_not be_nil
54
+ end
55
+ end
56
+
57
+ describe 'bind_as' do
58
+ let(:args) { {:filter => Net::LDAP::Filter.eq('sAMAccountName', 'username'), :password => 'password', :size => 1} }
59
+ let(:rs) { Struct.new(:dn).new('new dn') }
60
+
61
+ it 'should bind simple' do
62
+ 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'})
63
+ adaptor.connection.should_receive(:open).and_yield(adaptor.connection)
64
+ adaptor.connection.should_receive(:search).with(args).and_return([rs])
65
+ adaptor.connection.should_receive(:bind).with({:username => 'new dn', :password => args[:password], :method => :simple}).and_return(true)
66
+ adaptor.bind_as(args).should == rs
67
+ end
68
+
69
+ it 'should bind sasl' do
70
+ 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'})
71
+ adaptor.connection.should_receive(:open).and_yield(adaptor.connection)
72
+ adaptor.connection.should_receive(:search).with(args).and_return([rs])
73
+ adaptor.connection.should_receive(:bind).and_return(true)
74
+ adaptor.bind_as(args).should == rs
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,194 @@
1
+ require 'spec_helper'
2
+ describe "OmniAuth::Strategies::LDAP" do
3
+ # :title => "My LDAP",
4
+ # :host => '10.101.10.1',
5
+ # :port => 389,
6
+ # :method => :plain,
7
+ # :base => 'dc=intridea, dc=com',
8
+ # :uid => 'sAMAccountName',
9
+ # :name_proc => Proc.new {|name| name.gsub(/@.*$/,'')}
10
+ # :bind_dn => 'default_bind_dn'
11
+ # :password => 'password'
12
+ class MyLdapProvider < OmniAuth::Strategies::LDAP; end
13
+
14
+ let(:app) do
15
+ Rack::Builder.new {
16
+ use OmniAuth::Test::PhonySession
17
+ use MyLdapProvider, :name => 'ldap', :title => 'MyLdap Form', :host => '192.168.1.145', :base => 'dc=score, dc=local', :name_proc => Proc.new {|name| name.gsub(/@.*$/,'')}
18
+ run lambda { |env| [404, {'Content-Type' => 'text/plain'}, [env.key?('omniauth.auth').to_s]] }
19
+ }.to_app
20
+ end
21
+
22
+ let(:session) do
23
+ last_request.env['rack.session']
24
+ end
25
+
26
+ it 'should add a camelization for itself' do
27
+ OmniAuth::Utils.camelize('ldap').should == 'LDAP'
28
+ end
29
+
30
+ describe '/auth/ldap' do
31
+ before(:each){ get '/auth/ldap' }
32
+
33
+ it 'should display a form' do
34
+ last_response.status.should == 200
35
+ last_response.body.should be_include("<form")
36
+ end
37
+
38
+ it 'should have the callback as the action for the form' do
39
+ last_response.body.should be_include("action='/auth/ldap/callback'")
40
+ end
41
+
42
+ it 'should have a text field for each of the fields' do
43
+ last_response.body.scan('<input').size.should == 2
44
+ end
45
+ it 'should have a label of the form title' do
46
+ last_response.body.scan('MyLdap Form').size.should > 1
47
+ end
48
+ end
49
+
50
+ describe 'post /auth/ldap/callback' do
51
+ before(:each) do
52
+ @adaptor = double(OmniAuth::LDAP::Adaptor, {:uid => 'ping'})
53
+ @adaptor.stub(:filter)
54
+ OmniAuth::LDAP::Adaptor.stub(:new).and_return(@adaptor)
55
+ end
56
+
57
+ context 'failure' do
58
+ before(:each) do
59
+ @adaptor.stub(:bind_as).and_return(false)
60
+ end
61
+
62
+ context "when username is not preset" do
63
+ it 'should redirect to error page' do
64
+ post('/auth/ldap/callback', {})
65
+
66
+ last_response.should be_redirect
67
+ last_response.headers['Location'].should =~ %r{missing_credentials}
68
+ end
69
+ end
70
+
71
+ context "when username is empty" do
72
+ it 'should redirect to error page' do
73
+ post('/auth/ldap/callback', {:username => ""})
74
+
75
+ last_response.should be_redirect
76
+ last_response.headers['Location'].should =~ %r{missing_credentials}
77
+ end
78
+ end
79
+
80
+ context "when username is present" do
81
+ context "and password is not preset" do
82
+ it 'should redirect to error page' do
83
+ post('/auth/ldap/callback', {:username => "ping"})
84
+
85
+ last_response.should be_redirect
86
+ last_response.headers['Location'].should =~ %r{missing_credentials}
87
+ end
88
+ end
89
+
90
+ context "and password is empty" do
91
+ it 'should redirect to error page' do
92
+ post('/auth/ldap/callback', {:username => "ping", :password => ""})
93
+
94
+ last_response.should be_redirect
95
+ last_response.headers['Location'].should =~ %r{missing_credentials}
96
+ end
97
+ end
98
+ end
99
+
100
+ context "when username and password are present" do
101
+ context "and bind on LDAP server failed" do
102
+ it 'should redirect to error page' do
103
+ post('/auth/ldap/callback', {:username => 'ping', :password => 'password'})
104
+
105
+ last_response.should be_redirect
106
+ last_response.headers['Location'].should =~ %r{invalid_credentials}
107
+ end
108
+ context 'and filter is set' do
109
+ it 'should bind with filter' do
110
+ @adaptor.stub(:filter).and_return('uid=%{username}')
111
+ Net::LDAP::Filter.should_receive(:construct).with('uid=ping')
112
+ post('/auth/ldap/callback', {:username => 'ping', :password => 'password'})
113
+
114
+ last_response.should be_redirect
115
+ last_response.headers['Location'].should =~ %r{invalid_credentials}
116
+ end
117
+ end
118
+
119
+ end
120
+
121
+ context "and communication with LDAP server caused an exception" do
122
+ before :each do
123
+ @adaptor.stub(:bind_as).and_throw(Exception.new('connection_error'))
124
+ end
125
+
126
+ it 'should redirect to error page' do
127
+ post('/auth/ldap/callback', {:username => "ping", :password => "password"})
128
+
129
+ last_response.should be_redirect
130
+ last_response.headers['Location'].should =~ %r{ldap_error}
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ context 'success' do
137
+ let(:auth_hash){ last_request.env['omniauth.auth'] }
138
+
139
+ before(:each) do
140
+ @adaptor.stub(:filter)
141
+ @adaptor.stub(:bind_as).and_return(Net::LDAP::Entry.from_single_ldif_string(
142
+ %Q{dn: cn=ping, dc=intridea, dc=com
143
+ mail: ping@intridea.com
144
+ givenname: Ping
145
+ sn: Yu
146
+ telephonenumber: 555-555-5555
147
+ mobile: 444-444-4444
148
+ uid: ping
149
+ title: dev
150
+ address: k street
151
+ l: Washington
152
+ st: DC
153
+ co: U.S.A
154
+ postofficebox: 20001
155
+ wwwhomepage: www.intridea.com
156
+ jpegphoto: http://www.intridea.com/ping.jpg
157
+ description: omniauth-ldap
158
+ }
159
+ ))
160
+ end
161
+
162
+ it 'should not redirect to error page' do
163
+ post('/auth/ldap/callback', {:username => 'ping', :password => 'password'})
164
+ last_response.should_not be_redirect
165
+ end
166
+
167
+ context 'and filter is set' do
168
+ it 'should bind with filter' do
169
+ @adaptor.stub(:filter).and_return('uid=%{username}')
170
+ Net::LDAP::Filter.should_receive(:construct).with('uid=ping')
171
+ post('/auth/ldap/callback', {:username => 'ping', :password => 'password'})
172
+
173
+ last_response.should_not be_redirect
174
+ end
175
+ end
176
+
177
+ it 'should map user info to Auth Hash' do
178
+ post('/auth/ldap/callback', {:username => 'ping', :password => 'password'})
179
+ auth_hash.uid.should == 'cn=ping, dc=intridea, dc=com'
180
+ auth_hash.info.email.should == 'ping@intridea.com'
181
+ auth_hash.info.first_name.should == 'Ping'
182
+ auth_hash.info.last_name.should == 'Yu'
183
+ auth_hash.info.phone.should == '555-555-5555'
184
+ auth_hash.info.mobile.should == '444-444-4444'
185
+ auth_hash.info.nickname.should == 'ping'
186
+ auth_hash.info.title.should == 'dev'
187
+ auth_hash.info.location.should == 'k street, Washington, DC, U.S.A 20001'
188
+ auth_hash.info.url.should == 'www.intridea.com'
189
+ auth_hash.info.image.should == 'http://www.intridea.com/ping.jpg'
190
+ auth_hash.info.description.should == 'omniauth-ldap'
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,14 @@
1
+ $:.unshift File.expand_path('..', __FILE__)
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+ require 'rspec'
6
+ require 'rack/test'
7
+ require 'omniauth'
8
+ require 'omniauth-ldap'
9
+
10
+ RSpec.configure do |config|
11
+ config.include Rack::Test::Methods
12
+ config.extend OmniAuth::Test::StrategyMacros, :type => :strategy
13
+ end
14
+
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-ldap2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Ping Yu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: omniauth
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: net-ldap
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pyu-ruby-sasl
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubyntlm
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack-test
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: libnotify
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: ruby-debug19
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: A LDAP strategy for OmniAuth.
140
+ email:
141
+ - ping@intridea.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rspec"
148
+ - Gemfile
149
+ - Guardfile
150
+ - README.md
151
+ - Rakefile
152
+ - lib/omniauth-ldap.rb
153
+ - lib/omniauth-ldap/adaptor.rb
154
+ - lib/omniauth-ldap/version.rb
155
+ - lib/omniauth/strategies/ldap.rb
156
+ - omniauth-ldap2.gemspec
157
+ - spec/omniauth-ldap/adaptor_spec.rb
158
+ - spec/omniauth/strategies/ldap_spec.rb
159
+ - spec/spec_helper.rb
160
+ homepage: https://github.com/intridea/omniauth-ldap
161
+ licenses:
162
+ - MIT
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ requirements: []
179
+ rubyforge_project:
180
+ rubygems_version: 2.6.12
181
+ signing_key:
182
+ specification_version: 4
183
+ summary: A LDAP strategy for OmniAuth.
184
+ test_files:
185
+ - spec/omniauth-ldap/adaptor_spec.rb
186
+ - spec/omniauth/strategies/ldap_spec.rb
187
+ - spec/spec_helper.rb