diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2024-05-30 14:17:52 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-30 14:17:52 +0200 |
commit | 7f2a1e5b7a1724727bc274e73a9a6fd09249c7a6 (patch) | |
tree | fbc014b411855706acf31e082373e45b96640287 /lib/private/Share20 | |
parent | 7292a8d8fe7e4cf8667194f4af587f6923a0f884 (diff) | |
parent | 31b0a44cf65b6625636ea0fa15fb1a1122b525e1 (diff) | |
download | nextcloud-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.php | 194 | ||||
-rw-r--r-- | lib/private/Share20/Exception/BackendError.php | 21 | ||||
-rw-r--r-- | lib/private/Share20/Exception/InvalidShare.php | 21 | ||||
-rw-r--r-- | lib/private/Share20/Exception/ProviderException.php | 22 | ||||
-rw-r--r-- | lib/private/Share20/Hooks.php | 28 | ||||
-rw-r--r-- | lib/private/Share20/LegacyHooks.php | 25 | ||||
-rw-r--r-- | lib/private/Share20/Manager.php | 468 | ||||
-rw-r--r-- | lib/private/Share20/ProviderFactory.php | 54 | ||||
-rw-r--r-- | lib/private/Share20/PublicShareTemplateFactory.php | 22 | ||||
-rw-r--r-- | lib/private/Share20/Share.php | 81 | ||||
-rw-r--r-- | lib/private/Share20/ShareAttributes.php | 21 | ||||
-rw-r--r-- | lib/private/Share20/ShareDisableChecker.php | 85 | ||||
-rw-r--r-- | lib/private/Share20/ShareHelper.php | 22 | ||||
-rw-r--r-- | lib/private/Share20/UserRemovedListener.php | 21 |
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; |