github-ldap 1.3.3 → 1.4.0

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +15 -2
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile +4 -0
  5. data/README.md +15 -1
  6. data/Rakefile +1 -1
  7. data/github-ldap.gemspec +2 -2
  8. data/lib/github/ldap.rb +55 -12
  9. data/lib/github/ldap/domain.rb +6 -2
  10. data/lib/github/ldap/filter.rb +15 -7
  11. data/lib/github/ldap/group.rb +1 -1
  12. data/lib/github/ldap/instrumentation.rb +28 -0
  13. data/lib/github/ldap/membership_validators.rb +18 -0
  14. data/lib/github/ldap/membership_validators/active_directory.rb +56 -0
  15. data/lib/github/ldap/membership_validators/base.rb +37 -0
  16. data/lib/github/ldap/membership_validators/classic.rb +34 -0
  17. data/lib/github/ldap/membership_validators/recursive.rb +93 -0
  18. data/lib/github/ldap/server.rb +2 -0
  19. data/script/changelog +29 -0
  20. data/script/cibuild-apacheds +7 -0
  21. data/script/cibuild-openldap +7 -0
  22. data/script/install-openldap +44 -0
  23. data/script/package +7 -0
  24. data/script/release +16 -0
  25. data/test/domain_test.rb +71 -89
  26. data/test/filter_test.rb +12 -1
  27. data/test/fixtures/common/seed.ldif +369 -0
  28. data/test/fixtures/openldap/memberof.ldif +33 -0
  29. data/test/fixtures/openldap/slapd.conf.ldif +67 -0
  30. data/test/fixtures/posixGroup.schema.ldif +34 -8
  31. data/test/group_test.rb +19 -25
  32. data/test/ldap_test.rb +28 -21
  33. data/test/membership_validators/active_directory_test.rb +68 -0
  34. data/test/membership_validators/classic_test.rb +51 -0
  35. data/test/membership_validators/recursive_test.rb +56 -0
  36. data/test/membership_validators_test.rb +46 -0
  37. data/test/posix_group_test.rb +25 -28
  38. data/test/support/vm/openldap/.gitignore +1 -0
  39. data/test/support/vm/openldap/README.md +32 -0
  40. data/test/support/vm/openldap/Vagrantfile +35 -0
  41. data/test/test_helper.rb +72 -10
  42. metadata +52 -27
  43. data/test/fixtures/github-with-looped-subgroups.ldif +0 -82
  44. data/test/fixtures/github-with-missing-entries.ldif +0 -85
  45. data/test/fixtures/github-with-posixGroups.ldif +0 -50
  46. data/test/fixtures/github-with-subgroups.ldif +0 -146
@@ -1,26 +1,52 @@
1
1
  version: 1
2
2
 
3
- dn: m-oid=1.3.6.1.4.1.18055.0.4.1.2.1001,ou=attributeTypes,cn=other,ou=schema
3
+ # attributetype ( 1.3.6.1.1.1.1.1 NAME 'gidNumber'
4
+ # DESC 'An integer uniquely identifying a group in an administrative domain'
5
+ # EQUALITY integerMatch
6
+ # SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
7
+ dn: m-oid=1.3.6.1.1.1.1.1,ou=attributeTypes,cn=other,ou=schema
8
+ objectClass: metaAttributeType
9
+ objectClass: metaTop
10
+ objectClass: top
11
+ m-collective: FALSE
12
+ m-description: An integer uniquely identifying a group in an administrative domain
13
+ m-equality: integerMatch
14
+ m-name: gidNumber
15
+ m-syntax: 1.3.6.1.4.1.1466.115.121.1.27
16
+ m-usage: USER_APPLICATIONS
17
+ m-oid: 1.3.6.1.1.1.1.1
18
+
19
+ # attributetype ( 1.3.6.1.1.1.1.12 NAME 'memberUid'
20
+ # EQUALITY caseExactIA5Match
21
+ # SUBSTR caseExactIA5SubstringsMatch
22
+ # SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
23
+ dn: m-oid=1.3.6.1.1.1.1.12,ou=attributeTypes,cn=other,ou=schema
4
24
  objectClass: metaAttributeType
5
25
  objectClass: metaTop
6
26
  objectClass: top
7
27
  m-collective: FALSE
8
28
  m-description: memberUid
9
- m-equality: caseExactMatch
29
+ m-equality: caseExactIA5Match
10
30
  m-name: memberUid
11
- m-syntax: 1.3.6.1.4.1.1466.115.121.1.15
31
+ m-syntax: 1.3.6.1.4.1.1466.115.121.1.26
12
32
  m-usage: USER_APPLICATIONS
13
- m-oid: 1.3.6.1.4.1.18055.0.4.1.2.1001
33
+ m-oid: 1.3.6.1.1.1.1.12
14
34
 
15
- dn: m-oid=1.3.6.1.4.1.18055.0.4.1.3.1001,ou=objectClasses,cn=other,ou=schema
35
+ # objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' SUP top STRUCTURAL
36
+ # DESC 'Abstraction of a group of accounts'
37
+ # MUST ( cn $ gidNumber )
38
+ # MAY ( userPassword $ memberUid $ description ) )
39
+ dn: m-oid=1.3.6.1.1.1.2.2,ou=objectClasses,cn=other,ou=schema
16
40
  objectClass: metaObjectClass
17
41
  objectClass: metaTop
18
42
  objectClass: top
19
43
  m-description: posixGroup
20
- m-may: cn
21
- m-may: sn
44
+ m-must: cn
45
+ m-must: gidNumber
22
46
  m-may: memberUid
47
+ m-may: userPassword
48
+ m-may: description
23
49
  m-supobjectclass: top
24
50
  m-name: posixGroup
25
- m-oid: 1.3.6.1.4.1.18055.0.4.1.3.1001
51
+ m-oid: 1.3.6.1.1.1.2.2
26
52
  m-typeobjectclass: STRUCTURAL
data/test/group_test.rb CHANGED
@@ -1,54 +1,56 @@
1
1
  require_relative 'test_helper'
2
2
 
3
3
  class GitHubLdapGroupTest < GitHub::Ldap::Test
4
- def self.test_server_options
5
- {user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s}
6
- end
7
-
8
4
  def groups_domain
9
- @ldap.domain("ou=groups,dc=github,dc=com")
5
+ @ldap.domain("ou=Groups,dc=github,dc=com")
10
6
  end
11
7
 
12
8
  def setup
13
9
  @ldap = GitHub::Ldap.new(options)
14
- @group = @ldap.group("cn=enterprise,ou=groups,dc=github,dc=com")
10
+ @group = @ldap.group("cn=ghe-users,ou=Groups,dc=github,dc=com")
15
11
  end
16
12
 
17
13
  def test_group?
14
+ assert @group.group?(%w(group))
15
+ assert @group.group?(%w(groupOfUniqueNames))
16
+ assert @group.group?(%w(posixGroup))
17
+
18
18
  object_classes = %w(groupOfNames)
19
19
  assert @group.group?(object_classes)
20
20
  assert @group.group?(object_classes.map(&:downcase))
21
21
  end
22
22
 
23
23
  def test_subgroups
24
- assert_equal 3, @group.subgroups.size
24
+ group = @ldap.group("cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com")
25
+ assert_equal 2, group.subgroups.size
25
26
  end
26
27
 
27
28
  def test_members_from_subgroups
28
- assert_equal 4, @group.members.size
29
+ group = @ldap.group("cn=deeply-nested-group0.0,ou=Groups,dc=github,dc=com")
30
+ assert_equal 10, group.members.size
29
31
  end
30
32
 
31
33
  def test_all_domain_groups
32
34
  groups = groups_domain.all_groups
33
- assert_equal 8, groups.size
35
+ assert_equal 27, groups.size
34
36
  end
35
37
 
36
38
  def test_filter_domain_groups
37
- groups = groups_domain.filter_groups('devs')
39
+ groups = groups_domain.filter_groups('ghe-users')
38
40
  assert_equal 1, groups.size
39
41
  end
40
42
 
41
43
  def test_filter_domain_groups_limited
42
44
  groups = []
43
- groups_domain.filter_groups('enter', size: 1) do |entry|
45
+ groups_domain.filter_groups('deeply-nested-group', size: 1) do |entry|
44
46
  groups << entry
45
47
  end
46
48
  assert_equal 1, groups.size
47
49
  end
48
50
 
49
51
  def test_filter_domain_groups_unlimited
50
- groups = groups_domain.filter_groups('ent')
51
- assert_equal 3, groups.size
52
+ groups = groups_domain.filter_groups('deeply-nested-group')
53
+ assert_equal 5, groups.size
52
54
  end
53
55
 
54
56
  def test_unknown_group
@@ -58,33 +60,25 @@ class GitHubLdapGroupTest < GitHub::Ldap::Test
58
60
  end
59
61
 
60
62
  class GitHubLdapLoopedGroupTest < GitHub::Ldap::Test
61
- def self.test_server_options
62
- {user_fixtures: FIXTURES.join('github-with-looped-subgroups.ldif').to_s}
63
- end
64
-
65
63
  def setup
66
- @group = GitHub::Ldap.new(options).group("cn=enterprise,ou=groups,dc=github,dc=com")
64
+ @group = GitHub::Ldap.new(options).group("cn=recursively-nested-groups,ou=Groups,dc=github,dc=com")
67
65
  end
68
66
 
69
67
  def test_members_from_subgroups
70
- assert_equal 4, @group.members.size
68
+ assert_equal 10, @group.members.size
71
69
  end
72
70
  end
73
71
 
74
72
  class GitHubLdapMissingEntriesTest < GitHub::Ldap::Test
75
- def self.test_server_options
76
- {user_fixtures: FIXTURES.join('github-with-missing-entries.ldif').to_s}
77
- end
78
-
79
73
  def setup
80
74
  @ldap = GitHub::Ldap.new(options)
81
75
  end
82
76
 
83
77
  def test_load_right_members
84
- assert_equal 3, @ldap.domain("cn=spaniards,ou=groups,dc=github,dc=com").bind[:member].size
78
+ assert_equal 3, @ldap.domain("cn=missing-users,ou=groups,dc=github,dc=com").bind[:member].size
85
79
  end
86
80
 
87
81
  def test_ignore_missing_member_entries
88
- assert_equal 2, @ldap.group("cn=spaniards,ou=groups,dc=github,dc=com").members.size
82
+ assert_equal 2, @ldap.group("cn=missing-users,ou=groups,dc=github,dc=com").members.size
89
83
  end
90
84
  end
data/test/ldap_test.rb CHANGED
@@ -22,48 +22,55 @@ module GitHubLdapTestCases
22
22
  end
23
23
 
24
24
  def test_search_delegator
25
- @ldap.domain('dc=github,dc=com').valid_login? 'calavera', 'secret'
25
+ assert user = @ldap.domain('dc=github,dc=com').valid_login?('user1', 'passworD1')
26
26
 
27
- result = @ldap.search(
28
- {:base => 'dc=github,dc=com',
29
- :attributes => %w(uid),
30
- :filter => Net::LDAP::Filter.eq('uid', 'calavera')})
27
+ result = @ldap.search \
28
+ :base => 'dc=github,dc=com',
29
+ :attributes => %w(uid),
30
+ :filter => Net::LDAP::Filter.eq('uid', 'user1')
31
31
 
32
32
  refute result.empty?
33
- assert_equal 'calavera', result.first[:uid].first
33
+ assert_equal 'user1', result.first[:uid].first
34
34
  end
35
35
 
36
- def test_virtual_attributes_defaults
37
- @ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true))
38
-
39
- assert @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults"
40
- assert_equal 'memberOf', @ldap.virtual_attributes.virtual_membership
36
+ def test_virtual_attributes_disabled
37
+ refute @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes disabled"
41
38
  end
42
39
 
43
- def test_virtual_attributes_defaults
40
+ def test_virtual_attributes_configured
44
41
  ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true))
45
42
 
46
- assert ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults"
43
+ assert ldap.virtual_attributes.enabled?,
44
+ "Expected virtual attributes to be enabled"
47
45
  assert_equal 'memberOf', ldap.virtual_attributes.virtual_membership
48
46
  end
49
47
 
50
- def test_virtual_attributes_hash
48
+ def test_virtual_attributes_configured_with_membership_attribute
51
49
  ldap = GitHub::Ldap.new(options.merge(virtual_attributes: {virtual_membership: "isMemberOf"}))
52
50
 
53
- assert ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults"
51
+ assert ldap.virtual_attributes.enabled?,
52
+ "Expected virtual attributes to be enabled"
54
53
  assert_equal 'isMemberOf', ldap.virtual_attributes.virtual_membership
55
54
  end
56
55
 
57
- def test_virtual_attributes_disabled
58
- refute @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes disabled"
59
- end
60
-
61
56
  def test_search_domains
62
57
  ldap = GitHub::Ldap.new(options.merge(search_domains: ['dc=github,dc=com']))
63
- result = ldap.search(filter: Net::LDAP::Filter.eq('uid', 'calavera'))
58
+ result = ldap.search(filter: Net::LDAP::Filter.eq('uid', 'user1'))
64
59
 
65
60
  refute result.empty?
66
- assert_equal 'calavera', result.first[:uid].first
61
+ assert_equal 'user1', result.first[:uid].first
62
+ end
63
+
64
+ def test_instruments_search
65
+ events = @service.subscribe "search.github_ldap"
66
+ result = @ldap.search(filter: "(uid=user1)", :base => "dc=github,dc=com")
67
+ refute_predicate result, :empty?
68
+ payload, event_result = events.pop
69
+ assert payload
70
+ assert event_result
71
+ assert_equal result, event_result
72
+ assert_equal "(uid=user1)", payload[:filter].to_s
73
+ assert_equal "dc=github,dc=com", payload[:base]
67
74
  end
68
75
  end
69
76
 
@@ -0,0 +1,68 @@
1
+ require_relative '../test_helper'
2
+
3
+ # NOTE: Since this strategy is targeted at ActiveDirectory and we don't have
4
+ # AD setup in CI, we stub out actual queries and test against what AD *would*
5
+ # respond with.
6
+
7
+ class GitHubLdapActiveDirectoryMembershipValidatorsTest < GitHub::Ldap::Test
8
+ def setup
9
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com)))
10
+ @domain = @ldap.domain("dc=github,dc=com")
11
+ @entry = @domain.user?('user1')
12
+ @validator = GitHub::Ldap::MembershipValidators::ActiveDirectory
13
+ end
14
+
15
+ def make_validator(groups)
16
+ groups = @domain.groups(groups)
17
+ @validator.new(@ldap, groups)
18
+ end
19
+
20
+ def test_validates_user_in_group
21
+ validator = make_validator(%w(nested-group1))
22
+
23
+ @ldap.stub :search, [@entry] do
24
+ assert validator.perform(@entry)
25
+ end
26
+ end
27
+
28
+ def test_validates_user_in_child_group
29
+ validator = make_validator(%w(n-depth-nested-group1))
30
+
31
+ @ldap.stub :search, [@entry] do
32
+ assert validator.perform(@entry)
33
+ end
34
+ end
35
+
36
+ def test_validates_user_in_grandchild_group
37
+ validator = make_validator(%w(n-depth-nested-group2))
38
+
39
+ @ldap.stub :search, [@entry] do
40
+ assert validator.perform(@entry)
41
+ end
42
+ end
43
+
44
+ def test_validates_user_in_great_grandchild_group
45
+ validator = make_validator(%w(n-depth-nested-group3))
46
+
47
+ @ldap.stub :search, [@entry] do
48
+ assert validator.perform(@entry)
49
+ end
50
+ end
51
+
52
+ def test_does_not_validate_user_not_in_group
53
+ validator = make_validator(%w(ghe-admins))
54
+
55
+ @ldap.stub :search, [] do
56
+ refute validator.perform(@entry)
57
+ end
58
+ end
59
+
60
+ def test_does_not_validate_user_not_in_any_group
61
+ entry = @domain.user?('groupless-user1')
62
+ validator = make_validator(%w(all-users))
63
+
64
+ @ldap.stub :search, [] do
65
+ refute validator.perform(entry)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,51 @@
1
+ require_relative '../test_helper'
2
+
3
+ class GitHubLdapClassicMembershipValidatorsTest < GitHub::Ldap::Test
4
+ def setup
5
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com)))
6
+ @domain = @ldap.domain("dc=github,dc=com")
7
+ @entry = @domain.user?('user1')
8
+ @validator = GitHub::Ldap::MembershipValidators::Classic
9
+ end
10
+
11
+ def make_validator(groups)
12
+ groups = @domain.groups(groups)
13
+ @validator.new(@ldap, groups)
14
+ end
15
+
16
+ def test_validates_user_in_group
17
+ validator = make_validator(%w(nested-group1))
18
+ assert validator.perform(@entry)
19
+ end
20
+
21
+ def test_validates_user_in_child_group
22
+ validator = make_validator(%w(n-depth-nested-group1))
23
+ assert validator.perform(@entry)
24
+ end
25
+
26
+ def test_validates_user_in_grandchild_group
27
+ validator = make_validator(%w(n-depth-nested-group2))
28
+ assert validator.perform(@entry)
29
+ end
30
+
31
+ def test_validates_user_in_great_grandchild_group
32
+ validator = make_validator(%w(n-depth-nested-group3))
33
+ assert validator.perform(@entry)
34
+ end
35
+
36
+ def test_does_not_validate_user_not_in_group
37
+ validator = make_validator(%w(ghe-admins))
38
+ refute validator.perform(@entry)
39
+ end
40
+
41
+ def test_does_not_validate_user_not_in_any_group
42
+ @entry = @domain.user?('groupless-user1')
43
+ validator = make_validator(%w(all-users))
44
+ refute validator.perform(@entry)
45
+ end
46
+
47
+ def test_validates_user_in_posix_group
48
+ validator = make_validator(%w(posix-group1))
49
+ assert validator.perform(@entry)
50
+ end
51
+ end
@@ -0,0 +1,56 @@
1
+ require_relative '../test_helper'
2
+
3
+ class GitHubLdapRecursiveMembershipValidatorsTest < GitHub::Ldap::Test
4
+ def setup
5
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: %w(dc=github,dc=com)))
6
+ @domain = @ldap.domain("dc=github,dc=com")
7
+ @entry = @domain.user?('user1')
8
+ @validator = GitHub::Ldap::MembershipValidators::Recursive
9
+ end
10
+
11
+ def make_validator(groups)
12
+ groups = @domain.groups(groups)
13
+ @validator.new(@ldap, groups)
14
+ end
15
+
16
+ def test_validates_user_in_group
17
+ validator = make_validator(%w(nested-group1))
18
+ assert validator.perform(@entry)
19
+ end
20
+
21
+ def test_validates_user_in_child_group
22
+ validator = make_validator(%w(n-depth-nested-group1))
23
+ assert validator.perform(@entry)
24
+ end
25
+
26
+ def test_validates_user_in_grandchild_group
27
+ validator = make_validator(%w(n-depth-nested-group2))
28
+ assert validator.perform(@entry)
29
+ end
30
+
31
+ def test_validates_user_in_great_grandchild_group
32
+ validator = make_validator(%w(n-depth-nested-group3))
33
+ assert validator.perform(@entry)
34
+ end
35
+
36
+ def test_does_not_validate_user_in_great_granchild_group_with_depth
37
+ validator = make_validator(%w(n-depth-nested-group3))
38
+ refute validator.perform(@entry, 2)
39
+ end
40
+
41
+ def test_does_not_validate_user_not_in_group
42
+ validator = make_validator(%w(ghe-admins))
43
+ refute validator.perform(@entry)
44
+ end
45
+
46
+ def test_does_not_validate_user_not_in_any_group
47
+ @entry = @domain.user?('groupless-user1')
48
+ validator = make_validator(%w(all-users))
49
+ refute validator.perform(@entry)
50
+ end
51
+
52
+ def test_validates_user_in_posix_group
53
+ validator = make_validator(%w(posix-group1))
54
+ assert validator.perform(@entry)
55
+ end
56
+ end
@@ -0,0 +1,46 @@
1
+ require_relative 'test_helper'
2
+
3
+ module GitHubLdapMembershipValidatorsTestCases
4
+ def make_validator(groups)
5
+ groups = @domain.groups(groups)
6
+ @validator.new(@ldap, groups)
7
+ end
8
+
9
+ def test_validates_user_in_group
10
+ validator = make_validator(%w(ghe-users))
11
+ assert validator.perform(@entry)
12
+ end
13
+
14
+ def test_does_not_validate_user_not_in_group
15
+ validator = make_validator(%w(ghe-admins))
16
+ refute validator.perform(@entry)
17
+ end
18
+
19
+ def test_does_not_validate_user_not_in_any_group
20
+ @entry = @domain.user?('groupless-user1')
21
+ validator = make_validator(%w(ghe-users ghe-admins))
22
+ refute validator.perform(@entry)
23
+ end
24
+ end
25
+
26
+ class GitHubLdapMembershipValidatorsClassicTest < GitHub::Ldap::Test
27
+ include GitHubLdapMembershipValidatorsTestCases
28
+
29
+ def setup
30
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: "dc=github,dc=com"))
31
+ @domain = @ldap.domain("dc=github,dc=com")
32
+ @entry = @domain.user?('user1')
33
+ @validator = GitHub::Ldap::MembershipValidators::Classic
34
+ end
35
+ end
36
+
37
+ class GitHubLdapMembershipValidatorsRecursiveTest < GitHub::Ldap::Test
38
+ include GitHubLdapMembershipValidatorsTestCases
39
+
40
+ def setup
41
+ @ldap = GitHub::Ldap.new(options.merge(search_domains: "dc=github,dc=com"))
42
+ @domain = @ldap.domain("dc=github,dc=com")
43
+ @entry = @domain.user?('user1')
44
+ @validator = GitHub::Ldap::MembershipValidators::Recursive
45
+ end
46
+ end