omniauth-ldap2 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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