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 +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
|