nextcloud/apps/files/lib/Controller/TransferOwnershipController.php
Ferdinand Thiessen 320283b67d fix(files): Properly handle denied ownership transfers
When the receiver denies the transfer the notification handler was missing,
so no notification was created for the transfer owner.

But also the internal notification was created two times:
1. When rejecting the transfer
2. By the reject function when dismissing the notification

This is fixed by only relying on the dismiss function.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2024-06-25 18:01:04 +02:00

217 lines
6.8 KiB
PHP

<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Julius Härtl <jus@bitgrid.net>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files\Controller;
use OCA\Files\BackgroundJob\TransferOwnership;
use OCA\Files\Db\TransferOwnership as TransferOwnershipEntity;
use OCA\Files\Db\TransferOwnershipMapper;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\Files\IHomeStorage;
use OCP\Files\IRootFolder;
use OCP\IRequest;
use OCP\IUserManager;
use OCP\Notification\IManager as NotificationManager;
class TransferOwnershipController extends OCSController {
/** @var string */
private $userId;
/** @var NotificationManager */
private $notificationManager;
/** @var ITimeFactory */
private $timeFactory;
/** @var IJobList */
private $jobList;
/** @var TransferOwnershipMapper */
private $mapper;
/** @var IUserManager */
private $userManager;
/** @var IRootFolder */
private $rootFolder;
public function __construct(string $appName,
IRequest $request,
string $userId,
NotificationManager $notificationManager,
ITimeFactory $timeFactory,
IJobList $jobList,
TransferOwnershipMapper $mapper,
IUserManager $userManager,
IRootFolder $rootFolder) {
parent::__construct($appName, $request);
$this->userId = $userId;
$this->notificationManager = $notificationManager;
$this->timeFactory = $timeFactory;
$this->jobList = $jobList;
$this->mapper = $mapper;
$this->userManager = $userManager;
$this->rootFolder = $rootFolder;
}
/**
* @NoAdminRequired
*
* Transfer the ownership to another user
*
* @param string $recipient Username of the recipient
* @param string $path Path of the file
*
* @return DataResponse<Http::STATUS_OK|Http::STATUS_BAD_REQUEST|Http::STATUS_FORBIDDEN, array<empty>, array{}>
*
* 200: Ownership transferred successfully
* 400: Transferring ownership is not possible
* 403: Transferring ownership is not allowed
*/
public function transfer(string $recipient, string $path): DataResponse {
$recipientUser = $this->userManager->get($recipient);
if ($recipientUser === null) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
$userRoot = $this->rootFolder->getUserFolder($this->userId);
try {
$node = $userRoot->get($path);
} catch (\Exception $e) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
if ($node->getOwner()->getUID() !== $this->userId || !$node->getStorage()->instanceOfStorage(IHomeStorage::class)) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
$transferOwnership = new TransferOwnershipEntity();
$transferOwnership->setSourceUser($this->userId);
$transferOwnership->setTargetUser($recipient);
$transferOwnership->setFileId($node->getId());
$transferOwnership->setNodeName($node->getName());
$transferOwnership = $this->mapper->insert($transferOwnership);
$notification = $this->notificationManager->createNotification();
$notification->setUser($recipient)
->setApp($this->appName)
->setDateTime($this->timeFactory->getDateTime())
->setSubject('transferownershipRequest', [
'sourceUser' => $this->userId,
'targetUser' => $recipient,
'nodeName' => $node->getName(),
])
->setObject('transfer', (string)$transferOwnership->getId());
$this->notificationManager->notify($notification);
return new DataResponse([]);
}
/**
* @NoAdminRequired
*
* Accept an ownership transfer
*
* @param int $id ID of the ownership transfer
*
* @return DataResponse<Http::STATUS_OK|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, array<empty>, array{}>
*
* 200: Ownership transfer accepted successfully
* 403: Accepting ownership transfer is not allowed
* 404: Ownership transfer not found
*/
public function accept(int $id): DataResponse {
try {
$transferOwnership = $this->mapper->getById($id);
} catch (DoesNotExistException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
if ($transferOwnership->getTargetUser() !== $this->userId) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
$notification = $this->notificationManager->createNotification();
$notification->setApp('files')
->setObject('transfer', (string)$id);
$this->notificationManager->markProcessed($notification);
$newTransferOwnership = new TransferOwnershipEntity();
$newTransferOwnership->setNodeName($transferOwnership->getNodeName());
$newTransferOwnership->setFileId($transferOwnership->getFileId());
$newTransferOwnership->setSourceUser($transferOwnership->getSourceUser());
$newTransferOwnership->setTargetUser($transferOwnership->getTargetUser());
$this->mapper->insert($newTransferOwnership);
$this->jobList->add(TransferOwnership::class, [
'id' => $newTransferOwnership->getId(),
]);
return new DataResponse([], Http::STATUS_OK);
}
/**
* @NoAdminRequired
*
* Reject an ownership transfer
*
* @param int $id ID of the ownership transfer
*
* @return DataResponse<Http::STATUS_OK|Http::STATUS_FORBIDDEN|Http::STATUS_NOT_FOUND, array<empty>, array{}>
*
* 200: Ownership transfer rejected successfully
* 403: Rejecting ownership transfer is not allowed
* 404: Ownership transfer not found
*/
public function reject(int $id): DataResponse {
try {
$transferOwnership = $this->mapper->getById($id);
} catch (DoesNotExistException $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
if ($transferOwnership->getTargetUser() !== $this->userId) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}
$notification = $this->notificationManager->createNotification();
$notification->setApp('files')
->setObject('transfer', (string)$id);
$this->notificationManager->markProcessed($notification);
$this->mapper->delete($transferOwnership);
// A "request denied" notification will be created by Notifier::dismissNotification
return new DataResponse([], Http::STATUS_OK);
}
}