aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorskjnldsv <skjnldsv@protonmail.com>2025-03-25 11:24:43 +0100
committerskjnldsv <skjnldsv@protonmail.com>2025-03-25 11:50:36 +0100
commit903971fddcdaa850d00edeec5927a3591767efdd (patch)
tree3b738d84c88012e9b1a249e1e1b96c4b6fa88883
parenta55519268fafdd21c834a705c64ff2a27369aae7 (diff)
downloadnextcloud-server-fix/tag-fileid-check.tar.gz
nextcloud-server-fix/tag-fileid-check.zip
fix(dav): filter user files when updating tagsfix/tag-fileid-check
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
-rw-r--r--apps/dav/lib/SystemTag/SystemTagPlugin.php53
-rw-r--r--apps/dav/lib/SystemTag/SystemTagsObjectList.php7
-rw-r--r--apps/dav/lib/SystemTag/SystemTagsRelationsCollection.php2
-rw-r--r--apps/dav/tests/unit/SystemTag/SystemTagPluginTest.php10
4 files changed, 70 insertions, 2 deletions
diff --git a/apps/dav/lib/SystemTag/SystemTagPlugin.php b/apps/dav/lib/SystemTag/SystemTagPlugin.php
index d70eff17ac1..cbc9c6c1882 100644
--- a/apps/dav/lib/SystemTag/SystemTagPlugin.php
+++ b/apps/dav/lib/SystemTag/SystemTagPlugin.php
@@ -11,6 +11,8 @@ use OCA\DAV\Connector\Sabre\Directory;
use OCA\DAV\Connector\Sabre\FilesPlugin;
use OCA\DAV\Connector\Sabre\Node;
use OCP\AppFramework\Http;
+use OCP\Constants;
+use OCP\Files\IRootFolder;
use OCP\IGroupManager;
use OCP\IUser;
use OCP\IUserSession;
@@ -68,7 +70,8 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
protected ISystemTagManager $tagManager,
protected IGroupManager $groupManager,
protected IUserSession $userSession,
- private ISystemTagObjectMapper $tagMapper,
+ protected IRootFolder $rootFolder,
+ protected ISystemTagObjectMapper $tagMapper,
) {
}
@@ -387,6 +390,11 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
}
if (isset($props[self::OBJECTIDS_PROPERTYNAME])) {
+ $user = $this->userSession->getUser();
+ if (!$user) {
+ throw new Forbidden('You don’t have permissions to update tags');
+ }
+
$propValue = $props[self::OBJECTIDS_PROPERTYNAME];
if (!$propValue instanceof SystemTagsObjectList || count($propValue->getObjects()) === 0) {
throw new BadRequest('Invalid object-ids property');
@@ -399,10 +407,35 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
throw new BadRequest('Invalid object-ids property. All object types must be of the same type: ' . $node->getName());
}
+ // Only files are supported at the moment
+ // Also see SystemTagsRelationsCollection file
+ if ($objectTypes[0] !== 'files') {
+ throw new BadRequest('Invalid object-ids property type. Only files are supported');
+ }
+
+ // Get all current tagged objects
+ $taggedObjects = $this->tagMapper->getObjectIdsForTags([$node->getSystemTag()->getId()], 'files');
+ $toAddObjects = array_map(fn ($value) => (string)$value, array_keys($objects));
+
+ // Compute the tags to add and remove
+ $addedObjects = array_values(array_diff($toAddObjects, $taggedObjects));
+ $removedObjects = array_values(array_diff($taggedObjects, $toAddObjects));
+
+ // Check permissions for each object to be freshly tagged or untagged
+ if (!$this->canUpdateTagForFileIds(array_merge($addedObjects, $removedObjects))) {
+ throw new Forbidden('You don’t have permissions to update tags');
+ }
+
$this->tagMapper->setObjectIdsForTag($node->getSystemTag()->getId(), $node->getName(), array_keys($objects));
}
if ($props[self::OBJECTIDS_PROPERTYNAME] === null) {
+ // Check the user have permissions to remove the tag from all currently tagged objects
+ $taggedObjects = $this->tagMapper->getObjectIdsForTags([$node->getSystemTag()->getId()], 'files');
+ if (!$this->canUpdateTagForFileIds($taggedObjects)) {
+ throw new Forbidden('You don’t have permissions to update tags');
+ }
+
$this->tagMapper->setObjectIdsForTag($node->getSystemTag()->getId(), $node->getName(), []);
}
@@ -483,4 +516,22 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
return true;
});
}
+
+ /**
+ * Check if the user can update the tag for the given file ids
+ *
+ * @param list<string> $fileIds
+ * @return bool
+ */
+ private function canUpdateTagForFileIds(array $fileIds): bool {
+ $user = $this->userSession->getUser();
+ $userFolder = $this->rootFolder->getUserFolder($user->getUID());
+ foreach ($fileIds as $fileId) {
+ $userNode = $userFolder->getFirstNodeById((int)$fileId);
+ if (empty($userNode) || !(($userNode->getPermissions() & Constants::PERMISSION_UPDATE) === Constants::PERMISSION_UPDATE)) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/apps/dav/lib/SystemTag/SystemTagsObjectList.php b/apps/dav/lib/SystemTag/SystemTagsObjectList.php
index 0743b8d8130..5ccc924eb53 100644
--- a/apps/dav/lib/SystemTag/SystemTagsObjectList.php
+++ b/apps/dav/lib/SystemTag/SystemTagsObjectList.php
@@ -31,6 +31,11 @@ class SystemTagsObjectList implements XmlSerializable, XmlDeserializable {
) {
}
+ /**
+ * Get the object ids and their types.
+ *
+ * @return array<string, string>
+ */
public function getObjects(): array {
return $this->objects;
}
@@ -55,7 +60,7 @@ class SystemTagsObjectList implements XmlSerializable, XmlDeserializable {
}
}
if ($id !== '' && $type !== '') {
- $objects[$id] = $type;
+ $objects[(string)$id] = (string)$type;
}
}
}
diff --git a/apps/dav/lib/SystemTag/SystemTagsRelationsCollection.php b/apps/dav/lib/SystemTag/SystemTagsRelationsCollection.php
index dc0f1bc5d36..0839a5bc995 100644
--- a/apps/dav/lib/SystemTag/SystemTagsRelationsCollection.php
+++ b/apps/dav/lib/SystemTag/SystemTagsRelationsCollection.php
@@ -28,6 +28,8 @@ class SystemTagsRelationsCollection extends SimpleCollection {
IRootFolder $rootFolder,
) {
$children = [
+ // Only files are supported at the moment
+ // Also see SystemTagPlugin::OBJECTIDS_PROPERTYNAME supported types
new SystemTagsObjectTypeCollection(
'files',
$tagManager,
diff --git a/apps/dav/tests/unit/SystemTag/SystemTagPluginTest.php b/apps/dav/tests/unit/SystemTag/SystemTagPluginTest.php
index e1517eec425..ab5253147a7 100644
--- a/apps/dav/tests/unit/SystemTag/SystemTagPluginTest.php
+++ b/apps/dav/tests/unit/SystemTag/SystemTagPluginTest.php
@@ -12,6 +12,7 @@ use OCA\DAV\SystemTag\SystemTagNode;
use OCA\DAV\SystemTag\SystemTagPlugin;
use OCA\DAV\SystemTag\SystemTagsByIdCollection;
use OCA\DAV\SystemTag\SystemTagsObjectMappingCollection;
+use OCP\Files\IRootFolder;
use OCP\IGroupManager;
use OCP\IUser;
use OCP\IUserSession;
@@ -57,6 +58,11 @@ class SystemTagPluginTest extends \Test\TestCase {
private $userSession;
/**
+ * @var IRootFolder
+ */
+ private $rootFolder;
+
+ /**
* @var IUser
*/
private $user;
@@ -95,13 +101,17 @@ class SystemTagPluginTest extends \Test\TestCase {
->expects($this->any())
->method('isLoggedIn')
->willReturn(true);
+
$this->tagMapper = $this->getMockBuilder(ISystemTagObjectMapper::class)
->getMock();
+ $this->rootFolder = $this->getMockBuilder(IRootFolder::class)
+ ->getMock();
$this->plugin = new SystemTagPlugin(
$this->tagManager,
$this->groupManager,
$this->userSession,
+ $this->rootFolder,
$this->tagMapper
);
$this->plugin->initialize($this->server);