Skip to content

Commit d1fd735

Browse files
authored
Merge pull request #57374 from nextcloud/rename-dav-permissions
fix: allow renaming files with just update permissions
2 parents ca245b4 + 8f7b0b4 commit d1fd735

File tree

8 files changed

+63
-20
lines changed

8 files changed

+63
-20
lines changed

apps/dav/lib/BulkUpload/BulkUploadPlugin.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public function httpPost(RequestInterface $request, ResponseInterface $response)
7676
'error' => false,
7777
'etag' => $node->getETag(),
7878
'fileid' => DavUtil::getDavFileId($node->getId()),
79-
'permissions' => DavUtil::getDavPermissions($node),
79+
'permissions' => DavUtil::getDavPermissions($node, $node->getParent()),
8080
];
8181
} catch (\Exception $e) {
8282
$this->logger->error($e->getMessage(), ['path' => $headers['x-file-path']]);

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,19 @@ public function checkMove(string $source, string $target): void {
203203
// First check copyable (move only needs additional delete permission)
204204
$this->checkCopy($source, $target);
205205

206-
// The source needs to be deletable for moving
207-
$sourceNodeFileInfo = $sourceNode->getFileInfo();
208-
if (!$sourceNodeFileInfo->isDeletable()) {
209-
throw new Forbidden($source . ' cannot be deleted');
206+
[$sourceDir] = \Sabre\Uri\split($source);
207+
[$destinationDir, ] = \Sabre\Uri\split($target);
208+
209+
if ($sourceDir === $destinationDir) {
210+
if (!$sourceNode->canRename()) {
211+
throw new Forbidden($source . ' cannot be renamed');
212+
}
213+
} else {
214+
// The source needs to be deletable for moving
215+
$sourceNodeFileInfo = $sourceNode->getFileInfo();
216+
if (!$sourceNodeFileInfo->isDeletable()) {
217+
throw new Forbidden($source . ' cannot be deleted');
218+
}
210219
}
211220

212221
// The source is not allowed to be the parent of the target

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ public function getPath(): string {
112112
return $this->path;
113113
}
114114

115+
/**
116+
* Check if this node can be renamed
117+
*/
118+
public function canRename(): bool {
119+
return DavUtil::canRename($this->node, $this->node->getParent());
120+
}
121+
115122
/**
116123
* Renames the node
117124
*
@@ -123,10 +130,8 @@ public function getPath(): string {
123130
* @throws LockedException
124131
*/
125132
public function setName($name): void {
126-
// rename is only allowed if the delete privilege is granted
127-
// (basically rename is a copy with delete of the original node)
128-
if (!$this->info->isDeletable() && !($this->info->getMountPoint() instanceof MoveableMount && $this->info->getInternalPath() === '')) {
129-
throw new Forbidden();
133+
if (!$this->canRename()) {
134+
throw new Forbidden('');
130135
}
131136

132137
/** @var string $parentPath */
@@ -320,7 +325,7 @@ public function getNoteFromShare(?string $user): ?string {
320325
}
321326

322327
public function getDavPermissions(): string {
323-
return DavUtil::getDavPermissions($this->info);
328+
return DavUtil::getDavPermissions($this->info, $this->node->getParent());
324329
}
325330

326331
/**

apps/dav/tests/unit/Connector/Sabre/NodeTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public static function davPermissionsProvider(): array {
4242
[Constants::PERMISSION_ALL, 'file', true, Constants::PERMISSION_ALL, true, '' , 'SRMGDNVW'],
4343
[Constants::PERMISSION_ALL, 'file', true, Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE, true, '' , 'SRMGDNV'],
4444
[Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, 'file', true, Constants::PERMISSION_ALL, false, 'test', 'SGDNVW'],
45-
[Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGD'],
45+
[Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGDN'],
4646
[Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGNVW'],
4747
[Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RGDNVW'],
4848
[Constants::PERMISSION_ALL - Constants::PERMISSION_READ, 'file', false, Constants::PERMISSION_ALL, false, 'test', 'RDNVW'],

apps/files/src/actions/renameAction.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,15 @@ export const action: IFileAction = {
3737
: filesStore.getNode(dirname(node.source))
3838
const parentPermissions = parentNode?.permissions || Permission.NONE
3939

40-
// Only enable if the node have the delete permission
41-
// and if the parent folder allows creating files
42-
return Boolean(node.permissions & Permission.DELETE)
43-
&& Boolean(parentPermissions & Permission.CREATE)
40+
// Enable if the node has update permissions or the node
41+
// has delete permission and the parent folder allows creating files
42+
return (
43+
(
44+
Boolean(node.permissions & Permission.DELETE)
45+
&& Boolean(parentPermissions & Permission.CREATE)
46+
)
47+
|| Boolean(node.permissions & Permission.UPDATE)
48+
)
4449
},
4550

4651
async exec({ nodes }) {

dist/files-init.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/files-init.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/public/Files/DavUtil.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace OCP\Files;
99

10+
use OC\Files\Mount\MoveableMount;
1011
use OCP\Constants;
1112
use OCP\Files\Mount\IMovableMount;
1213

@@ -33,7 +34,7 @@ public static function getDavFileId(int $id): string {
3334
*
3435
* @since 25.0.0
3536
*/
36-
public static function getDavPermissions(FileInfo $info): string {
37+
public static function getDavPermissions(FileInfo $info, FileInfo $parent): string {
3738
$permissions = $info->getPermissions();
3839
$p = '';
3940
if ($info->isShared()) {
@@ -51,8 +52,11 @@ public static function getDavPermissions(FileInfo $info): string {
5152
if ($permissions & Constants::PERMISSION_DELETE) {
5253
$p .= 'D';
5354
}
55+
if (self::canRename($info, $parent)) {
56+
$p .= 'N'; // Renamable
57+
}
5458
if ($permissions & Constants::PERMISSION_UPDATE) {
55-
$p .= 'NV'; // Renameable, Movable
59+
$p .= 'V'; // Movable
5660
}
5761

5862
// since we always add update permissions for the root of movable mounts
@@ -76,4 +80,24 @@ public static function getDavPermissions(FileInfo $info): string {
7680
}
7781
return $p;
7882
}
83+
84+
public static function canRename(FileInfo $info, FileInfo $parent): bool {
85+
// the root of a movable mountpoint can be renamed regardless of the file permissions
86+
if ($info->getMountPoint() instanceof MoveableMount && $info->getInternalPath() === '') {
87+
return true;
88+
}
89+
90+
// we allow renaming the file if either the file has update permissions
91+
if ($info->isUpdateable()) {
92+
return true;
93+
}
94+
95+
// or the file can be deleted and the parent has create permissions
96+
if ($info->getStorage() instanceof IHomeStorage && $info->getInternalPath() === 'files') {
97+
// can't rename the users home
98+
return false;
99+
}
100+
101+
return $info->isDeletable() && $parent->isCreatable();
102+
}
79103
}

0 commit comments

Comments
 (0)