Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: Renaming does not need update but delete permissions
Renaming is basically copy + delete (a move), so no need to update permissions.
Especially if the node is in a invalid directory the node should be moveable but not editable.

Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Aug 28, 2024
commit 183fcef39b40608e931f6470ef48182c657bd918
6 changes: 3 additions & 3 deletions apps/dav/lib/Connector/Sabre/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ public function getPath() {
* @throws \Sabre\DAV\Exception\Forbidden
*/
public function setName($name) {
// rename is only allowed if the update privilege is granted
if (!($this->info->isUpdateable() || ($this->info->getMountPoint() instanceof MoveableMount && $this->info->getInternalPath() === ''))) {
// 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();
}

Expand All @@ -129,7 +130,6 @@ public function setName($name) {
// verify path of the target
$this->verifyPath($newPath);


if (!$this->fileView->rename($this->path, $newPath)) {
throw new \Sabre\DAV\Exception('Failed to rename '. $this->path . ' to ' . $newPath);
}
Expand Down
2 changes: 1 addition & 1 deletion apps/files/src/actions/moveOrCopyActionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export type MoveCopyResult = {

export const canMove = (nodes: Node[]) => {
const minPermission = nodes.reduce((min, node) => Math.min(min, node.permissions), Permission.ALL)
return (minPermission & Permission.UPDATE) !== 0
return Boolean(minPermission & Permission.DELETE)
}

export const canDownload = (nodes: Node[]) => {
Expand Down
4 changes: 2 additions & 2 deletions apps/files/src/actions/renameAction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ describe('Rename action enabled tests', () => {
source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
owner: 'admin',
mime: 'text/plain',
permissions: Permission.UPDATE,
permissions: Permission.UPDATE | Permission.DELETE,
})

expect(action.enabled).toBeDefined()
expect(action.enabled!([file], view)).toBe(true)
})

test('Disabled for node without UPDATE permission', () => {
test('Disabled for node without DELETE permission', () => {
const file = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
Expand Down
2 changes: 1 addition & 1 deletion apps/files/src/actions/renameAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const action = new FileAction({
enabled: (nodes: Node[]) => {
return nodes.length > 0 && nodes
.map(node => node.permissions)
.every(permission => (permission & Permission.UPDATE) !== 0)
.every(permission => Boolean(permission & Permission.DELETE))
},

async exec(node: Node) {
Expand Down
13 changes: 8 additions & 5 deletions lib/private/Files/Node/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -432,11 +432,14 @@ public function move($targetPath) {
$targetPath = $this->normalizePath($targetPath);
$parent = $this->root->get(dirname($targetPath));
if (
$parent instanceof Folder and
$this->isValidPath($targetPath) and
(
$parent->isCreatable() ||
($parent->getInternalPath() === '' && $parent->getMountPoint() instanceof MoveableMount)
($parent instanceof Folder)
&& $this->isValidPath($targetPath)
&& (
$parent->isCreatable()
|| (
$parent->getInternalPath() === ''
&& ($parent->getMountPoint() instanceof MoveableMount)
)
)
) {
$nonExisting = $this->createNonExistingNode($targetPath);
Expand Down
6 changes: 3 additions & 3 deletions tests/lib/Files/Node/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use OC\Files\View;
use OC\Memcache\ArrayCache;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\Mount\IMountManager;
use OCP\ICacheFactory;
use OCP\IUserManager;
Expand Down Expand Up @@ -46,8 +47,7 @@ class IntegrationTest extends \Test\TestCase {
protected function setUp(): void {
parent::setUp();

/** @var IMountManager $manager */
$manager = \OC::$server->get(IMountManager::class);
$manager = \OCP\Server::get(IMountManager::class);

\OC_Hook::clear('OC_Filesystem');

Expand All @@ -64,7 +64,7 @@ protected function setUp(): void {
$manager,
$this->view,
$user,
\OC::$server->getUserMountCache(),
\OCP\Server::get(IUserMountCache::class),
$this->createMock(LoggerInterface::class),
$this->createMock(IUserManager::class),
$this->createMock(IEventDispatcher::class),
Expand Down