/** * recurse through all groups, adding parent groups to $all_group_dns array. * * @param array $current_group_entries of ldap group entries that are starting point. should include at least 1 entry. * @param array $all_group_dns as array of all groups user is a member of. MIXED CASE VALUES * @param array $tested_group_ids as array of tested group dn, cn, uid, etc. MIXED CASE VALUES * whether these value are dn, cn, uid, etc depends on what attribute members, uniquemember, memberUid contains * whatever attribute is in $this->$tested_group_ids to avoid redundant recursing * @param int $level of recursion * @param int $max_levels as max recursion allowed * * given set of groups entries ($current_group_entries such as it, hr, accounting), * find parent groups (such as staff, people, users) and add them to list of group memberships ($all_group_dns) * * (&(objectClass=[$this->groupObjectClass])(|([$this->groupMembershipsAttr]=groupid1)([$this->groupMembershipsAttr]=groupid2)) * * @return FALSE for error or misconfiguration, otherwise TRUE. results are passed by reference. */ public function groupMembershipsFromEntryRecursive($current_group_entries, &$all_group_dns, &$tested_group_ids, $level, $max_levels) { if (!$this->groupGroupEntryMembershipsConfigured || !is_array($current_group_entries) || count($current_group_entries) == 0) { return FALSE; } if (isset($current_group_entries['count'])) { unset($current_group_entries['count']); } $ors = array(); foreach ($current_group_entries as $i => $group_entry) { if ($this->groupMembershipsAttrMatchingUserAttr == 'dn') { $member_id = $group_entry['dn']; } else { // maybe cn, uid, etc is held $member_id = ldap_servers_get_first_rdn_value_from_dn($group_entry['dn'], $this->groupMembershipsAttrMatchingUserAttr); if (!$member_id) { if ($this->detailed_watchdog_log) { watchdog('ldap_server', 'group_entry: %ge', array('%ge' => pretty_print_ldap_entry($group_entry))); } // group not identified by simple checks yet! // examine the entry and see if it matches the configured groupObjectClass $goc = $group_entry['objectclass']; // TODO do we need to ensure such entry is there? if (is_array($goc)) { // TODO is it always an array? foreach ($goc as $g) { $g = drupal_strtolower($g); if ($g == $this->groupObjectClass) { // found a group, current user must be member in it - so: if ($this->detailed_watchdog_log) { watchdog('ldap_server', 'adding %mi', array('%mi' => $member_id)); } $member_id = $group_entry['dn']; break; } } } } } if ($member_id && !in_array($member_id, $tested_group_ids)) { $tested_group_ids[] = $member_id; $all_group_dns[] = $group_entry['dn']; // add $group_id (dn, cn, uid) to query $ors[] = $this->groupMembershipsAttr . '=' . ldap_pear_escape_filter_value($member_id); } } if ($level < $max_levels && count($ors)) { $count = count($ors); for ($i = 0; $i < $count; $i = $i + LDAP_SERVER_LDAP_QUERY_CHUNK) { // only 50 or so per query $current_ors = array_slice($ors, $i, LDAP_SERVER_LDAP_QUERY_CHUNK); $or = '(|(' . join(")(", $current_ors) . '))'; // e.g. (|(cn=group1)(cn=group2)) or (|(dn=cn=group1,ou=blah...)(dn=cn=group2,ou=blah...)) $query_for_parent_groups = '(&(objectClass=' . $this->groupObjectClass . ')' . $or . ')'; foreach ($this->basedn as $base_dn) { // need to search on all basedns one at a time $group_entries = $this->search($base_dn, $query_for_parent_groups); // no attributes, just dns needed if ($group_entries !== FALSE) { $this->groupMembershipsFromEntryRecursive($group_entries, $all_group_dns, $tested_group_ids, $level + 1, $max_levels); } } } } return TRUE; }
/** * recurse through all groups, adding parent groups to $all_group_dns array. * * @param array $current_group_entries of ldap group entries that are starting point. should include at least 1 entry. * @param array $all_group_dns as array of all groups user is a member of. MIXED CASE VALUES * @param array $tested_group_ids as array of tested group dn, cn, uid, etc. MIXED CASE VALUES * whether these value are dn, cn, uid, etc depends on what attribute members, uniquemember, memberUid contains * whatever attribute is in $this->$tested_group_ids to avoid redundant recursing * @param int $level of recursion * @param int $max_levels as max recursion allowed * * given set of groups entries ($current_group_entries such as it, hr, accounting), * find parent groups (such as staff, people, users) and add them to list of group memberships ($all_group_dns) * * (&(objectClass=[$this->groupObjectClass])(|([$this->groupMembershipsAttr]=groupid1)([$this->groupMembershipsAttr]=groupid2)) * * @return FALSE for error or misconfiguration, otherwise TRUE. results are passed by reference. */ public function groupMembershipsFromEntryRecursive($current_group_entries, &$all_group_dns, &$tested_group_ids, $level, $max_levels) { if (!$this->groupGroupEntryMembershipsConfigured || !is_array($current_group_entries) || count($current_group_entries) == 0) { return FALSE; } if (isset($current_group_entries['count'])) { unset($current_group_entries['count']); } $ors = array(); foreach ($current_group_entries as $i => $group_entry) { if ($this->groupMembershipsAttrMatchingUserAttr == 'dn') { $member_id = $group_entry['dn']; } else { // maybe cn, uid, etc is held $member_id = ldap_servers_get_first_rdn_value_from_dn($group_entry['dn'], $this->groupMembershipsAttrMatchingUserAttr); } if ($member_id && !in_array($member_id, $tested_group_ids)) { $tested_group_ids[] = $member_id; $all_group_dns[] = $group_entry['dn']; // add $group_id (dn, cn, uid) to query $ors[] = $this->groupMembershipsAttr . '=' . ldap_pear_escape_filter_value($member_id); } } if ($level < $max_levels && count($ors)) { $count = count($ors); for ($i = 0; $i < $count; $i = $i + LDAP_SERVER_LDAP_QUERY_CHUNK) { // only 50 or so per query $current_ors = array_slice($ors, $i, LDAP_SERVER_LDAP_QUERY_CHUNK); $or = '(|(' . join(")(", $current_ors) . '))'; // e.g. (|(cn=group1)(cn=group2)) or (|(dn=cn=group1,ou=blah...)(dn=cn=group2,ou=blah...)) $query_for_parent_groups = '(&(objectClass=' . $this->groupObjectClass . ')' . $or . ')'; foreach ($this->basedn as $base_dn) { // need to search on all basedns one at a time $group_entries = $this->search($base_dn, $query_for_parent_groups); // no attributes, just dns needed if ($group_entries !== FALSE) { $this->groupMembershipsFromEntryRecursive($group_entries, $all_group_dns, $tested_group_ids, $level + 1, $max_levels); } } } } return TRUE; }
/** * not working yet * will be ton of permission issues with service accounts * need configurable obj type to avoid binding to a million user entries, printers, etc. */ private function deriveFromAttrGroupsResursive(&$all_groups, &$groups_by_level, $level, $derive_from_attribute_name, $max_depth) { // derive query with & of all groups at current level // e.g. (|(distinguishedname=cn=content editors,ou=groups,dc=ad,dc=myuniversity,dc=edu)(distinguishedname=cn=content approvers,ou=groups,dc=ad,dc=myuniversity,dc=edu)) // execute query and loop through it to populate $groups_by_level[$level + 1] // call recursively provided max depth not excluded and $groups_by_level[$level + 1] > 0 // this needs to be configurable also and default per ldap implementation $group_values = ldap_pear_escape_filter_value($groups_by_level[$derive_from_attribute_name][$level]); $filter = "(&\n (objectClass=" . $this->groupObjectClass . ")\n (" . $derive_from_attribute_name . "=*)\n (|\n (distinguishedname=" . join(")\n (distinguishedname=", $group_values) . ")\n )\n)"; $level++; foreach ($this->basedn as $base_dn) { // need to search on all basedns one at a time $entries = $this->search($base_dn, $filter, array($derive_from_attribute_name)); foreach ($entries as $entry) { $attr_values = array(); if (is_array($entry) && count($entry)) { if (isset($entry[$derive_from_attribute_name])) { $attr_values = $entry[$derive_from_attribute_name]; } elseif (isset($entry[drupal_strtolower($derive_from_attribute_name)])) { $attr_values = $entry[drupal_strtolower($derive_from_attribute_name)]; } else { foreach ($entry as $attr_name => $values) { if (strcasecmp($derive_from_attribute_name, $attr_name) != 0) { continue; } $attr_values = $entry[$attr_name]; break; } } if (count($attr_values)) { for ($i = 0; $i < $attr_values['count']; $i++) { $value = (string) $attr_values[$i]; if (!in_array($value, $all_groups)) { $groups_by_level[$derive_from_attribute_name][$level][] = $value; $all_groups[] = $value; } } } } } } if (isset($groups_by_level[$derive_from_attribute_name][$level]) && count($groups_by_level[$derive_from_attribute_name][$level]) && $level < $max_depth) { $this->deriveFromAttrGroupsResursive($all_groups, $groups_by_level, $level, $derive_from_attribute_name, $max_depth); } }