Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion lib/ACL/ACLManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

/**
Expand Down
24 changes: 24 additions & 0 deletions tests/ACL/ACLManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class ACLManagerTest extends TestCase {
private IUserMapping&MockObject $dummyMapping;
/** @var array<string, list<Rule>> */
private array $rules = [];
/** @var list<string> paths that were resolved through a query rather than the cache */
private array $requestedPaths = [];

#[\Override]
protected function setUp(): void {
Expand All @@ -44,6 +46,7 @@ protected function setUp(): void {
->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);

Expand Down Expand Up @@ -121,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 = [
Expand Down
Loading