From 2f49d6b494ae8e9b5f951d68f611ca25ff87e8f2 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 17:00:32 -0800 Subject: [PATCH 01/11] :fire: unnused variables --- lib/github/ldap/member_search/recursive.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 5fe2489..b1af919 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -49,11 +49,6 @@ def perform(group) depth.times do |n| # find every (new, unique) member entry depth_subentries = entries.each_with_object([]) do |entry, depth_entries| - submembers = entry["member"] - - # skip any members we've already found - submembers.reject! { |dn| found.key?(dn) } - # find members of subgroup, including subgroups (N queries) subentries = member_entries(entry) next if subentries.empty? From 212816c8407d5a72ea332450a56919c5d91a3546 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 17:00:54 -0800 Subject: [PATCH 02/11] Skip searching for entries already found --- lib/github/ldap/member_search/recursive.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index b1af919..f75ebfc 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -50,7 +50,7 @@ def perform(group) # find every (new, unique) member entry depth_subentries = entries.each_with_object([]) do |entry, depth_entries| # find members of subgroup, including subgroups (N queries) - subentries = member_entries(entry) + subentries = member_entries(entry, found) next if subentries.empty? # track found subentries @@ -75,11 +75,15 @@ def perform(group) # entry. # # Returns an Array of Net::LDAP::Entry objects. - def member_entries(entry) + def member_entries(entry, found = {}) entries = [] dns = member_dns(entry) uids = member_uids(entry) + # skip any entries we've already found + dns.reject! { |dn| found.key?(dn) } + uids.reject! { |uid| found.any? { |entry| entry['uid'].include?(uid) } } + entries.concat entries_by_uid(uids) unless uids.empty? entries.concat entries_by_dn(dns) unless dns.empty? From 2a62c2112def6afd865e7a161955a082a5de492b Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 21:54:11 -0800 Subject: [PATCH 03/11] Find subgroups, then materialize members --- lib/github/ldap/member_search/recursive.rb | 104 ++++++++++----------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index f75ebfc..8eaa2e6 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -34,72 +34,70 @@ def initialize(ldap, options = {}) # # Returns Array of Net::LDAP::Entry objects. def perform(group) - found = Hash.new - - # find members (N queries) - entries = member_entries(group) - return [] if entries.empty? - - # track found entries - entries.each do |entry| - found[entry.dn] = entry + found = Hash.new + searched = [] + entries = [] + + # if this is a posixGroup, return members immediately (no nesting) + uids = member_uids(group) + return entries_by_uid(uids) if uids.any? + + # track group + searched << group.dn + found[group.dn] = group + + # pull out base group's member DNs + dns = member_dns(group) + + # search for base group's subgroups + filter = ALL_GROUPS_FILTER + groups = dns.each_with_object([]) do |dn, groups| + groups.concat ldap.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: attrs, filter: filter) + searched << dn end - # descend to `depth` levels, at most - depth.times do |n| - # find every (new, unique) member entry - depth_subentries = entries.each_with_object([]) do |entry, depth_entries| - # find members of subgroup, including subgroups (N queries) - subentries = member_entries(entry, found) - next if subentries.empty? + # track found groups + groups.each { |g| found[g.dn] = g } - # track found subentries - subentries.each { |entry| found[entry.dn] = entry } + # recursively find subgroups + unless groups.empty? + depth.times do |n| + # pull out subgroups' member DNs to search through + sub_dns = groups.each_with_object([]) do |subgroup, sub_dns| + sub_dns.concat member_dns(subgroup) + end - # collect all entries for this depth - depth_entries.concat subentries - end + # give up if there's nothing else to search for + break if sub_dns.empty? - # stop if there are no more subgroups to search - break if depth_subentries.empty? + # filter out if already searched for + sub_dns.reject! { |dn| searched.include?(dn) } - # go one level deeper - entries = depth_subentries - end + # search for subgroups + subgroups = sub_dns.each_with_object([]) do |dn, subgroups| + subgroups.concat ldap.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: attrs, filter: filter) + searched << dn + end - # return all found entries - found.values - end + break if subgroups.empty? - # Internal: Fetch member entries, including subgroups, for the given - # entry. - # - # Returns an Array of Net::LDAP::Entry objects. - def member_entries(entry, found = {}) - entries = [] - dns = member_dns(entry) - uids = member_uids(entry) + # track found groups + subgroups.each { |g| found[g.dn] = g } - # skip any entries we've already found - dns.reject! { |dn| found.key?(dn) } - uids.reject! { |uid| found.any? { |entry| entry['uid'].include?(uid) } } + # descend another level + groups = subgroups + end + end - entries.concat entries_by_uid(uids) unless uids.empty? - entries.concat entries_by_dn(dns) unless dns.empty? + # take found groups and combine groups and members into list of entries + found.values.each do |group| + entries << group + # just need member DNs as Net::LDAP::Entry objects + entries.concat member_dns(group).map { |dn| Net::LDAP::Entry.new(dn) } + end entries end - private :member_entries - - # Internal: Bind a list of DNs to their respective entries. - # - # Returns an Array of Net::LDAP::Entry objects. - def entries_by_dn(members) - members.map do |dn| - ldap.domain(dn).bind(attributes: attrs) - end.compact - end - private :entries_by_dn # Internal: Fetch entries by UID. # From cb4bd2ce9f2d18ef627c05f63b2f8105586fcf5c Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Wed, 14 Jan 2015 22:05:24 -0800 Subject: [PATCH 04/11] Ensure unique entries --- lib/github/ldap/member_search/recursive.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 8eaa2e6..ab1374c 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -89,12 +89,14 @@ def perform(group) end end - # take found groups and combine groups and members into list of entries - found.values.each do |group| + # pull member DNs, discarding dupes and subgroup DNs + member_dns = found.values.each_with_object([]) do |group, member_dns| entries << group - # just need member DNs as Net::LDAP::Entry objects - entries.concat member_dns(group).map { |dn| Net::LDAP::Entry.new(dn) } - end + member_dns.concat member_dns(group) + end.uniq.reject { |dn| found.key?(dn) } + + # wrap member DNs in Net::LDAP::Entry objects + entries.concat member_dns.map { |dn| Net::LDAP::Entry.new(dn) } entries end From 909da53b87a6c7e304fbb97d1afd0747aab7a959 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 13:37:50 -0800 Subject: [PATCH 05/11] Doc, reorder variables to make purpose explicit --- lib/github/ldap/member_search/recursive.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index ab1374c..9b04df3 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -34,9 +34,11 @@ def initialize(ldap, options = {}) # # Returns Array of Net::LDAP::Entry objects. def perform(group) - found = Hash.new + # track groups found + found = Hash.new + + # track DNs searched for (so we don't repeat searches) searched = [] - entries = [] # if this is a posixGroup, return members immediately (no nesting) uids = member_uids(group) @@ -89,6 +91,9 @@ def perform(group) end end + # entries to return + entries = [] + # pull member DNs, discarding dupes and subgroup DNs member_dns = found.values.each_with_object([]) do |group, member_dns| entries << group From 09e05a67d030739a78b690afe3d1a3576869fade Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 13:41:11 -0800 Subject: [PATCH 06/11] Use a Set to track DNs searched for --- lib/github/ldap/member_search/recursive.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 9b04df3..fa3d5a8 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -37,8 +37,8 @@ def perform(group) # track groups found found = Hash.new - # track DNs searched for (so we don't repeat searches) - searched = [] + # track all DNs searched for (so we don't repeat searches) + searched = Set.new # if this is a posixGroup, return members immediately (no nesting) uids = member_uids(group) From 52654ad40bd92932e93089278ae6e79302f14d48 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 13:56:13 -0800 Subject: [PATCH 07/11] Short circuit after filtering out already searched DNs --- lib/github/ldap/member_search/recursive.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index fa3d5a8..d082de0 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -69,12 +69,12 @@ def perform(group) sub_dns.concat member_dns(subgroup) end - # give up if there's nothing else to search for - break if sub_dns.empty? - # filter out if already searched for sub_dns.reject! { |dn| searched.include?(dn) } + # give up if there's nothing else to search for + break if sub_dns.empty? + # search for subgroups subgroups = sub_dns.each_with_object([]) do |dn, subgroups| subgroups.concat ldap.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: attrs, filter: filter) From 8fce4a79dc48cffaa67c62d7ab68120427a074ca Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 14:22:47 -0800 Subject: [PATCH 08/11] Extract group search method --- lib/github/ldap/member_search/recursive.rb | 31 ++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index d082de0..5ee8420 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -52,9 +52,8 @@ def perform(group) dns = member_dns(group) # search for base group's subgroups - filter = ALL_GROUPS_FILTER groups = dns.each_with_object([]) do |dn, groups| - groups.concat ldap.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: attrs, filter: filter) + groups.concat find_groups_by_dn(dn) searched << dn end @@ -77,13 +76,14 @@ def perform(group) # search for subgroups subgroups = sub_dns.each_with_object([]) do |dn, subgroups| - subgroups.concat ldap.search(base: dn, scope: Net::LDAP::SearchScope_BaseObject, attributes: attrs, filter: filter) - searched << dn + subgroups.concat find_groups_by_dn(dn) + searched << dn end + # give up if there were no subgroups found break if subgroups.empty? - # track found groups + # track found subgroups subgroups.each { |g| found[g.dn] = g } # descend another level @@ -106,6 +106,27 @@ def perform(group) entries end + # Internal: Search for Groups by DN. + # + # Given a Distinguished Name (DN) String value, find the Group entry + # that matches it. The DN may map to a `person` entry, but we want to + # filter those out. + # + # This will find zero or one entry most of the time, but it's not + # guaranteed so we account for the possibility of more. + # + # This method is intended to be used with `Array#concat` by the caller. + # + # Returns an Array of zero or more Net::LDAP::Entry objects. + def find_groups_by_dn(dn) + ldap.search \ + base: dn, + scope: Net::LDAP::SearchScope_BaseObject, + attributes: attrs, + filter: ALL_GROUPS_FILTER + end + private :find_group_by_dn + # Internal: Fetch entries by UID. # # Returns an Array of Net::LDAP::Entry objects. From 2e5943b9d822a2a2a277ba2bbca33791ac4a9671 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 14:23:30 -0800 Subject: [PATCH 09/11] Fix spacing [skip ci] --- lib/github/ldap/member_search/recursive.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 5ee8420..5235d5a 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -77,7 +77,7 @@ def perform(group) # search for subgroups subgroups = sub_dns.each_with_object([]) do |dn, subgroups| subgroups.concat find_groups_by_dn(dn) - searched << dn + searched << dn end # give up if there were no subgroups found From de8d8b46a8d8dcf25589bd46d1cc41f506e6dd6a Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 14:29:23 -0800 Subject: [PATCH 10/11] Tweak member DN collection, mapping --- lib/github/ldap/member_search/recursive.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index 5235d5a..bbc557f 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -94,14 +94,14 @@ def perform(group) # entries to return entries = [] - # pull member DNs, discarding dupes and subgroup DNs - member_dns = found.values.each_with_object([]) do |group, member_dns| + # collect all member DNs, discarding dupes and subgroup DNs + members = found.values.each_with_object([]) do |group, dns| entries << group - member_dns.concat member_dns(group) + dns.concat member_dns(group) end.uniq.reject { |dn| found.key?(dn) } # wrap member DNs in Net::LDAP::Entry objects - entries.concat member_dns.map { |dn| Net::LDAP::Entry.new(dn) } + entries.concat members.map! { |dn| Net::LDAP::Entry.new(dn) } entries end From 973959d4f4aec8b62bb5e9f837e0cbe2c206cf68 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Thu, 15 Jan 2015 14:29:35 -0800 Subject: [PATCH 11/11] Fix method name --- lib/github/ldap/member_search/recursive.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/github/ldap/member_search/recursive.rb b/lib/github/ldap/member_search/recursive.rb index bbc557f..a36aa4d 100644 --- a/lib/github/ldap/member_search/recursive.rb +++ b/lib/github/ldap/member_search/recursive.rb @@ -125,7 +125,7 @@ def find_groups_by_dn(dn) attributes: attrs, filter: ALL_GROUPS_FILTER end - private :find_group_by_dn + private :find_groups_by_dn # Internal: Fetch entries by UID. #