From 99a62615b045615b507058d2ab9b91c50c2cd0a6 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Mon, 19 Oct 2020 13:17:06 +0200 Subject: [PATCH] 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 --- apps/user_ldap/lib/Group_LDAP.php | 2 +- apps/user_ldap/tests/Group_LDAPTest.php | 42 +++++++++++++++++++++---- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php index 54a8eaf08ae5a..d5c464b1a603d 100644 --- a/apps/user_ldap/lib/Group_LDAP.php +++ b/apps/user_ldap/lib/Group_LDAP.php @@ -343,7 +343,7 @@ private function walkNestedGroups(string $dn, \Closure $fetcher, array $list): a } $seen = []; - while ($record = array_pop($list)) { + while ($record = array_shift($list)) { $recordDN = $recordMode ? $record['dn'][0] : $record; if ($recordDN === $dn || array_key_exists($recordDN, $seen)) { // Prevent loops diff --git a/apps/user_ldap/tests/Group_LDAPTest.php b/apps/user_ldap/tests/Group_LDAPTest.php index 60ec90c1efb90..a770149c243a2 100644 --- a/apps/user_ldap/tests/Group_LDAPTest.php +++ b/apps/user_ldap/tests/Group_LDAPTest.php @@ -727,23 +727,36 @@ public function testGetUserGroupsMemberOfDisabled() { $groupBackend->getUserGroups('userX'); } - public function testGetGroupsByMember() { + public function nestedGroupsProvider(): array { + return [ + [ true ], + [ false ], + ]; + } + + /** + * @dataProvider nestedGroupsProvider + */ + public function testGetGroupsByMember(bool $nestedGroups) { $access = $this->getAccessMock(); $pluginManager = $this->getPluginManagerMock(); + $groupFilter = '(&(objectclass=nextcloudGroup)(nextcloudEnabled=TRUE))'; $access->connection = $this->createMock(Connection::class); $access->connection->expects($this->any()) ->method('__get') - ->willReturnCallback(function ($name) { + ->willReturnCallback(function ($name) use ($nestedGroups, $groupFilter) { switch ($name) { case 'useMemberOfToDetectMembership': return 0; case 'ldapDynamicGroupMemberURL': return ''; case 'ldapNestedGroups': - return false; + return $nestedGroups; case 'ldapGroupMemberAssocAttr': return 'member'; + case 'ldapGroupFilter': + return $groupFilter; } return 1; }); @@ -756,10 +769,15 @@ public function testGetGroupsByMember() { $access->expects($this->exactly(2)) ->method('username2dn') ->willReturn($dn); - $access->expects($this->any()) ->method('readAttribute') ->willReturn([]); + $access->expects($this->any()) + ->method('combineFilterWithAnd') + ->willReturnCallback(function (array $filterParts) { + // ⚠ returns a pseudo-filter only, not real LDAP Filter syntax + return implode('&', $filterParts); + }); $group1 = [ 'cn' => 'group1', @@ -774,9 +792,21 @@ public function testGetGroupsByMember() { ->method('nextcloudGroupNames') ->with([$group1, $group2]) ->willReturn(['group1', 'group2']); - $access->expects($this->once()) + $access->expects($nestedGroups ? $this->atLeastOnce() : $this->once()) ->method('fetchListOfGroups') - ->willReturn([$group1, $group2]); + ->willReturnCallback(function ($filter, $attr, $limit, $offset) use ($nestedGroups, $groupFilter, $group1, $group2) { + static $firstRun = true; + if (!$nestedGroups) { + // When nested groups are enabled, groups cannot be filtered early as it would + // exclude intermediate groups. But we can, and should, when working with flat groups. + $this->assertTrue(strpos($filter, $groupFilter) !== false); + } + if ($firstRun) { + $firstRun = false; + return [$group1, $group2]; + } + return []; + }); $access->expects($this->any()) ->method('dn2groupname') ->willReturnCallback(function (string $dn) {