aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_sharing/lib/External/Manager.php
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_sharing/lib/External/Manager.php')
-rw-r--r--apps/files_sharing/lib/External/Manager.php293
1 files changed, 150 insertions, 143 deletions
diff --git a/apps/files_sharing/lib/External/Manager.php b/apps/files_sharing/lib/External/Manager.php
index f18d8346dc4..ff4781eba0f 100644
--- a/apps/files_sharing/lib/External/Manager.php
+++ b/apps/files_sharing/lib/External/Manager.php
@@ -1,35 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Hansson <daniel@techandme.se>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Stefan Weil <sw@weilnetz.de>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_Sharing\External;
@@ -38,11 +12,13 @@ use Doctrine\DBAL\Driver\Exception;
use OC\Files\Filesystem;
use OCA\FederatedFileSharing\Events\FederatedShareAddedEvent;
use OCA\Files_Sharing\Helper;
+use OCA\Files_Sharing\ResponseDefinitions;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Federation\ICloudFederationFactory;
use OCP\Federation\ICloudFederationProviderManager;
use OCP\Files;
+use OCP\Files\Events\InvalidateMountCacheEvent;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IStorageFactory;
use OCP\Http\Client\IClientService;
@@ -56,77 +32,32 @@ use OCP\Share;
use OCP\Share\IShare;
use Psr\Log\LoggerInterface;
+/**
+ * @psalm-import-type Files_SharingRemoteShare from ResponseDefinitions
+ */
class Manager {
public const STORAGE = '\OCA\Files_Sharing\External\Storage';
/** @var string|null */
private $uid;
- /** @var IDBConnection */
- private $connection;
-
- /** @var \OC\Files\Mount\Manager */
- private $mountManager;
-
- /** @var IStorageFactory */
- private $storageLoader;
-
- /** @var IClientService */
- private $clientService;
-
- /** @var IManager */
- private $notificationManager;
-
- /** @var IDiscoveryService */
- private $discoveryService;
-
- /** @var ICloudFederationProviderManager */
- private $cloudFederationProviderManager;
-
- /** @var ICloudFederationFactory */
- private $cloudFederationFactory;
-
- /** @var IGroupManager */
- private $groupManager;
-
- /** @var IUserManager */
- private $userManager;
-
- /** @var IEventDispatcher */
- private $eventDispatcher;
-
- /** @var LoggerInterface */
- private $logger;
-
public function __construct(
- IDBConnection $connection,
- \OC\Files\Mount\Manager $mountManager,
- IStorageFactory $storageLoader,
- IClientService $clientService,
- IManager $notificationManager,
- IDiscoveryService $discoveryService,
- ICloudFederationProviderManager $cloudFederationProviderManager,
- ICloudFederationFactory $cloudFederationFactory,
- IGroupManager $groupManager,
- IUserManager $userManager,
- IUserSession $userSession,
- IEventDispatcher $eventDispatcher,
- LoggerInterface $logger
+ private IDBConnection $connection,
+ private \OC\Files\Mount\Manager $mountManager,
+ private IStorageFactory $storageLoader,
+ private IClientService $clientService,
+ private IManager $notificationManager,
+ private IDiscoveryService $discoveryService,
+ private ICloudFederationProviderManager $cloudFederationProviderManager,
+ private ICloudFederationFactory $cloudFederationFactory,
+ private IGroupManager $groupManager,
+ private IUserManager $userManager,
+ IUserSession $userSession,
+ private IEventDispatcher $eventDispatcher,
+ private LoggerInterface $logger,
) {
$user = $userSession->getUser();
- $this->connection = $connection;
- $this->mountManager = $mountManager;
- $this->storageLoader = $storageLoader;
- $this->clientService = $clientService;
$this->uid = $user ? $user->getUID() : null;
- $this->notificationManager = $notificationManager;
- $this->discoveryService = $discoveryService;
- $this->cloudFederationProviderManager = $cloudFederationProviderManager;
- $this->cloudFederationFactory = $cloudFederationFactory;
- $this->groupManager = $groupManager;
- $this->userManager = $userManager;
- $this->eventDispatcher = $eventDispatcher;
- $this->logger = $logger;
}
/**
@@ -146,7 +77,7 @@ class Manager {
* @throws \Doctrine\DBAL\Exception
*/
public function addShare($remote, $token, $password, $name, $owner, $shareType, $accepted = false, $user = null, $remoteId = '', $parent = -1) {
- $user = $user ? $user : $this->uid;
+ $user = $user ?? $this->uid;
$accepted = $accepted ? IShare::STATUS_ACCEPTED : IShare::STATUS_PENDING;
$name = Filesystem::normalizePath('/' . $name);
@@ -195,7 +126,7 @@ class Manager {
'mountpoint' => $mountPoint,
'owner' => $owner
];
- return $this->mountShare($options);
+ return $this->mountShare($options, $user);
}
/**
@@ -226,18 +157,29 @@ class Manager {
$query->execute([$remote, $token, $password, $name, $owner, $user, $mountPoint, $hash, $accepted, $remoteId, $parent, $shareType]);
}
+ private function fetchShare(int $id): array|false {
+ $getShare = $this->connection->prepare('
+ SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`, `parent`, `share_type`, `password`, `mountpoint_hash`
+ FROM `*PREFIX*share_external`
+ WHERE `id` = ?');
+ $result = $getShare->execute([$id]);
+ $share = $result->fetch();
+ $result->closeCursor();
+ return $share;
+ }
+
/**
- * get share
+ * get share by token
*
- * @param int $id share id
+ * @param string $token
* @return mixed share of false
*/
- private function fetchShare($id) {
+ private function fetchShareByToken($token) {
$getShare = $this->connection->prepare('
SELECT `id`, `remote`, `remote_id`, `share_token`, `name`, `owner`, `user`, `mountpoint`, `accepted`, `parent`, `share_type`, `password`, `mountpoint_hash`
FROM `*PREFIX*share_external`
- WHERE `id` = ?');
- $result = $getShare->execute([$id]);
+ WHERE `share_token` = ?');
+ $result = $getShare->execute([$token]);
$share = $result->fetch();
$result->closeCursor();
return $share;
@@ -257,20 +199,54 @@ class Manager {
return null;
}
- /**
- * get share
- *
- * @param int $id share id
- * @return mixed share of false
- */
- public function getShare($id) {
+ public function getShare(int $id, ?string $user = null): array|false {
+ $user = $user ?? $this->uid;
$share = $this->fetchShare($id);
- $validShare = is_array($share) && isset($share['share_type']) && isset($share['user']);
+ if ($share === false) {
+ return false;
+ }
// check if the user is allowed to access it
- if ($validShare && (int)$share['share_type'] === IShare::TYPE_USER && $share['user'] === $this->uid) {
+ if ($this->canAccessShare($share, $user)) {
return $share;
- } elseif ($validShare && (int)$share['share_type'] === IShare::TYPE_GROUP) {
+ }
+
+ return false;
+ }
+
+ /**
+ * Get share by token
+ *
+ * @param string $token
+ * @return array|false
+ */
+ public function getShareByToken(string $token): array|false {
+ $share = $this->fetchShareByToken($token);
+
+ // We do not check if the user is allowed to access it here,
+ // as this is not used from a user context.
+ if ($share === false) {
+ return false;
+ }
+
+ return $share;
+ }
+
+ private function canAccessShare(array $share, string $user): bool {
+ $validShare = isset($share['share_type']) && isset($share['user']);
+
+ if (!$validShare) {
+ return false;
+ }
+
+ // If the share is a user share, check if the user is the recipient
+ if ((int)$share['share_type'] === IShare::TYPE_USER
+ && $share['user'] === $user) {
+ return true;
+ }
+
+ // If the share is a group share, check if the user is in the group
+ if ((int)$share['share_type'] === IShare::TYPE_GROUP) {
$parentId = (int)$share['parent'];
if ($parentId !== -1) {
// we just retrieved a sub-share, switch to the parent entry for verification
@@ -278,9 +254,10 @@ class Manager {
} else {
$groupShare = $share;
}
- $user = $this->userManager->get($this->uid);
+
+ $user = $this->userManager->get($user);
if ($this->groupManager->get($groupShare['user'])->inGroup($user)) {
- return $share;
+ return true;
}
}
@@ -307,13 +284,22 @@ class Manager {
* @param int $id
* @return bool True if the share could be accepted, false otherwise
*/
- public function acceptShare($id) {
- $share = $this->getShare($id);
+ public function acceptShare(int $id, ?string $user = null) {
+ // If we're auto-accepting a share, we need to know the user id
+ // as there is no session available while processing the share
+ // from the remote server request.
+ $user = $user ?? $this->uid;
+ if ($user === null) {
+ $this->logger->error('No user specified for accepting share');
+ return false;
+ }
+
+ $share = $this->getShare($id, $user);
$result = false;
if ($share) {
- \OC_Util::setupFS($this->uid);
- $shareFolder = Helper::getShareFolder(null, $this->uid);
+ \OC_Util::setupFS($user);
+ $shareFolder = Helper::getShareFolder(null, $user);
$mountPoint = Files::buildNotExistingFileName($shareFolder, $share['name']);
$mountPoint = Filesystem::normalizePath($mountPoint);
$hash = md5($mountPoint);
@@ -326,14 +312,14 @@ class Manager {
`mountpoint` = ?,
`mountpoint_hash` = ?
WHERE `id` = ? AND `user` = ?');
- $userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $this->uid]);
+ $userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $user]);
} else {
$parentId = (int)$share['parent'];
if ($parentId !== -1) {
// this is the sub-share
$subshare = $share;
} else {
- $subshare = $this->fetchUserShare($id, $this->uid);
+ $subshare = $this->fetchUserShare($id, $user);
}
if ($subshare !== null) {
@@ -344,7 +330,7 @@ class Manager {
`mountpoint` = ?,
`mountpoint_hash` = ?
WHERE `id` = ? AND `user` = ?');
- $acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $this->uid]);
+ $acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $user]);
$result = true;
} catch (Exception $e) {
$this->logger->emergency('Could not update share', ['exception' => $e]);
@@ -358,7 +344,7 @@ class Manager {
$share['password'],
$share['name'],
$share['owner'],
- $this->uid,
+ $user,
$mountPoint, $hash, 1,
$share['remote_id'],
$id,
@@ -370,17 +356,18 @@ class Manager {
}
}
}
+
if ($userShareAccepted !== false) {
$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept');
$event = new FederatedShareAddedEvent($share['remote']);
$this->eventDispatcher->dispatchTyped($event);
- $this->eventDispatcher->dispatchTyped(new Files\Events\InvalidateMountCacheEvent($this->userManager->get($this->uid)));
+ $this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($user)));
$result = true;
}
}
// Make sure the user has no notification for something that does not exist anymore.
- $this->processNotification($id);
+ $this->processNotification($id, $user);
return $result;
}
@@ -391,17 +378,23 @@ class Manager {
* @param int $id
* @return bool True if the share could be declined, false otherwise
*/
- public function declineShare($id) {
- $share = $this->getShare($id);
+ public function declineShare(int $id, ?string $user = null) {
+ $user = $user ?? $this->uid;
+ if ($user === null) {
+ $this->logger->error('No user specified for declining share');
+ return false;
+ }
+
+ $share = $this->getShare($id, $user);
$result = false;
if ($share && (int)$share['share_type'] === IShare::TYPE_USER) {
$removeShare = $this->connection->prepare('
DELETE FROM `*PREFIX*share_external` WHERE `id` = ? AND `user` = ?');
- $removeShare->execute([$id, $this->uid]);
+ $removeShare->execute([$id, $user]);
$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
- $this->processNotification($id);
+ $this->processNotification($id, $user);
$result = true;
} elseif ($share && (int)$share['share_type'] === IShare::TYPE_GROUP) {
$parentId = (int)$share['parent'];
@@ -409,7 +402,7 @@ class Manager {
// this is the sub-share
$subshare = $share;
} else {
- $subshare = $this->fetchUserShare($id, $this->uid);
+ $subshare = $this->fetchUserShare($id, $user);
}
if ($subshare !== null) {
@@ -428,7 +421,7 @@ class Manager {
$share['password'],
$share['name'],
$share['owner'],
- $this->uid,
+ $user,
$share['mountpoint'],
$share['mountpoint_hash'],
0,
@@ -441,16 +434,27 @@ class Manager {
$result = false;
}
}
- $this->processNotification($id);
+ $this->processNotification($id, $user);
}
return $result;
}
- public function processNotification(int $remoteShare): void {
+ public function processNotification(int $remoteShare, ?string $user = null): void {
+ $user = $user ?? $this->uid;
+ if ($user === null) {
+ $this->logger->error('No user specified for processing notification');
+ return;
+ }
+
+ $share = $this->fetchShare($remoteShare);
+ if ($share === false) {
+ return;
+ }
+
$filter = $this->notificationManager->createNotification();
$filter->setApp('files_sharing')
- ->setUser($this->uid)
+ ->setUser($user)
->setObject('remote_share', (string)$remoteShare);
$this->notificationManager->markProcessed($filter);
}
@@ -550,9 +554,10 @@ class Manager {
return rtrim(substr($path, strlen($prefix)), '/');
}
- public function getMount($data) {
+ public function getMount($data, ?string $user = null) {
+ $user = $user ?? $this->uid;
$data['manager'] = $this;
- $mountPoint = '/' . $this->uid . '/files' . $data['mountpoint'];
+ $mountPoint = '/' . $user . '/files' . $data['mountpoint'];
$data['mountpoint'] = $mountPoint;
$data['certificateManager'] = \OC::$server->getCertificateManager();
return new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
@@ -562,8 +567,8 @@ class Manager {
* @param array $data
* @return Mount
*/
- protected function mountShare($data) {
- $mount = $this->getMount($data);
+ protected function mountShare($data, ?string $user = null) {
+ $mount = $this->getMount($data, $user);
$this->mountManager->addMount($mount);
return $mount;
}
@@ -594,7 +599,7 @@ class Manager {
');
$result = (bool)$query->execute([$target, $targetHash, $sourceHash, $this->uid]);
- $this->eventDispatcher->dispatchTyped(new Files\Events\InvalidateMountCacheEvent($this->userManager->get($this->uid)));
+ $this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($this->uid)));
return $result;
}
@@ -733,12 +738,12 @@ class Manager {
$qb = $this->connection->getQueryBuilder();
// delete group share entry and matching sub-entries
$qb->delete('share_external')
- ->where(
- $qb->expr()->orX(
- $qb->expr()->eq('id', $qb->createParameter('share_id')),
- $qb->expr()->eq('parent', $qb->createParameter('share_parent_id'))
- )
- );
+ ->where(
+ $qb->expr()->orX(
+ $qb->expr()->eq('id', $qb->createParameter('share_id')),
+ $qb->expr()->eq('parent', $qb->createParameter('share_parent_id'))
+ )
+ );
foreach ($shares as $share) {
$qb->setParameter('share_id', $share['id']);
@@ -756,7 +761,7 @@ class Manager {
/**
* return a list of shares which are not yet accepted by the user
*
- * @return array list of open server-to-server shares
+ * @return list<Files_SharingRemoteShare> list of open server-to-server shares
*/
public function getOpenShares() {
return $this->getShares(false);
@@ -765,7 +770,7 @@ class Manager {
/**
* return a list of shares which are accepted by the user
*
- * @return array list of accepted server-to-server shares
+ * @return list<Files_SharingRemoteShare> list of accepted server-to-server shares
*/
public function getAcceptedShares() {
return $this->getShares(true);
@@ -777,9 +782,11 @@ class Manager {
* @param bool|null $accepted True for accepted only,
* false for not accepted,
* null for all shares of the user
- * @return array list of open server-to-server shares
+ * @return list<Files_SharingRemoteShare> list of open server-to-server shares
*/
private function getShares($accepted) {
+ // Not allowing providing a user here,
+ // as we only want to retrieve shares for the current user.
$user = $this->userManager->get($this->uid);
$groups = $this->groupManager->getUserGroups($user);
$userGroups = [];