Skip to content

Commit fd2bb1b

Browse files
committed
Add ActiveDirectory membership validator
1 parent 0bdbdf2 commit fd2bb1b

File tree

2 files changed

+57
-0
lines changed

2 files changed

+57
-0
lines changed

lib/github/ldap/membership_validators.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'github/ldap/membership_validators/base'
22
require 'github/ldap/membership_validators/classic'
33
require 'github/ldap/membership_validators/recursive'
4+
require 'github/ldap/membership_validators/active_directory'
45

56
module GitHub
67
class Ldap
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
module GitHub
2+
class Ldap
3+
module MembershipValidators
4+
ATTRS = %w(dn)
5+
OID = "1.2.840.113556.1.4.1941"
6+
7+
# Validates membership using the ActiveDirectory "in chain" matching rule.
8+
#
9+
# The 1.2.840.113556.1.4.1941 matching rule (LDAP_MATCHING_RULE_IN_CHAIN)
10+
# "walks the chain of ancestry in objects all the way to the root until
11+
# it finds a match".
12+
# Source: http://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx
13+
#
14+
# This means we have an efficient method of searching membership even in
15+
# nested groups, performed on the server side.
16+
class ActiveDirectory < Base
17+
def perform(entry, depth = DEFAULT_MAX_DEPTH)
18+
domains.each do |domain|
19+
# search for the entry on the condition that the entry is a member
20+
# of one of the groups or their subgroups.
21+
#
22+
# Sets the entry to the base and scopes the search to the base,
23+
# according to the source documentation, found here:
24+
# http://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx
25+
matched = domain.search \
26+
filter: membership_in_chain_filter(entry),
27+
base: entry.dn,
28+
scope: Net::LDAP::SearchScope_BaseObject,
29+
attributes: ATTRS
30+
31+
return true unless matched.empty?
32+
end
33+
34+
false
35+
end
36+
37+
# Internal: Constructs a membership filter using the "in chain"
38+
# extended matching rule afforded by ActiveDirectory.
39+
#
40+
# Returns a Net::LDAP::Filter object.
41+
def membership_in_chain_filter(entry)
42+
group_dns.map do |dn|
43+
Net::LDAP::Filter.ex("memberOf:#{OID}", dn)
44+
end.reduce(:|)
45+
end
46+
47+
# Internal: the group DNs to check against.
48+
#
49+
# Returns an Array of String DNs.
50+
def group_dns
51+
@group_dns ||= groups.map(&:dn)
52+
end
53+
end
54+
end
55+
end
56+
end

0 commit comments

Comments
 (0)