github-ldap 1.10.0 → 1.10.1
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.
- checksums.yaml +4 -4
- data/.travis.yml +7 -1
- data/CHANGELOG.md +4 -0
- data/Gemfile +4 -0
- data/README.md +1 -0
- data/github-ldap.gemspec +2 -2
- data/lib/github/ldap.rb +46 -1
- data/lib/github/ldap/connection_cache.rb +26 -0
- data/lib/github/ldap/domain.rb +1 -4
- data/lib/github/ldap/membership_validators/active_directory.rb +10 -2
- data/lib/github/ldap/referral_chaser.rb +98 -0
- data/lib/github/ldap/url.rb +87 -0
- data/lib/github/ldap/user_search/active_directory.rb +51 -0
- data/lib/github/ldap/user_search/default.rb +40 -0
- data/test/connection_cache_test.rb +18 -0
- data/test/domain_test.rb +9 -0
- data/test/ldap_test.rb +26 -0
- data/test/referral_chaser_test.rb +102 -0
- data/test/test_helper.rb +2 -0
- data/test/url_test.rb +85 -0
- data/test/user_search/active_directory_test.rb +53 -0
- data/test/user_search/default_test.rb +19 -0
- metadata +20 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ef4b9c90ff1e809834fe933d93ba5679dd19a1a
|
4
|
+
data.tar.gz: 107f31fc7a1b7d30de40ab06976ff2f4ae291adf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 606264ea40bdb144f2b13b2e15cf8bc07af9c291730efe9b0b3c4c0f39b318317e580937a7f23e21514553209e3e24591628900276347fed2d7a395b75bd39fa
|
7
|
+
data.tar.gz: 95c0822fdaf65f6c1c426ef2a2972c64e61a7e6a2655285e84ae4fd7cd43f795b5d5f1e8cf0929a7ae8e4be35a119170b814e0c01f892394f75e83e14413984d
|
data/.travis.yml
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
-
|
3
|
+
- 2.0.0
|
4
4
|
- 2.1.0
|
5
5
|
|
6
6
|
env:
|
7
7
|
- TESTENV=openldap
|
8
8
|
- TESTENV=apacheds
|
9
9
|
|
10
|
+
# https://docs.travis-ci.com/user/hosts/
|
11
|
+
addons:
|
12
|
+
hosts:
|
13
|
+
- ad1.ghe.dev
|
14
|
+
- ad2.ghe.dev
|
15
|
+
|
10
16
|
install:
|
11
17
|
- if [ "$TESTENV" = "openldap" ]; then ./script/install-openldap; fi
|
12
18
|
- bundle install
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -28,6 +28,7 @@ There are a few configuration options required to use this adapter:
|
|
28
28
|
|
29
29
|
* host: is the host address where the ldap server lives.
|
30
30
|
* port: is the port where the ldap server lives.
|
31
|
+
* hosts: (optional) an enumerable of pairs of hosts and corresponding ports with which to attempt opening connections (default [[host, port]]). Overrides host and port if set.
|
31
32
|
* encryption: is the encryption protocol, disabled by default. The valid options are `ssl` and `tls`.
|
32
33
|
* uid: is the field name in the ldap server used to authenticate your users, in ActiveDirectory this is `sAMAccountName`.
|
33
34
|
|
data/github-ldap.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = "github-ldap"
|
5
|
-
spec.version = "1.10.
|
5
|
+
spec.version = "1.10.1"
|
6
6
|
spec.authors = ["David Calavera", "Matt Todd"]
|
7
7
|
spec.email = ["david.calavera@gmail.com", "chiology@gmail.com"]
|
8
8
|
spec.description = %q{LDAP authentication for humans}
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
|
|
15
15
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
16
|
spec.require_paths = ["lib"]
|
17
17
|
|
18
|
-
spec.add_dependency 'net-ldap', '~> 0.
|
18
|
+
spec.add_dependency 'net-ldap', '~> 0.16.0'
|
19
19
|
|
20
20
|
spec.add_development_dependency "bundler", "~> 1.3"
|
21
21
|
spec.add_development_dependency 'ladle'
|
data/lib/github/ldap.rb
CHANGED
@@ -10,6 +10,11 @@ require 'github/ldap/virtual_attributes'
|
|
10
10
|
require 'github/ldap/instrumentation'
|
11
11
|
require 'github/ldap/member_search'
|
12
12
|
require 'github/ldap/membership_validators'
|
13
|
+
require 'github/ldap/user_search/default'
|
14
|
+
require 'github/ldap/user_search/active_directory'
|
15
|
+
require 'github/ldap/connection_cache'
|
16
|
+
require 'github/ldap/referral_chaser'
|
17
|
+
require 'github/ldap/url'
|
13
18
|
|
14
19
|
module GitHub
|
15
20
|
class Ldap
|
@@ -38,11 +43,17 @@ module GitHub
|
|
38
43
|
#
|
39
44
|
# Returns the return value of the block.
|
40
45
|
def_delegator :@connection, :open
|
46
|
+
def_delegator :@connection, :host
|
41
47
|
|
42
48
|
attr_reader :uid, :search_domains, :virtual_attributes,
|
43
49
|
:membership_validator,
|
44
50
|
:member_search_strategy,
|
45
|
-
:instrumentation_service
|
51
|
+
:instrumentation_service,
|
52
|
+
:user_search_strategy,
|
53
|
+
:connection,
|
54
|
+
:admin_user,
|
55
|
+
:admin_password,
|
56
|
+
:port
|
46
57
|
|
47
58
|
# Build a new GitHub::Ldap instance
|
48
59
|
#
|
@@ -50,6 +61,9 @@ module GitHub
|
|
50
61
|
#
|
51
62
|
# host: required string ldap server host address
|
52
63
|
# port: required string or number ldap server port
|
64
|
+
# hosts: an enumerable of pairs of hosts and corresponding ports with
|
65
|
+
# which to attempt opening connections (default [[host, port]]). Overrides
|
66
|
+
# host and port if set.
|
53
67
|
# encryption: optional string. `ssl` or `tls`. nil by default
|
54
68
|
# admin_user: optional string ldap administrator user dn for authentication
|
55
69
|
# admin_password: optional string ldap administrator user password
|
@@ -69,9 +83,15 @@ module GitHub
|
|
69
83
|
def initialize(options = {})
|
70
84
|
@uid = options[:uid] || "sAMAccountName"
|
71
85
|
|
86
|
+
# Keep a reference to these as default auth for a Global Catalog if needed
|
87
|
+
@admin_user = options[:admin_user]
|
88
|
+
@admin_password = options[:admin_password]
|
89
|
+
@port = options[:port]
|
90
|
+
|
72
91
|
@connection = Net::LDAP.new({
|
73
92
|
host: options[:host],
|
74
93
|
port: options[:port],
|
94
|
+
hosts: options[:hosts],
|
75
95
|
instrumentation_service: options[:instrumentation_service]
|
76
96
|
})
|
77
97
|
|
@@ -98,6 +118,9 @@ module GitHub
|
|
98
118
|
# configure both the membership validator and the member search strategies
|
99
119
|
configure_search_strategy(options[:search_strategy])
|
100
120
|
|
121
|
+
# configure the strategy used by Domain#user? to look up a user entry for login
|
122
|
+
configure_user_search_strategy(options[:user_search_strategy])
|
123
|
+
|
101
124
|
# enables instrumenting queries
|
102
125
|
@instrumentation_service = options[:instrumentation_service]
|
103
126
|
end
|
@@ -281,6 +304,28 @@ module GitHub
|
|
281
304
|
end
|
282
305
|
end
|
283
306
|
|
307
|
+
# Internal: Set the user search strategy that will be used by
|
308
|
+
# Domain#user?.
|
309
|
+
#
|
310
|
+
# strategy - Can be either 'default' or 'global_catalog'.
|
311
|
+
# 'default' strategy will search the configured
|
312
|
+
# domain controller with a search base relative
|
313
|
+
# to the controller's domain context.
|
314
|
+
# 'global_catalog' will search the entire forest
|
315
|
+
# using Active Directory's Global Catalog
|
316
|
+
# functionality.
|
317
|
+
def configure_user_search_strategy(strategy)
|
318
|
+
@user_search_strategy =
|
319
|
+
case strategy.to_s
|
320
|
+
when "default"
|
321
|
+
GitHub::Ldap::UserSearch::Default.new(self)
|
322
|
+
when "global_catalog"
|
323
|
+
GitHub::Ldap::UserSearch::ActiveDirectory.new(self)
|
324
|
+
else
|
325
|
+
GitHub::Ldap::UserSearch::Default.new(self)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
284
329
|
# Internal: Configure the member search strategy.
|
285
330
|
#
|
286
331
|
#
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module GitHub
|
2
|
+
class Ldap
|
3
|
+
|
4
|
+
# A simple cache of GitHub::Ldap objects to prevent creating multiple
|
5
|
+
# instances of connections that point to the same URI/host.
|
6
|
+
class ConnectionCache
|
7
|
+
|
8
|
+
# Public - Create or return cached instance of GitHub::Ldap created with options,
|
9
|
+
# where the cache key is the value of options[:host].
|
10
|
+
#
|
11
|
+
# options - Initialization attributes suitable for creating a new connection with
|
12
|
+
# GitHub::Ldap.new(options)
|
13
|
+
#
|
14
|
+
# Returns true or false.
|
15
|
+
def self.get_connection(options={})
|
16
|
+
@cache ||= self.new
|
17
|
+
@cache.get_connection(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_connection(options)
|
21
|
+
@connections ||= {}
|
22
|
+
@connections[options[:host]] ||= GitHub::Ldap.new(options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/github/ldap/domain.rb
CHANGED
@@ -115,10 +115,7 @@ module GitHub
|
|
115
115
|
# Returns the user if the login matches any `uid`.
|
116
116
|
# Returns nil if there are no matches.
|
117
117
|
def user?(login, search_options = {})
|
118
|
-
|
119
|
-
filter: login_filter(@uid, login),
|
120
|
-
size: 1
|
121
|
-
search(options).first
|
118
|
+
@ldap.user_search_strategy.perform(login, @base_name, @uid, search_options).first
|
122
119
|
end
|
123
120
|
|
124
121
|
# Check if a user can be bound with a password.
|
@@ -24,15 +24,23 @@ module GitHub
|
|
24
24
|
# Sets the entry to the base and scopes the search to the base,
|
25
25
|
# according to the source documentation, found here:
|
26
26
|
# http://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx
|
27
|
-
|
27
|
+
#
|
28
|
+
# Use ReferralChaser to chase any potential referrals for an entry that may be owned by a different
|
29
|
+
# domain controller.
|
30
|
+
matched = referral_chaser.search \
|
28
31
|
filter: membership_in_chain_filter(entry),
|
29
32
|
base: entry.dn,
|
30
33
|
scope: Net::LDAP::SearchScope_BaseObject,
|
34
|
+
return_referrals: true,
|
31
35
|
attributes: ATTRS
|
32
36
|
|
33
37
|
# membership validated if entry was matched and returned as a result
|
34
38
|
# Active Directory DNs are case-insensitive
|
35
|
-
matched.map { |m| m.dn.downcase }.include?(entry.dn.downcase)
|
39
|
+
Array(matched).map { |m| m.dn.downcase }.include?(entry.dn.downcase)
|
40
|
+
end
|
41
|
+
|
42
|
+
def referral_chaser
|
43
|
+
@referral_chaser ||= GitHub::Ldap::ReferralChaser.new(@ldap)
|
36
44
|
end
|
37
45
|
|
38
46
|
# Internal: Constructs a membership filter using the "in chain"
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module GitHub
|
2
|
+
class Ldap
|
3
|
+
|
4
|
+
# This class adds referral chasing capability to a GitHub::Ldap connection.
|
5
|
+
#
|
6
|
+
# See: https://technet.microsoft.com/en-us/library/cc978014.aspx
|
7
|
+
# http://www.umich.edu/~dirsvcs/ldap/doc/other/ldap-ref.html
|
8
|
+
#
|
9
|
+
class ReferralChaser
|
10
|
+
|
11
|
+
# Public - Creates a ReferralChaser that decorates an instance of GitHub::Ldap
|
12
|
+
# with additional functionality to the #search method, allowing it to chase
|
13
|
+
# any referral entries and aggregate the results into a single response.
|
14
|
+
#
|
15
|
+
# connection - The instance of GitHub::Ldap to use for searching. Will use
|
16
|
+
# the connection's authentication, (admin_user and admin_password) as credentials
|
17
|
+
# for connecting to referred domain controllers.
|
18
|
+
def initialize(connection)
|
19
|
+
@connection = connection
|
20
|
+
@admin_user = connection.admin_user
|
21
|
+
@admin_password = connection.admin_password
|
22
|
+
@port = connection.port
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public - Search the domain controller represented by this instance's connection.
|
26
|
+
# If a referral is returned, search only one of the domain controllers indicated
|
27
|
+
# by the referral entries, per RFC 4511 (https://tools.ietf.org/html/rfc4511):
|
28
|
+
#
|
29
|
+
# "If the client wishes to progress the operation, it contacts one of
|
30
|
+
# the supported services found in the referral. If multiple URIs are
|
31
|
+
# present, the client assumes that any supported URI may be used to
|
32
|
+
# progress the operation."
|
33
|
+
#
|
34
|
+
# options - is a hash with the same options that Net::LDAP::Connection#search supports.
|
35
|
+
# Referral searches will use the given options, but will replace options[:base]
|
36
|
+
# with the referral URL's base search dn.
|
37
|
+
#
|
38
|
+
# Does not take a block argument as GitHub::Ldap and Net::LDAP::Connection#search do.
|
39
|
+
#
|
40
|
+
# Will not recursively follow any subsequent referrals.
|
41
|
+
#
|
42
|
+
# Returns an Array of Net::LDAP::Entry.
|
43
|
+
def search(options)
|
44
|
+
search_results = []
|
45
|
+
referral_entries = []
|
46
|
+
|
47
|
+
search_results = connection.search(options) do |entry|
|
48
|
+
if entry && entry[:search_referrals]
|
49
|
+
referral_entries << entry
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
unless referral_entries.empty?
|
54
|
+
entry = referral_entries.first
|
55
|
+
referral_string = entry[:search_referrals].first
|
56
|
+
if GitHub::Ldap::URL.valid?(referral_string)
|
57
|
+
referral = Referral.new(referral_string, admin_user, admin_password, port)
|
58
|
+
search_results = referral.search(options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Array(search_results)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
attr_reader :connection, :admin_user, :admin_password, :port
|
68
|
+
|
69
|
+
# Represents a referral entry from an LDAP search result. Constructs a corresponding
|
70
|
+
# GitHub::Ldap object from the paramaters on the referral_url and provides a #search
|
71
|
+
# method to continue the search on the referred domain.
|
72
|
+
class Referral
|
73
|
+
def initialize(referral_url, admin_user, admin_password, port=nil)
|
74
|
+
url = GitHub::Ldap::URL.new(referral_url)
|
75
|
+
@search_base = url.dn
|
76
|
+
|
77
|
+
connection_options = {
|
78
|
+
host: url.host,
|
79
|
+
port: port || url.port,
|
80
|
+
scope: url.scope,
|
81
|
+
admin_user: admin_user,
|
82
|
+
admin_password: admin_password
|
83
|
+
}
|
84
|
+
|
85
|
+
@connection = GitHub::Ldap::ConnectionCache.get_connection(connection_options)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Search the referred domain controller with options, merging in the referred search
|
89
|
+
# base DN onto options[:base].
|
90
|
+
def search(options)
|
91
|
+
connection.search(options.merge(base: search_base))
|
92
|
+
end
|
93
|
+
|
94
|
+
attr_reader :search_base, :connection
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module GitHub
|
2
|
+
class Ldap
|
3
|
+
|
4
|
+
# This class represents an LDAP URL
|
5
|
+
#
|
6
|
+
# See: https://tools.ietf.org/html/rfc4516#section-2
|
7
|
+
# https://docs.oracle.com/cd/E19957-01/817-6707/urls.html
|
8
|
+
#
|
9
|
+
class URL
|
10
|
+
extend Forwardable
|
11
|
+
SCOPES = {
|
12
|
+
"base" => Net::LDAP::SearchScope_BaseObject,
|
13
|
+
"one" => Net::LDAP::SearchScope_SingleLevel,
|
14
|
+
"sub" => Net::LDAP::SearchScope_WholeSubtree
|
15
|
+
}
|
16
|
+
SCOPES.default = Net::LDAP::SearchScope_BaseObject
|
17
|
+
|
18
|
+
attr_reader :dn, :attributes, :scope, :filter
|
19
|
+
|
20
|
+
def_delegators :@uri, :port, :host, :scheme
|
21
|
+
|
22
|
+
# Public - Creates a new GitHub::Ldap::URL object with :port, :host and :scheme
|
23
|
+
# delegated to a URI object parsed from url_string, and then parses the
|
24
|
+
# query params according to the LDAP specification.
|
25
|
+
#
|
26
|
+
# url_string - An LDAP URL string.
|
27
|
+
# returns - a GitHub::Ldap::URL with the following attributes:
|
28
|
+
# host - Name or IP of the LDAP server.
|
29
|
+
# port - The given port, defaults to 389.
|
30
|
+
# dn - The base search DN.
|
31
|
+
# attributes - The comma-delimited list of attributes to be returned.
|
32
|
+
# scope - The scope of the search.
|
33
|
+
# filter - Search filter to apply to entries within the specified scope of the search.
|
34
|
+
#
|
35
|
+
# Supported LDAP URL strings look like this, where sections in brackets are optional:
|
36
|
+
#
|
37
|
+
# ldap[s]://[hostport][/[dn[?[attributes][?[scope][?[filter]]]]]]
|
38
|
+
#
|
39
|
+
# where:
|
40
|
+
#
|
41
|
+
# hostport is a host name with an optional ":portnumber"
|
42
|
+
# dn is the base DN to be used for an LDAP search operation
|
43
|
+
# attributes is a comma separated list of attributes to be retrieved
|
44
|
+
# scope is one of these three strings: base one sub (default=base)
|
45
|
+
# filter is LDAP search filter as used in a call to ldap_search
|
46
|
+
#
|
47
|
+
# For example:
|
48
|
+
#
|
49
|
+
# ldap://dc4.ghe.local:456/CN=Maggie,DC=dc4,DC=ghe,DC=local?cn,mail?base?(cn=Charlie)
|
50
|
+
#
|
51
|
+
def initialize(url_string)
|
52
|
+
if !self.class.valid?(url_string)
|
53
|
+
raise InvalidLdapURLException.new("Invalid LDAP URL: #{url_string}")
|
54
|
+
end
|
55
|
+
@uri = URI(url_string)
|
56
|
+
@dn = URI.unescape(@uri.path.sub(/^\//, ""))
|
57
|
+
if @uri.query
|
58
|
+
@attributes, @scope, @filter = @uri.query.split("?")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.valid?(url_string)
|
63
|
+
url_string =~ URI::regexp && ["ldap", "ldaps"].include?(URI(url_string).scheme)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Maps the returned scope value from the URL to one of Net::LDAP::Scopes
|
67
|
+
#
|
68
|
+
# The URL scope value can be one of:
|
69
|
+
# "base" - retrieves information only about the DN (base_dn) specified.
|
70
|
+
# "one" - retrieves information about entries one level below the DN (base_dn) specified. The base entry is not included in this scope.
|
71
|
+
# "sub" - retrieves information about entries at all levels below the DN (base_dn) specified. The base entry is included in this scope.
|
72
|
+
#
|
73
|
+
# Which will map to one of the following Net::LDAP::Scopes:
|
74
|
+
# SearchScope_BaseObject = 0
|
75
|
+
# SearchScope_SingleLevel = 1
|
76
|
+
# SearchScope_WholeSubtree = 2
|
77
|
+
#
|
78
|
+
# If no scope or an invalid scope is given, defaults to SearchScope_BaseObject
|
79
|
+
def net_ldap_scope
|
80
|
+
Net::LDAP::SearchScopes[SCOPES[scope]]
|
81
|
+
end
|
82
|
+
|
83
|
+
class InvalidLdapURLException < Exception; end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module GitHub
|
2
|
+
class Ldap
|
3
|
+
module UserSearch
|
4
|
+
class ActiveDirectory < Default
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
# Private - Overridden from base class to set the base to "", and use the
|
9
|
+
# Global Catalog to perform the user search.
|
10
|
+
def search(search_options)
|
11
|
+
Array(global_catalog_connection.search(search_options.merge(options)))
|
12
|
+
end
|
13
|
+
|
14
|
+
def global_catalog_connection
|
15
|
+
GlobalCatalog.connection(ldap)
|
16
|
+
end
|
17
|
+
|
18
|
+
# When doing a global search for a user's DN, set the search base to blank
|
19
|
+
def options
|
20
|
+
super.merge(base: "")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class GlobalCatalog < Net::LDAP
|
25
|
+
STANDARD_GC_PORT = 3268
|
26
|
+
LDAPS_GC_PORT = 3269
|
27
|
+
|
28
|
+
# Returns a connection to the Active Directory Global Catalog
|
29
|
+
#
|
30
|
+
# See: https://technet.microsoft.com/en-us/library/cc728188(v=ws.10).aspx
|
31
|
+
#
|
32
|
+
def self.connection(ldap)
|
33
|
+
@global_catalog_instance ||= begin
|
34
|
+
netldap = ldap.connection
|
35
|
+
# This is ugly, but Net::LDAP doesn't expose encryption or auth
|
36
|
+
encryption = netldap.instance_variable_get(:@encryption)
|
37
|
+
auth = netldap.instance_variable_get(:@auth)
|
38
|
+
|
39
|
+
new({
|
40
|
+
host: ldap.host,
|
41
|
+
instrumentation_service: ldap.instrumentation_service,
|
42
|
+
port: encryption ? LDAPS_GC_PORT : STANDARD_GC_PORT,
|
43
|
+
auth: auth,
|
44
|
+
encryption: encryption
|
45
|
+
})
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module GitHub
|
2
|
+
class Ldap
|
3
|
+
module UserSearch
|
4
|
+
# The default user search strategy, mainly for allowing Domain#user? to
|
5
|
+
# search for a user on the configured domain controller, or use the Global
|
6
|
+
# Catalog to search across the entire Active Directory forest.
|
7
|
+
class Default
|
8
|
+
include Filter
|
9
|
+
|
10
|
+
def initialize(ldap)
|
11
|
+
@ldap = ldap
|
12
|
+
@options = {
|
13
|
+
:attributes => [],
|
14
|
+
:paged_searches_supported => true,
|
15
|
+
:size => 1
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Performs a normal search on the configured domain controller
|
20
|
+
# using the default base DN, uid, search_options
|
21
|
+
def perform(login, base_name, uid, search_options)
|
22
|
+
search_options[:filter] = login_filter(uid, login)
|
23
|
+
search_options[:base] = base_name
|
24
|
+
search(options.merge(search_options))
|
25
|
+
end
|
26
|
+
|
27
|
+
# The default search. This can be overridden by a child class
|
28
|
+
# like GitHub::Ldap::UserSearch::ActiveDirectory to change the
|
29
|
+
# scope of the search.
|
30
|
+
def search(options)
|
31
|
+
ldap.search(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :options, :ldap
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class GitHubLdapConnectionCacheTestCases < GitHub::Ldap::Test
|
4
|
+
|
5
|
+
def test_returns_cached_connection
|
6
|
+
conn1 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad1.ghe.dev"))
|
7
|
+
conn2 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad1.ghe.dev"))
|
8
|
+
assert_equal conn1.object_id, conn2.object_id
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_creates_new_connections_per_host
|
12
|
+
conn1 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad1.ghe.dev"))
|
13
|
+
conn2 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad2.ghe.dev"))
|
14
|
+
conn3 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad2.ghe.dev"))
|
15
|
+
refute_equal conn1.object_id, conn2.object_id
|
16
|
+
assert_equal conn2.object_id, conn3.object_id
|
17
|
+
end
|
18
|
+
end
|
data/test/domain_test.rb
CHANGED
@@ -140,6 +140,15 @@ module GitHubLdapDomainTestCases
|
|
140
140
|
assert user = @domain.user?('user1')
|
141
141
|
refute @domain.auth(user, 'foo'), 'Expected user not not bind'
|
142
142
|
end
|
143
|
+
|
144
|
+
def test_user_search_returns_first_entry
|
145
|
+
entry = mock("Net::Ldap::Entry")
|
146
|
+
search_strategy = mock("GitHub::Ldap::UserSearch::Default")
|
147
|
+
search_strategy.stubs(:perform).returns([entry])
|
148
|
+
@ldap.expects(:user_search_strategy).returns(search_strategy)
|
149
|
+
user = @domain.user?('user1', :attributes => [:cn])
|
150
|
+
assert_equal entry, user
|
151
|
+
end
|
143
152
|
end
|
144
153
|
|
145
154
|
class GitHubLdapDomainTest < GitHub::Ldap::Test
|
data/test/ldap_test.rb
CHANGED
@@ -9,6 +9,21 @@ module GitHubLdapTestCases
|
|
9
9
|
assert @ldap.test_connection, "Ldap connection expected to succeed"
|
10
10
|
end
|
11
11
|
|
12
|
+
def test_connection_with_list_of_hosts_with_one_valid_host
|
13
|
+
ldap = GitHub::Ldap.new(options.merge(hosts: [["localhost", options[:port]]]))
|
14
|
+
assert ldap.test_connection, "Ldap connection expected to succeed"
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_connection_with_list_of_hosts_with_first_valid
|
18
|
+
ldap = GitHub::Ldap.new(options.merge(hosts: [["localhost", options[:port]], ["invalid.local", options[:port]]]))
|
19
|
+
assert ldap.test_connection, "Ldap connection expected to succeed"
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_connection_with_list_of_hosts_with_first_invalid
|
23
|
+
ldap = GitHub::Ldap.new(options.merge(hosts: [["invalid.local", options[:port]], ["localhost", options[:port]]]))
|
24
|
+
assert ldap.test_connection, "Ldap connection expected to succeed"
|
25
|
+
end
|
26
|
+
|
12
27
|
def test_simple_tls
|
13
28
|
assert_equal :simple_tls, @ldap.check_encryption(:ssl)
|
14
29
|
assert_equal :simple_tls, @ldap.check_encryption('SSL')
|
@@ -114,6 +129,17 @@ module GitHubLdapTestCases
|
|
114
129
|
assert_equal GitHub::Ldap::MemberSearch::Recursive, @ldap.member_search_strategy
|
115
130
|
end
|
116
131
|
|
132
|
+
def test_user_search_strategy_global_catalog_when_configured
|
133
|
+
@ldap.configure_user_search_strategy("global_catalog")
|
134
|
+
assert_kind_of GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_user_search_strategy_default_when_configured
|
138
|
+
@ldap.configure_user_search_strategy("default")
|
139
|
+
refute_kind_of GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy
|
140
|
+
assert_kind_of GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy
|
141
|
+
end
|
142
|
+
|
117
143
|
def test_capabilities
|
118
144
|
assert_kind_of Net::LDAP::Entry, @ldap.capabilities
|
119
145
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class GitHubLdapReferralChaserTestCases < GitHub::Ldap::Test
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@ldap = GitHub::Ldap.new(options)
|
7
|
+
@chaser = GitHub::Ldap::ReferralChaser.new(@ldap)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_creates_referral_with_connection_credentials
|
11
|
+
@ldap.expects(:search).yields({ search_referrals: ["ldap://dc1.ghe.local/"]}).returns([])
|
12
|
+
|
13
|
+
referral = mock("GitHub::Ldap::ReferralChaser::Referral")
|
14
|
+
referral.stubs(:search).returns([])
|
15
|
+
|
16
|
+
GitHub::Ldap::ReferralChaser::Referral.expects(:new)
|
17
|
+
.with("ldap://dc1.ghe.local/", "uid=admin,dc=github,dc=com", "passworD1", options[:port])
|
18
|
+
.returns(referral)
|
19
|
+
|
20
|
+
@chaser.search({})
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_creates_referral_with_default_port
|
24
|
+
@ldap.expects(:search).yields({
|
25
|
+
search_referrals: ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]
|
26
|
+
}).returns([])
|
27
|
+
|
28
|
+
stub_referral_connection = mock("GitHub::Ldap")
|
29
|
+
stub_referral_connection.stubs(:search).returns([])
|
30
|
+
GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(port: options[:port])).returns(stub_referral_connection)
|
31
|
+
chaser = GitHub::Ldap::ReferralChaser.new(@ldap)
|
32
|
+
chaser.search({})
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_creates_referral_for_first_referral_string
|
36
|
+
@ldap.expects(:search).multiple_yields([
|
37
|
+
{ search_referrals:
|
38
|
+
["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local",
|
39
|
+
"ldap://dc2.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]
|
40
|
+
}
|
41
|
+
],[
|
42
|
+
{ search_referrals:
|
43
|
+
["ldap://dc3.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local",
|
44
|
+
"ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]
|
45
|
+
}
|
46
|
+
]).returns([])
|
47
|
+
|
48
|
+
referral = mock("GitHub::Ldap::ReferralChaser::Referral")
|
49
|
+
referral.stubs(:search).returns([])
|
50
|
+
|
51
|
+
GitHub::Ldap::ReferralChaser::Referral.expects(:new)
|
52
|
+
.with(
|
53
|
+
"ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local",
|
54
|
+
"uid=admin,dc=github,dc=com",
|
55
|
+
"passworD1",
|
56
|
+
options[:port])
|
57
|
+
.returns(referral)
|
58
|
+
|
59
|
+
@chaser.search({})
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_returns_referral_search_results
|
63
|
+
@ldap.expects(:search).multiple_yields([
|
64
|
+
{ search_referrals:
|
65
|
+
["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local",
|
66
|
+
"ldap://dc2.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]
|
67
|
+
}
|
68
|
+
],[
|
69
|
+
{ search_referrals:
|
70
|
+
["ldap://dc3.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local",
|
71
|
+
"ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]
|
72
|
+
}
|
73
|
+
]).returns([])
|
74
|
+
|
75
|
+
referral = mock("GitHub::Ldap::ReferralChaser::Referral")
|
76
|
+
referral.expects(:search).returns(["result", "result"])
|
77
|
+
|
78
|
+
GitHub::Ldap::ReferralChaser::Referral.expects(:new).returns(referral)
|
79
|
+
|
80
|
+
results = @chaser.search({})
|
81
|
+
assert_equal(["result", "result"], results)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_handle_blank_url_string_in_referral
|
85
|
+
@ldap.expects(:search).yields({ search_referrals: [""] })
|
86
|
+
|
87
|
+
results = @chaser.search({})
|
88
|
+
assert_equal([], results)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_returns_referral_search_results
|
92
|
+
@ldap.expects(:search).yields({ foo: ["not a referral"] })
|
93
|
+
|
94
|
+
GitHub::Ldap::ReferralChaser::Referral.expects(:new).never
|
95
|
+
results = @chaser.search({})
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_referral_should_use_host_from_referral_string
|
99
|
+
GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(host: "dc4.ghe.local"))
|
100
|
+
GitHub::Ldap::ReferralChaser::Referral.new("ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "", "")
|
101
|
+
end
|
102
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -13,6 +13,8 @@ require 'github/ldap/server'
|
|
13
13
|
require 'minitest/mock'
|
14
14
|
require 'minitest/autorun'
|
15
15
|
|
16
|
+
require 'mocha/mini_test'
|
17
|
+
|
16
18
|
if ENV.fetch('TESTENV', "apacheds") == "apacheds"
|
17
19
|
# Make sure we clean up running test server
|
18
20
|
# NOTE: We need to do this manually since its internal `at_exit` hook
|
data/test/url_test.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class GitHubLdapURLTestCases < GitHub::Ldap::Test
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local:123/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local?cn,mail,telephoneNumber?base?(cn=Charlie)")
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_host
|
10
|
+
assert_equal "dc4.ghe.local", @url.host
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_port
|
14
|
+
assert_equal 123, @url.port
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_scheme
|
18
|
+
assert_equal "ldap", @url.scheme
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_default_port
|
22
|
+
url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local?attributes?scope?filter")
|
23
|
+
assert_equal 389, url.port
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_simple_url
|
27
|
+
url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local")
|
28
|
+
assert_equal 389, url.port
|
29
|
+
assert_equal "dc4.ghe.local", url.host
|
30
|
+
assert_equal "ldap", url.scheme
|
31
|
+
assert_equal "", url.dn
|
32
|
+
assert_equal nil, url.attributes
|
33
|
+
assert_equal nil, url.filter
|
34
|
+
assert_equal nil, url.scope
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_invalid_scheme
|
38
|
+
ex = assert_raises(GitHub::Ldap::URL::InvalidLdapURLException) do
|
39
|
+
GitHub::Ldap::URL.new("http://dc4.ghe.local")
|
40
|
+
end
|
41
|
+
assert_equal("Invalid LDAP URL: http://dc4.ghe.local", ex.message)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_invalid_url
|
45
|
+
ex = assert_raises(GitHub::Ldap::URL::InvalidLdapURLException) do
|
46
|
+
GitHub::Ldap::URL.new("not a url")
|
47
|
+
end
|
48
|
+
assert_equal("Invalid LDAP URL: not a url", ex.message)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_parse_dn
|
52
|
+
assert_equal "CN=Maggie Mae,CN=Users,DC=dc4,DC=ghe,DC=local", @url.dn
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_parse_attributes
|
56
|
+
assert_equal "cn,mail,telephoneNumber", @url.attributes
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_parse_filter
|
60
|
+
assert_equal "(cn=Charlie)", @url.filter
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_parse_scope
|
64
|
+
assert_equal "base", @url.scope
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_default_scope
|
68
|
+
url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/base_dn?cn=joe??filter")
|
69
|
+
assert_equal "", url.scope
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_net_ldap_scopes
|
73
|
+
sub_scope_url = GitHub::Ldap::URL.new("ldap://ghe.local/base_dn?cn=joe?sub?filter")
|
74
|
+
one_scope_url = GitHub::Ldap::URL.new("ldap://ghe.local/base_dn?cn=joe?one?filter")
|
75
|
+
base_scope_url = GitHub::Ldap::URL.new("ldap://ghe.local/base_dn?cn=joe?base?filter")
|
76
|
+
default_scope_url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/base_dn?cn=joe??filter")
|
77
|
+
invalid_scope_url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/base_dn?cn=joe?invalid?filter")
|
78
|
+
|
79
|
+
assert_equal Net::LDAP::SearchScope_BaseObject, base_scope_url.net_ldap_scope
|
80
|
+
assert_equal Net::LDAP::SearchScope_SingleLevel, one_scope_url.net_ldap_scope
|
81
|
+
assert_equal Net::LDAP::SearchScope_WholeSubtree, sub_scope_url.net_ldap_scope
|
82
|
+
assert_equal Net::LDAP::SearchScope_BaseObject, default_scope_url.net_ldap_scope
|
83
|
+
assert_equal Net::LDAP::SearchScope_BaseObject, invalid_scope_url.net_ldap_scope
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
class GitHubLdapActiveDirectoryUserSearchTests < GitHub::Ldap::Test
|
4
|
+
|
5
|
+
def test_global_catalog_returns_empty_array_for_no_results
|
6
|
+
ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev'))
|
7
|
+
ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(ldap)
|
8
|
+
|
9
|
+
mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog")
|
10
|
+
mock_global_catalog_connection.expects(:search).returns(nil)
|
11
|
+
ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection)
|
12
|
+
results = ad_user_search.perform("login", "CN=Joe", "uid", {})
|
13
|
+
assert_equal [], results
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_global_catalog_returns_array_of_results
|
17
|
+
ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev'))
|
18
|
+
ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(ldap)
|
19
|
+
|
20
|
+
mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog")
|
21
|
+
stub_entry = mock("Net::LDAP::Entry")
|
22
|
+
|
23
|
+
mock_global_catalog_connection.expects(:search).returns(stub_entry)
|
24
|
+
ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection)
|
25
|
+
|
26
|
+
results = ad_user_search.perform("login", "CN=Joe", "uid", {})
|
27
|
+
assert_equal [stub_entry], results
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_searches_with_empty_base_dn
|
31
|
+
ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev'))
|
32
|
+
ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(ldap)
|
33
|
+
|
34
|
+
mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog")
|
35
|
+
mock_global_catalog_connection.expects(:search).with(has_entry(:base => ""))
|
36
|
+
ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection)
|
37
|
+
ad_user_search.perform("login", "CN=Joe", "uid", {})
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_global_catalog_default_settings
|
41
|
+
ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev'))
|
42
|
+
global_catalog = GitHub::Ldap::UserSearch::GlobalCatalog.connection(ldap)
|
43
|
+
instrumentation_service = global_catalog.instance_variable_get(:@instrumentation_service)
|
44
|
+
|
45
|
+
auth = global_catalog.instance_variable_get(:@auth)
|
46
|
+
assert_equal :simple, auth[:method]
|
47
|
+
assert_equal "uid=admin,dc=github,dc=com", auth[:username]
|
48
|
+
assert_equal "passworD1", auth[:password]
|
49
|
+
assert_equal "ghe.dev", global_catalog.host
|
50
|
+
assert_equal 3268, global_catalog.port
|
51
|
+
assert_equal "MockInstrumentationService", instrumentation_service.class.name
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
class GitHubLdapActiveDirectoryUserSearchTests < GitHub::Ldap::Test
|
4
|
+
def setup
|
5
|
+
@ldap = GitHub::Ldap.new(options)
|
6
|
+
@default_user_search = GitHub::Ldap::UserSearch::Default.new(@ldap)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_default_search_options
|
10
|
+
@ldap.expects(:search).with(has_entries(
|
11
|
+
attributes: [],
|
12
|
+
size: 1,
|
13
|
+
paged_searches_supported: true,
|
14
|
+
base: "CN=HI,CN=McDunnough",
|
15
|
+
filter: kind_of(Net::LDAP::Filter)
|
16
|
+
))
|
17
|
+
@default_user_search.perform("","CN=HI,CN=McDunnough","",{})
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: github-ldap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.10.
|
4
|
+
version: 1.10.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Calavera
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2017-02-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: net-ldap
|
@@ -17,14 +17,14 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 0.
|
20
|
+
version: 0.16.0
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 0.
|
27
|
+
version: 0.16.0
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: bundler
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -98,6 +98,7 @@ files:
|
|
98
98
|
- Rakefile
|
99
99
|
- github-ldap.gemspec
|
100
100
|
- lib/github/ldap.rb
|
101
|
+
- lib/github/ldap/connection_cache.rb
|
101
102
|
- lib/github/ldap/domain.rb
|
102
103
|
- lib/github/ldap/filter.rb
|
103
104
|
- lib/github/ldap/fixtures.ldif
|
@@ -114,7 +115,11 @@ files:
|
|
114
115
|
- lib/github/ldap/membership_validators/classic.rb
|
115
116
|
- lib/github/ldap/membership_validators/recursive.rb
|
116
117
|
- lib/github/ldap/posix_group.rb
|
118
|
+
- lib/github/ldap/referral_chaser.rb
|
117
119
|
- lib/github/ldap/server.rb
|
120
|
+
- lib/github/ldap/url.rb
|
121
|
+
- lib/github/ldap/user_search/active_directory.rb
|
122
|
+
- lib/github/ldap/user_search/default.rb
|
118
123
|
- lib/github/ldap/virtual_attributes.rb
|
119
124
|
- lib/github/ldap/virtual_group.rb
|
120
125
|
- script/changelog
|
@@ -123,6 +128,7 @@ files:
|
|
123
128
|
- script/install-openldap
|
124
129
|
- script/package
|
125
130
|
- script/release
|
131
|
+
- test/connection_cache_test.rb
|
126
132
|
- test/domain_test.rb
|
127
133
|
- test/filter_test.rb
|
128
134
|
- test/fixtures/common/seed.ldif
|
@@ -138,6 +144,7 @@ files:
|
|
138
144
|
- test/membership_validators/classic_test.rb
|
139
145
|
- test/membership_validators/recursive_test.rb
|
140
146
|
- test/posix_group_test.rb
|
147
|
+
- test/referral_chaser_test.rb
|
141
148
|
- test/support/vm/activedirectory/.gitignore
|
142
149
|
- test/support/vm/activedirectory/README.md
|
143
150
|
- test/support/vm/activedirectory/env.sh.example
|
@@ -146,6 +153,9 @@ files:
|
|
146
153
|
- test/support/vm/openldap/README.md
|
147
154
|
- test/support/vm/openldap/Vagrantfile
|
148
155
|
- test/test_helper.rb
|
156
|
+
- test/url_test.rb
|
157
|
+
- test/user_search/active_directory_test.rb
|
158
|
+
- test/user_search/default_test.rb
|
149
159
|
homepage: https://github.com/github/github-ldap
|
150
160
|
licenses:
|
151
161
|
- MIT
|
@@ -166,11 +176,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
176
|
version: '0'
|
167
177
|
requirements: []
|
168
178
|
rubyforge_project:
|
169
|
-
rubygems_version: 2.5.
|
179
|
+
rubygems_version: 2.5.2
|
170
180
|
signing_key:
|
171
181
|
specification_version: 4
|
172
182
|
summary: LDAP client authentication wrapper without all the boilerplate
|
173
183
|
test_files:
|
184
|
+
- test/connection_cache_test.rb
|
174
185
|
- test/domain_test.rb
|
175
186
|
- test/filter_test.rb
|
176
187
|
- test/fixtures/common/seed.ldif
|
@@ -186,6 +197,7 @@ test_files:
|
|
186
197
|
- test/membership_validators/classic_test.rb
|
187
198
|
- test/membership_validators/recursive_test.rb
|
188
199
|
- test/posix_group_test.rb
|
200
|
+
- test/referral_chaser_test.rb
|
189
201
|
- test/support/vm/activedirectory/.gitignore
|
190
202
|
- test/support/vm/activedirectory/README.md
|
191
203
|
- test/support/vm/activedirectory/env.sh.example
|
@@ -194,3 +206,6 @@ test_files:
|
|
194
206
|
- test/support/vm/openldap/README.md
|
195
207
|
- test/support/vm/openldap/Vagrantfile
|
196
208
|
- test/test_helper.rb
|
209
|
+
- test/url_test.rb
|
210
|
+
- test/user_search/active_directory_test.rb
|
211
|
+
- test/user_search/default_test.rb
|