Skip to content

Commit 1e00d56

Browse files
committed
fix nested group retrieval also for 2 other cases
and also consolidate logic in one method Signed-off-by: Arthur Schiwon <[email protected]>
1 parent 3eda3a6 commit 1e00d56

File tree

2 files changed

+77
-60
lines changed

2 files changed

+77
-60
lines changed

apps/user_ldap/lib/Connection.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@
6262
* @property string ldapEmailAttribute
6363
* @property string ldapExtStorageHomeAttribute
6464
* @property string homeFolderNamingRule
65+
* @property bool|string ldapNestedGroups
66+
* @property string[] ldapBaseGroups
67+
* @property string ldapGroupFilter
6568
*/
6669
class Connection extends LDAPUtility {
6770
private $ldapConnectionRes = null;

apps/user_ldap/lib/Group_LDAP.php

Lines changed: 74 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,12 @@ public function getDynamicGroupMembers($dnGroup) {
218218
*/
219219
private function _groupMembers($dnGroup, &$seen = null) {
220220
if ($seen === null) {
221-
$seen = array();
221+
$seen = [];
222222
}
223-
$allMembers = array();
223+
$allMembers = [];
224224
if (array_key_exists($dnGroup, $seen)) {
225225
// avoid loops
226-
return array();
226+
return [];
227227
}
228228
// used extensively in cron job, caching makes sense for nested groups
229229
$cacheKey = '_groupMembers'.$dnGroup;
@@ -232,19 +232,12 @@ private function _groupMembers($dnGroup, &$seen = null) {
232232
return $groupMembers;
233233
}
234234
$seen[$dnGroup] = 1;
235-
$members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr,
236-
$this->access->connection->ldapGroupFilter);
235+
$members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr);
237236
if (is_array($members)) {
238-
foreach ($members as $member) {
239-
$allMembers[$member] = 1;
240-
$nestedGroups = $this->access->connection->ldapNestedGroups;
241-
if (!empty($nestedGroups)) {
242-
$subMembers = $this->_groupMembers($member, $seen);
243-
if ($subMembers) {
244-
$allMembers += $subMembers;
245-
}
246-
}
247-
}
237+
$fetcher = function($memberDN, &$seen) {
238+
return $this->_groupMembers($memberDN, $seen);
239+
};
240+
$allMembers = $this->walkNestedGroups($dnGroup, $fetcher, $members);
248241
}
249242

250243
$allMembers += $this->getDynamicGroupMembers($dnGroup);
@@ -257,42 +250,69 @@ private function _groupMembers($dnGroup, &$seen = null) {
257250
* @param string $DN
258251
* @param array|null &$seen
259252
* @return array
253+
* @throws \OC\ServerNotAvailableException
260254
*/
261255
private function _getGroupDNsFromMemberOf($DN) {
262256
$groups = $this->access->readAttribute($DN, 'memberOf');
263257
if (!is_array($groups)) {
264-
return array();
258+
return [];
265259
}
266-
$nestedGroups = (int) $this->access->connection->ldapNestedGroups;
267-
if ($nestedGroups === 1) {
268-
$seen = array();
269-
while ($group = array_pop($groups)) {
270-
if ($group === $DN || array_key_exists($group, $seen)) {
271-
// Prevent loops
272-
continue;
273-
}
274-
$seen[$group] = 1;
275260

276-
// Resolve nested groups
277-
if (isset($cachedNestedGroups[$group])) {
278-
$nestedGroups = $cachedNestedGroups[$group];
279-
} else {
280-
$nestedGroups = $this->access->readAttribute($group, 'memberOf');
281-
if (!is_array($nestedGroups)) {
282-
$nestedGroups = [];
283-
}
284-
$cachedNestedGroups[$group] = $nestedGroups;
285-
}
286-
foreach ($nestedGroups as $nestedGroup) {
287-
array_push($groups, $nestedGroup);
261+
$fetcher = function($groupDN) {
262+
if (isset($this->cachedNestedGroups[$groupDN])) {
263+
$nestedGroups = $this->cachedNestedGroups[$groupDN];
264+
} else {
265+
$nestedGroups = $this->access->readAttribute($groupDN, 'memberOf');
266+
if (!is_array($nestedGroups)) {
267+
$nestedGroups = [];
288268
}
269+
$this->cachedNestedGroups[$groupDN] = $nestedGroups;
289270
}
290-
// Get unique group DN's from those we have visited in the loop
291-
$groups = array_keys($seen);
292-
}
271+
return $nestedGroups;
272+
};
273+
274+
$groups = $this->walkNestedGroups($DN, $fetcher, $groups);
293275
return $this->access->groupsMatchFilter($groups);
294276
}
295277

278+
/**
279+
* @param string $dn
280+
* @param \Closure $fetcher args: string $dn, array $seen, returns: string[] of dns
281+
* @param array $list
282+
* @return array
283+
*/
284+
private function walkNestedGroups(string $dn, \Closure $fetcher, array $list): array {
285+
$nestedRecords = (int) $this->access->connection->ldapNestedGroups;
286+
if ($nestedRecords !== 1) {
287+
return $list;
288+
}
289+
$seen = [];
290+
$recordMode = false;
291+
while ($record = array_pop($list)) {
292+
if(is_string($record)) {
293+
$recordDN = $record;
294+
} else if(is_array($record) && isset($record['dn'][0])) {
295+
$recordDN = $record['dn'][0];
296+
$recordMode = true;
297+
}
298+
if(!isset($recordDN)) {
299+
throw new \InvalidArgumentException('Supplied list is incompatible');
300+
}
301+
if ($recordDN === $dn || array_key_exists($recordDN, $seen)) {
302+
// Prevent loops
303+
continue;
304+
}
305+
$fetched = $fetcher($record, $seen);
306+
$list = array_merge($list, $fetched);
307+
$seen[$recordDN] = $record;
308+
}
309+
// Get unique group DN's from those we have visited in the loop
310+
if($recordMode) {
311+
return($seen);
312+
}
313+
return array_keys($seen);
314+
}
315+
296316
/**
297317
* translates a gidNumber into an ownCloud internal name
298318
* @param string $gid as given by gidNumber on POSIX LDAP
@@ -753,34 +773,28 @@ public function getUserGroups($uid) {
753773
*/
754774
private function getGroupsByMember($dn, &$seen = null) {
755775
if ($seen === null) {
756-
$seen = array();
776+
$seen = [];
757777
}
758-
$allGroups = array();
759778
if (array_key_exists($dn, $seen)) {
760779
// avoid loops
761-
return array();
780+
return [];
762781
}
782+
$allGroups = [];
763783
$seen[$dn] = true;
764-
$filter = $this->access->combineFilterWithAnd(array(
765-
$this->access->connection->ldapGroupFilter,
766-
$this->access->connection->ldapGroupMemberAssocAttr.'='.$dn
767-
));
784+
$filter = $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn;
768785
$groups = $this->access->fetchListOfGroups($filter,
769-
array($this->access->connection->ldapGroupDisplayName, 'dn'));
786+
[$this->access->connection->ldapGroupDisplayName, 'dn']);
770787
if (is_array($groups)) {
771-
foreach ($groups as $groupobj) {
772-
$groupDN = $groupobj['dn'][0];
773-
$allGroups[$groupDN] = $groupobj;
774-
$nestedGroups = $this->access->connection->ldapNestedGroups;
775-
if (!empty($nestedGroups)) {
776-
$supergroups = $this->getGroupsByMember($groupDN, $seen);
777-
if (is_array($supergroups) && (count($supergroups)>0)) {
778-
$allGroups = array_merge($allGroups, $supergroups);
779-
}
788+
$fetcher = function ($dn, &$seen) {
789+
if(is_array($dn) && isset($dn['dn'][0])) {
790+
$dn = $dn['dn'][0];
780791
}
781-
}
792+
return $this->getGroupsByMember($dn, $seen);
793+
};
794+
$allGroups = $this->walkNestedGroups($dn, $fetcher, $groups);
782795
}
783-
return $allGroups;
796+
$visibleGroups = $this->access->groupsMatchFilter(array_keys($allGroups));
797+
return array_intersect($allGroups, array_flip($visibleGroups));
784798
}
785799

786800
/**
@@ -827,7 +841,7 @@ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) {
827841

828842
$primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $search, $limit, $offset);
829843
$posixGroupUsers = $this->getUsersInGidNumber($groupDN, $search, $limit, $offset);
830-
$members = array_keys($this->_groupMembers($groupDN));
844+
$members = $this->_groupMembers($groupDN);
831845
if(!$members && empty($posixGroupUsers) && empty($primaryUsers)) {
832846
//in case users could not be retrieved, return empty result set
833847
$this->access->connection->writeToCache($cacheKey, []);

0 commit comments

Comments
 (0)