diff options
-rw-r--r-- | apps/files_sharing/lib/Controller/ShareAPIController.php | 24 | ||||
-rw-r--r-- | lib/private/Share20/Manager.php | 76 | ||||
-rw-r--r-- | lib/public/Share/Exceptions/ShareTokenException.php | 16 | ||||
-rw-r--r-- | lib/public/Share/IManager.php | 9 |
4 files changed, 90 insertions, 35 deletions
diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 57022f7ea1c..b00c97f0dd3 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -21,6 +21,7 @@ use OCA\Files_Sharing\SharedStorage; use OCA\GlobalSiteSelector\Service\SlaveService; use OCP\App\IAppManager; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\ApiRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\UserRateLimit; use OCP\AppFramework\Http\DataResponse; @@ -52,6 +53,7 @@ use OCP\Lock\LockedException; use OCP\Mail\IMailer; use OCP\Server; use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\Exceptions\ShareTokenException; use OCP\Share\IManager; use OCP\Share\IProviderFactory; use OCP\Share\IShare; @@ -2172,4 +2174,26 @@ class ShareAPIController extends OCSController { throw new OCSNotFoundException($this->l->t('Wrong share ID, share does not exist')); } } + + /** + * Get a unique share token + * + * @throws OCSException Failed to generate a unique token + * + * @return DataResponse<Http::STATUS_OK, array{token: string}, array{}> + * + * 200: Token generated successfully + */ + #[ApiRoute(verb: 'GET', url: '/api/v1/token')] + #[NoAdminRequired] + public function generateToken(): DataResponse { + try { + $token = $this->shareManager->generateToken(); + return new DataResponse([ + 'token' => $token, + ]); + } catch (ShareTokenException $e) { + throw new OCSException($this->l->t('Failed to generate a unique token')); + } + } } diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 219f3d86380..4138819f6b1 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -43,6 +43,7 @@ use OCP\Share\Events\ShareDeletedFromSelfEvent; use OCP\Share\Exceptions\AlreadySharedException; use OCP\Share\Exceptions\GenericShareException; use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\Exceptions\ShareTokenException; use OCP\Share\IManager; use OCP\Share\IProviderFactory; use OCP\Share\IShare; @@ -659,41 +660,7 @@ class Manager implements IManager { $this->linkCreateChecks($share); $this->setLinkParent($share); - // Initial token length - $tokenLength = \OC\Share\Helper::getTokenLength(); - - do { - $tokenExists = false; - - for ($i = 0; $i <= 2; $i++) { - // Generate a new token - $token = $this->secureRandom->generate( - $tokenLength, - \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE - ); - - try { - // Try to fetch a share with the generated token - $this->getShareByToken($token); - $tokenExists = true; // Token exists, we need to try again - } catch (\OCP\Share\Exceptions\ShareNotFound $e) { - // Token is unique, exit the loop - $tokenExists = false; - break; - } - } - - // If we've reached the maximum attempts and the token still exists, increase the token length - if ($tokenExists) { - $tokenLength++; - - // Check if the token length exceeds the maximum allowed length - if ($tokenLength > \OC\Share\Constants::MAX_TOKEN_LENGTH) { - throw new \Exception('Unable to generate a unique share token. Maximum token length exceeded.'); - } - } - } while ($tokenExists); - + $token = $this->generateToken(); // Set the unique token $share->setToken($token); @@ -2025,4 +1992,43 @@ class Manager implements IManager { yield from $provider->getAllShares(); } } + + public function generateToken(): string { + // Initial token length + $tokenLength = \OC\Share\Helper::getTokenLength(); + + do { + $tokenExists = false; + + for ($i = 0; $i <= 2; $i++) { + // Generate a new token + $token = $this->secureRandom->generate( + $tokenLength, + ISecureRandom::CHAR_HUMAN_READABLE, + ); + + try { + // Try to fetch a share with the generated token + $this->getShareByToken($token); + $tokenExists = true; // Token exists, we need to try again + } catch (ShareNotFound $e) { + // Token is unique, exit the loop + $tokenExists = false; + break; + } + } + + // If we've reached the maximum attempts and the token still exists, increase the token length + if ($tokenExists) { + $tokenLength++; + + // Check if the token length exceeds the maximum allowed length + if ($tokenLength > \OC\Share\Constants::MAX_TOKEN_LENGTH) { + throw new ShareTokenException('Unable to generate a unique share token. Maximum token length exceeded.'); + } + } + } while ($tokenExists); + + return $token; + } } diff --git a/lib/public/Share/Exceptions/ShareTokenException.php b/lib/public/Share/Exceptions/ShareTokenException.php new file mode 100644 index 00000000000..027d00640e9 --- /dev/null +++ b/lib/public/Share/Exceptions/ShareTokenException.php @@ -0,0 +1,16 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCP\Share\Exceptions; + +use Exception; + +/** + * @since 31.0.0 + */ +class ShareTokenException extends Exception { +} diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index c3a8494a5ac..85b421c04f6 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -13,6 +13,7 @@ use OCP\Files\Node; use OCP\IUser; use OCP\Share\Exceptions\GenericShareException; use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\Exceptions\ShareTokenException; /** * This interface allows to manage sharing files between users and groups. @@ -522,4 +523,12 @@ interface IManager { * @since 18.0.0 */ public function getAllShares(): iterable; + + /** + * Generate a unique share token + * + * @throws ShareTokenException Failed to generate a unique token + * @since 31.0.0 + */ + public function generateToken(): string; } |