"ad1.ghe.dev"))
+ conn2 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad1.ghe.dev"))
+ assert_equal conn1.object_id, conn2.object_id
+ end
+
+ def test_creates_new_connections_per_host
+ conn1 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad1.ghe.dev"))
+ conn2 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad2.ghe.dev"))
+ conn3 = GitHub::Ldap::ConnectionCache.get_connection(options.merge(:host => "ad2.ghe.dev"))
+ refute_equal conn1.object_id, conn2.object_id
+ assert_equal conn2.object_id, conn3.object_id
+ end
+end
diff --git a/test/domain_test.rb b/test/domain_test.rb
index 470e00d..4fc0dee 100644
--- a/test/domain_test.rb
+++ b/test/domain_test.rb
@@ -7,13 +7,13 @@ def setup
end
def test_user_valid_login
- user = @domain.valid_login?('calavera', 'passworD1')
- assert_equal 'uid=calavera,dc=github,dc=com', user.dn
+ assert user = @domain.valid_login?('user1', 'passworD1')
+ assert_equal 'uid=user1,ou=People,dc=github,dc=com', user.dn
end
def test_user_with_invalid_password
- assert !@domain.valid_login?('calavera', 'foo'),
- "Login `calavera` expected to be invalid with password `foo`"
+ assert !@domain.valid_login?('user1', 'foo'),
+ "Login `user1` expected to be invalid with password `foo`"
end
def test_user_with_invalid_login
@@ -22,115 +22,132 @@ def test_user_with_invalid_login
end
def test_groups_in_server
- assert_equal 2, @domain.groups(%w(Enterprise People)).size
+ assert_equal 2, @domain.groups(%w(ghe-users ghe-admins)).size
end
def test_user_in_group
- user = @domain.valid_login?('calavera', 'passworD1')
+ assert user = @domain.valid_login?('user1', 'passworD1')
- assert @domain.is_member?(user, %w(Enterprise People)),
- "Expected `Enterprise` or `Poeple` to include the member `#{user.dn}`"
+ assert @domain.is_member?(user, %w(ghe-users ghe-admins)),
+ "Expected `ghe-users` or `ghe-admins` to include the member `#{user.dn}`"
end
def test_user_not_in_different_group
- user = @domain.valid_login?('calavera', 'passworD1')
+ user = @domain.valid_login?('user1', 'passworD1')
- assert !@domain.is_member?(user, %w(People)),
- "Expected `Poeple` not to include the member `#{user.dn}`"
+ refute @domain.is_member?(user, %w(ghe-admins)),
+ "Expected `ghe-admins` not to include the member `#{user.dn}`"
end
def test_user_without_group
- user = @domain.valid_login?('ldaptest', 'secret')
+ user = @domain.valid_login?('groupless-user1', 'passworD1')
- assert !@domain.is_member?(user, %w(People)),
- "Expected `People` not to include the member `#{user.dn}`"
+ assert !@domain.is_member?(user, %w(all-users)),
+ "Expected `all-users` not to include the member `#{user.dn}`"
end
- def test_authenticate_doesnt_return_invalid_users
- user = @domain.authenticate!('calavera', 'passworD1')
- assert_equal 'uid=calavera,dc=github,dc=com', user.dn
+ def test_authenticate_returns_valid_users
+ user = @domain.authenticate!('user1', 'passworD1')
+ assert_equal 'uid=user1,ou=People,dc=github,dc=com', user.dn
end
def test_authenticate_doesnt_return_invalid_users
- assert !@domain.authenticate!('calavera', 'foo'),
+ refute @domain.authenticate!('user1', 'foo'),
"Expected `authenticate!` to not return an invalid user"
end
def test_authenticate_check_valid_user_and_groups
- user = @domain.authenticate!('calavera', 'passworD1', %w(Enterprise People))
+ user = @domain.authenticate!('user1', 'passworD1', %w(ghe-users ghe-admins))
- assert_equal 'uid=calavera,dc=github,dc=com', user.dn
+ assert_equal 'uid=user1,ou=People,dc=github,dc=com', user.dn
end
def test_authenticate_doesnt_return_valid_users_in_different_groups
- assert !@domain.authenticate!('calavera', 'passworD1', %w(People)),
+ refute @domain.authenticate!('user1', 'passworD1', %w(ghe-admins)),
"Expected `authenticate!` to not return an user"
end
def test_membership_empty_for_non_members
- user = @ldap.domain('uid=calavera,dc=github,dc=com').bind
+ user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind
- assert @domain.membership(user, %w(People)).empty?,
- "Expected `calavera` not to be a member of `People`."
+ assert @domain.membership(user, %w(ghe-admins)).empty?,
+ "Expected `user1` not to be a member of `ghe-admins`."
end
def test_membership_groups_for_members
- user = @ldap.domain('uid=calavera,dc=github,dc=com').bind
- groups = @domain.membership(user, %w(Enterprise People))
+ user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind
+ groups = @domain.membership(user, %w(ghe-users ghe-admins))
assert_equal 1, groups.size
- assert_equal 'cn=Enterprise,ou=Group,dc=github,dc=com', groups.first.dn
+ assert_equal 'cn=ghe-users,ou=Groups,dc=github,dc=com', groups.first.dn
end
def test_membership_with_virtual_attributes
ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true))
- user = ldap.domain('uid=calavera,dc=github,dc=com').bind
- user[:memberof] = 'cn=Enterprise,ou=Group,dc=github,dc=com'
+
+ user = ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind
+ user[:memberof] = 'cn=ghe-admins,ou=Groups,dc=github,dc=com'
domain = ldap.domain("dc=github,dc=com")
- groups = domain.membership(user, %w(Enterprise People))
+ groups = domain.membership(user, %w(ghe-admins))
assert_equal 1, groups.size
- assert_equal 'cn=Enterprise,ou=Group,dc=github,dc=com', groups.first.dn
+ assert_equal 'cn=ghe-admins,ou=Groups,dc=github,dc=com', groups.first.dn
end
def test_search
assert 1, @domain.search(
attributes: %w(uid),
- filter: Net::LDAP::Filter.eq('uid', 'calavera')).size
+ filter: Net::LDAP::Filter.eq('uid', 'user1')).size
end
def test_search_override_base_name
assert 1, @domain.search(
base: "this base name is incorrect",
attributes: %w(uid),
- filter: Net::LDAP::Filter.eq('uid', 'calavera')).size
+ filter: Net::LDAP::Filter.eq('uid', 'user1')).size
end
def test_user_exists
- assert_equal 'uid=calavera,dc=github,dc=com', @domain.user?('calavera').dn
+ assert user = @domain.user?('user1')
+ assert_equal 'uid=user1,ou=People,dc=github,dc=com', user.dn
end
def test_user_wildcards_are_filtered
- assert !@domain.user?('cal*'), 'Expected uid `cal*` to not complete'
+ refute @domain.user?('user*'), 'Expected uid `user*` to not complete'
end
def test_user_does_not_exist
- assert !@domain.user?('foobar'), 'Expected uid `foobar` to not exist.'
+ refute @domain.user?('foobar'), 'Expected uid `foobar` to not exist.'
end
def test_user_returns_every_attribute
- assert_equal ['calavera@github.com'], @domain.user?('calavera')[:mail]
+ assert user = @domain.user?('user1')
+ assert_equal ['user1@github.com'], user[:mail]
+ end
+
+ def test_user_returns_subset_of_attributes
+ assert entry = @domain.user?('user1', :attributes => [:cn])
+ assert_equal [:dn, :cn], entry.attribute_names
end
def test_auth_binds
- user = @domain.user?('calavera')
- assert @domain.auth(user, 'passworD1'), 'Expected user to be bound.'
+ assert user = @domain.user?('user1')
+ assert @domain.auth(user, 'passworD1'), 'Expected user to bind'
end
def test_auth_does_not_bind
- user = @domain.user?('calavera')
- assert !@domain.auth(user, 'foo'), 'Expected user not to be bound.'
+ assert user = @domain.user?('user1')
+ refute @domain.auth(user, 'foo'), 'Expected user not not bind'
+ end
+
+ def test_user_search_returns_first_entry
+ entry = mock("Net::Ldap::Entry")
+ search_strategy = mock("GitHub::Ldap::UserSearch::Default")
+ search_strategy.stubs(:perform).returns([entry])
+ @ldap.expects(:user_search_strategy).returns(search_strategy)
+ user = @domain.user?('user1', :attributes => [:cn])
+ assert_equal entry, user
end
end
@@ -143,48 +160,37 @@ class GitHubLdapDomainUnauthenticatedTest < GitHub::Ldap::UnauthenticatedTest
end
class GitHubLdapDomainNestedGroupsTest < GitHub::Ldap::Test
- def self.test_server_options
- {user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s}
- end
-
def setup
@ldap = GitHub::Ldap.new(options)
@domain = @ldap.domain("dc=github,dc=com")
end
def test_membership_in_subgroups
- user = @ldap.domain('uid=rubiojr,ou=users,dc=github,dc=com').bind
+ user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind
- assert @domain.is_member?(user, %w(enterprise-ops)),
- "Expected `enterprise-ops` to include the member `#{user.dn}`"
+ assert @domain.is_member?(user, %w(nested-groups)),
+ "Expected `nested-groups` to include the member `#{user.dn}`"
end
def test_membership_in_deeply_nested_subgroups
- assert user = @ldap.domain('uid=user1.1.1.1,ou=users,dc=github,dc=com').bind
+ assert user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind
- assert @domain.is_member?(user, %w(group1)),
- "Expected `group1` to include the member `#{user.dn}` via deep recursion"
+ assert @domain.is_member?(user, %w(n-depth-nested-group4)),
+ "Expected `n-depth-nested-group4` to include the member `#{user.dn}` via deep recursion"
end
end
class GitHubLdapPosixGroupsWithRecursionFallbackTest < GitHub::Ldap::Test
- def self.test_server_options
- {
- custom_schemas: FIXTURES.join('posixGroup.schema.ldif'),
- user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s,
- # so we exercise the recursive group search fallback
- recursive_group_search_fallback: true
- }
- end
-
def setup
- @ldap = GitHub::Ldap.new(options)
+ opts = options.merge \
+ recursive_group_search_fallback: true
+ @ldap = GitHub::Ldap.new(opts)
@domain = @ldap.domain("dc=github,dc=com")
- @cn = "enterprise-posix-devs"
+ @cn = "posix-group1"
end
def test_membership_for_posixGroups
- assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind
+ assert user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind
assert @domain.is_member?(user, [@cn]),
"Expected `#{@cn}` to include the member `#{user.dn}`"
@@ -192,23 +198,16 @@ def test_membership_for_posixGroups
end
class GitHubLdapPosixGroupsWithoutRecursionTest < GitHub::Ldap::Test
- def self.test_server_options
- {
- custom_schemas: FIXTURES.join('posixGroup.schema.ldif'),
- user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s,
- # so we test the test the non-recursive group membership search
- recursive_group_search_fallback: false
- }
- end
-
def setup
- @ldap = GitHub::Ldap.new(options)
+ opts = options.merge \
+ recursive_group_search_fallback: false
+ @ldap = GitHub::Ldap.new(opts)
@domain = @ldap.domain("dc=github,dc=com")
- @cn = "enterprise-posix-devs"
+ @cn = "posix-group1"
end
def test_membership_for_posixGroups
- assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind
+ assert user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind
assert @domain.is_member?(user, [@cn]),
"Expected `#{@cn}` to include the member `#{user.dn}`"
@@ -218,27 +217,32 @@ def test_membership_for_posixGroups
# Specifically testing that this doesn't break when posixGroups are not
# supported.
class GitHubLdapWithoutPosixGroupsTest < GitHub::Ldap::Test
- def self.test_server_options
- {
- custom_schemas: FIXTURES.join('posixGroup.schema.ldif'),
- user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s,
- # so we test the test the non-recursive group membership search
- recursive_group_search_fallback: false,
- # explicitly disable posixGroup support (even if the schema supports it)
- posix_support: false
- }
- end
-
def setup
- @ldap = GitHub::Ldap.new(options)
+ opts = options.merge \
+ recursive_group_search_fallback: false, # test non-recursive group membership search
+ posix_support: false # disable posixGroup support
+ @ldap = GitHub::Ldap.new(opts)
@domain = @ldap.domain("dc=github,dc=com")
- @cn = "enterprise-posix-devs"
+ @cn = "posix-group1"
end
def test_membership_for_posixGroups
- assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind
+ assert user = @ldap.domain('uid=user1,ou=People,dc=github,dc=com').bind
refute @domain.is_member?(user, [@cn]),
"Expected `#{@cn}` to not include the member `#{user.dn}`"
end
end
+
+class GitHubLdapActiveDirectoryGroupsTest < GitHub::Ldap::Test
+ def run(*)
+ return super if self.class.test_env == "activedirectory"
+ Minitest::Result.from(self)
+ end
+
+ def test_filter_groups
+ domain = GitHub::Ldap.new(options).domain("DC=ad,DC=ghe,DC=local")
+ results = domain.filter_groups("ghe-admins")
+ assert_equal 1, results.size
+ end
+end
diff --git a/test/filter_test.rb b/test/filter_test.rb
index 8fc6ba2..4da83c9 100644
--- a/test/filter_test.rb
+++ b/test/filter_test.rb
@@ -1,6 +1,6 @@
require_relative 'test_helper'
-class FilterTest < Minitest::Test
+class FilterTest < GitHub::Ldap::Test
class Subject
include GitHub::Ldap::Filter
def initialize(ldap)
@@ -16,11 +16,12 @@ def [](field)
end
def setup
- @ldap = GitHub::Ldap.new(:uid => 'uid')
+ @ldap = GitHub::Ldap.new(options.merge(:uid => 'uid'))
@subject = Subject.new(@ldap)
@me = 'uid=calavera,dc=github,dc=com'
@uid = "calavera"
- @entry = Entry.new(@me, @uid)
+ @entry = Net::LDAP::Entry.new(@me)
+ @entry[:uid] = @uid
end
def test_member_present
@@ -32,6 +33,11 @@ def test_member_equal
@subject.member_filter(@entry).to_s
end
+ def test_member_equal_with_string
+ assert_equal "(|(member=#{@me})(uniqueMember=#{@me}))",
+ @subject.member_filter(@entry.dn).to_s
+ end
+
def test_posix_member_without_uid
@entry.uid = nil
assert_nil @subject.posix_member_filter(@entry, @ldap.uid)
@@ -42,6 +48,11 @@ def test_posix_member_equal
@subject.posix_member_filter(@entry, @ldap.uid).to_s
end
+ def test_posix_member_equal_string
+ assert_equal "(memberUid=#{@uid})",
+ @subject.posix_member_filter(@uid).to_s
+ end
+
def test_groups_reduced
assert_equal "(|(cn=Enterprise)(cn=People))",
@subject.group_filter(%w(Enterprise People)).to_s
diff --git a/test/fixtures/common/seed.ldif b/test/fixtures/common/seed.ldif
new file mode 100644
index 0000000..29284bb
--- /dev/null
+++ b/test/fixtures/common/seed.ldif
@@ -0,0 +1,369 @@
+dn: ou=People,dc=github,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: People
+
+dn: ou=Groups,dc=github,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: Groups
+
+# Directory Superuser
+dn: uid=admin,dc=github,dc=com
+uid: admin
+cn: system administrator
+sn: administrator
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+displayName: Directory Superuser
+userPassword: passworD1
+
+# Users 1-10
+
+dn: uid=user1,ou=People,dc=github,dc=com
+uid: user1
+cn: user1
+sn: user1
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: user1@github.com
+
+dn: uid=user2,ou=People,dc=github,dc=com
+uid: user2
+cn: user2
+sn: user2
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: user2@github.com
+
+dn: uid=user3,ou=People,dc=github,dc=com
+uid: user3
+cn: user3
+sn: user3
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: user3@github.com
+
+dn: uid=user4,ou=People,dc=github,dc=com
+uid: user4
+cn: user4
+sn: user4
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: user4@github.com
+
+dn: uid=user5,ou=People,dc=github,dc=com
+uid: user5
+cn: user5
+sn: user5
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: user5@github.com
+
+dn: uid=user6,ou=People,dc=github,dc=com
+uid: user6
+cn: user6
+sn: user6
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: user6@github.com
+
+dn: uid=user7,ou=People,dc=github,dc=com
+uid: user7
+cn: user7
+sn: user7
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: user7@github.com
+
+dn: uid=user8,ou=People,dc=github,dc=com
+uid: user8
+cn: user8
+sn: user8
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: user8@github.com
+
+dn: uid=user9,ou=People,dc=github,dc=com
+uid: user9
+cn: user9
+sn: user9
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: user9@github.com
+
+dn: uid=user10,ou=People,dc=github,dc=com
+uid: user10
+cn: user10
+sn: user10
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: user10@github.com
+
+# Emailless User
+
+dn: uid=emailless-user1,ou=People,dc=github,dc=com
+uid: emailless-user1
+cn: emailless-user1
+sn: emailless-user1
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+
+# Groupless User
+
+dn: uid=groupless-user1,ou=People,dc=github,dc=com
+uid: groupless-user1
+cn: groupless-user1
+sn: groupless-user1
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+
+# Admin User
+
+dn: uid=admin1,ou=People,dc=github,dc=com
+uid: admin1
+cn: admin1
+sn: admin1
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+userPassword: passworD1
+mail: admin1@github.com
+
+# Groups
+
+dn: cn=ghe-users,ou=Groups,dc=github,dc=com
+cn: ghe-users
+objectClass: groupOfNames
+member: uid=user1,ou=People,dc=github,dc=com
+member: uid=emailless-user1,ou=People,dc=github,dc=com
+
+dn: cn=all-users,ou=Groups,dc=github,dc=com
+cn: all-users
+objectClass: groupOfNames
+member: cn=ghe-users,ou=Groups,dc=github,dc=com
+member: uid=user1,ou=People,dc=github,dc=com
+member: uid=user2,ou=People,dc=github,dc=com
+member: uid=user3,ou=People,dc=github,dc=com
+member: uid=user4,ou=People,dc=github,dc=com
+member: uid=user5,ou=People,dc=github,dc=com
+member: uid=user6,ou=People,dc=github,dc=com
+member: uid=user7,ou=People,dc=github,dc=com
+member: uid=user8,ou=People,dc=github,dc=com
+member: uid=user9,ou=People,dc=github,dc=com
+member: uid=user10,ou=People,dc=github,dc=com
+member: uid=emailless-user1,ou=People,dc=github,dc=com
+
+dn: cn=ghe-admins,ou=Groups,dc=github,dc=com
+cn: ghe-admins
+objectClass: groupOfNames
+member: uid=admin1,ou=People,dc=github,dc=com
+
+dn: cn=all-admins,ou=Groups,dc=github,dc=com
+cn: all-admins
+objectClass: groupOfNames
+member: cn=ghe-admins,ou=Groups,dc=github,dc=com
+member: uid=admin1,ou=People,dc=github,dc=com
+
+dn: cn=n-member-group10,ou=Groups,dc=github,dc=com
+cn: n-member-group10
+objectClass: groupOfNames
+member: uid=user1,ou=People,dc=github,dc=com
+member: uid=user2,ou=People,dc=github,dc=com
+member: uid=user3,ou=People,dc=github,dc=com
+member: uid=user4,ou=People,dc=github,dc=com
+member: uid=user5,ou=People,dc=github,dc=com
+member: uid=user6,ou=People,dc=github,dc=com
+member: uid=user7,ou=People,dc=github,dc=com
+member: uid=user8,ou=People,dc=github,dc=com
+member: uid=user9,ou=People,dc=github,dc=com
+member: uid=user10,ou=People,dc=github,dc=com
+
+dn: cn=nested-group1,ou=Groups,dc=github,dc=com
+cn: nested-group1
+objectClass: groupOfNames
+member: uid=user1,ou=People,dc=github,dc=com
+member: uid=user2,ou=People,dc=github,dc=com
+member: uid=user3,ou=People,dc=github,dc=com
+member: uid=user4,ou=People,dc=github,dc=com
+member: uid=user5,ou=People,dc=github,dc=com
+member: uid=user6,ou=People,dc=github,dc=com
+member: uid=user7,ou=People,dc=github,dc=com
+member: uid=user8,ou=People,dc=github,dc=com
+member: uid=user9,ou=People,dc=github,dc=com
+member: uid=user10,ou=People,dc=github,dc=com
+
+dn: cn=nested-groups,ou=Groups,dc=github,dc=com
+cn: nested-groups
+objectClass: groupOfNames
+member: cn=nested-group1,ou=Groups,dc=github,dc=com
+
+dn: cn=n-member-nested-group1,ou=Groups,dc=github,dc=com
+cn: n-member-nested-group1
+objectClass: groupOfNames
+member: cn=nested-group1,ou=Groups,dc=github,dc=com
+
+dn: cn=deeply-nested-group0.0.0,ou=Groups,dc=github,dc=com
+cn: deeply-nested-group0.0.0
+objectClass: groupOfNames
+member: uid=user1,ou=People,dc=github,dc=com
+member: uid=user2,ou=People,dc=github,dc=com
+member: uid=user3,ou=People,dc=github,dc=com
+member: uid=user4,ou=People,dc=github,dc=com
+member: uid=user5,ou=People,dc=github,dc=com
+
+dn: cn=deeply-nested-group0.0.1,ou=Groups,dc=github,dc=com
+cn: deeply-nested-group0.0.1
+objectClass: groupOfNames
+member: uid=user6,ou=People,dc=github,dc=com
+member: uid=user7,ou=People,dc=github,dc=com
+member: uid=user8,ou=People,dc=github,dc=com
+member: uid=user9,ou=People,dc=github,dc=com
+member: uid=user10,ou=People,dc=github,dc=com
+
+dn: cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com
+cn: deeply-nested-group0.0
+objectClass: groupOfNames
+member: cn=deeply-nested-group0.0.0,ou=Groups,dc=github,dc=com
+member: cn=deeply-nested-group0.0.1,ou=Groups,dc=github,dc=com
+
+dn: cn=deeply-nested-group0,ou=Groups,dc=github,dc=com
+cn: deeply-nested-group0
+objectClass: groupOfNames
+member: cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com
+
+dn: cn=deeply-nested-groups,ou=Groups,dc=github,dc=com
+cn: deeply-nested-groups
+objectClass: groupOfNames
+member: cn=deeply-nested-group0,ou=Groups,dc=github,dc=com
+
+dn: cn=n-depth-nested-group1,ou=Groups,dc=github,dc=com
+cn: n-depth-nested-group1
+objectClass: groupOfNames
+member: cn=nested-group1,ou=Groups,dc=github,dc=com
+
+dn: cn=n-depth-nested-group2,ou=Groups,dc=github,dc=com
+cn: n-depth-nested-group2
+objectClass: groupOfNames
+member: cn=n-depth-nested-group1,ou=Groups,dc=github,dc=com
+
+dn: cn=n-depth-nested-group3,ou=Groups,dc=github,dc=com
+cn: n-depth-nested-group3
+objectClass: groupOfNames
+member: cn=n-depth-nested-group2,ou=Groups,dc=github,dc=com
+
+dn: cn=n-depth-nested-group4,ou=Groups,dc=github,dc=com
+cn: n-depth-nested-group4
+objectClass: groupOfNames
+member: cn=n-depth-nested-group3,ou=Groups,dc=github,dc=com
+
+dn: cn=n-depth-nested-group5,ou=Groups,dc=github,dc=com
+cn: n-depth-nested-group5
+objectClass: groupOfNames
+member: cn=n-depth-nested-group4,ou=Groups,dc=github,dc=com
+
+dn: cn=n-depth-nested-group6,ou=Groups,dc=github,dc=com
+cn: n-depth-nested-group6
+objectClass: groupOfNames
+member: cn=n-depth-nested-group5,ou=Groups,dc=github,dc=com
+
+dn: cn=n-depth-nested-group7,ou=Groups,dc=github,dc=com
+cn: n-depth-nested-group7
+objectClass: groupOfNames
+member: cn=n-depth-nested-group6,ou=Groups,dc=github,dc=com
+
+dn: cn=n-depth-nested-group8,ou=Groups,dc=github,dc=com
+cn: n-depth-nested-group8
+objectClass: groupOfNames
+member: cn=n-depth-nested-group7,ou=Groups,dc=github,dc=com
+
+dn: cn=n-depth-nested-group9,ou=Groups,dc=github,dc=com
+cn: n-depth-nested-group9
+objectClass: groupOfNames
+member: cn=n-depth-nested-group8,ou=Groups,dc=github,dc=com
+
+dn: cn=head-group,ou=Groups,dc=github,dc=com
+cn: head-group
+objectClass: groupOfNames
+member: cn=tail-group,ou=Groups,dc=github,dc=com
+member: uid=user1,ou=People,dc=github,dc=com
+member: uid=user2,ou=People,dc=github,dc=com
+member: uid=user3,ou=People,dc=github,dc=com
+member: uid=user4,ou=People,dc=github,dc=com
+member: uid=user5,ou=People,dc=github,dc=com
+
+dn: cn=tail-group,ou=Groups,dc=github,dc=com
+cn: tail-group
+objectClass: groupOfNames
+member: cn=head-group,ou=Groups,dc=github,dc=com
+member: uid=user6,ou=People,dc=github,dc=com
+member: uid=user7,ou=People,dc=github,dc=com
+member: uid=user8,ou=People,dc=github,dc=com
+member: uid=user9,ou=People,dc=github,dc=com
+member: uid=user10,ou=People,dc=github,dc=com
+
+dn: cn=recursively-nested-groups,ou=Groups,dc=github,dc=com
+cn: recursively-nested-groups
+objectClass: groupOfNames
+member: cn=head-group,ou=Groups,dc=github,dc=com
+member: cn=tail-group,ou=Groups,dc=github,dc=com
+
+# posixGroup
+
+dn: cn=posix-group1,ou=Groups,dc=github,dc=com
+cn: posix-group1
+objectClass: posixGroup
+gidNumber: 1001
+memberUid: user1
+memberUid: user2
+memberUid: user3
+memberUid: user4
+memberUid: user5
+
+# missing members
+
+dn: cn=missing-users,ou=Groups,dc=github,dc=com
+cn: missing-users
+objectClass: groupOfNames
+member: uid=user1,ou=People,dc=github,dc=com
+member: uid=user2,ou=People,dc=github,dc=com
+member: uid=nonexistent-user,ou=People,dc=github,dc=com
diff --git a/test/fixtures/github-with-looped-subgroups.ldif b/test/fixtures/github-with-looped-subgroups.ldif
deleted file mode 100644
index 02868fe..0000000
--- a/test/fixtures/github-with-looped-subgroups.ldif
+++ /dev/null
@@ -1,82 +0,0 @@
-version: 1
-
-# Admin user
-
-dn: uid=admin,dc=github,dc=com
-objectClass: top
-objectClass: person
-objectClass: organizationalPerson
-objectClass: inetOrgPerson
-cn: system administrator
-sn: administrator
-displayName: Directory Superuser
-uid: admin
-userPassword: secret
-
-# Groups
-
-dn: ou=groups,dc=github,dc=com
-objectclass: organizationalUnit
-
-dn: cn=enterprise,ou=groups,dc=github,dc=com
-cn: Enterprise
-objectClass: groupOfNames
-member: uid=calavera,ou=users,dc=github,dc=com
-member: cn=enterprise-devs,ou=groups,dc=github,dc=com
-member: cn=enterprise-ops,ou=groups,dc=github,dc=com
-
-dn: cn=enterprise-devs,ou=groups,dc=github,dc=com
-cn: enterprise-devs
-objectClass: groupOfNames
-member: uid=benburkert,ou=users,dc=github,dc=com
-member: cn=enterprise,ou=groups,dc=github,dc=com
-
-dn: cn=enterprise-ops,ou=groups,dc=github,dc=com
-cn: enterprise-ops
-objectClass: groupOfNames
-member: uid=sbryant,ou=users,dc=github,dc=com
-member: cn=spaniards,ou=groups,dc=github,dc=com
-
-dn: cn=spaniards,ou=groups,dc=github,dc=com
-cn: spaniards
-objectClass: groupOfNames
-member: uid=calavera,ou=users,dc=github,dc=com
-member: uid=rubiojr,ou=users,dc=github,dc=com
-
-# Users
-
-dn: ou=users,dc=github,dc=com
-objectclass: organizationalUnit
-
-dn: uid=calavera,ou=users,dc=github,dc=com
-cn: David Calavera
-cn: David
-sn: Calavera
-uid: calavera
-userPassword: passworD1
-mail: calavera@github.com
-objectClass: inetOrgPerson
-
-dn: uid=benburkert,ou=users,dc=github,dc=com
-cn: benburkert
-sn: benburkert
-uid: benburkert
-userPassword: passworD1
-mail: benburkert@github.com
-objectClass: inetOrgPerson
-
-dn: uid=sbryant,ou=users,dc=github,dc=com
-cn: sbryant
-sn: sbryant
-uid: sbryant
-userPassword: passworD1
-mail: sbryant@github.com
-objectClass: inetOrgPerson
-
-dn: uid=rubiojr,ou=users,dc=github,dc=com
-cn: rubiojr
-sn: rubiojr
-uid: rubiojr
-userPassword: passworD1
-mail: rubiojr@github.com
-objectClass: inetOrgPerson
diff --git a/test/fixtures/github-with-missing-entries.ldif b/test/fixtures/github-with-missing-entries.ldif
deleted file mode 100644
index be8d316..0000000
--- a/test/fixtures/github-with-missing-entries.ldif
+++ /dev/null
@@ -1,85 +0,0 @@
-version: 1
-
-# Admin user
-
-dn: uid=admin,dc=github,dc=com
-objectClass: top
-objectClass: person
-objectClass: organizationalPerson
-objectClass: inetOrgPerson
-cn: system administrator
-sn: administrator
-displayName: Directory Superuser
-uid: admin
-userPassword: secret
-
-# Groups
-
-dn: ou=groups,dc=github,dc=com
-objectclass: organizationalUnit
-
-dn: cn=enterprise,ou=groups,dc=github,dc=com
-cn: Enterprise
-objectClass: groupOfNames
-member: uid=calavera,ou=users,dc=github,dc=com
-member: cn=enterprise-devs,ou=groups,dc=github,dc=com
-member: cn=enterprise-ops,ou=groups,dc=github,dc=com
-
-dn: cn=enterprise-devs,ou=groups,dc=github,dc=com
-cn: enterprise-devs
-objectClass: groupOfNames
-member: uid=benburkert,ou=users,dc=github,dc=com
-member: cn=enterprise,ou=groups,dc=github,dc=com
-
-dn: cn=enterprise-ops,ou=groups,dc=github,dc=com
-cn: enterprise-ops
-objectClass: groupOfNames
-member: uid=sbryant,ou=users,dc=github,dc=com
-member: cn=spaniards,ou=groups,dc=github,dc=com
-
-# The last member of this group is missing on purpose.
-# See: https://github.com/github/github-ldap/pull/18
-dn: cn=spaniards,ou=groups,dc=github,dc=com
-cn: spaniards
-objectClass: groupOfNames
-member: uid=calavera,ou=users,dc=github,dc=com
-member: uid=rubiojr,ou=users,dc=github,dc=com
-member: uid=felipe,ou=users,dc=github,dc=com
-
-# Users
-
-dn: ou=users,dc=github,dc=com
-objectclass: organizationalUnit
-
-dn: uid=calavera,ou=users,dc=github,dc=com
-cn: David Calavera
-cn: David
-sn: Calavera
-uid: calavera
-userPassword: passworD1
-mail: calavera@github.com
-objectClass: inetOrgPerson
-
-dn: uid=benburkert,ou=users,dc=github,dc=com
-cn: benburkert
-sn: benburkert
-uid: benburkert
-userPassword: passworD1
-mail: benburkert@github.com
-objectClass: inetOrgPerson
-
-dn: uid=sbryant,ou=users,dc=github,dc=com
-cn: sbryant
-sn: sbryant
-uid: sbryant
-userPassword: passworD1
-mail: sbryant@github.com
-objectClass: inetOrgPerson
-
-dn: uid=rubiojr,ou=users,dc=github,dc=com
-cn: rubiojr
-sn: rubiojr
-uid: rubiojr
-userPassword: passworD1
-mail: rubiojr@github.com
-objectClass: inetOrgPerson
diff --git a/test/fixtures/github-with-posixGroups.ldif b/test/fixtures/github-with-posixGroups.ldif
deleted file mode 100644
index ac8b3a0..0000000
--- a/test/fixtures/github-with-posixGroups.ldif
+++ /dev/null
@@ -1,50 +0,0 @@
-version: 1
-
-# Admin user
-
-dn: uid=admin,dc=github,dc=com
-objectClass: top
-objectClass: person
-objectClass: organizationalPerson
-objectClass: inetOrgPerson
-cn: system administrator
-sn: administrator
-displayName: Directory Superuser
-uid: admin
-userPassword: secret
-
-# Groups
-
-dn: ou=groups,dc=github,dc=com
-objectclass: organizationalUnit
-ou: groups
-
-# Posix Groups
-
-dn: cn=enterprise-posix-devs,ou=groups,dc=github,dc=com
-cn: enterprise-posix-devs
-objectClass: posixGroup
-memberUid: benburkert
-memberUid: mtodd
-
-# Users
-
-dn: ou=users,dc=github,dc=com
-objectclass: organizationalUnit
-ou: users
-
-dn: uid=benburkert,ou=users,dc=github,dc=com
-cn: benburkert
-sn: benburkert
-uid: benburkert
-userPassword: passworD1
-mail: benburkert@github.com
-objectClass: inetOrgPerson
-
-dn: uid=mtodd,ou=users,dc=github,dc=com
-cn: mtodd
-sn: mtodd
-uid: mtodd
-userPassword: passworD1
-mail: mtodd@github.com
-objectClass: inetOrgPerson
diff --git a/test/fixtures/github-with-subgroups.ldif b/test/fixtures/github-with-subgroups.ldif
deleted file mode 100644
index 00dc929..0000000
--- a/test/fixtures/github-with-subgroups.ldif
+++ /dev/null
@@ -1,146 +0,0 @@
-version: 1
-
-# Admin user
-
-dn: uid=admin,dc=github,dc=com
-objectClass: top
-objectClass: person
-objectClass: organizationalPerson
-objectClass: inetOrgPerson
-cn: system administrator
-sn: administrator
-displayName: Directory Superuser
-uid: admin
-userPassword: secret
-
-# Groups
-
-dn: ou=groups,dc=github,dc=com
-objectclass: organizationalUnit
-ou: groups
-
-dn: cn=enterprise,ou=groups,dc=github,dc=com
-cn: Enterprise
-objectClass: groupOfNames
-member: uid=calavera,ou=users,dc=github,dc=com
-member: cn=enterprise-devs,ou=groups,dc=github,dc=com
-member: cn=enterprise-ops,ou=groups,dc=github,dc=com
-
-dn: cn=enterprise-devs,ou=groups,dc=github,dc=com
-cn: enterprise-devs
-objectClass: groupOfNames
-member: uid=benburkert,ou=users,dc=github,dc=com
-
-dn: cn=enterprise-ops,ou=groups,dc=github,dc=com
-cn: enterprise-ops
-objectClass: groupOfNames
-member: uid=sbryant,ou=users,dc=github,dc=com
-member: cn=spaniards,ou=groups,dc=github,dc=com
-
-dn: cn=spaniards,ou=groups,dc=github,dc=com
-cn: spaniards
-objectClass: groupOfNames
-member: uid=calavera,ou=users,dc=github,dc=com
-member: uid=rubiojr,ou=users,dc=github,dc=com
-
-dn: cn=group1,ou=groups,dc=github,dc=com
-cn: group1
-objectClass: groupOfNames
-member: uid=user1,ou=users,dc=github,dc=com
-member: cn=group1.1,ou=groups,dc=github,dc=com
-
-dn: cn=group1.1,ou=groups,dc=github,dc=com
-cn: group1
-objectClass: groupOfNames
-member: uid=user1.1,ou=users,dc=github,dc=com
-member: cn=group1.1.1,ou=groups,dc=github,dc=com
-
-dn: cn=group1.1.1,ou=groups,dc=github,dc=com
-cn: group1
-objectClass: groupOfNames
-member: uid=user1.1.1,ou=users,dc=github,dc=com
-member: cn=group1.1.1.1,ou=groups,dc=github,dc=com
-
-dn: cn=group1.1.1.1,ou=groups,dc=github,dc=com
-cn: group1
-objectClass: groupOfNames
-member: uid=user1.1.1.1,ou=users,dc=github,dc=com
-
-# Users
-
-dn: ou=users,dc=github,dc=com
-objectclass: organizationalUnit
-ou: users
-
-dn: uid=calavera,ou=users,dc=github,dc=com
-cn: David Calavera
-cn: David
-sn: Calavera
-uid: calavera
-userPassword: passworD1
-mail: calavera@github.com
-objectClass: inetOrgPerson
-
-dn: uid=benburkert,ou=users,dc=github,dc=com
-cn: benburkert
-sn: benburkert
-uid: benburkert
-userPassword: passworD1
-mail: benburkert@github.com
-objectClass: inetOrgPerson
-
-dn: uid=sbryant,ou=users,dc=github,dc=com
-cn: sbryant
-sn: sbryant
-uid: sbryant
-userPassword: passworD1
-mail: sbryant@github.com
-objectClass: inetOrgPerson
-
-dn: uid=rubiojr,ou=users,dc=github,dc=com
-cn: rubiojr
-sn: rubiojr
-uid: rubiojr
-userPassword: passworD1
-mail: rubiojr@github.com
-objectClass: inetOrgPerson
-
-dn: uid=mtodd,ou=users,dc=github,dc=com
-cn: mtodd
-sn: mtodd
-uid: mtodd
-userPassword: passworD1
-mail: mtodd@github.com
-objectClass: inetOrgPerson
-
-dn: uid=user1,ou=users,dc=github,dc=com
-uid: user1
-sn: user1
-cn: user1
-userPassword: passworD1
-mail: user1@github.com
-objectClass: inetOrgPerson
-
-dn: uid=user1.1,ou=users,dc=github,dc=com
-uid: user1.1
-sn: user1.1
-cn: user1.1
-userPassword: passworD1
-mail: user1.1@github.com
-objectClass: inetOrgPerson
-
-dn: uid=user1.1.1,ou=users,dc=github,dc=com
-uid: user1.1.1
-sn: user1.1.1
-cn: user1.1.1
-userPassword: passworD1
-mail: user1.1.1@github.com
-objectClass: inetOrgPerson
-
-dn: uid=user1.1.1.1,ou=users,dc=github,dc=com
-uid: user1.1.1.1
-sn: user1.1.1.1
-cn: user1.1.1.1
-userPassword: passworD1
-mail: user1.1.1.1@github.com
-objectClass: inetOrgPerson
diff --git a/test/fixtures/openldap/memberof.ldif b/test/fixtures/openldap/memberof.ldif
new file mode 100644
index 0000000..dac7c6b
--- /dev/null
+++ b/test/fixtures/openldap/memberof.ldif
@@ -0,0 +1,33 @@
+dn: cn=module,cn=config
+cn: module
+objectClass: olcModuleList
+objectClass: top
+olcModulePath: /usr/lib/ldap
+olcModuleLoad: memberof.la
+
+dn: olcOverlay={0}memberof,olcDatabase={1}hdb,cn=config
+objectClass: olcConfig
+objectClass: olcMemberOf
+objectClass: olcOverlayConfig
+objectClass: top
+olcOverlay: memberof
+olcMemberOfDangling: ignore
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+
+dn: cn=module,cn=config
+cn: module
+objectclass: olcModuleList
+objectclass: top
+olcmoduleload: refint.la
+olcmodulepath: /usr/lib/ldap
+
+dn: olcOverlay={1}refint,olcDatabase={1}hdb,cn=config
+objectClass: olcConfig
+objectClass: olcOverlayConfig
+objectClass: olcRefintConfig
+objectClass: top
+olcOverlay: {1}refint
+olcRefintAttribute: memberof member manager owner
diff --git a/test/fixtures/openldap/slapd.conf.ldif b/test/fixtures/openldap/slapd.conf.ldif
new file mode 100644
index 0000000..7d88769
--- /dev/null
+++ b/test/fixtures/openldap/slapd.conf.ldif
@@ -0,0 +1,67 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcPidFile: /var/run/slapd/slapd.pid
+olcArgsFile: /var/run/slapd/slapd.args
+olcLogLevel: none
+olcToolThreads: 1
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcSizeLimit: 500
+olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break
+olcAccess: {1}to dn.exact="" by * read
+olcAccess: {2}to dn.base="cn=Subschema" by * read
+
+dn: olcDatabase=config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: config
+olcAccess: to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file:///etc/ldap/schema/core.ldif
+include: file:///etc/ldap/schema/cosine.ldif
+include: file:///etc/ldap/schema/nis.ldif
+include: file:///etc/ldap/schema/inetorgperson.ldif
+
+dn: cn=module{0},cn=config
+objectClass: olcModuleList
+cn: module{0}
+olcModulePath: /usr/lib/ldap
+olcModuleLoad: back_hdb
+
+dn: olcBackend=hdb,cn=config
+objectClass: olcBackendConfig
+olcBackend: hdb
+
+dn: olcDatabase=hdb,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcHdbConfig
+olcDatabase: hdb
+olcDbCheckpoint: 512 30
+olcDbConfig: set_cachesize 1 0 0
+olcDbConfig: set_lk_max_objects 1500
+olcDbConfig: set_lk_max_locks 1500
+olcDbConfig: set_lk_max_lockers 1500
+olcLastMod: TRUE
+olcSuffix: dc=github,dc=com
+olcDbDirectory: /var/lib/ldap
+olcRootDN: cn=admin,dc=github,dc=com
+# admin's password: "passworD1"
+olcRootPW: {SHA}LFSkM9eegU6j3PeGG7UuHrT/KZM=
+olcDbIndex: objectClass eq
+olcAccess: to attrs=userPassword,shadowLastChange
+ by self write
+ by anonymous auth
+ by dn="cn=admin,dc=github,dc=com" write
+ by * none
+olcAccess: to dn.base="" by * read
+olcAccess: to *
+ by self write
+ by dn="cn=admin,dc=github,dc=com" write
+ by * read
diff --git a/test/fixtures/posixGroup.schema.ldif b/test/fixtures/posixGroup.schema.ldif
index 94dd488..3ba04e0 100644
--- a/test/fixtures/posixGroup.schema.ldif
+++ b/test/fixtures/posixGroup.schema.ldif
@@ -1,26 +1,52 @@
version: 1
-dn: m-oid=1.3.6.1.4.1.18055.0.4.1.2.1001,ou=attributeTypes,cn=other,ou=schema
+# attributetype ( 1.3.6.1.1.1.1.1 NAME 'gidNumber'
+# DESC 'An integer uniquely identifying a group in an administrative domain'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+dn: m-oid=1.3.6.1.1.1.1.1,ou=attributeTypes,cn=other,ou=schema
+objectClass: metaAttributeType
+objectClass: metaTop
+objectClass: top
+m-collective: FALSE
+m-description: An integer uniquely identifying a group in an administrative domain
+m-equality: integerMatch
+m-name: gidNumber
+m-syntax: 1.3.6.1.4.1.1466.115.121.1.27
+m-usage: USER_APPLICATIONS
+m-oid: 1.3.6.1.1.1.1.1
+
+# attributetype ( 1.3.6.1.1.1.1.12 NAME 'memberUid'
+# EQUALITY caseExactIA5Match
+# SUBSTR caseExactIA5SubstringsMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+dn: m-oid=1.3.6.1.1.1.1.12,ou=attributeTypes,cn=other,ou=schema
objectClass: metaAttributeType
objectClass: metaTop
objectClass: top
m-collective: FALSE
m-description: memberUid
-m-equality: caseExactMatch
+m-equality: caseExactIA5Match
m-name: memberUid
-m-syntax: 1.3.6.1.4.1.1466.115.121.1.15
+m-syntax: 1.3.6.1.4.1.1466.115.121.1.26
m-usage: USER_APPLICATIONS
-m-oid: 1.3.6.1.4.1.18055.0.4.1.2.1001
+m-oid: 1.3.6.1.1.1.1.12
-dn: m-oid=1.3.6.1.4.1.18055.0.4.1.3.1001,ou=objectClasses,cn=other,ou=schema
+# objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' SUP top STRUCTURAL
+# DESC 'Abstraction of a group of accounts'
+# MUST ( cn $ gidNumber )
+# MAY ( userPassword $ memberUid $ description ) )
+dn: m-oid=1.3.6.1.1.1.2.2,ou=objectClasses,cn=other,ou=schema
objectClass: metaObjectClass
objectClass: metaTop
objectClass: top
m-description: posixGroup
-m-may: cn
-m-may: sn
+m-must: cn
+m-must: gidNumber
m-may: memberUid
+m-may: userPassword
+m-may: description
m-supobjectclass: top
m-name: posixGroup
-m-oid: 1.3.6.1.4.1.18055.0.4.1.3.1001
+m-oid: 1.3.6.1.1.1.2.2
m-typeobjectclass: STRUCTURAL
diff --git a/test/group_test.rb b/test/group_test.rb
index 6f1714d..1ed5f82 100644
--- a/test/group_test.rb
+++ b/test/group_test.rb
@@ -1,17 +1,13 @@
require_relative 'test_helper'
class GitHubLdapGroupTest < GitHub::Ldap::Test
- def self.test_server_options
- {user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s}
- end
-
def groups_domain
- @ldap.domain("ou=groups,dc=github,dc=com")
+ @ldap.domain("ou=Groups,dc=github,dc=com")
end
def setup
@ldap = GitHub::Ldap.new(options)
- @group = @ldap.group("cn=enterprise,ou=groups,dc=github,dc=com")
+ @group = @ldap.group("cn=ghe-users,ou=Groups,dc=github,dc=com")
end
def test_group?
@@ -25,34 +21,36 @@ def test_group?
end
def test_subgroups
- assert_equal 3, @group.subgroups.size
+ group = @ldap.group("cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com")
+ assert_equal 2, group.subgroups.size
end
def test_members_from_subgroups
- assert_equal 4, @group.members.size
+ group = @ldap.group("cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com")
+ assert_equal 10, group.members.size
end
def test_all_domain_groups
groups = groups_domain.all_groups
- assert_equal 8, groups.size
+ assert_equal 27, groups.size
end
def test_filter_domain_groups
- groups = groups_domain.filter_groups('devs')
+ groups = groups_domain.filter_groups('ghe-users')
assert_equal 1, groups.size
end
def test_filter_domain_groups_limited
groups = []
- groups_domain.filter_groups('enter', size: 1) do |entry|
+ groups_domain.filter_groups('deeply-nested-group', size: 1) do |entry|
groups << entry
end
assert_equal 1, groups.size
end
def test_filter_domain_groups_unlimited
- groups = groups_domain.filter_groups('ent')
- assert_equal 3, groups.size
+ groups = groups_domain.filter_groups('deeply-nested-group')
+ assert_equal 5, groups.size
end
def test_unknown_group
@@ -62,33 +60,25 @@ def test_unknown_group
end
class GitHubLdapLoopedGroupTest < GitHub::Ldap::Test
- def self.test_server_options
- {user_fixtures: FIXTURES.join('github-with-looped-subgroups.ldif').to_s}
- end
-
def setup
- @group = GitHub::Ldap.new(options).group("cn=enterprise,ou=groups,dc=github,dc=com")
+ @group = GitHub::Ldap.new(options).group("cn=recursively-nested-groups,ou=Groups,dc=github,dc=com")
end
def test_members_from_subgroups
- assert_equal 4, @group.members.size
+ assert_equal 10, @group.members.size
end
end
class GitHubLdapMissingEntriesTest < GitHub::Ldap::Test
- def self.test_server_options
- {user_fixtures: FIXTURES.join('github-with-missing-entries.ldif').to_s}
- end
-
def setup
@ldap = GitHub::Ldap.new(options)
end
def test_load_right_members
- assert_equal 3, @ldap.domain("cn=spaniards,ou=groups,dc=github,dc=com").bind[:member].size
+ assert_equal 3, @ldap.domain("cn=missing-users,ou=groups,dc=github,dc=com").bind[:member].size
end
def test_ignore_missing_member_entries
- assert_equal 2, @ldap.group("cn=spaniards,ou=groups,dc=github,dc=com").members.size
+ assert_equal 2, @ldap.group("cn=missing-users,ou=groups,dc=github,dc=com").members.size
end
end
diff --git a/test/ldap_test.rb b/test/ldap_test.rb
index 27861d3..d5e9297 100644
--- a/test/ldap_test.rb
+++ b/test/ldap_test.rb
@@ -9,74 +9,153 @@ def test_connection_with_default_options
assert @ldap.test_connection, "Ldap connection expected to succeed"
end
+ def test_connection_with_list_of_hosts_with_one_valid_host
+ ldap = GitHub::Ldap.new(options.merge(hosts: [["localhost", options[:port]]]))
+ assert ldap.test_connection, "Ldap connection expected to succeed"
+ end
+
+ def test_connection_with_list_of_hosts_with_first_valid
+ ldap = GitHub::Ldap.new(options.merge(hosts: [["localhost", options[:port]], ["invalid.local", options[:port]]]))
+ assert ldap.test_connection, "Ldap connection expected to succeed"
+ end
+
+ def test_connection_with_list_of_hosts_with_first_invalid
+ ldap = GitHub::Ldap.new(options.merge(hosts: [["invalid.local", options[:port]], ["localhost", options[:port]]]))
+ assert ldap.test_connection, "Ldap connection expected to succeed"
+ end
+
def test_simple_tls
- assert_equal :simple_tls, @ldap.check_encryption(:ssl)
- assert_equal :simple_tls, @ldap.check_encryption('SSL')
- assert_equal :simple_tls, @ldap.check_encryption(:simple_tls)
+ expected = { method: :simple_tls, tls_options: { } }
+ assert_equal expected, @ldap.check_encryption(:ssl)
+ assert_equal expected, @ldap.check_encryption('SSL')
+ assert_equal expected, @ldap.check_encryption(:simple_tls)
end
def test_start_tls
- assert_equal :start_tls, @ldap.check_encryption(:tls)
- assert_equal :start_tls, @ldap.check_encryption('TLS')
- assert_equal :start_tls, @ldap.check_encryption(:start_tls)
+ expected = { method: :start_tls, tls_options: { } }
+ assert_equal expected, @ldap.check_encryption(:tls)
+ assert_equal expected, @ldap.check_encryption('TLS')
+ assert_equal expected, @ldap.check_encryption(:start_tls)
+ end
+
+ def test_tls_validation
+ assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_PEER } },
+ @ldap.check_encryption(:tls, verify_mode: OpenSSL::SSL::VERIFY_PEER))
+ assert_equal({ method: :start_tls, tls_options: { verify_mode: OpenSSL::SSL::VERIFY_NONE } },
+ @ldap.check_encryption(:tls, verify_mode: OpenSSL::SSL::VERIFY_NONE))
+ assert_equal({ method: :start_tls, tls_options: { cert_store: "some/path" } },
+ @ldap.check_encryption(:tls, cert_store: "some/path"))
+ assert_equal({ method: :start_tls, tls_options: {} },
+ @ldap.check_encryption(:tls, nil))
end
def test_search_delegator
- @ldap.domain('dc=github,dc=com').valid_login? 'calavera', 'secret'
+ assert user = @ldap.domain('dc=github,dc=com').valid_login?('user1', 'passworD1')
- result = @ldap.search(
- {:base => 'dc=github,dc=com',
- :attributes => %w(uid),
- :filter => Net::LDAP::Filter.eq('uid', 'calavera')})
+ result = @ldap.search \
+ :base => 'dc=github,dc=com',
+ :attributes => %w(uid),
+ :filter => Net::LDAP::Filter.eq('uid', 'user1')
refute result.empty?
- assert_equal 'calavera', result.first[:uid].first
+ assert_equal 'user1', result.first[:uid].first
end
- def test_virtual_attributes_defaults
- @ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true))
-
- assert @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults"
- assert_equal 'memberOf', @ldap.virtual_attributes.virtual_membership
+ def test_virtual_attributes_disabled
+ refute @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes disabled"
end
- def test_virtual_attributes_defaults
+ def test_virtual_attributes_configured
ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true))
- assert ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults"
+ assert ldap.virtual_attributes.enabled?,
+ "Expected virtual attributes to be enabled"
assert_equal 'memberOf', ldap.virtual_attributes.virtual_membership
end
- def test_virtual_attributes_hash
+ def test_virtual_attributes_configured_with_membership_attribute
ldap = GitHub::Ldap.new(options.merge(virtual_attributes: {virtual_membership: "isMemberOf"}))
- assert ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults"
+ assert ldap.virtual_attributes.enabled?,
+ "Expected virtual attributes to be enabled"
assert_equal 'isMemberOf', ldap.virtual_attributes.virtual_membership
end
- def test_virtual_attributes_disabled
- refute @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes disabled"
- end
-
def test_search_domains
ldap = GitHub::Ldap.new(options.merge(search_domains: ['dc=github,dc=com']))
- result = ldap.search(filter: Net::LDAP::Filter.eq('uid', 'calavera'))
+ result = ldap.search(filter: Net::LDAP::Filter.eq('uid', 'user1'))
refute result.empty?
- assert_equal 'calavera', result.first[:uid].first
+ assert_equal 'user1', result.first[:uid].first
end
def test_instruments_search
events = @service.subscribe "search.github_ldap"
- result = @ldap.search(filter: "(uid=calavera)", :base => "dc=github,dc=com")
+ result = @ldap.search(filter: "(uid=user1)", :base => "dc=github,dc=com")
refute_predicate result, :empty?
payload, event_result = events.pop
assert payload
assert event_result
assert_equal result, event_result
- assert_equal "(uid=calavera)", payload[:filter].to_s
+ assert_equal "(uid=user1)", payload[:filter].to_s
assert_equal "dc=github,dc=com", payload[:base]
end
+
+ def test_search_strategy_defaults
+ assert_equal GitHub::Ldap::MembershipValidators::Recursive, @ldap.membership_validator
+ assert_equal GitHub::Ldap::MemberSearch::Recursive, @ldap.member_search_strategy
+ end
+
+ def test_search_strategy_detects_active_directory
+ caps = Net::LDAP::Entry.new
+ caps[:supportedcapabilities] = [GitHub::Ldap::ACTIVE_DIRECTORY_V51_OID]
+
+ @ldap.stub :capabilities, caps do
+ @ldap.configure_search_strategy :detect
+
+ assert_equal GitHub::Ldap::MembershipValidators::ActiveDirectory, @ldap.membership_validator
+ assert_equal GitHub::Ldap::MemberSearch::ActiveDirectory, @ldap.member_search_strategy
+ end
+ end
+
+ def test_search_strategy_configured_to_classic
+ @ldap.configure_search_strategy :classic
+ assert_equal GitHub::Ldap::MembershipValidators::Classic, @ldap.membership_validator
+ assert_equal GitHub::Ldap::MemberSearch::Classic, @ldap.member_search_strategy
+ end
+
+ def test_search_strategy_configured_to_recursive
+ @ldap.configure_search_strategy :recursive
+ assert_equal GitHub::Ldap::MembershipValidators::Recursive, @ldap.membership_validator
+ assert_equal GitHub::Ldap::MemberSearch::Recursive, @ldap.member_search_strategy
+ end
+
+ def test_search_strategy_configured_to_active_directory
+ @ldap.configure_search_strategy :active_directory
+ assert_equal GitHub::Ldap::MembershipValidators::ActiveDirectory, @ldap.membership_validator
+ assert_equal GitHub::Ldap::MemberSearch::ActiveDirectory, @ldap.member_search_strategy
+ end
+
+ def test_search_strategy_misconfigured_to_unrecognized_strategy_falls_back_to_default
+ @ldap.configure_search_strategy :unknown
+ assert_equal GitHub::Ldap::MembershipValidators::Recursive, @ldap.membership_validator
+ assert_equal GitHub::Ldap::MemberSearch::Recursive, @ldap.member_search_strategy
+ end
+
+ def test_user_search_strategy_global_catalog_when_configured
+ @ldap.configure_user_search_strategy("global_catalog")
+ assert_kind_of GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy
+ end
+
+ def test_user_search_strategy_default_when_configured
+ @ldap.configure_user_search_strategy("default")
+ refute_kind_of GitHub::Ldap::UserSearch::ActiveDirectory, @ldap.user_search_strategy
+ assert_kind_of GitHub::Ldap::UserSearch::Default, @ldap.user_search_strategy
+ end
+
+ def test_capabilities
+ assert_kind_of Net::LDAP::Entry, @ldap.capabilities
+ end
end
class GitHubLdapTest < GitHub::Ldap::Test
diff --git a/test/member_search/active_directory_test.rb b/test/member_search/active_directory_test.rb
new file mode 100644
index 0000000..19f2c96
--- /dev/null
+++ b/test/member_search/active_directory_test.rb
@@ -0,0 +1,79 @@
+require_relative '../test_helper'
+
+class GitHubLdapActiveDirectoryMemberSearchStubbedTest < GitHub::Ldap::Test
+ # Only run when AD integration tests aren't run
+ def run(*)
+ return super if self.class.test_env != "activedirectory"
+ Minitest::Result.from(self)
+ end
+
+ def find_group(cn)
+ @domain.groups([cn]).first
+ end
+
+ def setup
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com)))
+ @domain = @ldap.domain("dc=github,dc=com")
+ @entry = @domain.user?('user1')
+ @strategy = GitHub::Ldap::MemberSearch::ActiveDirectory.new(@ldap)
+ end
+
+ def test_finds_group_members
+ members =
+ @ldap.stub :search, [@entry] do
+ @strategy.perform(find_group("nested-group1")).map(&:dn)
+ end
+ assert_includes members, @entry.dn
+ end
+
+ def test_finds_nested_group_members
+ members =
+ @ldap.stub :search, [@entry] do
+ @strategy.perform(find_group("n-depth-nested-group1")).map(&:dn)
+ end
+ assert_includes members, @entry.dn
+ end
+
+ def test_finds_deeply_nested_group_members
+ members =
+ @ldap.stub :search, [@entry] do
+ @strategy.perform(find_group("n-depth-nested-group9")).map(&:dn)
+ end
+ assert_includes members, @entry.dn
+ end
+end
+
+# See test/support/vm/activedirectory/README.md for details
+class GitHubLdapActiveDirectoryMemberSearchIntegrationTest < GitHub::Ldap::Test
+ # Only run this test suite if ActiveDirectory is configured
+ def run(*)
+ return super if self.class.test_env == "activedirectory"
+ Minitest::Result.from(self)
+ end
+
+ def find_group(cn)
+ @domain.groups([cn]).first
+ end
+
+ def setup
+ @ldap = GitHub::Ldap.new(options)
+ @domain = @ldap.domain(options[:search_domains])
+ @entry = @domain.user?('user1')
+ @strategy = GitHub::Ldap::MemberSearch::ActiveDirectory.new(@ldap)
+ end
+
+ def test_finds_group_members
+ members = @strategy.perform(find_group("nested-group1")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+
+ def test_finds_nested_group_members
+ members = @strategy.perform(find_group("n-depth-nested-group1")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+
+ def test_finds_deeply_nested_group_members
+ members = @strategy.perform(find_group("n-depth-nested-group9")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+end
diff --git a/test/member_search/classic_test.rb b/test/member_search/classic_test.rb
new file mode 100644
index 0000000..656e12b
--- /dev/null
+++ b/test/member_search/classic_test.rb
@@ -0,0 +1,40 @@
+require_relative '../test_helper'
+
+class GitHubLdapRecursiveMemberSearchTest < GitHub::Ldap::Test
+ def setup
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com)))
+ @domain = @ldap.domain("dc=github,dc=com")
+ @entry = @domain.user?('user1')
+ @strategy = GitHub::Ldap::MemberSearch::Classic.new(@ldap)
+ end
+
+ def find_group(cn)
+ @domain.groups([cn]).first
+ end
+
+ def test_finds_group_members
+ members = @strategy.perform(find_group("nested-group1")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+
+ def test_finds_nested_group_members
+ members = @strategy.perform(find_group("n-depth-nested-group1")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+
+ def test_finds_deeply_nested_group_members
+ members = @strategy.perform(find_group("n-depth-nested-group9")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+
+ def test_finds_posix_group_members
+ members = @strategy.perform(find_group("posix-group1")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+
+ def test_does_not_respect_configured_depth_limit
+ strategy = GitHub::Ldap::MemberSearch::Classic.new(@ldap, depth: 2)
+ members = strategy.perform(find_group("n-depth-nested-group9")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+end
diff --git a/test/member_search/recursive_test.rb b/test/member_search/recursive_test.rb
new file mode 100644
index 0000000..a2d388d
--- /dev/null
+++ b/test/member_search/recursive_test.rb
@@ -0,0 +1,40 @@
+require_relative '../test_helper'
+
+class GitHubLdapRecursiveMemberSearchTest < GitHub::Ldap::Test
+ def setup
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com)))
+ @domain = @ldap.domain("dc=github,dc=com")
+ @entry = @domain.user?('user1')
+ @strategy = GitHub::Ldap::MemberSearch::Recursive.new(@ldap)
+ end
+
+ def find_group(cn)
+ @domain.groups([cn]).first
+ end
+
+ def test_finds_group_members
+ members = @strategy.perform(find_group("nested-group1")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+
+ def test_finds_nested_group_members
+ members = @strategy.perform(find_group("n-depth-nested-group1")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+
+ def test_finds_deeply_nested_group_members
+ members = @strategy.perform(find_group("n-depth-nested-group9")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+
+ def test_finds_posix_group_members
+ members = @strategy.perform(find_group("posix-group1")).map(&:dn)
+ assert_includes members, @entry.dn
+ end
+
+ def test_respects_configured_depth_limit
+ strategy = GitHub::Ldap::MemberSearch::Recursive.new(@ldap, depth: 2)
+ members = strategy.perform(find_group("n-depth-nested-group9")).map(&:dn)
+ refute_includes members, @entry.dn
+ end
+end
diff --git a/test/membership_validators/active_directory_test.rb b/test/membership_validators/active_directory_test.rb
new file mode 100644
index 0000000..2160f8d
--- /dev/null
+++ b/test/membership_validators/active_directory_test.rb
@@ -0,0 +1,137 @@
+require_relative '../test_helper'
+
+class GitHubLdapActiveDirectoryMembershipValidatorsStubbedTest < GitHub::Ldap::Test
+ # Only run when AD integration tests aren't run
+ def run(*)
+ return super if self.class.test_env != "activedirectory"
+ Minitest::Result.from(self)
+ end
+
+ def setup
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com)))
+ @domain = @ldap.domain("dc=github,dc=com")
+ @entry = @domain.user?('user1')
+ @validator = GitHub::Ldap::MembershipValidators::ActiveDirectory
+ end
+
+ def make_validator(groups)
+ groups = @domain.groups(groups)
+ @validator.new(@ldap, groups)
+ end
+
+ def test_validates_user_in_group
+ validator = make_validator(%w(nested-group1))
+
+ @ldap.stub :search, [@entry] do
+ assert validator.perform(@entry)
+ end
+ end
+
+ def test_validates_user_in_child_group
+ validator = make_validator(%w(n-depth-nested-group1))
+
+ @ldap.stub :search, [@entry] do
+ assert validator.perform(@entry)
+ end
+ end
+
+ def test_validates_user_in_grandchild_group
+ validator = make_validator(%w(n-depth-nested-group2))
+
+ @ldap.stub :search, [@entry] do
+ assert validator.perform(@entry)
+ end
+ end
+
+ def test_validates_user_in_great_grandchild_group
+ validator = make_validator(%w(n-depth-nested-group3))
+
+ @ldap.stub :search, [@entry] do
+ assert validator.perform(@entry)
+ end
+ end
+
+ def test_does_not_validate_user_not_in_group
+ validator = make_validator(%w(ghe-admins))
+
+ @ldap.stub :search, [] do
+ refute validator.perform(@entry)
+ end
+ end
+
+ def test_does_not_validate_user_not_in_any_group
+ entry = @domain.user?('groupless-user1')
+ validator = make_validator(%w(all-users))
+
+ @ldap.stub :search, [] do
+ refute validator.perform(entry)
+ end
+ end
+end
+
+# See test/support/vm/activedirectory/README.md for details
+class GitHubLdapActiveDirectoryMembershipValidatorsIntegrationTest < GitHub::Ldap::Test
+ # Only run this test suite if ActiveDirectory is configured
+ def run(*)
+ return super if self.class.test_env == "activedirectory"
+ Minitest::Result.from(self)
+ end
+
+ def setup
+ @ldap = GitHub::Ldap.new(options)
+ @domain = @ldap.domain(options[:search_domains])
+ @entry = @domain.user?('user1')
+ @validator = GitHub::Ldap::MembershipValidators::ActiveDirectory
+ end
+
+ def make_validator(groups)
+ groups = @domain.groups(groups)
+ @validator.new(@ldap, groups)
+ end
+
+ def test_validates_user_in_group
+ validator = make_validator(%w(nested-group1))
+ assert validator.perform(@entry)
+ end
+
+ def test_validates_user_in_child_group
+ validator = make_validator(%w(n-depth-nested-group1))
+ assert validator.perform(@entry)
+ end
+
+ def test_validates_user_in_grandchild_group
+ validator = make_validator(%w(n-depth-nested-group2))
+ assert validator.perform(@entry)
+ end
+
+ def test_validates_user_in_great_grandchild_group
+ validator = make_validator(%w(n-depth-nested-group3))
+ assert validator.perform(@entry)
+ end
+
+ def test_does_not_validate_user_not_in_group
+ validator = make_validator(%w(ghe-admins))
+ refute validator.perform(@entry)
+ end
+
+ def test_does_not_validate_user_not_in_any_group
+ skip "update AD ldif to have a groupless user"
+ @entry = @domain.user?('groupless-user1')
+ validator = make_validator(%w(all-users))
+ refute validator.perform(@entry)
+ end
+
+ def test_validates_user_in_posix_group
+ validator = make_validator(%w(posix-group1))
+ assert validator.perform(@entry)
+ end
+
+ def test_validates_user_in_group_with_differently_cased_dn
+ validator = make_validator(%w(all-users))
+ @entry[:dn].map(&:upcase!)
+ assert validator.perform(@entry)
+
+ @entry[:dn].map(&:downcase!)
+ assert validator.perform(@entry)
+ end
+end
diff --git a/test/membership_validators/classic_test.rb b/test/membership_validators/classic_test.rb
new file mode 100644
index 0000000..9b2c32b
--- /dev/null
+++ b/test/membership_validators/classic_test.rb
@@ -0,0 +1,51 @@
+require_relative '../test_helper'
+
+class GitHubLdapClassicMembershipValidatorsTest < GitHub::Ldap::Test
+ def setup
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com)))
+ @domain = @ldap.domain("dc=github,dc=com")
+ @entry = @domain.user?('user1')
+ @validator = GitHub::Ldap::MembershipValidators::Classic
+ end
+
+ def make_validator(groups)
+ groups = @domain.groups(groups)
+ @validator.new(@ldap, groups)
+ end
+
+ def test_validates_user_in_group
+ validator = make_validator(%w(nested-group1))
+ assert validator.perform(@entry)
+ end
+
+ def test_validates_user_in_child_group
+ validator = make_validator(%w(n-depth-nested-group1))
+ assert validator.perform(@entry)
+ end
+
+ def test_validates_user_in_grandchild_group
+ validator = make_validator(%w(n-depth-nested-group2))
+ assert validator.perform(@entry)
+ end
+
+ def test_validates_user_in_great_grandchild_group
+ validator = make_validator(%w(n-depth-nested-group3))
+ assert validator.perform(@entry)
+ end
+
+ def test_does_not_validate_user_not_in_group
+ validator = make_validator(%w(ghe-admins))
+ refute validator.perform(@entry)
+ end
+
+ def test_does_not_validate_user_not_in_any_group
+ @entry = @domain.user?('groupless-user1')
+ validator = make_validator(%w(all-users))
+ refute validator.perform(@entry)
+ end
+
+ def test_validates_user_in_posix_group
+ validator = make_validator(%w(posix-group1))
+ assert validator.perform(@entry)
+ end
+end
diff --git a/test/membership_validators/recursive_test.rb b/test/membership_validators/recursive_test.rb
new file mode 100644
index 0000000..072ffca
--- /dev/null
+++ b/test/membership_validators/recursive_test.rb
@@ -0,0 +1,56 @@
+require_relative '../test_helper'
+
+class GitHubLdapRecursiveMembershipValidatorsTest < GitHub::Ldap::Test
+ def setup
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com)))
+ @domain = @ldap.domain("dc=github,dc=com")
+ @entry = @domain.user?('user1')
+ @validator = GitHub::Ldap::MembershipValidators::Recursive
+ end
+
+ def make_validator(groups, options = {})
+ groups = @domain.groups(groups)
+ @validator.new(@ldap, groups, options)
+ end
+
+ def test_validates_user_in_group
+ validator = make_validator(%w(nested-group1))
+ assert validator.perform(@entry)
+ end
+
+ def test_validates_user_in_child_group
+ validator = make_validator(%w(n-depth-nested-group1))
+ assert validator.perform(@entry)
+ end
+
+ def test_validates_user_in_grandchild_group
+ validator = make_validator(%w(n-depth-nested-group2))
+ assert validator.perform(@entry)
+ end
+
+ def test_validates_user_in_great_grandchild_group
+ validator = make_validator(%w(n-depth-nested-group3))
+ assert validator.perform(@entry)
+ end
+
+ def test_does_not_validate_user_in_great_granchild_group_with_depth
+ validator = make_validator(%w(n-depth-nested-group3), depth: 2)
+ refute validator.perform(@entry)
+ end
+
+ def test_does_not_validate_user_not_in_group
+ validator = make_validator(%w(ghe-admins))
+ refute validator.perform(@entry)
+ end
+
+ def test_does_not_validate_user_not_in_any_group
+ @entry = @domain.user?('groupless-user1')
+ validator = make_validator(%w(all-users))
+ refute validator.perform(@entry)
+ end
+
+ def test_validates_user_in_posix_group
+ validator = make_validator(%w(posix-group1))
+ assert validator.perform(@entry)
+ end
+end
diff --git a/test/posix_group_test.rb b/test/posix_group_test.rb
index a71e252..e21b3ac 100644
--- a/test/posix_group_test.rb
+++ b/test/posix_group_test.rb
@@ -1,54 +1,51 @@
require_relative 'test_helper'
class GitHubLdapPosixGroupTest < GitHub::Ldap::Test
- def self.test_server_options
- {user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s}
- end
-
def setup
@simple_group = Net::LDAP::Entry._load("""
-dn: cn=enterprise-posix-devs,ou=groups,dc=github,dc=com
-cn: enterprise-posix-devs
+dn: cn=simple-group,ou=Groups,dc=github,dc=com
+cn: simple-group
objectClass: posixGroup
-memberUid: benburkert
-memberUid: mtodd""")
+memberUid: user1
+memberUid: user2""")
@one_level_deep_group = Net::LDAP::Entry._load("""
-dn: cn=enterprise-posix-ops,ou=groups,dc=github,dc=com
-cn: enterprise-posix-ops
+dn: cn=one-level-deep-group,ou=Groups,dc=github,dc=com
+cn: one-level-deep-group
objectClass: posixGroup
objectClass: groupOfNames
-memberUid: sbryant
-member: cn=spaniards,ou=groups,dc=github,dc=com""")
+memberUid: user6
+member: cn=ghe-users,ou=Groups,dc=github,dc=com""")
@two_levels_deep_group = Net::LDAP::Entry._load("""
-dn: cn=enterprise-posix,ou=groups,dc=github,dc=com
-cn: Enterprise Posix
+dn: cn=two-levels-deep-group,ou=Groups,dc=github,dc=com
+cn: two-levels-deep-group
objectClass: posixGroup
objectClass: groupOfNames
-memberUid: calavera
-member: cn=enterprise-devs,ou=groups,dc=github,dc=com
-member: cn=enterprise-ops,ou=groups,dc=github,dc=com""")
+memberUid: user6
+member: cn=n-depth-nested-group2,ou=Groups,dc=github,dc=com
+member: cn=posix-group1,ou=Groups,dc=github,dc=com""")
@empty_group = Net::LDAP::Entry._load("""
-dn: cn=enterprise-posix-empty,ou=groups,dc=github,dc=com
-cn: enterprise-posix-empty
+dn: cn=empty-group,ou=Groups,dc=github,dc=com
+cn: empty-group
objectClass: posixGroup""")
@ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com)))
end
def test_posix_group
- assert GitHub::Ldap::PosixGroup.valid?(@simple_group),
+ entry = @ldap.search(filter: "(cn=posix-group1)").first
+ assert GitHub::Ldap::PosixGroup.valid?(entry),
"Expected entry to be a valid posixGroup"
end
def test_posix_simple_members
- group = GitHub::Ldap::PosixGroup.new(@ldap, @simple_group)
+ assert group = @ldap.group("cn=posix-group1,ou=Groups,dc=github,dc=com")
members = group.members
- assert_equal 2, members.size
- assert_equal %w(benburkert mtodd), members.map(&:uid).flatten.sort
+ assert_equal 5, members.size
+ assert_equal %w(user1 user2 user3 user4 user5), members.map(&:uid).flatten.sort
end
def test_posix_combined_group
@@ -62,7 +59,7 @@ def test_posix_combined_group_unique_members
group = GitHub::Ldap::PosixGroup.new(@ldap, @two_levels_deep_group)
members = group.members
- assert_equal 4, members.size
+ assert_equal 10, members.size
end
def test_empty_subgroups
@@ -81,7 +78,7 @@ def test_posix_combined_group_subgroups
def test_is_member_simple_group
group = GitHub::Ldap::PosixGroup.new(@ldap, @simple_group)
- user = @ldap.domain("uid=benburkert,ou=users,dc=github,dc=com").bind
+ user = @ldap.domain("uid=user1,ou=People,dc=github,dc=com").bind
assert group.is_member?(user),
"Expected user in the memberUid list to be a member of the posixgroup"
@@ -89,7 +86,7 @@ def test_is_member_simple_group
def test_is_member_combined_group
group = GitHub::Ldap::PosixGroup.new(@ldap, @one_level_deep_group)
- user = @ldap.domain("uid=calavera,ou=users,dc=github,dc=com").bind
+ user = @ldap.domain("uid=user1,ou=People,dc=github,dc=com").bind
assert group.is_member?(user),
"Expected user in a subgroup to be a member of the posixgroup"
@@ -97,7 +94,7 @@ def test_is_member_combined_group
def test_is_not_member_simple_group
group = GitHub::Ldap::PosixGroup.new(@ldap, @simple_group)
- user = @ldap.domain("uid=calavera,ou=users,dc=github,dc=com").bind
+ user = @ldap.domain("uid=user10,ou=People,dc=github,dc=com").bind
refute group.is_member?(user),
"Expected user to not be member when her uid is not in the list of memberUid"
@@ -105,7 +102,7 @@ def test_is_not_member_simple_group
def test_is_member_combined_group
group = GitHub::Ldap::PosixGroup.new(@ldap, @one_level_deep_group)
- user = @ldap.domain("uid=benburkert,ou=users,dc=github,dc=com").bind
+ user = @ldap.domain("uid=user10,ou=People,dc=github,dc=com").bind
refute group.is_member?(user),
"Expected user to not be member when she's not member of any subgroup"
diff --git a/test/referral_chaser_test.rb b/test/referral_chaser_test.rb
new file mode 100644
index 0000000..3a19973
--- /dev/null
+++ b/test/referral_chaser_test.rb
@@ -0,0 +1,102 @@
+require_relative 'test_helper'
+
+class GitHubLdapReferralChaserTestCases < GitHub::Ldap::Test
+
+ def setup
+ @ldap = GitHub::Ldap.new(options)
+ @chaser = GitHub::Ldap::ReferralChaser.new(@ldap)
+ end
+
+ def test_creates_referral_with_connection_credentials
+ @ldap.expects(:search).yields({ search_referrals: ["ldap://dc1.ghe.local/"]}).returns([])
+
+ referral = mock("GitHub::Ldap::ReferralChaser::Referral")
+ referral.stubs(:search).returns([])
+
+ GitHub::Ldap::ReferralChaser::Referral.expects(:new)
+ .with("ldap://dc1.ghe.local/", "uid=admin,dc=github,dc=com", "passworD1", options[:port])
+ .returns(referral)
+
+ @chaser.search({})
+ end
+
+ def test_creates_referral_with_default_port
+ @ldap.expects(:search).yields({
+ search_referrals: ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]
+ }).returns([])
+
+ stub_referral_connection = mock("GitHub::Ldap")
+ stub_referral_connection.stubs(:search).returns([])
+ GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(port: options[:port])).returns(stub_referral_connection)
+ chaser = GitHub::Ldap::ReferralChaser.new(@ldap)
+ chaser.search({})
+ end
+
+ def test_creates_referral_for_first_referral_string
+ @ldap.expects(:search).multiple_yields([
+ { search_referrals:
+ ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local",
+ "ldap://dc2.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]
+ }
+ ],[
+ { search_referrals:
+ ["ldap://dc3.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local",
+ "ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]
+ }
+ ]).returns([])
+
+ referral = mock("GitHub::Ldap::ReferralChaser::Referral")
+ referral.stubs(:search).returns([])
+
+ GitHub::Ldap::ReferralChaser::Referral.expects(:new)
+ .with(
+ "ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local",
+ "uid=admin,dc=github,dc=com",
+ "passworD1",
+ options[:port])
+ .returns(referral)
+
+ @chaser.search({})
+ end
+
+ def test_returns_referral_search_results
+ @ldap.expects(:search).multiple_yields([
+ { search_referrals:
+ ["ldap://dc1.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local",
+ "ldap://dc2.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]
+ }
+ ],[
+ { search_referrals:
+ ["ldap://dc3.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local",
+ "ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local"]
+ }
+ ]).returns([])
+
+ referral = mock("GitHub::Ldap::ReferralChaser::Referral")
+ referral.expects(:search).returns(["result", "result"])
+
+ GitHub::Ldap::ReferralChaser::Referral.expects(:new).returns(referral)
+
+ results = @chaser.search({})
+ assert_equal(["result", "result"], results)
+ end
+
+ def test_handle_blank_url_string_in_referral
+ @ldap.expects(:search).yields({ search_referrals: [""] })
+
+ results = @chaser.search({})
+ assert_equal([], results)
+ end
+
+ def test_returns_referral_search_results
+ @ldap.expects(:search).yields({ foo: ["not a referral"] })
+
+ GitHub::Ldap::ReferralChaser::Referral.expects(:new).never
+ results = @chaser.search({})
+ end
+
+ def test_referral_should_use_host_from_referral_string
+ GitHub::Ldap::ConnectionCache.expects(:get_connection).with(has_entry(host: "dc4.ghe.local"))
+ GitHub::Ldap::ReferralChaser::Referral.new("ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local", "", "")
+ end
+end
diff --git a/test/support/vm/activedirectory/.gitignore b/test/support/vm/activedirectory/.gitignore
new file mode 100644
index 0000000..137e678
--- /dev/null
+++ b/test/support/vm/activedirectory/.gitignore
@@ -0,0 +1 @@
+env.sh
diff --git a/test/support/vm/activedirectory/README.md b/test/support/vm/activedirectory/README.md
new file mode 100644
index 0000000..36155bd
--- /dev/null
+++ b/test/support/vm/activedirectory/README.md
@@ -0,0 +1,26 @@
+# Local ActiveDirectory Integration Testing
+
+Integration tests are not run for ActiveDirectory in continuous integration
+because we cannot install a Windows VM on TravisCI. To test ActiveDirectory,
+configure a local VM with AD running (this is left as an exercise for the
+reader).
+
+To run integration tests against the local ActiveDirectory VM, from the project
+root run:
+
+``` bash
+# duplicate example env.sh for specific config
+$ cp test/support/vm/activedirectory/env.sh{.example,}
+
+# edit env.sh and fill in with your VM's values, then
+$ source test/support/vm/activedirectory/env.sh
+
+# run all tests against AD
+$ time bundle exec rake
+
+# run a specific test file against AD
+$ time bundle exec ruby test/membership_validators/active_directory_test.rb
+
+# reset environment to test other LDAP servers
+$ source test/support/vm/activedirectory/reset-env.sh
+```
diff --git a/test/support/vm/activedirectory/env.sh.example b/test/support/vm/activedirectory/env.sh.example
new file mode 100644
index 0000000..3ca2c9b
--- /dev/null
+++ b/test/support/vm/activedirectory/env.sh.example
@@ -0,0 +1,8 @@
+# Copy this to ad-env.sh, and fill in with your own values
+
+export TESTENV=activedirectory
+export INTEGRATION_HOST=123.123.123.123
+export INTEGRATION_PORT=389
+export INTEGRATION_USER="CN=Administrator,CN=Users,DC=ad,DC=example,DC=com"
+export INTEGRATION_PASSWORD='passworD1'
+export INTEGRATION_SEARCH_DOMAINS='CN=Users,DC=example,DC=com'
diff --git a/test/support/vm/activedirectory/reset-env.sh b/test/support/vm/activedirectory/reset-env.sh
new file mode 100644
index 0000000..971423f
--- /dev/null
+++ b/test/support/vm/activedirectory/reset-env.sh
@@ -0,0 +1,6 @@
+unset TESTENV
+unset INTEGRATION_HOST
+unset INTEGRATION_PORT
+unset INTEGRATION_USER
+unset INTEGRATION_PASSWORD
+unset INTEGRATION_SEARCH_DOMAINS
diff --git a/test/support/vm/openldap/.gitignore b/test/support/vm/openldap/.gitignore
new file mode 100644
index 0000000..dace708
--- /dev/null
+++ b/test/support/vm/openldap/.gitignore
@@ -0,0 +1 @@
+/.vagrant
diff --git a/test/support/vm/openldap/README.md b/test/support/vm/openldap/README.md
new file mode 100644
index 0000000..ced5a63
--- /dev/null
+++ b/test/support/vm/openldap/README.md
@@ -0,0 +1,32 @@
+# Local OpenLDAP Integration Testing
+
+Set up a [Vagrant](http://www.vagrantup.com/) VM to run tests against OpenLDAP locally.
+
+To run tests against OpenLDAP (instead of ApacheDS) locally:
+
+``` bash
+# start VM (from the correct directory)
+$ cd test/support/vm/openldap/
+$ vagrant up
+
+# get the IP address of the VM
+$ ip=$(vagrant ssh -- "ifconfig eth1 | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1")
+
+# change back to root project directory
+$ cd ../../../..
+
+# run all tests against OpenLDAP
+$ time TESTENV=openldap INTEGRATION_HOST=$ip bundle exec rake
+
+# run a specific test file against OpenLDAP
+$ time TESTENV=openldap INTEGRATION_HOST=$ip bundle exec ruby test/membership_validators/recursive_test.rb
+
+# run OpenLDAP tests by default
+$ export TESTENV=openldap
+$ export TESTENV=$ip
+
+# now run tests without having to set ENV variables
+$ time bundle exec rake
+```
+
+You may need to `gem install vagrant` first in order to provision the VM.
diff --git a/test/support/vm/openldap/Vagrantfile b/test/support/vm/openldap/Vagrantfile
new file mode 100644
index 0000000..abb44ae
--- /dev/null
+++ b/test/support/vm/openldap/Vagrantfile
@@ -0,0 +1,35 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
+VAGRANTFILE_API_VERSION = "2"
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+ config.vm.hostname = "openldap.github.org"
+
+ config.vm.box = "hashicorp/precise64"
+
+ config.vm.network "private_network", type: :dhcp
+
+ config.ssh.forward_agent = true
+
+ # config.vm.provision "shell", inline: "apt-get update; exec env /vagrant_data/script/install-openldap"
+ config.vm.provision "shell", inline: 'echo "HIIIIIII"', run: "always"
+
+ config.vm.synced_folder "../../../..", "/vagrant_data"
+
+ config.vm.provider "vmware_fusion" do |vb, override|
+ override.vm.box = "hashicorp/precise64"
+ vb.memory = 4596
+ vb.vmx["displayname"] = "integration tests vm"
+ vb.vmx["numvcpus"] = "2"
+ end
+
+ config.vm.provider "virtualbox" do |vb, override|
+ vb.memory = 4096
+ vb.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"]
+ vb.customize ["modifyvm", :id, "--chipset", "ich9"]
+ vb.customize ["modifyvm", :id, "--vram", "16"]
+ end
+
+end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index d996c5f..e92caa6 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -10,38 +10,92 @@
require 'github/ldap'
require 'github/ldap/server'
+require 'minitest/mock'
require 'minitest/autorun'
+require 'mocha/minitest'
+
+if ENV.fetch('TESTENV', "apacheds") == "apacheds"
+ # Make sure we clean up running test server
+ # NOTE: We need to do this manually since its internal `at_exit` hook
+ # collides with Minitest's autorun at_exit handling, hence this hook.
+ Minitest.after_run do
+ GitHub::Ldap.stop_server
+ end
+end
+
class GitHub::Ldap::Test < Minitest::Test
+ def self.test_env
+ ENV.fetch("TESTENV", "apacheds")
+ end
+
def self.run(reporter, options = {})
start_server
- super
+ result = super
stop_server
+ result
end
def self.stop_server
- GitHub::Ldap.stop_server
+ if test_env == "apacheds"
+ # see Minitest.after_run hook above.
+ # GitHub::Ldap.stop_server
+ end
+ end
+
+ def self.test_server_options
+ {
+ custom_schemas: FIXTURES.join('posixGroup.schema.ldif').to_s,
+ user_fixtures: FIXTURES.join('common/seed.ldif').to_s,
+ allow_anonymous: true,
+ verbose: ENV.fetch("VERBOSE", "0") == "1"
+ }
end
def self.start_server
- server_opts = respond_to?(:test_server_options) ? test_server_options : {}
- GitHub::Ldap.start_server(server_opts)
+ if test_env == "apacheds"
+ # skip this if a server has already been started
+ return if GitHub::Ldap.ldap_server
+
+ GitHub::Ldap.start_server(test_server_options)
+ end
end
def options
@service = MockInstrumentationService.new
- @options ||= GitHub::Ldap.server_options.merge \
- host: 'localhost',
- uid: 'uid',
- :instrumentation_service => @service
+ @options ||=
+ case self.class.test_env
+ when "apacheds"
+ GitHub::Ldap.server_options.merge \
+ admin_user: 'uid=admin,dc=github,dc=com',
+ admin_password: 'passworD1',
+ host: 'localhost',
+ uid: 'uid',
+ instrumentation_service: @service
+ when "openldap"
+ {
+ host: ENV.fetch("INTEGRATION_HOST", "localhost"),
+ port: 389,
+ admin_user: 'uid=admin,dc=github,dc=com',
+ admin_password: 'passworD1',
+ search_domains: %w(dc=github,dc=com),
+ uid: 'uid',
+ instrumentation_service: @service
+ }
+ when "activedirectory"
+ {
+ host: ENV.fetch("INTEGRATION_HOST"),
+ port: ENV.fetch("INTEGRATION_PORT", 389),
+ admin_user: ENV.fetch("INTEGRATION_USER"),
+ admin_password: ENV.fetch("INTEGRATION_PASSWORD"),
+ search_domains: ENV.fetch("INTEGRATION_SEARCH_DOMAINS"),
+ instrumentation_service: @service
+ }
+ end
end
end
class GitHub::Ldap::UnauthenticatedTest < GitHub::Ldap::Test
- def self.start_server
- GitHub::Ldap.start_server(:allow_anonymous => true)
- end
-
def options
@options ||= begin
super.delete_if {|k, _| [:admin_user, :admin_password].include?(k)}
diff --git a/test/url_test.rb b/test/url_test.rb
new file mode 100644
index 0000000..db44ce2
--- /dev/null
+++ b/test/url_test.rb
@@ -0,0 +1,85 @@
+require_relative 'test_helper'
+
+class GitHubLdapURLTestCases < GitHub::Ldap::Test
+
+ def setup
+ @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)")
+ end
+
+ def test_host
+ assert_equal "dc4.ghe.local", @url.host
+ end
+
+ def test_port
+ assert_equal 123, @url.port
+ end
+
+ def test_scheme
+ assert_equal "ldap", @url.scheme
+ end
+
+ def test_default_port
+ url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/CN=Maggie%20Mae,CN=Users,DC=dc4,DC=ghe,DC=local?attributes?scope?filter")
+ assert_equal 389, url.port
+ end
+
+ def test_simple_url
+ url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local")
+ assert_equal 389, url.port
+ assert_equal "dc4.ghe.local", url.host
+ assert_equal "ldap", url.scheme
+ assert_equal "", url.dn
+ assert_equal nil, url.attributes
+ assert_equal nil, url.filter
+ assert_equal nil, url.scope
+ end
+
+ def test_invalid_scheme
+ ex = assert_raises(GitHub::Ldap::URL::InvalidLdapURLException) do
+ GitHub::Ldap::URL.new("http://dc4.ghe.local")
+ end
+ assert_equal("Invalid LDAP URL: http://dc4.ghe.local", ex.message)
+ end
+
+ def test_invalid_url
+ ex = assert_raises(GitHub::Ldap::URL::InvalidLdapURLException) do
+ GitHub::Ldap::URL.new("not a url")
+ end
+ assert_equal("Invalid LDAP URL: not a url", ex.message)
+ end
+
+ def test_parse_dn
+ assert_equal "CN=Maggie Mae,CN=Users,DC=dc4,DC=ghe,DC=local", @url.dn
+ end
+
+ def test_parse_attributes
+ assert_equal "cn,mail,telephoneNumber", @url.attributes
+ end
+
+ def test_parse_filter
+ assert_equal "(cn=Charlie)", @url.filter
+ end
+
+ def test_parse_scope
+ assert_equal "base", @url.scope
+ end
+
+ def test_default_scope
+ url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/base_dn?cn=joe??filter")
+ assert_equal "", url.scope
+ end
+
+ def test_net_ldap_scopes
+ sub_scope_url = GitHub::Ldap::URL.new("ldap://ghe.local/base_dn?cn=joe?sub?filter")
+ one_scope_url = GitHub::Ldap::URL.new("ldap://ghe.local/base_dn?cn=joe?one?filter")
+ base_scope_url = GitHub::Ldap::URL.new("ldap://ghe.local/base_dn?cn=joe?base?filter")
+ default_scope_url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/base_dn?cn=joe??filter")
+ invalid_scope_url = GitHub::Ldap::URL.new("ldap://dc4.ghe.local/base_dn?cn=joe?invalid?filter")
+
+ assert_equal Net::LDAP::SearchScope_BaseObject, base_scope_url.net_ldap_scope
+ assert_equal Net::LDAP::SearchScope_SingleLevel, one_scope_url.net_ldap_scope
+ assert_equal Net::LDAP::SearchScope_WholeSubtree, sub_scope_url.net_ldap_scope
+ assert_equal Net::LDAP::SearchScope_BaseObject, default_scope_url.net_ldap_scope
+ assert_equal Net::LDAP::SearchScope_BaseObject, invalid_scope_url.net_ldap_scope
+ end
+end
diff --git a/test/user_search/active_directory_test.rb b/test/user_search/active_directory_test.rb
new file mode 100644
index 0000000..32bed79
--- /dev/null
+++ b/test/user_search/active_directory_test.rb
@@ -0,0 +1,53 @@
+require_relative '../test_helper'
+
+class GitHubLdapActiveDirectoryUserSearchTests < GitHub::Ldap::Test
+
+ def test_global_catalog_returns_empty_array_for_no_results
+ ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev'))
+ ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(ldap)
+
+ mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog")
+ mock_global_catalog_connection.expects(:search).returns(nil)
+ ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection)
+ results = ad_user_search.perform("login", "CN=Joe", "uid", {})
+ assert_equal [], results
+ end
+
+ def test_global_catalog_returns_array_of_results
+ ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev'))
+ ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(ldap)
+
+ mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog")
+ stub_entry = mock("Net::LDAP::Entry")
+
+ mock_global_catalog_connection.expects(:search).returns([stub_entry])
+ ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection)
+
+ results = ad_user_search.perform("login", "CN=Joe", "uid", {})
+ assert_equal [stub_entry], results
+ end
+
+ def test_searches_with_empty_base_dn
+ ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev'))
+ ad_user_search = GitHub::Ldap::UserSearch::ActiveDirectory.new(ldap)
+
+ mock_global_catalog_connection = mock("GitHub::Ldap::UserSearch::GlobalCatalog")
+ mock_global_catalog_connection.expects(:search).with(has_entry(:base => ""))
+ ad_user_search.expects(:global_catalog_connection).returns(mock_global_catalog_connection)
+ ad_user_search.perform("login", "CN=Joe", "uid", {})
+ end
+
+ def test_global_catalog_default_settings
+ ldap = GitHub::Ldap.new(options.merge(host: 'ghe.dev'))
+ global_catalog = GitHub::Ldap::UserSearch::GlobalCatalog.connection(ldap)
+ instrumentation_service = global_catalog.instance_variable_get(:@instrumentation_service)
+
+ auth = global_catalog.instance_variable_get(:@auth)
+ assert_equal :simple, auth[:method]
+ assert_equal "uid=admin,dc=github,dc=com", auth[:username]
+ assert_equal "passworD1", auth[:password]
+ assert_equal "ghe.dev", global_catalog.host
+ assert_equal 3268, global_catalog.port
+ assert_equal "MockInstrumentationService", instrumentation_service.class.name
+ end
+end
diff --git a/test/user_search/default_test.rb b/test/user_search/default_test.rb
new file mode 100644
index 0000000..abc230a
--- /dev/null
+++ b/test/user_search/default_test.rb
@@ -0,0 +1,19 @@
+require_relative '../test_helper'
+
+class GitHubLdapActiveDirectoryUserSearchTests < GitHub::Ldap::Test
+ def setup
+ @ldap = GitHub::Ldap.new(options)
+ @default_user_search = GitHub::Ldap::UserSearch::Default.new(@ldap)
+ end
+
+ def test_default_search_options
+ @ldap.expects(:search).with(has_entries(
+ attributes: [],
+ size: 1,
+ paged_searches_supported: true,
+ base: "CN=HI,CN=McDunnough",
+ filter: kind_of(Net::LDAP::Filter)
+ ))
+ @default_user_search.perform("","CN=HI,CN=McDunnough","",{})
+ end
+end
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy