This commit is contained in:
backportbot[bot] 2026-04-03 03:30:39 +00:00 committed by GitHub
commit 9efc271fb5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 72 additions and 25 deletions

View file

@ -76,7 +76,7 @@ class BulkUploadPlugin extends ServerPlugin {
'error' => false,
'etag' => $node->getETag(),
'fileid' => DavUtil::getDavFileId($node->getId()),
'permissions' => DavUtil::getDavPermissions($node),
'permissions' => DavUtil::getDavPermissions($node, $node->getParent()),
];
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['path' => $headers['x-file-path']]);

View file

@ -207,10 +207,19 @@ class FilesPlugin extends ServerPlugin {
// First check copyable (move only needs additional delete permission)
$this->checkCopy($source, $target);
// The source needs to be deletable for moving
$sourceNodeFileInfo = $sourceNode->getFileInfo();
if (!$sourceNodeFileInfo->isDeletable()) {
throw new Forbidden($source . ' cannot be deleted');
[$sourceDir] = \Sabre\Uri\split($source);
[$destinationDir, ] = \Sabre\Uri\split($target);
if ($sourceDir === $destinationDir) {
if (!$sourceNode->canRename()) {
throw new Forbidden($source . ' cannot be renamed');
}
} else {
// The source needs to be deletable for moving
$sourceNodeFileInfo = $sourceNode->getFileInfo();
if (!$sourceNodeFileInfo->isDeletable()) {
throw new Forbidden($source . ' cannot be deleted');
}
}
// The source is not allowed to be the parent of the target

View file

@ -23,6 +23,7 @@ use OCP\Files\StorageNotAvailableException;
use OCP\Server;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
use Sabre\DAV\Exception\Forbidden;
abstract class Node implements \Sabre\DAV\INode {
/**
@ -103,6 +104,13 @@ abstract class Node implements \Sabre\DAV\INode {
return $this->path;
}
/**
* Check if this node can be renamed
*/
public function canRename(): bool {
return DavUtil::canRename($this->node, $this->node->getParent());
}
/**
* Renames the node
*
@ -111,10 +119,8 @@ abstract class Node implements \Sabre\DAV\INode {
* @throws \Sabre\DAV\Exception\Forbidden
*/
public function setName($name) {
// rename is only allowed if the delete privilege is granted
// (basically rename is a copy with delete of the original node)
if (!($this->info->isDeletable() || ($this->info->getMountPoint() instanceof MoveableMount && $this->info->getInternalPath() === ''))) {
throw new \Sabre\DAV\Exception\Forbidden();
if (!$this->canRename()) {
throw new Forbidden('');
}
[$parentPath,] = \Sabre\Uri\split($this->path);
@ -330,11 +336,8 @@ abstract class Node implements \Sabre\DAV\INode {
return null;
}
/**
* @return string
*/
public function getDavPermissions() {
return DavUtil::getDavPermissions($this->info);
public function getDavPermissions(): string {
return DavUtil::getDavPermissions($this->info, $this->node->getParent());
}
public function getOwner() {

View file

@ -42,7 +42,7 @@ class NodeTest extends \Test\TestCase {
[Constants::PERMISSION_ALL, 'file', true, Constants::PERMISSION_ALL, true, '' , 'SRMGDNVW'],
[Constants::PERMISSION_ALL, 'file', true, Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE, true, '' , 'SRMGDNV'],
[Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, 'file', true, Constants::PERMISSION_ALL, false, 'test', 'SGDNVW'],
[Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGD'],
[Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGDN'],
[Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGNVW'],
[Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGDNVW'],
[Constants::PERMISSION_ALL - Constants::PERMISSION_READ, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RDNVW'],

View file

@ -37,10 +37,15 @@ export const action: IFileAction = {
: filesStore.getNode(dirname(node.source))
const parentPermissions = parentNode?.permissions || Permission.NONE
// Only enable if the node have the delete permission
// and if the parent folder allows creating files
return Boolean(node.permissions & Permission.DELETE)
&& Boolean(parentPermissions & Permission.CREATE)
// Enable if the node has update permissions or the node
// has delete permission and the parent folder allows creating files
return (
(
Boolean(node.permissions & Permission.DELETE)
&& Boolean(parentPermissions & Permission.CREATE)
)
|| Boolean(node.permissions & Permission.UPDATE)
)
},
async exec({ nodes }) {

4
dist/files-init.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,6 +7,7 @@
namespace OCP\Files;
use OC\Files\Mount\MoveableMount;
use OCP\Constants;
use OCP\Files\Mount\IMovableMount;
@ -33,7 +34,7 @@ class DavUtil {
*
* @since 25.0.0
*/
public static function getDavPermissions(FileInfo $info): string {
public static function getDavPermissions(FileInfo $info, ?FileInfo $parent = null): string {
$permissions = $info->getPermissions();
$p = '';
if ($info->isShared()) {
@ -51,8 +52,17 @@ class DavUtil {
if ($permissions & Constants::PERMISSION_DELETE) {
$p .= 'D';
}
if ($permissions & Constants::PERMISSION_UPDATE) {
$p .= 'NV'; // Renameable, Movable
if ($parent) {
if (self::canRename($info, $parent)) {
$p .= 'N'; // Renamable
}
if ($permissions & Constants::PERMISSION_UPDATE) {
$p .= 'V'; // Movable
}
} else {
if ($permissions & Constants::PERMISSION_UPDATE) {
$p .= 'NV'; // Renamable, Movable
}
}
// since we always add update permissions for the root of movable mounts
@ -76,4 +86,24 @@ class DavUtil {
}
return $p;
}
public static function canRename(FileInfo $info, FileInfo $parent): bool {
// the root of a movable mountpoint can be renamed regardless of the file permissions
if ($info->getMountPoint() instanceof MoveableMount && $info->getInternalPath() === '') {
return true;
}
// we allow renaming the file if either the file has update permissions
if ($info->isUpdateable()) {
return true;
}
// or the file can be deleted and the parent has create permissions
if ($info->getStorage() instanceof IHomeStorage && $info->getInternalPath() === 'files') {
// can't rename the users home
return false;
}
return $info->isDeletable() && $parent->isCreatable();
}
}