Skip to content

Commit 04bafe7

Browse files
committed
[LDAP] improve processing nested groups
- split walkNestedGroups into two distinct methods, for $recordMode determiniation was always wrong in subsequent runs. This also simplifies the code. - add runtime caching to avoid duplicated LDAP requests. Later on, members are cached on Redis. Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
1 parent b23934a commit 04bafe7

File tree

1 file changed

+45
-34
lines changed

1 file changed

+45
-34
lines changed

apps/user_ldap/lib/Group_LDAP.php

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,7 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
6464
protected $groupPluginManager;
6565
/** @var LoggerInterface */
6666
protected $logger;
67-
68-
/**
69-
* @var string $ldapGroupMemberAssocAttr contains the LDAP setting (in lower case) with the same name
70-
*/
67+
/** @var string $ldapGroupMemberAssocAttr contains the LDAP setting (in lower case) with the same name */
7168
protected $ldapGroupMemberAssocAttr;
7269

7370
public function __construct(Access $access, GroupPluginManager $groupPluginManager) {
@@ -304,7 +301,7 @@ private function _groupMembers(string $dnGroup, ?array &$seen = null): array {
304301
$fetcher = function ($memberDN) use (&$seen) {
305302
return $this->_groupMembers($memberDN, $seen);
306303
};
307-
$allMembers = $this->walkNestedGroups($dnGroup, $fetcher, $members, $seen);
304+
$allMembers = $this->walkNestedGroupsReturnDNs($dnGroup, $fetcher, $members, $seen);
308305
}
309306

310307
$allMembers += $this->getDynamicGroupMembers($dnGroup);
@@ -351,29 +348,11 @@ private function _getGroupDNsFromMemberOf(string $dn): array {
351348
return $nestedGroups;
352349
};
353350

354-
$groups = $this->walkNestedGroups($dn, $fetcher, $groups);
351+
$groups = $this->walkNestedGroupsReturnDNs($dn, $fetcher, $groups);
355352
return $this->filterValidGroups($groups);
356353
}
357354

358-
private function walkNestedGroups(string $dn, Closure $fetcher, array $list, array &$seen = []): array {
359-
$nesting = (int)$this->access->connection->ldapNestedGroups;
360-
// depending on the input, we either have a list of DNs or a list of LDAP records
361-
// also, the output expects either DNs or records. Testing the first element should suffice.
362-
$recordMode = is_array($list) && isset($list[0]) && is_array($list[0]) && isset($list[0]['dn'][0]);
363-
364-
if ($nesting !== 1) {
365-
if ($recordMode) {
366-
// the keys are numeric, but should hold the DN
367-
return array_reduce($list, function ($transformed, $record) use ($dn) {
368-
if ($record['dn'][0] != $dn) {
369-
$transformed[$record['dn'][0]] = $record;
370-
}
371-
return $transformed;
372-
}, []);
373-
}
374-
return $list;
375-
}
376-
355+
private function processListFromWalkingNestedGroups(array &$list, array &$seen, string $dn, Closure $fetcher): void {
377356
while ($record = array_shift($list)) {
378357
$recordDN = $record['dn'][0] ?? $record;
379358
if ($recordDN === $dn || array_key_exists($recordDN, $seen)) {
@@ -386,9 +365,35 @@ private function walkNestedGroups(string $dn, Closure $fetcher, array $list, arr
386365
$seen[$recordDN] = $record;
387366
}
388367
}
368+
}
369+
370+
private function walkNestedGroupsReturnDNs(string $dn, Closure $fetcher, array $list, array &$seen = []): array {
371+
$nesting = (int)$this->access->connection->ldapNestedGroups;
372+
373+
if ($nesting !== 1) {
374+
return $list;
375+
}
376+
377+
$this->processListFromWalkingNestedGroups($list, $seen, $dn, $fetcher);
378+
return array_keys($seen);
379+
}
380+
381+
private function walkNestedGroupsReturnRecords(string $dn, Closure $fetcher, array $list, array &$seen = []): array {
382+
$nesting = (int)$this->access->connection->ldapNestedGroups;
383+
384+
if ($nesting !== 1) {
385+
// the keys are numeric, but we need them to hold the DN
386+
return array_reduce($list, function ($transformed, $record) use ($dn) {
387+
if ($record['dn'][0] != $dn) {
388+
$transformed[$record['dn'][0]] = $record;
389+
}
390+
return $transformed;
391+
}, []);
392+
}
389393

390-
// on record mode, filter out intermediate state
391-
return $recordMode ? array_filter($seen, 'is_array') : array_keys($seen);
394+
$this->processListFromWalkingNestedGroups($list, $seen, $dn, $fetcher);
395+
// filter out intermediate state
396+
return array_filter($seen, 'is_array');
392397
}
393398

394399
/**
@@ -847,6 +852,9 @@ private function getGroupsByMember(string $dn, array &$seen = null): array {
847852
// avoid loops
848853
return [];
849854
}
855+
if ($this->cachedGroupsByMember[$dn]) {
856+
return $this->cachedGroupsByMember[$dn];
857+
}
850858
$allGroups = [];
851859
$seen[$dn] = true;
852860
$filter = $this->access->connection->ldapGroupMemberAssocAttr . '=' . $dn;
@@ -871,14 +879,12 @@ private function getGroupsByMember(string $dn, array &$seen = null): array {
871879
return $this->getGroupsByMember($dn, $seen);
872880
};
873881

874-
if (empty($dn)) {
875-
$dn = "";
876-
}
877-
878-
$allGroups = $this->walkNestedGroups($dn, $fetcher, $groups, $seen);
882+
$allGroups = $this->walkNestedGroupsReturnRecords($dn, $fetcher, $groups, $seen);
879883
}
880884
$visibleGroups = $this->filterValidGroups($allGroups);
881-
return array_intersect_key($allGroups, $visibleGroups);
885+
$effectiveGroups = array_intersect_key($allGroups, $visibleGroups);
886+
$this->cachedGroupsByMember[$dn] = $effectiveGroups;
887+
return $effectiveGroups;
882888
}
883889

884890
/**
@@ -1184,7 +1190,12 @@ protected function filterValidGroups(array $listOfGroups): array {
11841190
$validGroupDNs = [];
11851191
foreach ($listOfGroups as $key => $item) {
11861192
$dn = is_string($item) ? $item : $item['dn'][0];
1187-
$gid = $this->access->dn2groupname($dn);
1193+
if (is_array($item) && !isset($item[$this->access->connection->ldapGroupDisplayName][0])) {
1194+
// group details were fetched, but a name attribute was not set: do not bother to re-fetch
1195+
continue;
1196+
}
1197+
$name = $item[$this->access->connection->ldapGroupDisplayName][0] ?? null;
1198+
$gid = $this->access->dn2groupname($dn, $name);
11881199
if (!$gid) {
11891200
continue;
11901201
}

0 commit comments

Comments
 (0)