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 +4 -4
- data/README.md +20 -1
- data/github-ldap.gemspec +1 -1
- data/lib/github/ldap.rb +35 -3
- data/lib/github/ldap/domain.rb +22 -12
- data/lib/github/ldap/filter.rb +20 -0
- data/lib/github/ldap/group.rb +61 -11
- data/lib/github/ldap/virtual_attributes.rb +18 -0
- data/lib/github/ldap/virtual_group.rb +24 -0
- data/test/domain_test.rb +40 -7
- data/test/filter_test.rb +16 -0
- data/test/fixtures/github-with-looped-subgroups.ldif +91 -0
- data/test/group_test.rb +14 -0
- data/test/ldap_test.rb +25 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5094836df33821281e971a9d3f1028df7cba6d71
|
4
|
+
data.tar.gz: f681bea4b0ae307888cd037db8b232e4e08af462
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
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
|
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(
|
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
|
-
|
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
|
data/lib/github/ldap/domain.rb
CHANGED
@@ -14,8 +14,8 @@ module GitHub
|
|
14
14
|
class Domain
|
15
15
|
include Filter
|
16
16
|
|
17
|
-
def initialize(
|
18
|
-
@
|
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
|
-
#
|
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(
|
51
|
-
search(filter: group_filter(group_names
|
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
|
-
#
|
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?(
|
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(
|
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
|
-
@
|
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
|
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(@
|
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
|
-
@
|
150
|
+
@ldap.get_operation_result
|
141
151
|
end
|
142
152
|
|
143
153
|
# Get the entry for this domain.
|
data/lib/github/ldap/filter.rb
CHANGED
@@ -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
|
data/lib/github/ldap/group.rb
CHANGED
@@ -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 =
|
24
|
+
groups, members = groups_and_members
|
25
25
|
results = members
|
26
26
|
|
27
|
-
|
28
|
-
|
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, _ =
|
40
|
+
groups, _ = groups_and_members
|
39
41
|
results = groups
|
40
42
|
|
41
|
-
|
42
|
-
|
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
|
-
#
|
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
|
-
@
|
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
|
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
|
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
|
45
|
-
"Expected `
|
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
|
-
|
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
|
-
|
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
|
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-
|
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
|