aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Share20
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@users.noreply.github.com>2024-05-30 14:17:52 +0200
committerGitHub <noreply@github.com>2024-05-30 14:17:52 +0200
commit7f2a1e5b7a1724727bc274e73a9a6fd09249c7a6 (patch)
treefbc014b411855706acf31e082373e45b96640287 /lib/private/Share20
parent7292a8d8fe7e4cf8667194f4af587f6923a0f884 (diff)
parent31b0a44cf65b6625636ea0fa15fb1a1122b525e1 (diff)
downloadnextcloud-server-7f2a1e5b7a1724727bc274e73a9a6fd09249c7a6.tar.gz
nextcloud-server-7f2a1e5b7a1724727bc274e73a9a6fd09249c7a6.zip
Merge branch 'master' into refactor/OC-Server-getMailer
Signed-off-by: John Molakvoæ <skjnldsv@users.noreply.github.com>
Diffstat (limited to 'lib/private/Share20')
-rw-r--r--lib/private/Share20/DefaultShareProvider.php194
-rw-r--r--lib/private/Share20/Exception/BackendError.php21
-rw-r--r--lib/private/Share20/Exception/InvalidShare.php21
-rw-r--r--lib/private/Share20/Exception/ProviderException.php22
-rw-r--r--lib/private/Share20/Hooks.php28
-rw-r--r--lib/private/Share20/LegacyHooks.php25
-rw-r--r--lib/private/Share20/Manager.php468
-rw-r--r--lib/private/Share20/ProviderFactory.php54
-rw-r--r--lib/private/Share20/PublicShareTemplateFactory.php22
-rw-r--r--lib/private/Share20/Share.php81
-rw-r--r--lib/private/Share20/ShareAttributes.php21
-rw-r--r--lib/private/Share20/ShareDisableChecker.php85
-rw-r--r--lib/private/Share20/ShareHelper.php22
-rw-r--r--lib/private/Share20/UserRemovedListener.php21
14 files changed, 413 insertions, 672 deletions
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php
index 5201cf074b1..03202f215b2 100644
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -1,36 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
- * @author Jan-Philipp Litza <jplitza@users.noreply.github.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Maxence Lange <maxence@artificial-owl.com>
- * @author phisch <git@philippschaffrath.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @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 OC\Share20;
@@ -38,12 +11,12 @@ use OC\Files\Cache\Cache;
use OC\Share20\Exception\BackendError;
use OC\Share20\Exception\InvalidShare;
use OC\Share20\Exception\ProviderException;
+use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Defaults;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
-use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IURLGenerator;
@@ -55,6 +28,7 @@ use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IAttributes;
use OCP\Share\IShare;
use OCP\Share\IShareProvider;
+use Psr\Log\LoggerInterface;
use function str_starts_with;
/**
@@ -90,19 +64,19 @@ class DefaultShareProvider implements IShareProvider {
/** @var IURLGenerator */
private $urlGenerator;
- /** @var IConfig */
- private $config;
+ private ITimeFactory $timeFactory;
public function __construct(
- IDBConnection $connection,
- IUserManager $userManager,
- IGroupManager $groupManager,
- IRootFolder $rootFolder,
- IMailer $mailer,
- Defaults $defaults,
- IFactory $l10nFactory,
- IURLGenerator $urlGenerator,
- IConfig $config) {
+ IDBConnection $connection,
+ IUserManager $userManager,
+ IGroupManager $groupManager,
+ IRootFolder $rootFolder,
+ IMailer $mailer,
+ Defaults $defaults,
+ IFactory $l10nFactory,
+ IURLGenerator $urlGenerator,
+ ITimeFactory $timeFactory,
+ ) {
$this->dbConn = $connection;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
@@ -111,7 +85,7 @@ class DefaultShareProvider implements IShareProvider {
$this->defaults = $defaults;
$this->l10nFactory = $l10nFactory;
$this->urlGenerator = $urlGenerator;
- $this->config = $config;
+ $this->timeFactory = $timeFactory;
}
/**
@@ -216,32 +190,22 @@ class DefaultShareProvider implements IShareProvider {
}
// Set the time this share was created
- $qb->setValue('stime', $qb->createNamedParameter(time()));
+ $shareTime = $this->timeFactory->now();
+ $qb->setValue('stime', $qb->createNamedParameter($shareTime->getTimestamp()));
// insert the data and fetch the id of the share
- $this->dbConn->beginTransaction();
- $qb->execute();
- $id = $this->dbConn->lastInsertId('*PREFIX*share');
+ $qb->executeStatement();
- // Now fetch the inserted share and create a complete share object
- $qb = $this->dbConn->getQueryBuilder();
- $qb->select('*')
- ->from('share')
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
-
- $cursor = $qb->execute();
- $data = $cursor->fetch();
- $this->dbConn->commit();
- $cursor->closeCursor();
+ // Update mandatory data
+ $id = $qb->getLastInsertId();
+ $share->setId((string)$id);
+ $share->setProviderId($this->identifier());
- if ($data === false) {
- throw new ShareNotFound('Newly created share could not be found');
- }
+ $share->setShareTime(\DateTime::createFromImmutable($shareTime));
$mailSendValue = $share->getMailSend();
- $data['mail_send'] = ($mailSendValue === null) ? true : $mailSendValue;
+ $share->setMailSend(($mailSendValue === null) ? true : $mailSendValue);
- $share = $this->createShare($data);
return $share;
}
@@ -661,6 +625,10 @@ class DefaultShareProvider implements IShareProvider {
}
public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) {
+ if (!$shallow) {
+ throw new \Exception("non-shallow getSharesInFolder is no longer supported");
+ }
+
$qb = $this->dbConn->getQueryBuilder();
$qb->select('s.*',
'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
@@ -701,21 +669,12 @@ class DefaultShareProvider implements IShareProvider {
}, $childMountNodes);
$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
- if ($shallow) {
- $qb->andWhere(
- $qb->expr()->orX(
- $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
- $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
- )
- );
- } else {
- $qb->andWhere(
- $qb->expr()->orX(
- $qb->expr()->like('f.path', $qb->createNamedParameter($this->dbConn->escapeLikeParameter($node->getInternalPath()) . '/%')),
- $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
- )
- );
- }
+ $qb->andWhere(
+ $qb->expr()->orX(
+ $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
+ $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
+ )
+ );
$qb->orderBy('id');
@@ -831,7 +790,7 @@ class DefaultShareProvider implements IShareProvider {
// If the recipient is set for a group share resolve to that user
if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) {
- $share = $this->resolveGroupShares([$share], $recipientId)[0];
+ $share = $this->resolveGroupShares([(int) $share->getId() => $share], $recipientId)[0];
}
return $share;
@@ -1008,7 +967,8 @@ class DefaultShareProvider implements IShareProvider {
}
if ($this->isAccessibleResult($data)) {
- $shares2[] = $this->createShare($data);
+ $share = $this->createShare($data);
+ $shares2[$share->getId()] = $share;
}
}
$cursor->closeCursor();
@@ -1129,61 +1089,40 @@ class DefaultShareProvider implements IShareProvider {
}
/**
- * @param Share[] $shares
+ * Update the data from group shares with any per-user modifications
+ *
+ * @param array<int, Share> $shareMap shares indexed by share id
* @param $userId
* @return Share[] The updates shares if no update is found for a share return the original
*/
- private function resolveGroupShares($shares, $userId) {
- $result = [];
-
- $start = 0;
- while (true) {
- /** @var Share[] $shareSlice */
- $shareSlice = array_slice($shares, $start, 100);
- $start += 100;
-
- if ($shareSlice === []) {
- break;
- }
-
- /** @var int[] $ids */
- $ids = [];
- /** @var Share[] $shareMap */
- $shareMap = [];
-
- foreach ($shareSlice as $share) {
- $ids[] = (int)$share->getId();
- $shareMap[$share->getId()] = $share;
- }
-
- $qb = $this->dbConn->getQueryBuilder();
-
- $query = $qb->select('*')
- ->from('share')
- ->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
- ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
- ->andWhere($qb->expr()->orX(
- $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
- $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
- ));
+ private function resolveGroupShares($shareMap, $userId) {
+ $qb = $this->dbConn->getQueryBuilder();
+ $query = $qb->select('*')
+ ->from('share')
+ ->where($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)))
+ ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
+ ->andWhere($qb->expr()->in('item_type', [$qb->createNamedParameter('file'), $qb->createNamedParameter('folder')]));
+
+ // this is called with either all group shares or one group share.
+ // for all shares it's easier to just only search by share_with,
+ // for a single share it's efficient to filter by parent
+ if (count($shareMap) === 1) {
+ $share = reset($shareMap);
+ $query->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
+ }
- $stmt = $query->execute();
+ $stmt = $query->execute();
- while ($data = $stmt->fetch()) {
+ while ($data = $stmt->fetch()) {
+ if (array_key_exists($data['parent'], $shareMap)) {
$shareMap[$data['parent']]->setPermissions((int)$data['permissions']);
$shareMap[$data['parent']]->setStatus((int)$data['accepted']);
$shareMap[$data['parent']]->setTarget($data['file_target']);
$shareMap[$data['parent']]->setParent($data['parent']);
}
-
- $stmt->closeCursor();
-
- foreach ($shareMap as $share) {
- $result[] = $share;
- }
}
- return $result;
+ return array_values($shareMap);
}
/**
@@ -1247,7 +1186,8 @@ class DefaultShareProvider implements IShareProvider {
)
);
} else {
- \OC::$server->getLogger()->logException(new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType));
+ $e = new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType);
+ \OCP\Server::get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]);
return;
}
@@ -1374,7 +1314,7 @@ class DefaultShareProvider implements IShareProvider {
$type = (int)$row['share_type'];
if ($type === IShare::TYPE_USER) {
$uid = $row['share_with'];
- $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
+ $users[$uid] = $users[$uid] ?? [];
$users[$uid][$row['id']] = $row;
} elseif ($type === IShare::TYPE_GROUP) {
$gid = $row['share_with'];
@@ -1387,14 +1327,14 @@ class DefaultShareProvider implements IShareProvider {
$userList = $group->getUsers();
foreach ($userList as $user) {
$uid = $user->getUID();
- $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
+ $users[$uid] = $users[$uid] ?? [];
$users[$uid][$row['id']] = $row;
}
} elseif ($type === IShare::TYPE_LINK) {
$link = true;
} elseif ($type === IShare::TYPE_USERGROUP && $currentAccess === true) {
$uid = $row['share_with'];
- $users[$uid] = isset($users[$uid]) ? $users[$uid] : [];
+ $users[$uid] = $users[$uid] ?? [];
$users[$uid][$row['id']] = $row;
}
}
diff --git a/lib/private/Share20/Exception/BackendError.php b/lib/private/Share20/Exception/BackendError.php
index b3e07495378..60f7dcc1a17 100644
--- a/lib/private/Share20/Exception/BackendError.php
+++ b/lib/private/Share20/Exception/BackendError.php
@@ -1,23 +1,8 @@
<?php
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @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 OC\Share20\Exception;
diff --git a/lib/private/Share20/Exception/InvalidShare.php b/lib/private/Share20/Exception/InvalidShare.php
index 464dc15a9f1..755efdfd2cc 100644
--- a/lib/private/Share20/Exception/InvalidShare.php
+++ b/lib/private/Share20/Exception/InvalidShare.php
@@ -1,23 +1,8 @@
<?php
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @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 OC\Share20\Exception;
diff --git a/lib/private/Share20/Exception/ProviderException.php b/lib/private/Share20/Exception/ProviderException.php
index b8e15dd9db8..cb79ab884b4 100644
--- a/lib/private/Share20/Exception/ProviderException.php
+++ b/lib/private/Share20/Exception/ProviderException.php
@@ -1,23 +1,9 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @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 OC\Share20\Exception;
diff --git a/lib/private/Share20/Hooks.php b/lib/private/Share20/Hooks.php
index ae08b20fde6..809b50791e5 100644
--- a/lib/private/Share20/Hooks.php
+++ b/lib/private/Share20/Hooks.php
@@ -1,32 +1,20 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @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 OC\Share20;
+use OCP\Share\IManager as IShareManager;
+
class Hooks {
public static function post_deleteUser($arguments) {
- \OC::$server->getShareManager()->userDeleted($arguments['uid']);
+ \OC::$server->get(IShareManager::class)->userDeleted($arguments['uid']);
}
public static function post_deleteGroup($arguments) {
- \OC::$server->getShareManager()->groupDeleted($arguments['gid']);
+ \OC::$server->get(IShareManager::class)->groupDeleted($arguments['gid']);
}
}
diff --git a/lib/private/Share20/LegacyHooks.php b/lib/private/Share20/LegacyHooks.php
index 24d07167fbd..99c2b0a9a87 100644
--- a/lib/private/Share20/LegacyHooks.php
+++ b/lib/private/Share20/LegacyHooks.php
@@ -1,28 +1,7 @@
<?php
/**
- * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Pauli Järvinen <pauli.jarvinen@gmail.com>
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Share20;
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index b03608f9872..e10c34fd0d2 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -1,52 +1,16 @@
<?php
+
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Bjoern Schiessle <bjoern@schiessle.org>
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Daniel Calviño Sánchez <danxuliu@gmail.com>
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Maxence Lange <maxence@artificial-owl.com>
- * @author Maxence Lange <maxence@nextcloud.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Pauli Järvinen <pauli.jarvinen@gmail.com>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Samuel <faust64@gmail.com>
- * @author szaimen <szaimen@e.mail.de>
- * @author Valdnet <47037905+Valdnet@users.noreply.github.com>
- * @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 OC\Share20;
-use OCP\Cache\CappedMemoryCache;
use OC\Files\Mount\MoveableMount;
use OC\KnownUser\KnownUserService;
use OC\Share20\Exception\ProviderException;
use OCA\Files_Sharing\AppInfo\Application;
-use OCA\Files_Sharing\ISharedStorage;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\File;
use OCP\Files\Folder;
@@ -55,6 +19,7 @@ use OCP\Files\Mount\IMountManager;
use OCP\Files\Node;
use OCP\HintException;
use OCP\IConfig;
+use OCP\IDateTimeZone;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IURLGenerator;
@@ -85,84 +50,34 @@ use Psr\Log\LoggerInterface;
* This class is the communication hub for all sharing related operations.
*/
class Manager implements IManager {
- /** @var IProviderFactory */
- private $factory;
- private LoggerInterface $logger;
- /** @var IConfig */
- private $config;
- /** @var ISecureRandom */
- private $secureRandom;
- /** @var IHasher */
- private $hasher;
- /** @var IMountManager */
- private $mountManager;
- /** @var IGroupManager */
- private $groupManager;
- /** @var IL10N */
- private $l;
- /** @var IFactory */
- private $l10nFactory;
- /** @var IUserManager */
- private $userManager;
- /** @var IRootFolder */
- private $rootFolder;
- /** @var CappedMemoryCache */
- private $sharingDisabledForUsersCache;
- /** @var LegacyHooks */
- private $legacyHooks;
- /** @var IMailer */
- private $mailer;
- /** @var IURLGenerator */
- private $urlGenerator;
- /** @var \OC_Defaults */
- private $defaults;
- /** @var IEventDispatcher */
- private $dispatcher;
- /** @var IUserSession */
- private $userSession;
- /** @var KnownUserService */
- private $knownUserService;
+
+ private IL10N|null $l;
+ private LegacyHooks $legacyHooks;
public function __construct(
- LoggerInterface $logger,
- IConfig $config,
- ISecureRandom $secureRandom,
- IHasher $hasher,
- IMountManager $mountManager,
- IGroupManager $groupManager,
- IL10N $l,
- IFactory $l10nFactory,
- IProviderFactory $factory,
- IUserManager $userManager,
- IRootFolder $rootFolder,
- IMailer $mailer,
- IURLGenerator $urlGenerator,
- \OC_Defaults $defaults,
- IEventDispatcher $dispatcher,
- IUserSession $userSession,
- KnownUserService $knownUserService
+ private LoggerInterface $logger,
+ private IConfig $config,
+ private ISecureRandom $secureRandom,
+ private IHasher $hasher,
+ private IMountManager $mountManager,
+ private IGroupManager $groupManager,
+ private IFactory $l10nFactory,
+ private IProviderFactory $factory,
+ private IUserManager $userManager,
+ private IRootFolder $rootFolder,
+ private IMailer $mailer,
+ private IURLGenerator $urlGenerator,
+ private \OC_Defaults $defaults,
+ private IEventDispatcher $dispatcher,
+ private IUserSession $userSession,
+ private KnownUserService $knownUserService,
+ private ShareDisableChecker $shareDisableChecker,
+ private IDateTimeZone $dateTimeZone
) {
- $this->logger = $logger;
- $this->config = $config;
- $this->secureRandom = $secureRandom;
- $this->hasher = $hasher;
- $this->mountManager = $mountManager;
- $this->groupManager = $groupManager;
- $this->l = $l;
- $this->l10nFactory = $l10nFactory;
- $this->factory = $factory;
- $this->userManager = $userManager;
- $this->rootFolder = $rootFolder;
- $this->sharingDisabledForUsersCache = new CappedMemoryCache();
+ $this->l = $this->l10nFactory->get('lib');
// The constructor of LegacyHooks registers the listeners of share events
// do not remove if those are not properly migrated
- $this->legacyHooks = new LegacyHooks($dispatcher);
- $this->mailer = $mailer;
- $this->urlGenerator = $urlGenerator;
- $this->defaults = $defaults;
- $this->dispatcher = $dispatcher;
- $this->userSession = $userSession;
- $this->knownUserService = $knownUserService;
+ $this->legacyHooks = new LegacyHooks($this->dispatcher);
}
/**
@@ -208,7 +123,7 @@ class Manager implements IManager {
*
* @suppress PhanUndeclaredClassMethod
*/
- protected function generalCreateChecks(IShare $share) {
+ protected function generalCreateChecks(IShare $share, bool $isUpdate = false) {
if ($share->getShareType() === IShare::TYPE_USER) {
// We expect a valid user as sharedWith for user shares
if (!$this->userManager->userExists($share->getSharedWith())) {
@@ -292,50 +207,15 @@ class Manager implements IManager {
throw new \InvalidArgumentException('A share requires permissions');
}
- $isFederatedShare = $share->getNode()->getStorage()->instanceOfStorage('\OCA\Files_Sharing\External\Storage');
$permissions = 0;
-
- if (!$isFederatedShare && $share->getNode()->getOwner() && $share->getNode()->getOwner()->getUID() !== $share->getSharedBy()) {
- $userMounts = array_filter($userFolder->getById($share->getNode()->getId()), function ($mount) {
- // We need to filter since there might be other mountpoints that contain the file
- // e.g. if the user has access to the same external storage that the file is originating from
- return $mount->getStorage()->instanceOfStorage(ISharedStorage::class);
- });
- $userMount = array_shift($userMounts);
- if ($userMount === null) {
- throw new GenericShareException('Could not get proper share mount for ' . $share->getNode()->getId() . '. Failing since else the next calls are called with null');
- }
- $mount = $userMount->getMountPoint();
- // When it's a reshare use the parent share permissions as maximum
- $userMountPointId = $mount->getStorageRootId();
- $userMountPoints = $userFolder->getById($userMountPointId);
- $userMountPoint = array_shift($userMountPoints);
-
- if ($userMountPoint === null) {
- throw new GenericShareException('Could not get proper user mount for ' . $userMountPointId . '. Failing since else the next calls are called with null');
- }
-
- /* Check if this is an incoming share */
- $incomingShares = $this->getSharedWith($share->getSharedBy(), IShare::TYPE_USER, $userMountPoint, -1, 0);
- $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_GROUP, $userMountPoint, -1, 0));
- $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_CIRCLE, $userMountPoint, -1, 0));
- $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_ROOM, $userMountPoint, -1, 0));
-
- /** @var IShare[] $incomingShares */
- if (!empty($incomingShares)) {
- foreach ($incomingShares as $incomingShare) {
- $permissions |= $incomingShare->getPermissions();
- }
- }
- } else {
- /*
- * Quick fix for #23536
- * Non moveable mount points do not have update and delete permissions
- * while we 'most likely' do have that on the storage.
- */
- $permissions = $share->getNode()->getPermissions();
- if (!($share->getNode()->getMountPoint() instanceof MoveableMount)) {
- $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
+ $nodesForUser = $userFolder->getById($share->getNodeId());
+ foreach ($nodesForUser as $node) {
+ if ($node->getInternalPath() === '' && !$node->getMountPoint() instanceof MoveableMount) {
+ // for the root of non-movable mount, the permissions we see if limited by the mount itself,
+ // so we instead use the "raw" permissions from the storage
+ $permissions |= $node->getStorage()->getPermissions('');
+ } else {
+ $permissions |= $node->getPermissions();
}
}
@@ -382,26 +262,6 @@ class Manager implements IManager {
$expirationDate = $share->getExpirationDate();
- if ($expirationDate !== null) {
- //Make sure the expiration date is a date
- $expirationDate->setTime(0, 0, 0);
-
- $date = new \DateTime();
- $date->setTime(0, 0, 0);
- if ($date >= $expirationDate) {
- $message = $this->l->t('Expiration date is in the past');
- throw new GenericShareException($message, $message, 404);
- }
- }
-
- // If expiredate is empty set a default one if there is a default
- $fullId = null;
- try {
- $fullId = $share->getFullId();
- } catch (\UnexpectedValueException $e) {
- // This is a new share
- }
-
if ($isRemote) {
$defaultExpireDate = $this->shareApiRemoteDefaultExpireDate();
$defaultExpireDays = $this->shareApiRemoteDefaultExpireDays();
@@ -413,29 +273,53 @@ class Manager implements IManager {
$configProp = 'internal_defaultExpDays';
$isEnforced = $this->shareApiInternalDefaultExpireDateEnforced();
}
- if ($fullId === null && $expirationDate === null && $defaultExpireDate) {
- $expirationDate = new \DateTime();
- $expirationDate->setTime(0, 0, 0);
- $days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays);
- if ($days > $defaultExpireDays) {
- $days = $defaultExpireDays;
+ // If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
+ // Then skip expiration date validation as null is accepted
+ if(!($share->getNoExpirationDate() && !$isEnforced)) {
+ if ($expirationDate != null) {
+ $expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
+ $expirationDate->setTime(0, 0, 0);
+
+ $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
+ $date->setTime(0, 0, 0);
+ if ($date >= $expirationDate) {
+ $message = $this->l->t('Expiration date is in the past');
+ throw new GenericShareException($message, $message, 404);
+ }
+ }
+
+ // If expiredate is empty set a default one if there is a default
+ $fullId = null;
+ try {
+ $fullId = $share->getFullId();
+ } catch (\UnexpectedValueException $e) {
+ // This is a new share
}
- $expirationDate->add(new \DateInterval('P' . $days . 'D'));
- }
- // If we enforce the expiration date check that is does not exceed
- if ($isEnforced) {
- if ($expirationDate === null) {
- throw new \InvalidArgumentException('Expiration date is enforced');
+ if ($fullId === null && $expirationDate === null && $defaultExpireDate) {
+ $expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
+ $expirationDate->setTime(0, 0, 0);
+ $days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays);
+ if ($days > $defaultExpireDays) {
+ $days = $defaultExpireDays;
+ }
+ $expirationDate->add(new \DateInterval('P' . $days . 'D'));
}
- $date = new \DateTime();
- $date->setTime(0, 0, 0);
- $date->add(new \DateInterval('P' . $defaultExpireDays . 'D'));
- if ($date < $expirationDate) {
- $message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $defaultExpireDays);
- throw new GenericShareException($message, $message, 404);
+ // If we enforce the expiration date check that is does not exceed
+ if ($isEnforced) {
+ if (empty($expirationDate)) {
+ throw new \InvalidArgumentException('Expiration date is enforced');
+ }
+
+ $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
+ $date->setTime(0, 0, 0);
+ $date->add(new \DateInterval('P' . $defaultExpireDays . 'D'));
+ if ($date < $expirationDate) {
+ $message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $defaultExpireDays);
+ throw new GenericShareException($message, $message, 404);
+ }
}
}
@@ -468,51 +352,57 @@ class Manager implements IManager {
*/
protected function validateExpirationDateLink(IShare $share) {
$expirationDate = $share->getExpirationDate();
-
- if ($expirationDate !== null) {
- //Make sure the expiration date is a date
- $expirationDate->setTime(0, 0, 0);
-
- $date = new \DateTime();
- $date->setTime(0, 0, 0);
- if ($date >= $expirationDate) {
- $message = $this->l->t('Expiration date is in the past');
- throw new GenericShareException($message, $message, 404);
+ $isEnforced = $this->shareApiLinkDefaultExpireDateEnforced();
+
+ // If $expirationDate is falsy, noExpirationDate is true and expiration not enforced
+ // Then skip expiration date validation as null is accepted
+ if(!($share->getNoExpirationDate() && !$isEnforced)) {
+ if ($expirationDate !== null) {
+ $expirationDate->setTimezone($this->dateTimeZone->getTimeZone());
+ $expirationDate->setTime(0, 0, 0);
+
+ $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
+ $date->setTime(0, 0, 0);
+ if ($date >= $expirationDate) {
+ $message = $this->l->t('Expiration date is in the past');
+ throw new GenericShareException($message, $message, 404);
+ }
}
- }
-
- // If expiredate is empty set a default one if there is a default
- $fullId = null;
- try {
- $fullId = $share->getFullId();
- } catch (\UnexpectedValueException $e) {
- // This is a new share
- }
-
- if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
- $expirationDate = new \DateTime();
- $expirationDate->setTime(0, 0, 0);
- $days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', (string)$this->shareApiLinkDefaultExpireDays());
- if ($days > $this->shareApiLinkDefaultExpireDays()) {
- $days = $this->shareApiLinkDefaultExpireDays();
+ // If expiredate is empty set a default one if there is a default
+ $fullId = null;
+ try {
+ $fullId = $share->getFullId();
+ } catch (\UnexpectedValueException $e) {
+ // This is a new share
+ }
+
+ if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
+ $expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone());
+ $expirationDate->setTime(0, 0, 0);
+
+ $days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', (string)$this->shareApiLinkDefaultExpireDays());
+ if ($days > $this->shareApiLinkDefaultExpireDays()) {
+ $days = $this->shareApiLinkDefaultExpireDays();
+ }
+ $expirationDate->add(new \DateInterval('P' . $days . 'D'));
}
- $expirationDate->add(new \DateInterval('P' . $days . 'D'));
- }
-
- // If we enforce the expiration date check that is does not exceed
- if ($this->shareApiLinkDefaultExpireDateEnforced()) {
- if ($expirationDate === null) {
- throw new \InvalidArgumentException('Expiration date is enforced');
+
+ // If we enforce the expiration date check that is does not exceed
+ if ($isEnforced) {
+ if (empty($expirationDate)) {
+ throw new \InvalidArgumentException('Expiration date is enforced');
+ }
+
+ $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
+ $date->setTime(0, 0, 0);
+ $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
+ if ($date < $expirationDate) {
+ $message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $this->shareApiLinkDefaultExpireDays());
+ throw new GenericShareException($message, $message, 404);
+ }
}
- $date = new \DateTime();
- $date->setTime(0, 0, 0);
- $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
- if ($date < $expirationDate) {
- $message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $this->shareApiLinkDefaultExpireDays());
- throw new GenericShareException($message, $message, 404);
- }
}
$accepted = true;
@@ -528,6 +418,9 @@ class Manager implements IManager {
throw new \Exception($message);
}
+ if ($expirationDate instanceof \DateTime) {
+ $expirationDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
+ }
$share->setExpirationDate($expirationDate);
return $share;
@@ -549,6 +442,11 @@ class Manager implements IManager {
$this->groupManager->getUserGroupIds($sharedBy),
$this->groupManager->getUserGroupIds($sharedWith)
);
+
+ // optional excluded groups
+ $excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
+ $groups = array_diff($groups, $excludedGroups);
+
if (empty($groups)) {
$message_t = $this->l->t('Sharing is only allowed with group members');
throw new \Exception($message_t);
@@ -574,7 +472,7 @@ class Manager implements IManager {
// Identical share already exists
if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) {
- $message = $this->l->t('Sharing %s failed, because this item is already shared with user %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]);
+ $message = $this->l->t('Sharing %s failed, because this item is already shared with the account %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]);
throw new AlreadySharedException($message, $existingShare);
}
@@ -585,7 +483,7 @@ class Manager implements IManager {
$user = $this->userManager->get($share->getSharedWith());
if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
- $message = $this->l->t('Sharing %s failed, because this item is already shared with user %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]);
+ $message = $this->l->t('Sharing %s failed, because this item is already shared with the account %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]);
throw new AlreadySharedException($message, $existingShare);
}
}
@@ -609,7 +507,10 @@ class Manager implements IManager {
if ($this->shareWithGroupMembersOnly()) {
$sharedBy = $this->userManager->get($share->getSharedBy());
$sharedWith = $this->groupManager->get($share->getSharedWith());
- if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
+
+ // optional excluded groups
+ $excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
+ if (is_null($sharedWith) || in_array($share->getSharedWith(), $excludedGroups) || !$sharedWith->inGroup($sharedBy)) {
throw new \Exception('Sharing is only allowed within your own groups');
}
}
@@ -881,12 +782,12 @@ class Manager implements IManager {
* @param \DateTime|null $expiration
*/
protected function sendMailNotification(IL10N $l,
- $filename,
- $link,
- $initiator,
- $shareWith,
- \DateTime $expiration = null,
- $note = '') {
+ $filename,
+ $link,
+ $initiator,
+ $shareWith,
+ ?\DateTime $expiration = null,
+ $note = '') {
$initiatorUser = $this->userManager->get($initiator);
$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
@@ -988,7 +889,7 @@ class Manager implements IManager {
throw new \InvalidArgumentException('Cannot share with the share owner');
}
- $this->generalCreateChecks($share);
+ $this->generalCreateChecks($share, true);
if ($share->getShareType() === IShare::TYPE_USER) {
$this->userCreateChecks($share);
@@ -1300,9 +1201,12 @@ class Manager implements IManager {
public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) {
$providers = $this->factory->getAllProviders();
+ if (!$shallow) {
+ throw new \Exception("non-shallow getSharesInFolder is no longer supported");
+ }
return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares, $shallow) {
- $newShares = $provider->getSharesInFolder($userId, $node, $reshares, $shallow);
+ $newShares = $provider->getSharesInFolder($userId, $node, $reshares);
foreach ($newShares as $fid => $data) {
if (!isset($shares[$fid])) {
$shares[$fid] = [];
@@ -1574,14 +1478,8 @@ class Manager implements IManager {
* @return bool
*/
public function checkPassword(IShare $share, $password) {
- $passwordProtected = $share->getShareType() !== IShare::TYPE_LINK
- || $share->getShareType() !== IShare::TYPE_EMAIL
- || $share->getShareType() !== IShare::TYPE_CIRCLE;
- if (!$passwordProtected) {
- //TODO maybe exception?
- return false;
- }
+ // if there is no password on the share object / passsword is null, there is nothing to check
if ($password === null || $share->getPassword() === null) {
return false;
}
@@ -1661,9 +1559,10 @@ class Manager implements IManager {
* |-folder2 (32)
* |-fileA (42)
*
- * fileA is shared with user1 and user1@server1
+ * fileA is shared with user1 and user1@server1 and email1@maildomain1
* folder2 is shared with group2 (user4 is a member of group2)
* folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
+ * and email2@maildomain2
*
* Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
* [
@@ -1677,7 +1576,10 @@ class Manager implements IManager {
* 'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
* ],
* public => bool
- * mail => bool
+ * mail => [
+ * 'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'],
+ * 'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'],
+ * ]
* ]
*
* The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
@@ -1685,7 +1587,7 @@ class Manager implements IManager {
* users => ['user1', 'user2', 'user4'],
* remote => bool,
* public => bool
- * mail => bool
+ * mail => ['email1@maildomain1', 'email2@maildomain2']
* ]
*
* This is required for encryption/activity
@@ -1705,9 +1607,9 @@ class Manager implements IManager {
$owner = $owner->getUID();
if ($currentAccess) {
- $al = ['users' => [], 'remote' => [], 'public' => false];
+ $al = ['users' => [], 'remote' => [], 'public' => false, 'mail' => []];
} else {
- $al = ['users' => [], 'remote' => false, 'public' => false];
+ $al = ['users' => [], 'remote' => false, 'public' => false, 'mail' => []];
}
if (!$this->userManager->userExists($owner)) {
return $al;
@@ -1716,8 +1618,7 @@ class Manager implements IManager {
//Get node for the owner and correct the owner in case of external storage
$userFolder = $this->rootFolder->getUserFolder($owner);
if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
- $nodes = $userFolder->getById($path->getId());
- $path = array_shift($nodes);
+ $path = $userFolder->getFirstNodeById($path->getId());
if ($path === null || $path->getOwner() === null) {
return [];
}
@@ -1946,6 +1847,21 @@ class Manager implements IManager {
}
/**
+ * If shareWithGroupMembersOnly is enabled, return an optional
+ * list of groups that must be excluded from the principle of
+ * belonging to the same group.
+ *
+ * @return array
+ */
+ public function shareWithGroupMembersOnlyExcludeGroupsList() {
+ if (!$this->shareWithGroupMembersOnly()) {
+ return [];
+ }
+ $excludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', '');
+ return json_decode($excludeGroups, true) ?? [];
+ }
+
+ /**
* Check if users can share with groups
*
* @return bool
@@ -2025,37 +1941,7 @@ class Manager implements IManager {
* @return bool
*/
public function sharingDisabledForUser($userId) {
- if ($userId === null) {
- return false;
- }
-
- if (isset($this->sharingDisabledForUsersCache[$userId])) {
- return $this->sharingDisabledForUsersCache[$userId];
- }
-
- if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
- $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
- $excludedGroups = json_decode($groupsList);
- if (is_null($excludedGroups)) {
- $excludedGroups = explode(',', $groupsList);
- $newValue = json_encode($excludedGroups);
- $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
- }
- $user = $this->userManager->get($userId);
- $usersGroups = $this->groupManager->getUserGroupIds($user);
- if (!empty($usersGroups)) {
- $remainingGroups = array_diff($usersGroups, $excludedGroups);
- // if the user is only in groups which are disabled for sharing then
- // sharing is also disabled for the user
- if (empty($remainingGroups)) {
- $this->sharingDisabledForUsersCache[$userId] = true;
- return true;
- }
- }
- }
-
- $this->sharingDisabledForUsersCache[$userId] = false;
- return false;
+ return $this->shareDisableChecker->sharingDisabledForUser($userId);
}
/**
diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php
index ff6d9b0eacd..2800db8177a 100644
--- a/lib/private/Share20/ProviderFactory.php
+++ b/lib/private/Share20/ProviderFactory.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 Calviño Sánchez <danxuliu@gmail.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Maxence Lange <maxence@nextcloud.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Samuel <faust64@gmail.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 OC\Share20;
@@ -41,10 +15,14 @@ use OCA\FederatedFileSharing\TokenHandler;
use OCA\ShareByMail\Settings\SettingsManager;
use OCA\ShareByMail\ShareByMailProvider;
use OCA\Talk\Share\RoomShareProvider;
+use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Defaults;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Federation\ICloudFederationFactory;
+use OCP\Files\IRootFolder;
use OCP\IServerContainer;
use OCP\Mail\IMailer;
+use OCP\Security\IHasher;
use OCP\Share\IManager;
use OCP\Share\IProviderFactory;
use OCP\Share\IShare;
@@ -100,12 +78,12 @@ class ProviderFactory implements IProviderFactory {
$this->serverContainer->getDatabaseConnection(),
$this->serverContainer->getUserManager(),
$this->serverContainer->getGroupManager(),
- $this->serverContainer->getLazyRootFolder(),
+ $this->serverContainer->get(IRootFolder::class),
$this->serverContainer->get(IMailer::class),
$this->serverContainer->query(Defaults::class),
$this->serverContainer->getL10NFactory(),
$this->serverContainer->getURLGenerator(),
- $this->serverContainer->getConfig()
+ $this->serverContainer->query(ITimeFactory::class),
);
}
@@ -142,7 +120,7 @@ class ProviderFactory implements IProviderFactory {
$this->serverContainer->query(\OCP\OCS\IDiscoveryService::class),
$this->serverContainer->getJobList(),
\OC::$server->getCloudFederationProviderManager(),
- \OC::$server->getCloudFederationFactory(),
+ \OC::$server->get(ICloudFederationFactory::class),
$this->serverContainer->query(IEventDispatcher::class),
$this->serverContainer->get(LoggerInterface::class),
);
@@ -156,7 +134,7 @@ class ProviderFactory implements IProviderFactory {
$notifications,
$tokenHandler,
$l,
- $this->serverContainer->getLazyRootFolder(),
+ $this->serverContainer->get(IRootFolder::class),
$this->serverContainer->getConfig(),
$this->serverContainer->getUserManager(),
$this->serverContainer->getCloudIdManager(),
@@ -191,15 +169,15 @@ class ProviderFactory implements IProviderFactory {
$this->serverContainer->getDatabaseConnection(),
$this->serverContainer->getSecureRandom(),
$this->serverContainer->getUserManager(),
- $this->serverContainer->getLazyRootFolder(),
+ $this->serverContainer->get(IRootFolder::class),
$this->serverContainer->getL10N('sharebymail'),
- $this->serverContainer->getLogger(),
+ $this->serverContainer->get(LoggerInterface::class),
$this->serverContainer->get(IMailer::class),
$this->serverContainer->getURLGenerator(),
$this->serverContainer->getActivityManager(),
$settingsManager,
$this->serverContainer->query(Defaults::class),
- $this->serverContainer->getHasher(),
+ $this->serverContainer->get(IHasher::class),
$this->serverContainer->get(IEventDispatcher::class),
$this->serverContainer->get(IManager::class)
);
@@ -233,7 +211,7 @@ class ProviderFactory implements IProviderFactory {
$this->serverContainer->getDatabaseConnection(),
$this->serverContainer->getSecureRandom(),
$this->serverContainer->getUserManager(),
- $this->serverContainer->getLazyRootFolder(),
+ $this->serverContainer->get(IRootFolder::class),
$this->serverContainer->getL10N('circles'),
$this->serverContainer->getLogger(),
$this->serverContainer->getURLGenerator()
diff --git a/lib/private/Share20/PublicShareTemplateFactory.php b/lib/private/Share20/PublicShareTemplateFactory.php
index 222f327496a..34dd9b13b61 100644
--- a/lib/private/Share20/PublicShareTemplateFactory.php
+++ b/lib/private/Share20/PublicShareTemplateFactory.php
@@ -2,34 +2,18 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2023 Louis Chemineau <louis@chmn.me>
- *
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
-
namespace OC\Share20;
use Exception;
use OC\AppFramework\Bootstrap\Coordinator;
use OCA\Files_Sharing\DefaultPublicShareTemplateProvider;
use OCP\Server;
-use OCP\Share\IShare;
use OCP\Share\IPublicShareTemplateFactory;
use OCP\Share\IPublicShareTemplateProvider;
+use OCP\Share\IShare;
class PublicShareTemplateFactory implements IPublicShareTemplateFactory {
public function __construct(
diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php
index 0a50fa0ccfb..ac95e3ac0d4 100644
--- a/lib/private/Share20/Share.php
+++ b/lib/private/Share20/Share.php
@@ -1,36 +1,14 @@
<?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 Calviño Sánchez <danxuliu@gmail.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author John Molakvoæ <skjnldsv@protonmail.com>
- * @author Maxence Lange <maxence@nextcloud.com>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @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 OC\Share20;
-use OCP\Files\File;
use OCP\Files\Cache\ICacheEntry;
+use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
@@ -90,28 +68,24 @@ class Share implements IShare {
private $mailSend;
/** @var string */
private $label = '';
-
- /** @var IRootFolder */
- private $rootFolder;
-
- /** @var IUserManager */
- private $userManager;
-
/** @var ICacheEntry|null */
private $nodeCacheEntry;
-
/** @var bool */
private $hideDownload = false;
- public function __construct(IRootFolder $rootFolder, IUserManager $userManager) {
- $this->rootFolder = $rootFolder;
- $this->userManager = $userManager;
+ private bool $noExpirationDate = false;
+
+ public function __construct(
+ private IRootFolder $rootFolder,
+ private IUserManager $userManager,
+ ) {
}
/**
* @inheritdoc
*/
public function setId($id) {
+ /** @var mixed $id Let's be safe until strong typing */
if (is_int($id)) {
$id = (string)$id;
}
@@ -188,12 +162,12 @@ class Share implements IShare {
$userFolder = $this->rootFolder->getUserFolder($this->sharedBy);
}
- $nodes = $userFolder->getById($this->fileId);
- if (empty($nodes)) {
+ $node = $userFolder->getFirstNodeById($this->fileId);
+ if (!$node) {
throw new NotFoundException('Node for share not found, fileid: ' . $this->fileId);
}
- $this->node = $nodes[0];
+ $this->node = $node;
}
return $this->node;
@@ -211,12 +185,16 @@ class Share implements IShare {
/**
* @inheritdoc
*/
- public function getNodeId() {
+ public function getNodeId(): int {
if ($this->fileId === null) {
$this->fileId = $this->getNode()->getId();
}
- return $this->fileId;
+ if ($this->fileId === null) {
+ throw new NotFoundException("Share source not found");
+ } else {
+ return $this->fileId;
+ }
}
/**
@@ -424,6 +402,21 @@ class Share implements IShare {
/**
* @inheritdoc
*/
+ public function setNoExpirationDate(bool $noExpirationDate) {
+ $this->noExpirationDate = $noExpirationDate;
+ return $this;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getNoExpirationDate(): bool {
+ return $this->noExpirationDate;
+ }
+
+ /**
+ * @inheritdoc
+ */
public function isExpired() {
return $this->getExpirationDate() !== null &&
$this->getExpirationDate() <= new \DateTime();
@@ -534,7 +527,7 @@ class Share implements IShare {
/**
* Set the parent of this share
*
- * @param int parent
+ * @param int $parent
* @return IShare
* @deprecated The new shares do not have parents. This is just here for legacy reasons.
*/
diff --git a/lib/private/Share20/ShareAttributes.php b/lib/private/Share20/ShareAttributes.php
index 9b97c94275d..abbbd36759b 100644
--- a/lib/private/Share20/ShareAttributes.php
+++ b/lib/private/Share20/ShareAttributes.php
@@ -1,22 +1,9 @@
<?php
+
/**
- * @author Piotr Mrowczynski <piotr@owncloud.com>
- *
- * @copyright Copyright (c) 2019, ownCloud GmbH
- * @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: 2023-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2019-2022 ownCloud GmbH
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Share20;
diff --git a/lib/private/Share20/ShareDisableChecker.php b/lib/private/Share20/ShareDisableChecker.php
new file mode 100644
index 00000000000..9b8bc01558c
--- /dev/null
+++ b/lib/private/Share20/ShareDisableChecker.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OC\Share20;
+
+use OCP\Cache\CappedMemoryCache;
+use OCP\IConfig;
+use OCP\IGroupManager;
+use OCP\IUserManager;
+
+/**
+ * split of from the share manager to allow using it with minimal DI
+ */
+class ShareDisableChecker {
+ private CappedMemoryCache $sharingDisabledForUsersCache;
+
+ public function __construct(
+ private IConfig $config,
+ private IUserManager $userManager,
+ private IGroupManager $groupManager,
+ ) {
+ $this->sharingDisabledForUsersCache = new CappedMemoryCache();
+ }
+
+
+ /**
+ * @param ?string $userId
+ * @return bool
+ */
+ public function sharingDisabledForUser(?string $userId) {
+ if ($userId === null) {
+ return false;
+ }
+
+ if (isset($this->sharingDisabledForUsersCache[$userId])) {
+ return $this->sharingDisabledForUsersCache[$userId];
+ }
+
+ $excludeGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no');
+
+ if ($excludeGroups && $excludeGroups !== 'no') {
+ $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
+ $excludedGroups = json_decode($groupsList);
+ if (is_null($excludedGroups)) {
+ $excludedGroups = explode(',', $groupsList);
+ $newValue = json_encode($excludedGroups);
+ $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
+ }
+ $user = $this->userManager->get($userId);
+ if (!$user) {
+ return false;
+ }
+ $usersGroups = $this->groupManager->getUserGroupIds($user);
+ if ($excludeGroups !== 'allow') {
+ if (!empty($usersGroups)) {
+ $remainingGroups = array_diff($usersGroups, $excludedGroups);
+ // if the user is only in groups which are disabled for sharing then
+ // sharing is also disabled for the user
+ if (empty($remainingGroups)) {
+ $this->sharingDisabledForUsersCache[$userId] = true;
+ return true;
+ }
+ }
+ } else {
+ if (!empty($usersGroups)) {
+ $remainingGroups = array_intersect($usersGroups, $excludedGroups);
+ // if the user is in any group which is allowed for sharing then
+ // sharing is also allowed for the user
+ if (!empty($remainingGroups)) {
+ $this->sharingDisabledForUsersCache[$userId] = false;
+ return false;
+ }
+ }
+ $this->sharingDisabledForUsersCache[$userId] = true;
+ return true;
+ }
+ }
+
+ $this->sharingDisabledForUsersCache[$userId] = false;
+ return false;
+ }
+}
diff --git a/lib/private/Share20/ShareHelper.php b/lib/private/Share20/ShareHelper.php
index 3debfe185e0..d4a54f1d687 100644
--- a/lib/private/Share20/ShareHelper.php
+++ b/lib/private/Share20/ShareHelper.php
@@ -1,25 +1,7 @@
<?php
/**
- * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @author Joas Schilling <coding@schilljs.com>
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Share20;
diff --git a/lib/private/Share20/UserRemovedListener.php b/lib/private/Share20/UserRemovedListener.php
index 3af7b5a3650..f06c945b591 100644
--- a/lib/private/Share20/UserRemovedListener.php
+++ b/lib/private/Share20/UserRemovedListener.php
@@ -3,25 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
- *
- * @author Joas Schilling <coding@schilljs.com>
- *
- * @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/>.
- *
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Share20;