github-ldap 1.0.16 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1fe44b5c68117c2b0d616f164f8b40901d2ad2dd
4
- data.tar.gz: a61c80ebbaabb8ff06793f13910440d403209a7b
3
+ metadata.gz: 5094836df33821281e971a9d3f1028df7cba6d71
4
+ data.tar.gz: f681bea4b0ae307888cd037db8b232e4e08af462
5
5
  SHA512:
6
- metadata.gz: 060534d71ad7bfcbd1682699cfa81cebb3552366bd661b94127df79542f97b9c640ebf4414785055ad88923931588bd89ac274a03fc635cb757353c1a717a133
7
- data.tar.gz: b20400377b5b2d508a910ba6acc3ffeec0a18e5aae7918290c2622be7536a5f625c74a06a31fc2f3d73c0a65cf425ce89e3de4c037a993c2f343c0c1a2bd82d4
6
+ metadata.gz: a6464449256546e16d1962bba8b3c6599a5eb3e6f41d965225c540a98c2d1f19313e90da62c3ca84a09ae374fdde525d68089da73c251681b28053c09e23e9b6
7
+ data.tar.gz: 6830909544c79613b8ea4626032303b6ef8a3aa8d25ea95fe6347257bae7ca9c2e3ca881c6711a3cd27836d866ae8e3bdfe18d7035053b78f7628a2760ad776c
data/README.md CHANGED
@@ -60,7 +60,26 @@ When we have the domain, we can check if a user can log in with a given password
60
60
  Or whether a user is member of the given groups:
61
61
 
62
62
  ```ruby
63
- domain.is_member? 'uid=calavera,dc=github,dc=com', %w(Enterprise)
63
+ entry = ldap.domain('uid=calavera,dc=github,dc=com').bind
64
+ domain.is_member? entry, %w(Enterprise)
65
+ ```
66
+
67
+ ### Virtual Attributes
68
+
69
+ Some LDAP servers have support for virtual attributes, or overlays. These allow to perform queries more efficiently on the server.
70
+
71
+ To enable virtual attributes you can set the option `virtual_attributes` initializing the ldap connection.
72
+ We use our default set of virtual names if this option is just set to `true`.
73
+
74
+ ```ruby
75
+ ldap = GitHub::Ldap.new {virtual_attributes: true}
76
+ ```
77
+
78
+ You can also override our defaults by providing your server mappings into a Hash.
79
+ The only mapping supported for now is to check virtual membership of individuals in groups.
80
+
81
+ ```ruby
82
+ ldap = GitHub::Ldap.new {virtual_attributes: {virtual_membership: 'memberOf'}}
64
83
  ```
65
84
 
66
85
  ### Testing support
data/github-ldap.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "github-ldap"
5
- spec.version = "1.0.16"
5
+ spec.version = "1.1.0"
6
6
  spec.authors = ["David Calavera"]
7
7
  spec.email = ["david.calavera@gmail.com"]
8
8
  spec.description = %q{Ldap authentication for humans}
data/lib/github/ldap.rb CHANGED
@@ -5,12 +5,14 @@ module GitHub
5
5
  require 'github/ldap/filter'
6
6
  require 'github/ldap/domain'
7
7
  require 'github/ldap/group'
8
+ require 'github/ldap/virtual_group'
9
+ require 'github/ldap/virtual_attributes'
8
10
 
9
11
  extend Forwardable
10
12
 
11
13
  # Utility method to perform searches against the ldap server.
12
14
  #
13
- # It takes the same arguments than Net:::LDAP#search.
15
+ # It takes the same arguments than Net::LDAP::Connection#search.
14
16
  # Returns an Array with the entries that match the search.
15
17
  # Returns nil if there are no entries that match the search.
16
18
  def_delegator :@connection, :search
@@ -21,6 +23,13 @@ module GitHub
21
23
  # If `code` is 0, the operation succeeded and there is no message.
22
24
  def_delegator :@connection, :get_operation_result, :last_operation_result
23
25
 
26
+ # Utility method to bind entries in the ldap server.
27
+ #
28
+ # It takes the same arguments than Net::LDAP::Connection#bind.
29
+ # Returns a Net::LDAP::Entry if the operation succeeded.
30
+ def_delegator :@connection, :bind
31
+
32
+ attr_reader :virtual_attributes
24
33
 
25
34
  def initialize(options = {})
26
35
  @uid = options[:uid] || "sAMAccountName"
@@ -34,6 +43,8 @@ module GitHub
34
43
  if encryption = check_encryption(options[:encryption])
35
44
  @connection.encryption(encryption)
36
45
  end
46
+
47
+ configure_virtual_attributes(options[:virtual_attributes])
37
48
  end
38
49
 
39
50
  # Determine whether to use encryption or not.
@@ -68,7 +79,7 @@ module GitHub
68
79
  #
69
80
  # Returns a new Domain object.
70
81
  def domain(base_name)
71
- Domain.new(base_name, @connection, @uid)
82
+ Domain.new(self, base_name, @uid)
72
83
  end
73
84
 
74
85
  # Creates a new group object to perform operations
@@ -77,7 +88,28 @@ module GitHub
77
88
  #
78
89
  # Returns a new Group object.
79
90
  def group(base_name)
80
- Group.new(self, domain(base_name).bind)
91
+ if @virtual_attributes.enabled?
92
+ VirtualGroup.new(self, domain(base_name).bind)
93
+ else
94
+ Group.new(self, domain(base_name).bind)
95
+ end
96
+ end
97
+
98
+ # Configure virtual attributes for this server.
99
+ # If the option is `true`, we'll use the default virual attributes.
100
+ # If it's a Hash we'll map the attributes in the hash.
101
+ #
102
+ # attributes: is the option set when Ldap is initialized.
103
+ #
104
+ # Returns a VirtualAttributes.
105
+ def configure_virtual_attributes(attributes)
106
+ @virtual_attributes = if attributes == true
107
+ VirtualAttributes.new(true)
108
+ elsif attributes.is_a?(Hash)
109
+ VirtualAttributes.new(true, attributes)
110
+ else
111
+ VirtualAttributes.new(false)
112
+ end
81
113
  end
82
114
  end
83
115
  end
@@ -14,8 +14,8 @@ module GitHub
14
14
  class Domain
15
15
  include Filter
16
16
 
17
- def initialize(base_name, connection, uid)
18
- @base_name, @connection, @uid = base_name, connection, uid
17
+ def initialize(ldap, base_name, uid)
18
+ @ldap, @base_name, @uid = ldap, base_name, uid
19
19
  end
20
20
 
21
21
  # List all groups under this tree, including subgroups.
@@ -43,26 +43,36 @@ module GitHub
43
43
 
44
44
  # List the groups that a user is member of.
45
45
  #
46
- # user_dn: is the dn for the user ldap entry.
46
+ # user_entry: is the entry for the user in the server.
47
47
  # group_names: is an array of group CNs.
48
48
  #
49
49
  # Return an Array with the groups that the given user is member of that belong to the given group list.
50
- def membership(user_dn, group_names)
51
- search(filter: group_filter(group_names, user_dn))
50
+ def membership(user_entry, group_names)
51
+ all_groups = search(filter: group_filter(group_names))
52
+ groups_map = all_groups.each_with_object({}) {|entry, hash| hash[entry.dn] = entry}
53
+
54
+ if @ldap.virtual_attributes.enabled?
55
+ member_of = groups_map.keys & user_entry[@ldap.virtual_attributes.user_membership]
56
+ member_of.map {|dn| group_map[dn]}
57
+ else
58
+ groups_map.each_with_object([]) do |(dn, group), acc|
59
+ acc << group if Group.new(@ldap, group).is_member?(user_entry.dn)
60
+ end
61
+ end
52
62
  end
53
63
 
54
64
  # Check if the user is include in any of the configured groups.
55
65
  #
56
- # user_dn: is the dn for the user ldap entry.
66
+ # user_entry: is the entry for the user in the server.
57
67
  # group_names: is an array of group CNs.
58
68
  #
59
69
  # Returns true if the user belongs to any of the groups.
60
70
  # Returns false otherwise.
61
- def is_member?(user_dn, group_names)
71
+ def is_member?(user_entry, group_names)
62
72
  return true if group_names.nil?
63
73
  return true if group_names.empty?
64
74
 
65
- user_membership = membership(user_dn, group_names)
75
+ user_membership = membership(user_entry, group_names)
66
76
 
67
77
  !user_membership.empty?
68
78
  end
@@ -97,7 +107,7 @@ module GitHub
97
107
  #
98
108
  # Returns true if the user can be bound.
99
109
  def auth(user, password)
100
- @connection.bind(method: :simple, username: user.dn, password: password)
110
+ @ldap.bind(method: :simple, username: user.dn, password: password)
101
111
  end
102
112
 
103
113
  # Authenticate a user with the ldap server.
@@ -112,7 +122,7 @@ module GitHub
112
122
  def authenticate!(login, password, group_names = nil)
113
123
  user = valid_login?(login, password)
114
124
 
115
- return user if user && is_member?(user.dn, group_names)
125
+ return user if user && is_member?(user, group_names)
116
126
  end
117
127
 
118
128
  # Search entries using this domain as base.
@@ -127,7 +137,7 @@ module GitHub
127
137
  options[:ignore_server_caps] ||= true
128
138
  options[:paged_searches_supported] ||= true
129
139
 
130
- Array(@connection.search(options))
140
+ Array(@ldap.search(options))
131
141
  end
132
142
 
133
143
  # Provide a meaningful result after a protocol operation (for example,
@@ -137,7 +147,7 @@ module GitHub
137
147
  # human-readable string.
138
148
  # See http://tools.ietf.org/html/rfc4511#appendix-A
139
149
  def get_operation_result
140
- @connection.get_operation_result
150
+ @ldap.get_operation_result
141
151
  end
142
152
 
143
153
  # Get the entry for this domain.
@@ -49,6 +49,26 @@ module GitHub
49
49
  def group_contains_filter(query)
50
50
  Net::LDAP::Filter.contains("cn", query) & ALL_GROUPS_FILTER
51
51
  end
52
+
53
+ # Filter to get all the members of a group using the virtual attribute `memberOf`.
54
+ #
55
+ # group_dn: is the group dn to look members for.
56
+ # attr: is the membership attribute.
57
+ #
58
+ # Returns a Net::LDAP::Filter
59
+ def members_of_group(group_dn, attr = 'memberOf')
60
+ Net::LDAP::Filter.eq(attr, group_dn)
61
+ end
62
+
63
+ # Filter to get all the members of a group that are groups using the virtual attribute `memberOf`.
64
+ #
65
+ # group_dn: is the group dn to look members for.
66
+ # attr: is the membership attribute.
67
+ #
68
+ # Returns a Net::LDAP::Filter
69
+ def subgroups_of_group(group_dn, attr = 'memberOf')
70
+ Net::LDAP::Filter.eq(attr, group_dn) & ALL_GROUPS_FILTER
71
+ end
52
72
  end
53
73
  end
54
74
  end
@@ -16,36 +16,50 @@ module GitHub
16
16
  @ldap, @entry = ldap, entry
17
17
  end
18
18
 
19
- # Get all members that belong to a group.
19
+ # Public - Get all members that belong to a group.
20
20
  # This list also includes the members of subgroups.
21
21
  #
22
22
  # Returns an array with all the member entries.
23
23
  def members
24
- groups, members = member_entries.partition {|e| group?(e[:objectclass])}
24
+ groups, members = groups_and_members
25
25
  results = members
26
26
 
27
- groups.each do |result|
28
- results.concat @ldap.group(result.dn).members
27
+ cache = load_cache(groups)
28
+
29
+ loop_cached_groups(groups, cache) do |_, users|
30
+ results.concat users
29
31
  end
30
32
 
31
33
  results.uniq {|m| m.dn }
32
34
  end
33
35
 
34
- # Get all the subgroups from a group recursively.
36
+ # Public - Get all the subgroups from a group recursively.
35
37
  #
36
38
  # Returns an array with all the subgroup entries.
37
39
  def subgroups
38
- groups, _ = member_entries.partition {|e| group?(e[:objectclass])}
40
+ groups, _ = groups_and_members
39
41
  results = groups
40
42
 
41
- groups.each do |result|
42
- results.concat @ldap.group(result.dn).subgroups
43
+ cache = load_cache(groups)
44
+
45
+ loop_cached_groups(groups, cache) do |subgroups, _|
46
+ results.concat subgroups
43
47
  end
44
48
 
45
49
  results
46
50
  end
47
51
 
48
- # Get all the member entries for a group.
52
+ # Public - Check if a user dn is included in the members of this group and its subgroups.
53
+ #
54
+ # user_dn: is the dn to check.
55
+ #
56
+ # Returns true if the dn is in the list of members.
57
+ def is_member?(user_dn)
58
+ members.detect {|entry| entry.dn == user_dn}
59
+ end
60
+
61
+
62
+ # Internal - Get all the member entries for a group.
49
63
  #
50
64
  # Returns an array of Net::LDAP::Entry.
51
65
  def member_entries
@@ -54,20 +68,56 @@ module GitHub
54
68
  end
55
69
  end
56
70
 
57
- # Get all the names under `member` and `uniqueMember`.
71
+ # Internal - Get all the names under `member` and `uniqueMember`.
58
72
  #
59
73
  # Returns an array with all the DN members.
60
74
  def member_names
61
75
  @entry[:member] + @entry[:uniqueMember]
62
76
  end
63
77
 
64
- # Check if an object class includes the member names
78
+ # Internal - Check if an object class includes the member names
65
79
  # Use `&` rathen than `include?` because both are arrays.
66
80
  #
67
81
  # Returns true if the object class includes one of the group class names.
68
82
  def group?(object_class)
69
83
  !(GROUP_CLASS_NAMES & object_class).empty?
70
84
  end
85
+
86
+ # Internal - Generate a hash with all the group DNs for caching purposes.
87
+ #
88
+ # groups: is an array of group entries.
89
+ #
90
+ # Returns a hash with the cache groups.
91
+ def load_cache(groups)
92
+ groups.each_with_object({}) {|entry, h| h[entry.dn] = true }
93
+ end
94
+
95
+ # Internal - Iterate over a collection of groups recursively.
96
+ # Remove groups already inspected before iterating over subgroups.
97
+ #
98
+ # groups: is an array of group entries.
99
+ # cache: is a hash where the keys are group dns.
100
+ # block: is a block to call with the groups and members of subgroups.
101
+ #
102
+ # Returns nothing.
103
+ def loop_cached_groups(groups, cache, &block)
104
+ groups.each do |result|
105
+ subgroups, members = @ldap.group(result.dn).groups_and_members
106
+
107
+ subgroups.delete_if {|entry| cache[entry.dn]}
108
+ subgroups.each {|entry| cache[entry.dn] = true}
109
+
110
+ block.call(subgroups, members)
111
+ loop_cached_groups(subgroups, cache, &block)
112
+ end
113
+ end
114
+
115
+ # Internal - Divide members of a group in user and subgroups.
116
+ #
117
+ # Returns two arrays, the first one with subgroups and the second one with users.
118
+ def groups_and_members
119
+ member_entries.partition {|e| group?(e[:objectclass])}
120
+ end
71
121
  end
72
122
  end
73
123
  end
@@ -0,0 +1,18 @@
1
+ module GitHub
2
+ class Ldap
3
+ class VirtualAttributes
4
+ def initialize(enabled, attributes = {})
5
+ @enabled = enabled
6
+ @attributes = attributes
7
+ end
8
+
9
+ def enabled?
10
+ @enabled
11
+ end
12
+
13
+ def virtual_membership
14
+ @attributes.fetch(:virtual_membership, "memberOf")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ module GitHub
2
+ class Ldap
3
+ class VirtualGroup < Group
4
+ def members
5
+ @ldap.search(filter: members_of_group(@entry.dn, membership_attribute))
6
+ end
7
+
8
+ def subgroups
9
+ @ldap.search(filter: subgroups_of_group(@entry.dn, membership_attribute))
10
+ end
11
+
12
+ def is_member(user_dn)
13
+ @ldap.search(filter: is_member_of_group(user_dn, @entry.dn, membership_attribute))
14
+ end
15
+
16
+ # Internal - Get the attribute to use for membership filtering.
17
+ #
18
+ # Returns a string.
19
+ def membership_attribute
20
+ @ldap.virual_attributes.virtual_membership
21
+ end
22
+ end
23
+ end
24
+ end
data/test/domain_test.rb CHANGED
@@ -2,7 +2,8 @@ require 'test_helper'
2
2
 
3
3
  module GitHubLdapDomainTestCases
4
4
  def setup
5
- @domain = GitHub::Ldap.new(options).domain("dc=github,dc=com")
5
+ @ldap = GitHub::Ldap.new(options)
6
+ @domain = @ldap.domain("dc=github,dc=com")
6
7
  end
7
8
 
8
9
  def test_user_valid_login
@@ -27,22 +28,22 @@ module GitHubLdapDomainTestCases
27
28
  def test_user_in_group
28
29
  user = @domain.valid_login?('calavera', 'passworD1')
29
30
 
30
- assert @domain.is_member?(user.dn, %w(Enterprise People)),
31
+ assert @domain.is_member?(user, %w(Enterprise People)),
31
32
  "Expected `Enterprise` or `Poeple` to include the member `#{user.dn}`"
32
33
  end
33
34
 
34
35
  def test_user_not_in_different_group
35
36
  user = @domain.valid_login?('calavera', 'passworD1')
36
37
 
37
- assert !@domain.is_member?(user.dn, %w(People)),
38
+ assert !@domain.is_member?(user, %w(People)),
38
39
  "Expected `Poeple` not to include the member `#{user.dn}`"
39
40
  end
40
41
 
41
42
  def test_user_without_group
42
43
  user = @domain.valid_login?('ldaptest', 'secret')
43
44
 
44
- assert !@domain.is_member?(user.dn, %w(People)),
45
- "Expected `Poeple` not to include the member `#{user.dn}`"
45
+ assert !@domain.is_member?(user, %w(People)),
46
+ "Expected `People` not to include the member `#{user.dn}`"
46
47
  end
47
48
 
48
49
  def test_authenticate_doesnt_return_invalid_users
@@ -67,12 +68,26 @@ module GitHubLdapDomainTestCases
67
68
  end
68
69
 
69
70
  def test_membership_empty_for_non_members
70
- assert @domain.membership('uid=calavera,dc=github,dc=com', %w(People)).empty?,
71
+ user = @ldap.domain('uid=calavera,dc=github,dc=com').bind
72
+
73
+ assert @domain.membership(user, %w(People)).empty?,
71
74
  "Expected `calavera` not to be a member of `People`."
72
75
  end
73
76
 
74
77
  def test_membership_groups_for_members
75
- groups = @domain.membership('uid=calavera,dc=github,dc=com', %w(Enterprise People))
78
+ user = @ldap.domain('uid=calavera,dc=github,dc=com').bind
79
+ groups = @domain.membership(user, %w(Enterprise People))
80
+
81
+ assert_equal 1, groups.size
82
+ assert_equal 'cn=Enterprise,ou=Group,dc=github,dc=com', groups.first.dn
83
+ end
84
+
85
+ def test_membership_with_virtual_attributes
86
+ ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true))
87
+ user = @ldap.domain('uid=calavera,dc=github,dc=com').bind
88
+ user[:memberof] = 'cn=Enterprise,ou=Group,dc=github,dc=com'
89
+
90
+ groups = @domain.membership(user, %w(Enterprise People))
76
91
 
77
92
  assert_equal 1, groups.size
78
93
  assert_equal 'cn=Enterprise,ou=Group,dc=github,dc=com', groups.first.dn
@@ -125,3 +140,21 @@ end
125
140
  class GitHubLdapDomainUnauthenticatedTest < GitHub::Ldap::UnauthenticatedTest
126
141
  include GitHubLdapDomainTestCases
127
142
  end
143
+
144
+ class GitHubLdapDomainNestedGroupsTest < GitHub::Ldap::Test
145
+ def self.test_server_options
146
+ {user_fixtures: FIXTURES.join('github-with-subgroups.ldif').to_s}
147
+ end
148
+
149
+ def setup
150
+ @ldap = GitHub::Ldap.new(options)
151
+ @domain = @ldap.domain("dc=github,dc=com")
152
+ end
153
+
154
+ def test_membership_in_subgroups
155
+ user = @ldap.domain('uid=rubiojr,ou=users,dc=github,dc=com').bind
156
+
157
+ assert @domain.is_member?(user, %w(enterprise-ops)),
158
+ "Expected `enterprise-ops` to include the member `#{user.dn}`"
159
+ end
160
+ end
data/test/filter_test.rb CHANGED
@@ -25,4 +25,20 @@ class FilterTest < Minitest::Test
25
25
  assert_equal "(&(|(member=#{@me})(uniqueMember=#{@me}))(|(cn=Enterprise)(cn=People)))",
26
26
  @subject.group_filter(%w(Enterprise People), @me).to_s
27
27
  end
28
+
29
+ def test_members_of_group
30
+ assert_equal "(memberOf=cn=group,dc=github,dc=com)",
31
+ @subject.members_of_group('cn=group,dc=github,dc=com').to_s
32
+
33
+ assert_equal "(isMemberOf=cn=group,dc=github,dc=com)",
34
+ @subject.members_of_group('cn=group,dc=github,dc=com', 'isMemberOf').to_s
35
+ end
36
+
37
+ def test_subgroups_of_group
38
+ assert_equal "(&(memberOf=cn=group,dc=github,dc=com)#{Subject::ALL_GROUPS_FILTER})",
39
+ @subject.subgroups_of_group('cn=group,dc=github,dc=com').to_s
40
+
41
+ assert_equal "(&(isMemberOf=cn=group,dc=github,dc=com)#{Subject::ALL_GROUPS_FILTER})",
42
+ @subject.subgroups_of_group('cn=group,dc=github,dc=com', 'isMemberOf').to_s
43
+ end
28
44
  end
@@ -0,0 +1,91 @@
1
+ version: 1
2
+
3
+ # Organizations
4
+
5
+ dn: dc=github,dc=com
6
+ cn: github
7
+ objectClass: dcObject
8
+ objectClass: organization
9
+ dc: github
10
+ o: GitHub Inc.
11
+
12
+ # Admin user
13
+
14
+ dn: uid=admin,dc=github,dc=com
15
+ objectClass: top
16
+ objectClass: person
17
+ objectClass: organizationalPerson
18
+ objectClass: inetOrgPerson
19
+ cn: system administrator
20
+ sn: administrator
21
+ displayName: Directory Superuser
22
+ uid: admin
23
+ userPassword: secret
24
+
25
+ # Groups
26
+
27
+ dn: ou=groups,dc=github,dc=com
28
+ objectclass: organizationalUnit
29
+
30
+ dn: cn=enterprise,ou=groups,dc=github,dc=com
31
+ cn: Enterprise
32
+ objectClass: groupOfNames
33
+ member: uid=calavera,ou=users,dc=github,dc=com
34
+ member: cn=enterprise-devs,ou=groups,dc=github,dc=com
35
+ member: cn=enterprise-ops,ou=groups,dc=github,dc=com
36
+
37
+ dn: cn=enterprise-devs,ou=groups,dc=github,dc=com
38
+ cn: enterprise-devs
39
+ objectClass: groupOfNames
40
+ member: uid=benburkert,ou=users,dc=github,dc=com
41
+ member: cn=enterprise,ou=groups,dc=github,dc=com
42
+
43
+ dn: cn=enterprise-ops,ou=groups,dc=github,dc=com
44
+ cn: enterprise-ops
45
+ objectClass: groupOfNames
46
+ member: uid=sbryant,ou=users,dc=github,dc=com
47
+ member: cn=spaniards,ou=groups,dc=github,dc=com
48
+
49
+ dn: cn=spaniards,ou=groups,dc=github,dc=com
50
+ cn: spaniards
51
+ objectClass: groupOfNames
52
+ member: uid=calavera,ou=users,dc=github,dc=com
53
+ member: uid=rubiojr,ou=users,dc=github,dc=com
54
+
55
+ # Users
56
+
57
+ dn: ou=users,dc=github,dc=com
58
+ objectclass: organizationalUnit
59
+
60
+ dn: uid=calavera,ou=users,dc=github,dc=com
61
+ cn: David Calavera
62
+ cn: David
63
+ sn: Calavera
64
+ uid: calavera
65
+ userPassword: passworD1
66
+ mail: calavera@github.com
67
+ objectClass: inetOrgPerson
68
+
69
+ dn: uid=benburkert,ou=users,dc=github,dc=com
70
+ cn: benburkert
71
+ sn: benburkert
72
+ uid: benburkert
73
+ userPassword: passworD1
74
+ mail: benburkert@github.com
75
+ objectClass: inetOrgPerson
76
+
77
+ dn: uid=sbryant,ou=users,dc=github,dc=com
78
+ cn: sbryant
79
+ sn: sbryant
80
+ uid: sbryant
81
+ userPassword: passworD1
82
+ mail: sbryant@github.com
83
+ objectClass: inetOrgPerson
84
+
85
+ dn: uid=rubiojr,ou=users,dc=github,dc=com
86
+ cn: rubiojr
87
+ sn: rubiojr
88
+ uid: rubiojr
89
+ userPassword: passworD1
90
+ mail: rubiojr@github.com
91
+ objectClass: inetOrgPerson
data/test/group_test.rb CHANGED
@@ -31,3 +31,17 @@ class GitHubLdapGroupTest < GitHub::Ldap::Test
31
31
  assert_equal 1, groups.size
32
32
  end
33
33
  end
34
+
35
+ class GitHubLdapLoopedGroupTest < GitHub::Ldap::Test
36
+ def self.test_server_options
37
+ {user_fixtures: FIXTURES.join('github-with-looped-subgroups.ldif').to_s}
38
+ end
39
+
40
+ def setup
41
+ @group = GitHub::Ldap.new(options).group("cn=enterprise,ou=groups,dc=github,dc=com")
42
+ end
43
+
44
+ def test_members_from_subgroups
45
+ assert_equal 4, @group.members.size
46
+ end
47
+ end
data/test/ldap_test.rb CHANGED
@@ -31,6 +31,31 @@ module GitHubLdapTestCases
31
31
 
32
32
  assert_equal 'calavera', result.first[:uid].first
33
33
  end
34
+
35
+ def test_virtual_attributes_defaults
36
+ @ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true))
37
+
38
+ assert @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults"
39
+ assert_equal 'memberOf', @ldap.virtual_attributes.virtual_membership
40
+ end
41
+
42
+ def test_virtual_attributes_defaults
43
+ ldap = GitHub::Ldap.new(options.merge(virtual_attributes: true))
44
+
45
+ assert ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults"
46
+ assert_equal 'memberOf', ldap.virtual_attributes.virtual_membership
47
+ end
48
+
49
+ def test_virtual_attributes_hash
50
+ ldap = GitHub::Ldap.new(options.merge(virtual_attributes: {virtual_membership: "isMemberOf"}))
51
+
52
+ assert ldap.virtual_attributes.enabled?, "Expected to have virtual attributes enabled with defaults"
53
+ assert_equal 'isMemberOf', ldap.virtual_attributes.virtual_membership
54
+ end
55
+
56
+ def test_virtual_attributes_disabled
57
+ refute @ldap.virtual_attributes.enabled?, "Expected to have virtual attributes disabled"
58
+ end
34
59
  end
35
60
 
36
61
  class GitHubLdapTest < GitHub::Ldap::Test
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: github-ldap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.16
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Calavera
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-20 00:00:00.000000000 Z
11
+ date: 2014-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-ldap
@@ -100,8 +100,11 @@ files:
100
100
  - lib/github/ldap/fixtures.ldif
101
101
  - lib/github/ldap/group.rb
102
102
  - lib/github/ldap/server.rb
103
+ - lib/github/ldap/virtual_attributes.rb
104
+ - lib/github/ldap/virtual_group.rb
103
105
  - test/domain_test.rb
104
106
  - test/filter_test.rb
107
+ - test/fixtures/github-with-looped-subgroups.ldif
105
108
  - test/fixtures/github-with-subgroups.ldif
106
109
  - test/group_test.rb
107
110
  - test/ldap_test.rb
@@ -133,6 +136,7 @@ summary: Ldap client authentication wrapper without all the boilerplate
133
136
  test_files:
134
137
  - test/domain_test.rb
135
138
  - test/filter_test.rb
139
+ - test/fixtures/github-with-looped-subgroups.ldif
136
140
  - test/fixtures/github-with-subgroups.ldif
137
141
  - test/group_test.rb
138
142
  - test/ldap_test.rb