Skip to content

Commit 744b141

Browse files
committed
Merge pull request #38 from github/configurable-member_filter-memberUid-support
Configurable posixGroup membership filter support
2 parents 80cf8dc + aebe03e commit 744b141

File tree

6 files changed

+88
-42
lines changed

6 files changed

+88
-42
lines changed

github-ldap.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Gem::Specification.new do |spec|
44
spec.name = "github-ldap"
5-
spec.version = "1.3.1"
5+
spec.version = "1.3.2"
66
spec.authors = ["David Calavera"]
77
spec.email = ["david.calavera@gmail.com"]
88
spec.description = %q{Ldap authentication for humans}

lib/github/ldap.rb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class Ldap
2323
# Returns a Net::LDAP::Entry if the operation succeeded.
2424
def_delegator :@connection, :bind
2525

26-
attr_reader :uid, :virtual_attributes, :search_domains
26+
attr_reader :uid, :search_domains, :virtual_attributes
2727

2828
def initialize(options = {})
2929
@uid = options[:uid] || "sAMAccountName"
@@ -43,6 +43,9 @@ def initialize(options = {})
4343
# enable fallback recursive group search unless option is false
4444
@recursive_group_search_fallback = (options[:recursive_group_search_fallback] != false)
4545

46+
# enable posixGroup support unless option is false
47+
@posix_support = (options[:posix_support] != false)
48+
4649
# search_domains is a connection of bases to perform searches
4750
# when a base is not explicitly provided.
4851
@search_domains = Array(options[:search_domains])
@@ -58,6 +61,17 @@ def recursive_group_search_fallback?
5861
@recursive_group_search_fallback
5962
end
6063

64+
# Public - Whether membership checks should include posixGroup filter
65+
# conditions on `memberUid`. Configurable since some LDAP servers don't
66+
# handle unsupported attribute queries gracefully.
67+
#
68+
# Enable by passing :posix_support => true.
69+
#
70+
# Returns true, false, or nil (assumed false).
71+
def posix_support_enabled?
72+
@posix_support
73+
end
74+
6175
# Public - Utility method to check if the connection with the server can be stablished.
6276
# It tries to bind with the ldap auth default configuration.
6377
#

lib/github/ldap/domain.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,14 @@ def membership(user_entry, group_names)
6666
end
6767
else
6868
# fallback to non-recursive group membership search
69-
filter = member_filter(user_entry) & group_filter(group_names)
69+
filter = member_filter(user_entry)
70+
71+
# include memberUid filter if enabled and entry has a UID set
72+
if @ldap.posix_support_enabled? && !user_entry[@ldap.uid].empty?
73+
filter |= posix_member_filter(user_entry, @ldap.uid)
74+
end
75+
76+
filter &= group_filter(group_names)
7077
search(filter: filter)
7178
end
7279
end

lib/github/ldap/filter.rb

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,31 @@ def group_filter(group_names)
2020

2121
# Filter to check group membership.
2222
#
23-
# entry: finds groups this Net::LDAP::Entry is a member of (optional)
24-
# uid_attr: specifies the memberUid attribute to match with (optional)
23+
# entry: finds groups this Net::LDAP::Entry is a member of (optional)
2524
#
2625
# Returns a Net::LDAP::Filter.
27-
def member_filter(entry = nil, uid_attr = @ldap.uid)
26+
def member_filter(entry = nil)
2827
if entry
29-
filter =
30-
MEMBERSHIP_NAMES. map {|n| Net::LDAP::Filter.eq(n, entry.dn) }.
31-
reduce(:|)
32-
33-
if !entry[uid_attr].empty?
34-
filter |=
35-
entry[uid_attr].map { |uid| Net::LDAP::Filter.eq("memberUid", uid) }.
36-
reduce(:|)
37-
end
38-
39-
filter
28+
MEMBERSHIP_NAMES.
29+
map {|n| Net::LDAP::Filter.eq(n, entry.dn) }.reduce(:|)
4030
else
41-
(MEMBERSHIP_NAMES + %w(memberUid)).
42-
map {|n| Net::LDAP::Filter.pres(n)}.reduce(:|)
31+
MEMBERSHIP_NAMES.
32+
map {|n| Net::LDAP::Filter.pres(n) }. reduce(:|)
33+
end
34+
end
35+
36+
# Filter to check group membership for posixGroups.
37+
#
38+
# Used by Domain#membership when posix_support_enabled? is true.
39+
#
40+
# entry: finds groups this Net::LDAP::Entry is a member of
41+
# uid_attr: specifies the memberUid attribute to match with
42+
#
43+
# Returns a Net::LDAP::Filter or nil if no entry has no UID set.
44+
def posix_member_filter(entry, uid_attr)
45+
if !entry[uid_attr].empty?
46+
entry[uid_attr].map { |uid| Net::LDAP::Filter.eq("memberUid", uid) }.
47+
reduce(:|)
4348
end
4449
end
4550

test/domain_test.rb

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -173,24 +173,18 @@ def self.test_server_options
173173
def setup
174174
@ldap = GitHub::Ldap.new(options)
175175
@domain = @ldap.domain("dc=github,dc=com")
176-
177-
@group = Net::LDAP::Entry._load("""
178-
dn: cn=enterprise-posix-devs,ou=groups,dc=github,dc=com
179-
cn: enterprise-posix-devs
180-
objectClass: posixGroup
181-
memberUid: benburkert
182-
memberUid: mtodd""")
176+
@cn = "enterprise-posix-devs"
183177
end
184178

185179
def test_membership_for_posixGroups
186180
assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind
187181

188-
assert @domain.is_member?(user, @group.cn),
189-
"Expected `#{@group.cn.first}` to include the member `#{user.dn}`"
182+
assert @domain.is_member?(user, [@cn]),
183+
"Expected `#{@cn}` to include the member `#{user.dn}`"
190184
end
191185
end
192186

193-
class GitHubLdapPosixGroupsTest < GitHub::Ldap::Test
187+
class GitHubLdapPosixGroupsWithoutRecursionTest < GitHub::Ldap::Test
194188
def self.test_server_options
195189
{
196190
custom_schemas: FIXTURES.join('posixGroup.schema.ldif'),
@@ -203,19 +197,41 @@ def self.test_server_options
203197
def setup
204198
@ldap = GitHub::Ldap.new(options)
205199
@domain = @ldap.domain("dc=github,dc=com")
200+
@cn = "enterprise-posix-devs"
201+
end
202+
203+
def test_membership_for_posixGroups
204+
assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind
206205

207-
@group = Net::LDAP::Entry._load("""
208-
dn: cn=enterprise-posix-devs,ou=groups,dc=github,dc=com
209-
cn: enterprise-posix-devs
210-
objectClass: posixGroup
211-
memberUid: benburkert
212-
memberUid: mtodd""")
206+
assert @domain.is_member?(user, [@cn]),
207+
"Expected `#{@cn}` to include the member `#{user.dn}`"
208+
end
209+
end
210+
211+
# Specifically testing that this doesn't break when posixGroups are not
212+
# supported.
213+
class GitHubLdapWithoutPosixGroupsTest < GitHub::Ldap::Test
214+
def self.test_server_options
215+
{
216+
custom_schemas: FIXTURES.join('posixGroup.schema.ldif'),
217+
user_fixtures: FIXTURES.join('github-with-posixGroups.ldif').to_s,
218+
# so we test the test the non-recursive group membership search
219+
recursive_group_search_fallback: false,
220+
# explicitly disable posixGroup support (even if the schema supports it)
221+
posix_support: false
222+
}
223+
end
224+
225+
def setup
226+
@ldap = GitHub::Ldap.new(options)
227+
@domain = @ldap.domain("dc=github,dc=com")
228+
@cn = "enterprise-posix-devs"
213229
end
214230

215231
def test_membership_for_posixGroups
216232
assert user = @ldap.domain('uid=mtodd,ou=users,dc=github,dc=com').bind
217233

218-
assert @domain.is_member?(user, @group.cn),
219-
"Expected `#{@group.cn.first}` to include the member `#{user.dn}`"
234+
refute @domain.is_member?(user, [@cn]),
235+
"Expected `#{@cn}` to not include the member `#{user.dn}`"
220236
end
221237
end

test/filter_test.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,22 @@ def setup
2424
end
2525

2626
def test_member_present
27-
assert_equal "(|(|(member=*)(uniqueMember=*))(memberUid=*))", @subject.member_filter.to_s
27+
assert_equal "(|(member=*)(uniqueMember=*))", @subject.member_filter.to_s
2828
end
2929

3030
def test_member_equal
31-
assert_equal "(|(|(member=#{@me})(uniqueMember=#{@me}))(memberUid=#{@uid}))",
31+
assert_equal "(|(member=#{@me})(uniqueMember=#{@me}))",
3232
@subject.member_filter(@entry).to_s
3333
end
3434

35-
def test_member_without_uid
35+
def test_posix_member_without_uid
3636
@entry.uid = nil
37-
assert_equal "(|(member=#{@me})(uniqueMember=#{@me}))",
38-
@subject.member_filter(@entry).to_s
37+
assert_nil @subject.posix_member_filter(@entry, @ldap.uid)
38+
end
39+
40+
def test_posix_member_equal
41+
assert_equal "(memberUid=#{@uid})",
42+
@subject.posix_member_filter(@entry, @ldap.uid).to_s
3943
end
4044

4145
def test_groups_reduced

0 commit comments

Comments
 (0)