Skip to content

Commit

Permalink
clearify the acl logic a bit
Browse files Browse the repository at this point in the history
Signed-off-by: Robin Appelman <robin@icewind.nl>
  • Loading branch information
icewind1991 authored and backportbot-nextcloud[bot] committed Sep 19, 2023
1 parent 0d3274e commit 7cc2c44
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 15 deletions.
2 changes: 1 addition & 1 deletion lib/ACL/ACLCacheWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,6 @@ private function preloadEntries(array $entries): array {
$paths = array_map(function (ICacheEntry $entry) {
return $entry->getPath();
}, $entries);
return $this->aclManager->preloadPaths($paths, false);
return $this->aclManager->getRelevantRulesForPath($paths, false);
}
}
47 changes: 34 additions & 13 deletions lib/ACL/ACLManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ private function getRootStorageId(): int {
}

/**
* @param array $paths
* @return (Rule[])[]
* Get the list of rules applicable for a set of paths
*
* @param string[] $paths
* @param bool $cache whether to cache the retrieved rules
* @return array<string, Rule[]> sorted parent first
*/
private function getRules(array $paths, bool $cache = true): array {
// beware: adding new rules to the cache besides the cap
Expand Down Expand Up @@ -87,10 +90,14 @@ private function getRules(array $paths, bool $cache = true): array {
}

/**
* Get a list of all path that might contain relevant rules when calculating the permissions for a path
*
* This contains the $path itself and any parent folder
*
* @param string $path
* @return string[]
*/
private function getParents(string $path): array {
private function getRelevantPaths(string $path): array {
$paths = [$path];
while ($path !== '') {
$path = dirname($path);
Expand All @@ -104,32 +111,46 @@ private function getParents(string $path): array {
}

/**
* @return Rule[][]
* Get the list of rules applicable for a set of paths, including rules for any parent
*
* @param string[] $paths
* @param bool $cache whether to cache the retrieved rules
* @return array<string, Rule[]> sorted parent first
*/
public function preloadPaths(array $paths, bool $cache = true): array {
public function getRelevantRulesForPath(array $paths, bool $cache = true): array {
$allPaths = [];
foreach ($paths as $path) {
$allPaths = array_unique(array_merge($allPaths, $this->getParents($path)));
$allPaths = array_unique(array_merge($allPaths, $this->getRelevantPaths($path)));
}
return $this->getRules($allPaths, $cache);
}

public function getACLPermissionsForPath(string $path): int {
$path = ltrim($path, '/');
$rules = $this->getRules($this->getParents($path));
$rules = $this->getRules($this->getRelevantPaths($path));

return $this->calculatePermissionsForPath($path, $rules);
return $this->calculatePermissionsForPath($rules);
}

/**
* @param string $path
* @param array<string, Rule[]> $rules list of rules per path
* @return int
*/
public function getPermissionsForPathFromRules(string $path, array $rules): int {
$path = ltrim($path, '/');
$parents = $this->getParents($path);
// filter to only the rules we care about
$rules = $nonCachedPaths = array_intersect_key($rules, array_flip($parents));
return $this->calculatePermissionsForPath($path, $rules);
$relevantPaths = $this->getRelevantPaths($path);
$rules = array_intersect_key($rules, array_flip($relevantPaths));
return $this->calculatePermissionsForPath($rules);
}

private function calculatePermissionsForPath(string $path, array $rules): int {
/**
* @param array<string, Rule[]> $rules list of rules per path, sorted parent first
* @return int
*/
private function calculatePermissionsForPath(array $rules): int {
// first combine all rules with the same path, then apply them on top of the current permissions
// since $rules is sorted parent first rules for subfolders overwrite the rules from the parent
return array_reduce($rules, function (int $permissions, array $rules): int {
$mergedRule = Rule::mergeRules($rules);
return $mergedRule->applyPermissions($permissions);
Expand Down
8 changes: 8 additions & 0 deletions lib/ACL/Rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ public function getPermissions(): int {
return $this->permissions;
}

/**
* Apply this rule to an existing permission set, returning the resulting permissions
*
* All permissions included in the current mask will overwrite the existing permissions
*
* @param int $permissions
* @return int
*/
public function applyPermissions(int $permissions): int {
$invertedMask = ~$this->mask;
// create a bitmask that has all inherit and allow bits set to 1 and all deny bits to 0
Expand Down
2 changes: 1 addition & 1 deletion lib/Mount/MountProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader) {
return $this->getJailPath($folder['folder_id']);
}, $foldersWithAcl);
$aclManager = $this->aclManagerFactory->getACLManager($user, $this->getRootStorageId());
$rootRules = $aclManager->preloadPaths($aclRootPaths);
$rootRules = $aclManager->getRelevantRulesForPath($aclRootPaths);

return array_values(array_filter(array_map(function ($folder) use ($user, $loader, $conflicts, $aclManager, $rootRules) {
// check for existing files in the user home and rename them if needed
Expand Down

0 comments on commit 7cc2c44

Please sign in to comment.