diff --git a/lib/ACL/ACLManager.php b/lib/ACL/ACLManager.php index 00055f14a..caa7c7035 100644 --- a/lib/ACL/ACLManager.php +++ b/lib/ACL/ACLManager.php @@ -24,6 +24,7 @@ namespace OCA\GroupFolders\ACL; use OC\Cache\CappedMemoryCache; +use OCA\GroupFolders\ACL\UserMapping\IUserMappingManager; use OCA\GroupFolders\Trash\TrashManager; use OCP\Constants; use OCP\Files\IRootFolder; @@ -35,12 +36,13 @@ class ACLManager { private $rootFolderProvider; public function __construct( - private RuleManager $ruleManager, - private TrashManager $trashManager, - private IUser $user, - callable $rootFolderProvider, - private ?int $rootStorageId = null, - private bool $inheritMergePerUser = false, + private RuleManager $ruleManager, + private TrashManager $trashManager, + private IUserMappingManager $userMappingManager, + private IUser $user, + callable $rootFolderProvider, + private ?int $rootStorageId = null, + private bool $inheritMergePerUser = false, ) { $this->ruleCache = new CappedMemoryCache(); $this->rootFolderProvider = $rootFolderProvider; @@ -104,7 +106,7 @@ private function getRelevantPaths(string $path): array { $fromTrashbin = str_starts_with($path, '__groupfolders/trash/'); if ($fromTrashbin) { /* Exploded path will look like ["__groupfolders", "trash", "1", "folderName.d2345678", "rest/of/the/path.txt"] */ - [,,$groupFolderId,$rootTrashedItemName] = explode('/', $path, 5); + [, , $groupFolderId, $rootTrashedItemName] = explode('/', $path, 5); $groupFolderId = (int)$groupFolderId; /* Remove the date part */ $separatorPos = strrpos($rootTrashedItemName, '.d'); @@ -152,6 +154,22 @@ public function getACLPermissionsForPath(string $path): int { return $this->calculatePermissionsForPath($rules); } + /** + * Check what the effective permissions would be for the current user for a path would be with a new set of rules + * + * @param string $path + * @param array $newRules + * @return int + */ + public function testACLPermissionsForPath(string $path, array $newRules): int { + $path = ltrim($path, '/'); + $rules = $this->getRules($this->getRelevantPaths($path)); + + $rules[$path] = $this->filterApplicableRulesToUser($newRules); + + return $this->calculatePermissionsForPath($rules); + } + /** * @param string $path * @param array $rules list of rules per path @@ -235,4 +253,25 @@ public function getPermissionsForTree(string $path): int { return $permissions & $denyMask; }, Constants::PERMISSION_ALL); } + + /** + * Filter a list to only the rules applicable to the current user + * + * @param Rule[] $rules + * @return Rule[] + */ + private function filterApplicableRulesToUser(array $rules): array { + $userMappings = $this->userMappingManager->getMappingsForUser($this->user); + return array_values(array_filter($rules, function(Rule $rule) use ($userMappings) { + foreach ($userMappings as $userMapping) { + if ( + $userMapping->getType() == $rule->getUserMapping()->getType() && + $userMapping->getId() == $rule->getUserMapping()->getId() + ) { + return true; + } + } + return false; + })); + } } diff --git a/lib/ACL/ACLManagerFactory.php b/lib/ACL/ACLManagerFactory.php index 11ea7ddfb..0a1f1979e 100644 --- a/lib/ACL/ACLManagerFactory.php +++ b/lib/ACL/ACLManagerFactory.php @@ -23,6 +23,7 @@ namespace OCA\GroupFolders\ACL; +use OCA\GroupFolders\ACL\UserMapping\IUserMappingManager; use OCA\GroupFolders\Trash\TrashManager; use OCP\IConfig; use OCP\IUser; @@ -34,6 +35,7 @@ public function __construct( private RuleManager $ruleManager, private TrashManager $trashManager, private IConfig $config, + private IUserMappingManager $userMappingManager, callable $rootFolderProvider, ) { $this->rootFolderProvider = $rootFolderProvider; @@ -43,6 +45,7 @@ public function getACLManager(IUser $user, ?int $rootStorageId = null): ACLManag return new ACLManager( $this->ruleManager, $this->trashManager, + $this->userMappingManager, $user, $this->rootFolderProvider, $rootStorageId, diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 46f82cf42..44d661010 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -225,6 +225,7 @@ public function register(IRegistrationContext $context): void { $c->get(RuleManager::class), $c->get(TrashManager::class), $c->get(IConfig::class), + $c->get(IUserMappingManager::class), $rootFolderProvider ); }); diff --git a/lib/DAV/ACLPlugin.php b/lib/DAV/ACLPlugin.php index 2d99f714c..aab9d98f5 100644 --- a/lib/DAV/ACLPlugin.php +++ b/lib/DAV/ACLPlugin.php @@ -24,6 +24,8 @@ namespace OCA\GroupFolders\DAV; use OCA\DAV\Connector\Sabre\Node; +use OCA\GroupFolders\ACL\ACLManager; +use OCA\GroupFolders\ACL\ACLManagerFactory; use OCA\GroupFolders\ACL\Rule; use OCA\GroupFolders\ACL\RuleManager; use OCA\GroupFolders\Folder\FolderManager; @@ -31,6 +33,7 @@ use OCP\Constants; use OCP\IUser; use OCP\IUserSession; +use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\INode; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; @@ -46,19 +49,14 @@ class ACLPlugin extends ServerPlugin { public const GROUP_FOLDER_ID = '{http://nextcloud.org/ns}group-folder-id'; private ?Server $server = null; - private RuleManager $ruleManager; - private FolderManager $folderManager; - private IUserSession $userSession; private ?IUser $user = null; public function __construct( - RuleManager $ruleManager, - IUserSession $userSession, - FolderManager $folderManager + private RuleManager $ruleManager, + private IUserSession $userSession, + private FolderManager $folderManager, + private ACLManagerFactory $aclManagerFactory, ) { - $this->ruleManager = $ruleManager; - $this->userSession = $userSession; - $this->folderManager = $folderManager; } private function isAdmin(string $path): bool { @@ -72,7 +70,7 @@ private function isAdmin(string $path): bool { public function initialize(Server $server): void { $this->server = $server; - $this->user = $user = $this->userSession->getUser(); + $this->user = $this->userSession->getUser(); $this->server->on('propFind', [$this, 'propFind']); $this->server->on('propPatch', [$this, 'propPatch']); @@ -211,6 +209,12 @@ public function propPatch(string $path, PropPatch $propPatch): void { ); }, $rawRules); + $aclManager = $this->aclManagerFactory->getACLManager($this->user); + $newPermissions = $aclManager->testACLPermissionsForPath($fileInfo->getPath(), $rules); + if (!($newPermissions & Constants::PERMISSION_READ)) { + throw new BadRequest("Request would revoke permissions for current user"); + } + $existingRules = array_reduce( $this->ruleManager->getAllRulesForPaths($mount->getNumericStorageId(), [$path]), function (array $rules, array $rulesForPath) {