From d1e7bcfa044279ee0bdc8092df7fda7f292f0168 Mon Sep 17 00:00:00 2001 From: Git'Fellow <12234510+solracsf@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:25:11 +0200 Subject: [PATCH] fix(acl): make preloadRulesForFolder actually warm the rule cache Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com> --- lib/ACL/ACLManager.php | 12 +++++++++++- tests/ACL/ACLManagerTest.php | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/lib/ACL/ACLManager.php b/lib/ACL/ACLManager.php index 2279c4e19..a0f7bb61d 100644 --- a/lib/ACL/ACLManager.php +++ b/lib/ACL/ACLManager.php @@ -241,8 +241,18 @@ public function getPermissionsForTree(int $folderId, int $storageId, string $pat } } + /** + * Warm the rule cache for the direct children of a folder. + * + * Used before listing a folder with many children (e.g. trash listings), + * so the subsequent per-child `getACLPermissionsForPath` lookups are + * served from the cache instead of querying the rules for each child path. + */ public function preloadRulesForFolder(int $storageId, int $parentId): void { - $this->ruleManager->getRulesForFilesByParent($this->user, $storageId, $parentId); + $rules = $this->ruleManager->getRulesForFilesByParent($this->user, $storageId, $parentId); + foreach ($rules as $path => $rulesForPath) { + $this->ruleCache->set($path, $rulesForPath); + } } /** diff --git a/tests/ACL/ACLManagerTest.php b/tests/ACL/ACLManagerTest.php index 22a156a40..ca16b12ca 100644 --- a/tests/ACL/ACLManagerTest.php +++ b/tests/ACL/ACLManagerTest.php @@ -29,6 +29,8 @@ class ACLManagerTest extends TestCase { private IUserMapping&MockObject $dummyMapping; /** @var array> */ private array $rules = []; + /** @var list paths that were resolved through a query rather than the cache */ + private array $requestedPaths = []; #[\Override] protected function setUp(): void { @@ -43,6 +45,8 @@ protected function setUp(): void { $this->ruleManager->method('getRulesForFilesByPath') ->willReturnCallback(function (IUser $user, int $storageId, array $paths): array { // fill with empty in case no rule was found + /** @var string[] $paths */ + $this->requestedPaths = array_values(array_merge($this->requestedPaths, $paths)); $rules = array_fill_keys($paths, []); $actualRules = array_filter($this->rules, fn (string $path): bool => array_search($path, $paths) !== false, ARRAY_FILTER_USE_KEY); @@ -120,6 +124,27 @@ public function testGetACLPermissionsForPathInTrashbin(): void { + public function testPreloadRulesForFolderPopulatesCache(): void { + // no per-path rules are available: anything resolved by a query returns empty + $this->rules = []; + $childPath = '__groupfolders/trash/1/subfolder2.d1700752274'; + $rule = new Rule($this->dummyMapping, 10, Constants::PERMISSION_SHARE, 0); // deny share + + $this->ruleManager->expects($this->once()) + ->method('getRulesForFilesByParent') + ->willReturn([$childPath => [$rule]]); + + $this->requestedPaths = []; + $this->aclManager->preloadRulesForFolder(0, 1); + + $permissions = $this->aclManager->getACLPermissionsForPath(0, 0, $childPath); + + // the rule supplied through preload must have been applied straight from the cache ... + $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, $permissions); + // ... without re-querying the leaf path that was preloaded + $this->assertNotContains($childPath, $this->requestedPaths); + } + public function testGetACLPermissionsForPathPerUserMerge(): void { $aclManager = $this->getAclManager(true); $this->rules = [