Skip to content

Commit 2d813f1

Browse files
authored
Merge pull request #51682 from nextcloud/fix/tag-fileid-check
fix(dav): filter user files when updating tags
2 parents 248d21a + 6fc4535 commit 2d813f1

File tree

4 files changed

+72
-2
lines changed

4 files changed

+72
-2
lines changed

apps/dav/lib/SystemTag/SystemTagPlugin.php

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use OCA\DAV\Connector\Sabre\FilesPlugin;
1212
use OCA\DAV\Connector\Sabre\Node;
1313
use OCP\AppFramework\Http;
14+
use OCP\Constants;
15+
use OCP\Files\IRootFolder;
1416
use OCP\IGroupManager;
1517
use OCP\IUser;
1618
use OCP\IUserSession;
@@ -68,7 +70,8 @@ public function __construct(
6870
protected ISystemTagManager $tagManager,
6971
protected IGroupManager $groupManager,
7072
protected IUserSession $userSession,
71-
private ISystemTagObjectMapper $tagMapper,
73+
protected IRootFolder $rootFolder,
74+
protected ISystemTagObjectMapper $tagMapper,
7275
) {
7376
}
7477

@@ -387,6 +390,11 @@ public function handleUpdateProperties($path, PropPatch $propPatch) {
387390
}
388391

389392
if (isset($props[self::OBJECTIDS_PROPERTYNAME])) {
393+
$user = $this->userSession->getUser();
394+
if (!$user) {
395+
throw new Forbidden('You don’t have permissions to update tags');
396+
}
397+
390398
$propValue = $props[self::OBJECTIDS_PROPERTYNAME];
391399
if (!$propValue instanceof SystemTagsObjectList || count($propValue->getObjects()) === 0) {
392400
throw new BadRequest('Invalid object-ids property');
@@ -399,10 +407,35 @@ public function handleUpdateProperties($path, PropPatch $propPatch) {
399407
throw new BadRequest('Invalid object-ids property. All object types must be of the same type: ' . $node->getName());
400408
}
401409

410+
// Only files are supported at the moment
411+
// Also see SystemTagsRelationsCollection file
412+
if ($objectTypes[0] !== 'files') {
413+
throw new BadRequest('Invalid object-ids property type. Only files are supported');
414+
}
415+
416+
// Get all current tagged objects
417+
$taggedObjects = $this->tagMapper->getObjectIdsForTags([$node->getSystemTag()->getId()], 'files');
418+
$toAddObjects = array_map(fn ($value) => (string)$value, array_keys($objects));
419+
420+
// Compute the tags to add and remove
421+
$addedObjects = array_values(array_diff($toAddObjects, $taggedObjects));
422+
$removedObjects = array_values(array_diff($taggedObjects, $toAddObjects));
423+
424+
// Check permissions for each object to be freshly tagged or untagged
425+
if (!$this->canUpdateTagForFileIds(array_merge($addedObjects, $removedObjects))) {
426+
throw new Forbidden('You don’t have permissions to update tags');
427+
}
428+
402429
$this->tagMapper->setObjectIdsForTag($node->getSystemTag()->getId(), $node->getName(), array_keys($objects));
403430
}
404431

405432
if ($props[self::OBJECTIDS_PROPERTYNAME] === null) {
433+
// Check the user have permissions to remove the tag from all currently tagged objects
434+
$taggedObjects = $this->tagMapper->getObjectIdsForTags([$node->getSystemTag()->getId()], 'files');
435+
if (!$this->canUpdateTagForFileIds($taggedObjects)) {
436+
throw new Forbidden('You don’t have permissions to update tags');
437+
}
438+
406439
$this->tagMapper->setObjectIdsForTag($node->getSystemTag()->getId(), $node->getName(), []);
407440
}
408441

@@ -483,4 +516,24 @@ public function handleUpdateProperties($path, PropPatch $propPatch) {
483516
return true;
484517
});
485518
}
519+
520+
/**
521+
* Check if the user can update the tag for the given file ids
522+
*
523+
* @param list<string> $fileIds
524+
* @return bool
525+
*/
526+
private function canUpdateTagForFileIds(array $fileIds): bool {
527+
$user = $this->userSession->getUser();
528+
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
529+
foreach ($fileIds as $fileId) {
530+
$nodes = $userFolder->getById((int)$fileId);
531+
foreach ($nodes as $node) {
532+
if (($node->getPermissions() & Constants::PERMISSION_UPDATE) === Constants::PERMISSION_UPDATE) {
533+
return true;
534+
}
535+
}
536+
}
537+
return false;
538+
}
486539
}

apps/dav/lib/SystemTag/SystemTagsObjectList.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ public function __construct(
3131
) {
3232
}
3333

34+
/**
35+
* Get the object ids and their types.
36+
*
37+
* @return array<string, string>
38+
*/
3439
public function getObjects(): array {
3540
return $this->objects;
3641
}
@@ -55,7 +60,7 @@ public static function xmlDeserialize(Reader $reader) {
5560
}
5661
}
5762
if ($id !== '' && $type !== '') {
58-
$objects[$id] = $type;
63+
$objects[(string)$id] = (string)$type;
5964
}
6065
}
6166
}

apps/dav/lib/SystemTag/SystemTagsRelationsCollection.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public function __construct(
2828
IRootFolder $rootFolder,
2929
) {
3030
$children = [
31+
// Only files are supported at the moment
32+
// Also see SystemTagPlugin::OBJECTIDS_PROPERTYNAME supported types
3133
new SystemTagsObjectTypeCollection(
3234
'files',
3335
$tagManager,

apps/dav/tests/unit/SystemTag/SystemTagPluginTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use OCA\DAV\SystemTag\SystemTagPlugin;
1313
use OCA\DAV\SystemTag\SystemTagsByIdCollection;
1414
use OCA\DAV\SystemTag\SystemTagsObjectMappingCollection;
15+
use OCP\Files\IRootFolder;
1516
use OCP\IGroupManager;
1617
use OCP\IUser;
1718
use OCP\IUserSession;
@@ -56,6 +57,11 @@ class SystemTagPluginTest extends \Test\TestCase {
5657
*/
5758
private $userSession;
5859

60+
/**
61+
* @var IRootFolder
62+
*/
63+
private $rootFolder;
64+
5965
/**
6066
* @var IUser
6167
*/
@@ -95,13 +101,17 @@ protected function setUp(): void {
95101
->expects($this->any())
96102
->method('isLoggedIn')
97103
->willReturn(true);
104+
98105
$this->tagMapper = $this->getMockBuilder(ISystemTagObjectMapper::class)
99106
->getMock();
107+
$this->rootFolder = $this->getMockBuilder(IRootFolder::class)
108+
->getMock();
100109

101110
$this->plugin = new SystemTagPlugin(
102111
$this->tagManager,
103112
$this->groupManager,
104113
$this->userSession,
114+
$this->rootFolder,
105115
$this->tagMapper
106116
);
107117
$this->plugin->initialize($this->server);

0 commit comments

Comments
 (0)