Skip to content

Commit b7f2ab6

Browse files
ArtificialOwlemoral435
authored andcommitted
metadata must be set as editable for PROPPATCH
Signed-off-by: Maxence Lange <[email protected]> Signed-off-by: Eduardo Morales <[email protected]>
1 parent ba58f0a commit b7f2ab6

File tree

8 files changed

+265
-58
lines changed

8 files changed

+265
-58
lines changed

apps/dav/lib/Connector/Sabre/FilesPlugin.php

Lines changed: 110 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@
3535
namespace OCA\DAV\Connector\Sabre;
3636

3737
use OC\AppFramework\Http\Request;
38-
use OC\FilesMetadata\Model\MetadataValueWrapper;
3938
use OCP\Constants;
4039
use OCP\Files\ForbiddenException;
4140
use OCP\Files\StorageNotAvailableException;
41+
use OCP\FilesMetadata\Exceptions\FilesMetadataException;
4242
use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
4343
use OCP\FilesMetadata\IFilesMetadataManager;
4444
use OCP\FilesMetadata\Model\IMetadataValueWrapper;
@@ -530,65 +530,131 @@ public function handleUpdateProperties($path, PropPatch $propPatch) {
530530
return true;
531531
});
532532

533+
$this->handleUpdatePropertiesMetadata($propPatch, $node);
533534

534-
/** @var IFilesMetadataManager */
535-
$filesMetadataManager = \OCP\Server::get(IFilesMetadataManager::class);
535+
/**
536+
* Disable modification of the displayname property for files and
537+
* folders via PROPPATCH. See PROPFIND for more information.
538+
*/
539+
$propPatch->handle(self::DISPLAYNAME_PROPERTYNAME, function ($displayName) {
540+
return 403;
541+
});
542+
}
543+
544+
545+
/**
546+
* handle the update of metadata from PROPPATCH requests
547+
*
548+
* @param PropPatch $propPatch
549+
* @param Node $node
550+
*
551+
* @throws FilesMetadataException
552+
*/
553+
private function handleUpdatePropertiesMetadata(PropPatch $propPatch, Node $node): void {
554+
$userId = $this->userSession->getUser()?->getUID();
555+
if (null === $userId) {
556+
return;
557+
}
558+
559+
$accessRight = $this->getMetadataFileAccessRight($node, $userId);
560+
$filesMetadataManager = $this->initFilesMetadataManager();
536561
$knownMetadata = $filesMetadataManager->getKnownMetadata();
537562

538563
foreach ($propPatch->getRemainingMutations() as $mutation) {
539564
if (!str_starts_with($mutation, self::FILE_METADATA_PREFIX)) {
540565
continue;
541566
}
542567

543-
$propPatch->handle($mutation, function (mixed $value) use ($knownMetadata, $node, $mutation, $filesMetadataManager): bool {
544-
$metadata = $filesMetadataManager->getMetadata((int)$node->getFileId(), true);
545-
$metadataKey = substr($mutation, strlen(self::FILE_METADATA_PREFIX));
568+
$propPatch->handle(
569+
$mutation,
570+
function (mixed $value) use ($accessRight, $knownMetadata, $node, $mutation, $filesMetadataManager): bool {
571+
$metadata = $filesMetadataManager->getMetadata((int)$node->getFileId(), true);
572+
$metadataKey = substr($mutation, strlen(self::FILE_METADATA_PREFIX));
546573

547-
// If the metadata is unknown, it defaults to string.
548-
try {
549-
$type = $knownMetadata->getType($metadataKey);
550-
} catch (FilesMetadataNotFoundException) {
551-
$type = IMetadataValueWrapper::TYPE_STRING;
552-
}
574+
// confirm metadata key is editable via PROPPATCH
575+
if ($knownMetadata->getEditPermission($metadataKey) < $accessRight) {
576+
throw new FilesMetadataException('you do not have enough rights to update \'' . $metadataKey . '\' on this node');
577+
}
578+
579+
// If the metadata is unknown, it defaults to string.
580+
try {
581+
$type = $knownMetadata->getType($metadataKey);
582+
} catch (FilesMetadataNotFoundException) {
583+
$type = IMetadataValueWrapper::TYPE_STRING;
584+
}
585+
586+
switch ($type) {
587+
case IMetadataValueWrapper::TYPE_STRING:
588+
$metadata->setString($metadataKey, $value, $knownMetadata->isIndex($metadataKey));
589+
break;
590+
case IMetadataValueWrapper::TYPE_INT:
591+
$metadata->setInt($metadataKey, $value, $knownMetadata->isIndex($metadataKey));
592+
break;
593+
case IMetadataValueWrapper::TYPE_FLOAT:
594+
$metadata->setFloat($metadataKey, $value);
595+
break;
596+
case IMetadataValueWrapper::TYPE_BOOL:
597+
$metadata->setBool($metadataKey, $value, $knownMetadata->isIndex($metadataKey));
598+
break;
599+
case IMetadataValueWrapper::TYPE_ARRAY:
600+
$metadata->setArray($metadataKey, $value);
601+
break;
602+
case IMetadataValueWrapper::TYPE_STRING_LIST:
603+
$metadata->setStringList(
604+
$metadataKey, $value, $knownMetadata->isIndex($metadataKey)
605+
);
606+
break;
607+
case IMetadataValueWrapper::TYPE_INT_LIST:
608+
$metadata->setIntList(
609+
$metadataKey, $value, $knownMetadata->isIndex($metadataKey)
610+
);
611+
break;
612+
}
613+
614+
$filesMetadataManager->saveMetadata($metadata);
553615

554-
switch ($type) {
555-
case IMetadataValueWrapper::TYPE_STRING:
556-
$metadata->setString($metadataKey, $value, $knownMetadata->isIndex($metadataKey));
557-
break;
558-
case IMetadataValueWrapper::TYPE_INT:
559-
$metadata->setInt($metadataKey, $value, $knownMetadata->isIndex($metadataKey));
560-
break;
561-
case IMetadataValueWrapper::TYPE_FLOAT:
562-
$metadata->setFloat($metadataKey, $value);
563-
break;
564-
case IMetadataValueWrapper::TYPE_BOOL:
565-
$metadata->setBool($metadataKey, $value, $knownMetadata->isIndex($metadataKey));
566-
break;
567-
case IMetadataValueWrapper::TYPE_ARRAY:
568-
$metadata->setArray($metadataKey, $value);
569-
break;
570-
case IMetadataValueWrapper::TYPE_STRING_LIST:
571-
$metadata->setStringList($metadataKey, $value, $knownMetadata->isIndex($metadataKey));
572-
break;
573-
case IMetadataValueWrapper::TYPE_INT_LIST:
574-
$metadata->setIntList($metadataKey, $value, $knownMetadata->isIndex($metadataKey));
575-
break;
616+
return true;
576617
}
618+
);
619+
}
620+
}
577621

578-
$filesMetadataManager->saveMetadata($metadata);
579-
return true;
580-
});
622+
/**
623+
* init default internal metadata
624+
*
625+
* @return IFilesMetadataManager
626+
*/
627+
private function initFilesMetadataManager(): IFilesMetadataManager {
628+
/** @var IFilesMetadataManager $manager */
629+
$manager = \OCP\Server::get(IFilesMetadataManager::class);
630+
$manager->initMetadata('files-live-photo', IMetadataValueWrapper::TYPE_STRING, false, IMetadataValueWrapper::EDIT_REQ_OWNERSHIP);
631+
632+
return $manager;
633+
}
634+
635+
/**
636+
* based on owner and shares, returns the bottom limit to update related metadata
637+
*
638+
* @param Node $node
639+
* @param string $userId
640+
*
641+
* @return int
642+
*/
643+
private function getMetadataFileAccessRight(Node $node, string $userId): int {
644+
if ($node->getOwner()?->getUID() === $userId) {
645+
return IMetadataValueWrapper::EDIT_REQ_OWNERSHIP;
646+
} else {
647+
$filePermissions = $node->getSharePermissions($userId);
648+
if ($filePermissions & Constants::PERMISSION_UPDATE) {
649+
return IMetadataValueWrapper::EDIT_REQ_WRITE_PERMISSION;
650+
}
581651
}
582652

583-
/**
584-
* Disable modification of the displayname property for files and
585-
* folders via PROPPATCH. See PROPFIND for more information.
586-
*/
587-
$propPatch->handle(self::DISPLAYNAME_PROPERTYNAME, function ($displayName) {
588-
return 403;
589-
});
653+
return IMetadataValueWrapper::EDIT_REQ_READ_PERMISSION;
590654
}
591655

656+
657+
592658
/**
593659
* @param string $filePath
594660
* @param \Sabre\DAV\INode $node

lib/private/FilesMetadata/FilesMetadataManager.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ public function getKnownMetadata(): IFilesMetadata {
254254
* @param string $key metadata key
255255
* @param string $type metadata type
256256
* @param bool $indexed TRUE if metadata can be search
257+
* @param int $editPermission remote edit permission via Webdav PROPPATCH
257258
*
258259
* @inheritDoc
259260
* @since 28.0.0
@@ -264,19 +265,31 @@ public function getKnownMetadata(): IFilesMetadata {
264265
* @see IMetadataValueWrapper::TYPE_STRING_LIST
265266
* @see IMetadataValueWrapper::TYPE_INT_LIST
266267
* @see IMetadataValueWrapper::TYPE_STRING
268+
* @see IMetadataValueWrapper::EDIT_FORBIDDEN
269+
* @see IMetadataValueWrapper::EDIT_REQ_OWNERSHIP
270+
* @see IMetadataValueWrapper::EDIT_REQ_WRITE_PERMISSION
271+
* @see IMetadataValueWrapper::EDIT_REQ_READ_PERMISSION
267272
*/
268-
public function initMetadata(string $key, string $type, bool $indexed): void {
273+
public function initMetadata(
274+
string $key,
275+
string $type,
276+
bool $indexed = false,
277+
int $editPermission = IMetadataValueWrapper::EDIT_FORBIDDEN
278+
): void {
269279
$current = $this->getKnownMetadata();
270280
try {
271-
if ($current->getType($key) === $type && $indexed === $current->isIndex($key)) {
281+
if ($current->getType($key) === $type
282+
&& $indexed === $current->isIndex($key)
283+
&& $editPermission === $current->getEditPermission($key)) {
272284
return; // if key exists, with same type and indexed, we do nothing.
273285
}
274286
} catch (FilesMetadataNotFoundException) {
275287
// if value does not exist, we keep on the writing of course
276288
}
277289

278-
$current->import([$key => ['type' => $type, 'indexed' => $indexed]]);
290+
$current->import([$key => ['type' => $type, 'indexed' => $indexed, 'editPermission' => $editPermission]]);
279291
$this->config->setAppValue('core', self::CONFIG_KEY, json_encode($current));
292+
$this->all = $current;
280293
}
281294

282295
/**

lib/private/FilesMetadata/MetadataQuery.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public function joinIndex(string $metadataKey, bool $enforce = false): string {
104104
$andX->add($expr->eq($this->getMetadataKeyField($metadataKey), $this->queryBuilder->createNamedParameter($metadataKey)));
105105

106106
if ($enforce) {
107-
$this->queryBuilder->rightJoin(
107+
$this->queryBuilder->innerJoin(
108108
$this->fileTableAlias,
109109
IndexRequestService::TABLE_METADATA_INDEX,
110110
$aliasIndex,
@@ -125,7 +125,7 @@ public function joinIndex(string $metadataKey, bool $enforce = false): string {
125125
/**
126126
* @throws FilesMetadataNotFoundException
127127
*/
128-
public function joinedTableAlias(string $metadataKey): string {
128+
private function joinedTableAlias(string $metadataKey): string {
129129
if (!array_key_exists($metadataKey, $this->knownJoinedIndex)) {
130130
throw new FilesMetadataNotFoundException('table related to ' . $metadataKey . ' not initiated, you need to use leftJoin() first.');
131131
}

lib/private/FilesMetadata/Model/FilesMetadata.php

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,38 @@ public function isIndex(string $key): bool {
124124
return $this->metadata[$key]?->isIndexed() ?? false;
125125
}
126126

127+
/**
128+
* @param string $key metadata key
129+
*
130+
* @inheritDoc
131+
* @return int edit permission
132+
* @throws FilesMetadataNotFoundException
133+
* @since 28.0.0
134+
*/
135+
public function getEditPermission(string $key): int {
136+
if (!array_key_exists($key, $this->metadata)) {
137+
throw new FilesMetadataNotFoundException();
138+
}
139+
140+
return $this->metadata[$key]->getEditPermission();
141+
}
142+
143+
/**
144+
* @param string $key metadata key
145+
* @param int $permission edit permission
146+
*
147+
* @inheritDoc
148+
* @throws FilesMetadataNotFoundException
149+
* @since 28.0.0
150+
*/
151+
public function setEditPermission(string $key, int $permission): void {
152+
if (!array_key_exists($key, $this->metadata)) {
153+
throw new FilesMetadataNotFoundException();
154+
}
155+
156+
$this->metadata[$key]->setEditPermission($permission);
157+
}
158+
127159
/**
128160
* @param string $key metadata key
129161
*
@@ -582,7 +614,7 @@ public function importFromDatabase(array $data, string $prefix = ''): IFilesMeta
582614
JSON_THROW_ON_ERROR
583615
)
584616
);
585-
} catch (JsonException $e) {
617+
} catch (JsonException) {
586618
throw new FilesMetadataNotFoundException();
587619
}
588620
}

lib/private/FilesMetadata/Model/MetadataValueWrapper.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class MetadataValueWrapper implements IMetadataValueWrapper {
3939
/** @var string|int|float|bool|array|string[]|int[] */
4040
private mixed $value = null;
4141
private bool $indexed = false;
42+
private int $editPermission = self::EDIT_FORBIDDEN;
4243

4344
/**
4445
* @param string $type value type
@@ -371,6 +372,28 @@ public function isIndexed(): bool {
371372
return $this->indexed;
372373
}
373374

375+
/**
376+
* @param int $permission edit permission
377+
*
378+
* @inheritDoc
379+
* @return self
380+
* @since 28.0.0
381+
*/
382+
public function setEditPermission(int $permission): self {
383+
$this->editPermission = $permission;
384+
385+
return $this;
386+
}
387+
388+
/**
389+
* @inheritDoc
390+
* @return int edit permission
391+
* @since 28.0.0
392+
*/
393+
public function getEditPermission(): int {
394+
return $this->editPermission;
395+
}
396+
374397
/**
375398
* @param array $data serialized version of the object
376399
*
@@ -383,15 +406,16 @@ public function import(array $data): self {
383406
$this->value = $data['value'] ?? null;
384407
$this->type = $data['type'] ?? '';
385408
$this->setIndexed($data['indexed'] ?? false);
386-
409+
$this->setEditPermission($data['editPermission'] ?? self::EDIT_FORBIDDEN);
387410
return $this;
388411
}
389412

390413
public function jsonSerialize(bool $emptyValues = false): array {
391414
return [
392415
'value' => ($emptyValues) ? null : $this->value,
393416
'type' => $this->getType(),
394-
'indexed' => $this->isIndexed()
417+
'indexed' => $this->isIndexed(),
418+
'editPermission' => $this->getEditPermission()
395419
];
396420
}
397421
}

lib/public/FilesMetadata/IFilesMetadataManager.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use OCP\FilesMetadata\Exceptions\FilesMetadataException;
3131
use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
3232
use OCP\FilesMetadata\Model\IFilesMetadata;
33+
use OCP\FilesMetadata\Model\IMetadataValueWrapper;
3334

3435
/**
3536
* Manager for FilesMetadata; manage files' metadata.
@@ -133,7 +134,20 @@ public function getKnownMetadata(): IFilesMetadata;
133134
* @param string $key metadata key
134135
* @param string $type metadata type
135136
* @param bool $indexed TRUE if metadata can be search
137+
* @param int $editPermission remote edit permission via Webdav PROPPATCH
138+
*
139+
* @see IMetadataValueWrapper::TYPE_INT
140+
* @see IMetadataValueWrapper::TYPE_FLOAT
141+
* @see IMetadataValueWrapper::TYPE_BOOL
142+
* @see IMetadataValueWrapper::TYPE_ARRAY
143+
* @see IMetadataValueWrapper::TYPE_STRING_LIST
144+
* @see IMetadataValueWrapper::TYPE_INT_LIST
145+
* @see IMetadataValueWrapper::TYPE_STRING
146+
* @see IMetadataValueWrapper::EDIT_FORBIDDEN
147+
* @see IMetadataValueWrapper::EDIT_REQ_OWNERSHIP
148+
* @see IMetadataValueWrapper::EDIT_REQ_WRITE_PERMISSION
149+
* @see IMetadataValueWrapper::EDIT_REQ_READ_PERMISSION
136150
* @since 28.0.0
137151
*/
138-
public function initMetadata(string $key, string $type, bool $indexed): void;
152+
public function initMetadata(string $key, string $type, bool $indexed, int $editPermission): void;
139153
}

0 commit comments

Comments
 (0)