Skip to content

Commit 85d5ac5

Browse files
committed
when nesting is not enabled, the group filter can be applied right away
- helps performance, but skipping unnecessary entries - reduces reoccuring info-level log output against groups that do not qualify ("no or empty name") Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
1 parent 227d237 commit 85d5ac5

File tree

2 files changed

+43
-7
lines changed

2 files changed

+43
-7
lines changed

apps/user_ldap/lib/Group_LDAP.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ private function walkNestedGroups(string $dn, \Closure $fetcher, array $list): a
343343
}
344344

345345
$seen = [];
346-
while ($record = array_pop($list)) {
346+
while ($record = array_shift($list)) {
347347
$recordDN = $recordMode ? $record['dn'][0] : $record;
348348
if ($recordDN === $dn || array_key_exists($recordDN, $seen)) {
349349
// Prevent loops
@@ -841,6 +841,12 @@ private function getGroupsByMember($dn, &$seen = null) {
841841
$allGroups = [];
842842
$seen[$dn] = true;
843843
$filter = $this->access->connection->ldapGroupMemberAssocAttr . '=' . $dn;
844+
845+
$nesting = (int)$this->access->connection->ldapNestedGroups;
846+
if ($nesting === 0) {
847+
$filter = $this->access->combineFilterWithAnd([$filter, $this->access->connection->ldapGroupFilter]);
848+
}
849+
844850
$groups = $this->access->fetchListOfGroups($filter,
845851
[strtolower($this->access->connection->ldapGroupMemberAssocAttr), $this->access->connection->ldapGroupDisplayName, 'dn']);
846852
if (is_array($groups)) {

apps/user_ldap/tests/Group_LDAPTest.php

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -727,23 +727,36 @@ public function testGetUserGroupsMemberOfDisabled() {
727727
$groupBackend->getUserGroups('userX');
728728
}
729729

730-
public function testGetGroupsByMember() {
730+
public function nestedGroupsProvider(): array {
731+
return [
732+
[ true ],
733+
[ false ],
734+
];
735+
}
736+
737+
/**
738+
* @dataProvider nestedGroupsProvider
739+
*/
740+
public function testGetGroupsByMember(bool $nestedGroups) {
731741
$access = $this->getAccessMock();
732742
$pluginManager = $this->getPluginManagerMock();
733743

744+
$groupFilter = '(&(objectclass=nextcloudGroup)(nextcloudEnabled=TRUE))';
734745
$access->connection = $this->createMock(Connection::class);
735746
$access->connection->expects($this->any())
736747
->method('__get')
737-
->willReturnCallback(function ($name) {
748+
->willReturnCallback(function ($name) use ($nestedGroups, $groupFilter) {
738749
switch ($name) {
739750
case 'useMemberOfToDetectMembership':
740751
return 0;
741752
case 'ldapDynamicGroupMemberURL':
742753
return '';
743754
case 'ldapNestedGroups':
744-
return false;
755+
return $nestedGroups;
745756
case 'ldapGroupMemberAssocAttr':
746757
return 'member';
758+
case 'ldapGroupFilter':
759+
return $groupFilter;
747760
}
748761
return 1;
749762
});
@@ -756,10 +769,15 @@ public function testGetGroupsByMember() {
756769
$access->expects($this->exactly(2))
757770
->method('username2dn')
758771
->willReturn($dn);
759-
760772
$access->expects($this->any())
761773
->method('readAttribute')
762774
->willReturn([]);
775+
$access->expects($this->any())
776+
->method('combineFilterWithAnd')
777+
->willReturnCallback(function (array $filterParts) {
778+
// ⚠ returns a pseudo-filter only, not real LDAP Filter syntax
779+
return implode('&', $filterParts);
780+
});
763781

764782
$group1 = [
765783
'cn' => 'group1',
@@ -774,9 +792,21 @@ public function testGetGroupsByMember() {
774792
->method('nextcloudGroupNames')
775793
->with([$group1, $group2])
776794
->willReturn(['group1', 'group2']);
777-
$access->expects($this->once())
795+
$access->expects($nestedGroups ? $this->atLeastOnce() : $this->once())
778796
->method('fetchListOfGroups')
779-
->willReturn([$group1, $group2]);
797+
->willReturnCallback(function ($filter, $attr, $limit, $offset) use ($nestedGroups, $groupFilter, $group1, $group2) {
798+
static $firstRun = true;
799+
if (!$nestedGroups) {
800+
// When nested groups are enabled, groups cannot be filtered early as it would
801+
// exclude intermediate groups. But we can, and should, when working with flat groups.
802+
$this->assertTrue(strpos($filter, $groupFilter) !== false);
803+
}
804+
if ($firstRun) {
805+
$firstRun = false;
806+
return [$group1, $group2];
807+
}
808+
return [];
809+
});
780810
$access->expects($this->any())
781811
->method('dn2groupname')
782812
->willReturnCallback(function (string $dn) {

0 commit comments

Comments
 (0)