github-ldap 1.0.16 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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