diff options
Diffstat (limited to 'lib/private/Share')
-rw-r--r-- | lib/private/Share/Constants.php | 27 | ||||
-rw-r--r-- | lib/private/Share/Helper.php | 138 | ||||
-rw-r--r-- | lib/private/Share/Share.php | 180 |
3 files changed, 345 insertions, 0 deletions
diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php new file mode 100644 index 00000000000..c55caee6f0a --- /dev/null +++ b/lib/private/Share/Constants.php @@ -0,0 +1,27 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Share; + +class Constants { + public const FORMAT_NONE = -1; + public const FORMAT_STATUSES = -2; + public const FORMAT_SOURCES = -3; // ToDo Check if it is still in use otherwise remove it + + public const RESPONSE_FORMAT = 'json'; // default response format for ocs calls + + public const MIN_TOKEN_LENGTH = 6; // 19,770,609,664 different possible variations + public const DEFAULT_TOKEN_LENGTH = 15; // 54,960,434,128,018,667,122,720,768 different possible variations + public const MAX_TOKEN_LENGTH = 32; // 8,167,835,760,036,914,488,254,418,108,462,708,901,695,678,621,570,564,096 different possible variations + public const TOKEN_LENGTH = self::DEFAULT_TOKEN_LENGTH; // old (oc7) length is 32, keep token length in db at least that for compatibility + + protected static $shareTypeUserAndGroups = -1; + protected static $shareTypeGroupUserUnique = 2; + protected static $backends = []; + protected static $backendTypes = []; + protected static $isResharingAllowed; +} diff --git a/lib/private/Share/Helper.php b/lib/private/Share/Helper.php new file mode 100644 index 00000000000..d53f9d6ed94 --- /dev/null +++ b/lib/private/Share/Helper.php @@ -0,0 +1,138 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Share; + +class Helper extends \OC\Share\Constants { + /** + * get default expire settings defined by the admin + * @return array contains 'defaultExpireDateSet', 'enforceExpireDate', 'expireAfterDays' + */ + public static function getDefaultExpireSetting() { + $config = \OC::$server->getConfig(); + + $defaultExpireSettings = ['defaultExpireDateSet' => false]; + + // get default expire settings + $defaultExpireDate = $config->getAppValue('core', 'shareapi_default_expire_date', 'no'); + if ($defaultExpireDate === 'yes') { + $enforceExpireDate = $config->getAppValue('core', 'shareapi_enforce_expire_date', 'no'); + $defaultExpireSettings['defaultExpireDateSet'] = true; + $defaultExpireSettings['expireAfterDays'] = (int)$config->getAppValue('core', 'shareapi_expire_after_n_days', '7'); + $defaultExpireSettings['enforceExpireDate'] = $enforceExpireDate === 'yes'; + } + + return $defaultExpireSettings; + } + + public static function calcExpireDate() { + $expireAfter = \OC\Share\Share::getExpireInterval() * 24 * 60 * 60; + $expireAt = time() + $expireAfter; + $date = new \DateTime(); + $date->setTimestamp($expireAt); + $date->setTime(0, 0, 0); + //$dateString = $date->format('Y-m-d') . ' 00:00:00'; + + return $date; + } + + /** + * calculate expire date + * @param array $defaultExpireSettings contains 'defaultExpireDateSet', 'enforceExpireDate', 'expireAfterDays' + * @param int $creationTime timestamp when the share was created + * @param int $userExpireDate expire timestamp set by the user + * @return mixed integer timestamp or False + */ + public static function calculateExpireDate($defaultExpireSettings, $creationTime, $userExpireDate = null) { + $expires = false; + $defaultExpires = null; + + if (!empty($defaultExpireSettings['defaultExpireDateSet'])) { + $defaultExpires = $creationTime + $defaultExpireSettings['expireAfterDays'] * 86400; + } + + + if (isset($userExpireDate)) { + // if the admin decided to enforce the default expire date then we only take + // the user defined expire date of it is before the default expire date + if ($defaultExpires && !empty($defaultExpireSettings['enforceExpireDate'])) { + $expires = min($userExpireDate, $defaultExpires); + } else { + $expires = $userExpireDate; + } + } elseif ($defaultExpires && !empty($defaultExpireSettings['enforceExpireDate'])) { + $expires = $defaultExpires; + } + + return $expires; + } + + /** + * Strips away a potential file names and trailing slashes: + * - http://localhost + * - http://localhost/ + * - http://localhost/index.php + * - http://localhost/index.php/s/{shareToken} + * + * all return: http://localhost + * + * @param string $remote + * @return string + */ + protected static function fixRemoteURL($remote) { + $remote = str_replace('\\', '/', $remote); + if ($fileNamePosition = strpos($remote, '/index.php')) { + $remote = substr($remote, 0, $fileNamePosition); + } + $remote = rtrim($remote, '/'); + + return $remote; + } + + /** + * check if two federated cloud IDs refer to the same user + * + * @param string $user1 + * @param string $server1 + * @param string $user2 + * @param string $server2 + * @return bool true if both users and servers are the same + */ + public static function isSameUserOnSameServer($user1, $server1, $user2, $server2) { + $normalizedServer1 = strtolower(\OC\Share\Share::removeProtocolFromUrl($server1)); + $normalizedServer2 = strtolower(\OC\Share\Share::removeProtocolFromUrl($server2)); + + if (rtrim($normalizedServer1, '/') === rtrim($normalizedServer2, '/')) { + // FIXME this should be a method in the user management instead + \OCP\Util::emitHook( + '\OCA\Files_Sharing\API\Server2Server', + 'preLoginNameUsedAsUserName', + ['uid' => &$user1] + ); + \OCP\Util::emitHook( + '\OCA\Files_Sharing\API\Server2Server', + 'preLoginNameUsedAsUserName', + ['uid' => &$user2] + ); + + if ($user1 === $user2) { + return true; + } + } + + return false; + } + + public static function getTokenLength(): int { + $config = \OCP\Server::get(\OCP\IAppConfig::class); + $tokenLength = $config->getValueInt('core', 'shareapi_token_length', self::DEFAULT_TOKEN_LENGTH); + $tokenLength = $tokenLength ?: self::DEFAULT_TOKEN_LENGTH; + + // Token length should be within the defined min and max limits + return max(self::MIN_TOKEN_LENGTH, min($tokenLength, self::MAX_TOKEN_LENGTH)); + } +} diff --git a/lib/private/Share/Share.php b/lib/private/Share/Share.php new file mode 100644 index 00000000000..1121d71e45f --- /dev/null +++ b/lib/private/Share/Share.php @@ -0,0 +1,180 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Share; + +use OCA\Files_Sharing\ShareBackend\File; +use Psr\Log\LoggerInterface; + +/** + * This class provides the ability for apps to share their content between users. + * Apps must create a backend class that implements OCP\Share_Backend and register it with this class. + * + * It provides the following hooks: + * - post_shared + */ +class Share extends Constants { + /** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask + * Construct permissions for share() and setPermissions with Or (|) e.g. + * Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE + * + * Check if permission is granted with And (&) e.g. Check if delete is + * granted: if ($permissions & PERMISSION_DELETE) + * + * Remove permissions with And (&) and Not (~) e.g. Remove the update + * permission: $permissions &= ~PERMISSION_UPDATE + * + * Apps are required to handle permissions on their own, this class only + * stores and manages the permissions of shares + * + * @see lib/public/Constants.php + */ + + /** + * Register a sharing backend class that implements OCP\Share_Backend for an item type + * + * @param string $itemType Item type + * @param string $class Backend class + * @param string $collectionOf (optional) Depends on item type + * @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files + * @return boolean true if backend is registered or false if error + */ + public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) { + if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') == 'yes') { + if (!isset(self::$backendTypes[$itemType])) { + self::$backendTypes[$itemType] = [ + 'class' => $class, + 'collectionOf' => $collectionOf, + 'supportedFileExtensions' => $supportedFileExtensions + ]; + return true; + } + \OC::$server->get(LoggerInterface::class)->warning( + 'Sharing backend ' . $class . ' not registered, ' . self::$backendTypes[$itemType]['class'] + . ' is already registered for ' . $itemType, + ['app' => 'files_sharing']); + } + return false; + } + + /** + * Get the backend class for the specified item type + * + * @param string $itemType + * @return \OCP\Share_Backend + * @throws \Exception + */ + public static function getBackend($itemType) { + $l = \OCP\Util::getL10N('lib'); + $logger = \OCP\Server::get(LoggerInterface::class); + if (isset(self::$backends[$itemType])) { + return self::$backends[$itemType]; + } elseif (isset(self::$backendTypes[$itemType]['class'])) { + $class = self::$backendTypes[$itemType]['class']; + if (class_exists($class)) { + self::$backends[$itemType] = new $class; + if (!(self::$backends[$itemType] instanceof \OCP\Share_Backend)) { + $message = 'Sharing backend %s must implement the interface OCP\Share_Backend'; + $message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', [$class]); + $logger->error(sprintf($message, $class), ['app' => 'OCP\Share']); + throw new \Exception($message_t); + } + return self::$backends[$itemType]; + } else { + $message = 'Sharing backend %s not found'; + $message_t = $l->t('Sharing backend %s not found', [$class]); + $logger->error(sprintf($message, $class), ['app' => 'OCP\Share']); + throw new \Exception($message_t); + } + } + $message = 'Sharing backend for %s not found'; + $message_t = $l->t('Sharing backend for %s not found', [$itemType]); + $logger->error(sprintf($message, $itemType), ['app' => 'OCP\Share']); + throw new \Exception($message_t); + } + + /** + * Check if resharing is allowed + * + * @return boolean true if allowed or false + * + * Resharing is allowed by default if not configured + */ + public static function isResharingAllowed() { + if (!isset(self::$isResharingAllowed)) { + if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') { + self::$isResharingAllowed = true; + } else { + self::$isResharingAllowed = false; + } + } + return self::$isResharingAllowed; + } + + /** + * group items with link to the same source + * + * @param array $items + * @param string $itemType + * @return array of grouped items + */ + protected static function groupItems($items, $itemType) { + $fileSharing = $itemType === 'file' || $itemType === 'folder'; + + $result = []; + + foreach ($items as $item) { + $grouped = false; + foreach ($result as $key => $r) { + // for file/folder shares we need to compare file_source, otherwise we compare item_source + // only group shares if they already point to the same target, otherwise the file where shared + // before grouping of shares was added. In this case we don't group them to avoid confusions + if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) + || (!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) { + // add the first item to the list of grouped shares + if (!isset($result[$key]['grouped'])) { + $result[$key]['grouped'][] = $result[$key]; + } + $result[$key]['permissions'] = (int)$item['permissions'] | (int)$r['permissions']; + $result[$key]['grouped'][] = $item; + $grouped = true; + break; + } + } + + if (!$grouped) { + $result[] = $item; + } + } + + return $result; + } + + /** + * remove protocol from URL + * + * @param string $url + * @return string + */ + public static function removeProtocolFromUrl($url) { + if (str_starts_with($url, 'https://')) { + return substr($url, strlen('https://')); + } elseif (str_starts_with($url, 'http://')) { + return substr($url, strlen('http://')); + } + + return $url; + } + + + /** + * @return int + */ + public static function getExpireInterval() { + return (int)\OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7'); + } +} |