diff options
Diffstat (limited to 'apps/user_ldap/lib/Group_LDAP.php')
-rw-r--r-- | apps/user_ldap/lib/Group_LDAP.php | 136 |
1 files changed, 76 insertions, 60 deletions
diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php index 2e3bc0b4a5c..1658807c0dd 100644 --- a/apps/user_ldap/lib/Group_LDAP.php +++ b/apps/user_ldap/lib/Group_LDAP.php @@ -218,12 +218,12 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD */ private function _groupMembers($dnGroup, &$seen = null) { if ($seen === null) { - $seen = array(); + $seen = []; } - $allMembers = array(); + $allMembers = []; if (array_key_exists($dnGroup, $seen)) { // avoid loops - return array(); + return []; } // used extensively in cron job, caching makes sense for nested groups $cacheKey = '_groupMembers'.$dnGroup; @@ -232,19 +232,12 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD return $groupMembers; } $seen[$dnGroup] = 1; - $members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr, - $this->access->connection->ldapGroupFilter); + $members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr); if (is_array($members)) { - foreach ($members as $member) { - $allMembers[$member] = 1; - $nestedGroups = $this->access->connection->ldapNestedGroups; - if (!empty($nestedGroups)) { - $subMembers = $this->_groupMembers($member, $seen); - if ($subMembers) { - $allMembers += $subMembers; - } - } - } + $fetcher = function($memberDN, &$seen) { + return $this->_groupMembers($memberDN, $seen); + }; + $allMembers = $this->walkNestedGroups($dnGroup, $fetcher, $members); } $allMembers += $this->getDynamicGroupMembers($dnGroup); @@ -257,40 +250,69 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD * @param string $DN * @param array|null &$seen * @return array + * @throws \OC\ServerNotAvailableException */ private function _getGroupDNsFromMemberOf($DN) { $groups = $this->access->readAttribute($DN, 'memberOf'); if (!is_array($groups)) { - return array(); + return []; } - $nestedGroups = (int) $this->access->connection->ldapNestedGroups; - if ($nestedGroups === 1) { - $seen = array(); - while ($group = array_pop($groups)) { - if ($group === $DN || array_key_exists($group, $seen)) { - // Prevent loops - continue; + + $fetcher = function($groupDN) { + if (isset($this->cachedNestedGroups[$groupDN])) { + $nestedGroups = $this->cachedNestedGroups[$groupDN]; + } else { + $nestedGroups = $this->access->readAttribute($groupDN, 'memberOf'); + if (!is_array($nestedGroups)) { + $nestedGroups = []; } - $seen[$group] = 1; + $this->cachedNestedGroups[$groupDN] = $nestedGroups; + } + return $nestedGroups; + }; - // Resolve nested groups - if (isset($cachedNestedGroups[$group])) { - $nestedGroups = $cachedNestedGroups[$group]; - } else { - $nestedGroups = $this->access->readAttribute($group, 'memberOf'); - if (!is_array($nestedGroups)) { - $nestedGroups = []; + $groups = $this->walkNestedGroups($DN, $fetcher, $groups); + return $this->access->groupsMatchFilter($groups); + } + + /** + * @param string $dn + * @param \Closure $fetcher args: string $dn, array $seen, returns: string[] of dns + * @param array $list + * @return array + */ + private function walkNestedGroups(string $dn, \Closure $fetcher, array $list): array { + $nesting = (int) $this->access->connection->ldapNestedGroups; + // depending on the input, we either have a list of DNs or a list of LDAP records + // also, the output expects either DNs or records. Testing the first element should suffice. + $recordMode = is_array($list) && isset($list[0]) && is_array($list[0]) && isset($list[0]['dn'][0]); + + if ($nesting !== 1) { + if($recordMode) { + // the keys are numeric, but should hold the DN + return array_reduce($list, function ($transformed, $record) use ($dn) { + if($record['dn'][0] != $dn) { + $transformed[$record['dn'][0]] = $record; } - $cachedNestedGroups[$group] = $nestedGroups; - } - foreach ($nestedGroups as $nestedGroup) { - array_push($groups, $nestedGroup); - } + return $transformed; + }, []); } - // Get unique group DN's from those we have visited in the loop - $groups = array_keys($seen); + return $list; } - return $this->access->groupsMatchFilter($groups); + + $seen = []; + while ($record = array_pop($list)) { + $recordDN = $recordMode ? $record['dn'][0] : $record; + if ($recordDN === $dn || array_key_exists($recordDN, $seen)) { + // Prevent loops + continue; + } + $fetched = $fetcher($record, $seen); + $list = array_merge($list, $fetched); + $seen[$recordDN] = $record; + } + + return $recordMode ? $seen : array_keys($seen); } /** @@ -753,34 +775,28 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD */ private function getGroupsByMember($dn, &$seen = null) { if ($seen === null) { - $seen = array(); + $seen = []; } - $allGroups = array(); if (array_key_exists($dn, $seen)) { // avoid loops - return array(); + return []; } + $allGroups = []; $seen[$dn] = true; - $filter = $this->access->combineFilterWithAnd(array( - $this->access->connection->ldapGroupFilter, - $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn - )); + $filter = $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn; $groups = $this->access->fetchListOfGroups($filter, - array($this->access->connection->ldapGroupDisplayName, 'dn')); + [$this->access->connection->ldapGroupDisplayName, 'dn']); if (is_array($groups)) { - foreach ($groups as $groupobj) { - $groupDN = $groupobj['dn'][0]; - $allGroups[$groupDN] = $groupobj; - $nestedGroups = $this->access->connection->ldapNestedGroups; - if (!empty($nestedGroups)) { - $supergroups = $this->getGroupsByMember($groupDN, $seen); - if (is_array($supergroups) && (count($supergroups)>0)) { - $allGroups = array_merge($allGroups, $supergroups); - } + $fetcher = function ($dn, &$seen) { + if(is_array($dn) && isset($dn['dn'][0])) { + $dn = $dn['dn'][0]; } - } + return $this->getGroupsByMember($dn, $seen); + }; + $allGroups = $this->walkNestedGroups($dn, $fetcher, $groups); } - return $allGroups; + $visibleGroups = $this->access->groupsMatchFilter(array_keys($allGroups)); + return array_intersect_key($allGroups, array_flip($visibleGroups)); } /** @@ -827,7 +843,7 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD $primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $search, $limit, $offset); $posixGroupUsers = $this->getUsersInGidNumber($groupDN, $search, $limit, $offset); - $members = array_keys($this->_groupMembers($groupDN)); + $members = $this->_groupMembers($groupDN); if(!$members && empty($posixGroupUsers) && empty($primaryUsers)) { //in case users could not be retrieved, return empty result set $this->access->connection->writeToCache($cacheKey, []); @@ -902,7 +918,7 @@ class Group_LDAP extends BackendUtility implements \OCP\GroupInterface, IGroupLD return false; } - $members = array_keys($this->_groupMembers($groupDN)); + $members = $this->_groupMembers($groupDN); $primaryUserCount = $this->countUsersInPrimaryGroup($groupDN, ''); if(!$members && $primaryUserCount === 0) { //in case users could not be retrieved, return empty result set |