diff options
Diffstat (limited to 'lib/private/Federation')
-rw-r--r-- | lib/private/Federation/CloudFederationFactory.php | 22 | ||||
-rw-r--r-- | lib/private/Federation/CloudFederationNotification.php | 22 | ||||
-rw-r--r-- | lib/private/Federation/CloudFederationProviderManager.php | 140 | ||||
-rw-r--r-- | lib/private/Federation/CloudFederationShare.php | 29 | ||||
-rw-r--r-- | lib/private/Federation/CloudId.php | 65 | ||||
-rw-r--r-- | lib/private/Federation/CloudIdManager.php | 121 |
6 files changed, 176 insertions, 223 deletions
diff --git a/lib/private/Federation/CloudFederationFactory.php b/lib/private/Federation/CloudFederationFactory.php index 391213c21e0..d06de0f2f58 100644 --- a/lib/private/Federation/CloudFederationFactory.php +++ b/lib/private/Federation/CloudFederationFactory.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * - * @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: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Federation; diff --git a/lib/private/Federation/CloudFederationNotification.php b/lib/private/Federation/CloudFederationNotification.php index aa1c3dc9e65..6ae805df1d9 100644 --- a/lib/private/Federation/CloudFederationNotification.php +++ b/lib/private/Federation/CloudFederationNotification.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * - * @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: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Federation; diff --git a/lib/private/Federation/CloudFederationProviderManager.php b/lib/private/Federation/CloudFederationProviderManager.php index 07df3e7a209..81b5d717a56 100644 --- a/lib/private/Federation/CloudFederationProviderManager.php +++ b/lib/private/Federation/CloudFederationProviderManager.php @@ -3,31 +3,14 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Maxence Lange <maxence@artificial-owl.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: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Federation; +use NCU\Security\Signature\ISignatureManager; use OC\AppFramework\Http; +use OC\OCM\OCMSignatoryManager; use OCP\App\IAppManager; use OCP\Federation\Exceptions\ProviderDoesNotExistsException; use OCP\Federation\ICloudFederationNotification; @@ -35,8 +18,10 @@ use OCP\Federation\ICloudFederationProvider; use OCP\Federation\ICloudFederationProviderManager; use OCP\Federation\ICloudFederationShare; use OCP\Federation\ICloudIdManager; +use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\Http\Client\IResponse; +use OCP\IAppConfig; use OCP\IConfig; use OCP\OCM\Exceptions\OCMProviderException; use OCP\OCM\IOCMDiscoveryService; @@ -56,10 +41,13 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager public function __construct( private IConfig $config, private IAppManager $appManager, + private IAppConfig $appConfig, private IClientService $httpClientService, private ICloudIdManager $cloudIdManager, private IOCMDiscoveryService $discoveryService, - private LoggerInterface $logger + private readonly ISignatureManager $signatureManager, + private readonly OCMSignatoryManager $signatoryManager, + private LoggerInterface $logger, ) { } @@ -118,21 +106,11 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager public function sendShare(ICloudFederationShare $share) { $cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith()); try { - $ocmProvider = $this->discoveryService->discover($cloudID->getRemote()); - } catch (OCMProviderException $e) { - return false; - } - - $client = $this->httpClientService->newClient(); - try { - $response = $client->post($ocmProvider->getEndPoint() . '/shares', [ - 'body' => json_encode($share->getShare()), - 'headers' => ['content-type' => 'application/json'], - 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), - 'timeout' => 10, - 'connect_timeout' => 10, - ]); - + try { + $response = $this->postOcmPayload($cloudID->getRemote(), '/shares', json_encode($share->getShare())); + } catch (OCMProviderException) { + return false; + } if ($response->getStatusCode() === Http::STATUS_CREATED) { $result = json_decode($response->getBody(), true); return (is_array($result)) ? $result : []; @@ -158,17 +136,9 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager */ public function sendCloudShare(ICloudFederationShare $share): IResponse { $cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith()); - $ocmProvider = $this->discoveryService->discover($cloudID->getRemote()); - $client = $this->httpClientService->newClient(); try { - return $client->post($ocmProvider->getEndPoint() . '/shares', [ - 'body' => json_encode($share->getShare()), - 'headers' => ['content-type' => 'application/json'], - 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), - 'timeout' => 10, - 'connect_timeout' => 10, - ]); + return $this->postOcmPayload($cloudID->getRemote(), '/shares', json_encode($share->getShare()), $client); } catch (\Throwable $e) { $this->logger->error('Error while sending share to federation server: ' . $e->getMessage(), ['exception' => $e]); try { @@ -187,20 +157,11 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager */ public function sendNotification($url, ICloudFederationNotification $notification) { try { - $ocmProvider = $this->discoveryService->discover($url); - } catch (OCMProviderException $e) { - return false; - } - - $client = $this->httpClientService->newClient(); - try { - $response = $client->post($ocmProvider->getEndPoint() . '/notifications', [ - 'body' => json_encode($notification->getMessage()), - 'headers' => ['content-type' => 'application/json'], - 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), - 'timeout' => 10, - 'connect_timeout' => 10, - ]); + try { + $response = $this->postOcmPayload($url, '/notifications', json_encode($notification->getMessage())); + } catch (OCMProviderException) { + return false; + } if ($response->getStatusCode() === Http::STATUS_CREATED) { $result = json_decode($response->getBody(), true); return (is_array($result)) ? $result : []; @@ -220,17 +181,9 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager * @throws OCMProviderException */ public function sendCloudNotification(string $url, ICloudFederationNotification $notification): IResponse { - $ocmProvider = $this->discoveryService->discover($url); - $client = $this->httpClientService->newClient(); try { - return $client->post($ocmProvider->getEndPoint() . '/notifications', [ - 'body' => json_encode($notification->getMessage()), - 'headers' => ['content-type' => 'application/json'], - 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), - 'timeout' => 10, - 'connect_timeout' => 10, - ]); + return $this->postOcmPayload($url, '/notifications', json_encode($notification->getMessage()), $client); } catch (\Throwable $e) { $this->logger->error('Error while sending notification to federation server: ' . $e->getMessage(), ['exception' => $e]); try { @@ -249,4 +202,53 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager public function isReady() { return $this->appManager->isEnabledForUser('cloud_federation_api'); } + + /** + * @param string $cloudId + * @param string $uri + * @param string $payload + * + * @return IResponse + * @throws OCMProviderException + */ + private function postOcmPayload(string $cloudId, string $uri, string $payload, ?IClient $client = null): IResponse { + $ocmProvider = $this->discoveryService->discover($cloudId); + $uri = $ocmProvider->getEndPoint() . '/' . ltrim($uri, '/'); + $client = $client ?? $this->httpClientService->newClient(); + return $client->post($uri, $this->prepareOcmPayload($uri, $payload)); + } + + /** + * @param string $uri + * @param string $payload + * + * @return array + */ + private function prepareOcmPayload(string $uri, string $payload): array { + $payload = array_merge($this->getDefaultRequestOptions(), ['body' => $payload]); + + if ($this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_ENFORCED, lazy: true) + && $this->signatoryManager->getRemoteSignatory($this->signatureManager->extractIdentityFromUri($uri)) === null) { + return $payload; + } + + if (!$this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_DISABLED, lazy: true)) { + $signedPayload = $this->signatureManager->signOutgoingRequestIClientPayload( + $this->signatoryManager, + $payload, + 'post', $uri + ); + } + + return $signedPayload ?? $payload; + } + + private function getDefaultRequestOptions(): array { + return [ + 'headers' => ['content-type' => 'application/json'], + 'timeout' => 10, + 'connect_timeout' => 10, + 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), + ]; + } } diff --git a/lib/private/Federation/CloudFederationShare.php b/lib/private/Federation/CloudFederationShare.php index 4b741b28bee..2eb06b3acea 100644 --- a/lib/private/Federation/CloudFederationShare.php +++ b/lib/private/Federation/CloudFederationShare.php @@ -1,25 +1,8 @@ <?php + /** - * @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @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: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Federation; @@ -38,6 +21,8 @@ class CloudFederationShare implements ICloudFederationShare { 'ownerDisplayName' => '', 'sharedBy' => '', 'sharedByDisplayName' => '', + 'sender' => '', + 'senderDisplayName' => '', 'protocol' => [] ]; @@ -66,7 +51,7 @@ class CloudFederationShare implements ICloudFederationShare { $sharedByDisplayName = '', $shareType = '', $resourceType = '', - $sharedSecret = '' + $sharedSecret = '', ) { $this->setShareWith($shareWith); $this->setResourceName($name); @@ -173,6 +158,7 @@ class CloudFederationShare implements ICloudFederationShare { */ public function setSharedBy($sharedBy) { $this->share['sharedBy'] = $sharedBy; + $this->share['sender'] = $sharedBy; } /** @@ -184,6 +170,7 @@ class CloudFederationShare implements ICloudFederationShare { */ public function setSharedByDisplayName($sharedByDisplayName) { $this->share['sharedByDisplayName'] = $sharedByDisplayName; + $this->share['senderDisplayName'] = $sharedByDisplayName; } /** diff --git a/lib/private/Federation/CloudId.php b/lib/private/Federation/CloudId.php index 50e974831a6..b807c29d812 100644 --- a/lib/private/Federation/CloudId.php +++ b/lib/private/Federation/CloudId.php @@ -3,54 +3,21 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017, Robin Appelman <robin@icewind.nl> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Robin Appelman <robin@icewind.nl> - * @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\Federation; use OCP\Federation\ICloudId; +use OCP\Federation\ICloudIdManager; class CloudId implements ICloudId { - /** @var string */ - private $id; - /** @var string */ - private $user; - /** @var string */ - private $remote; - /** @var string|null */ - private $displayName; - - /** - * CloudId constructor. - * - * @param string $id - * @param string $user - * @param string $remote - */ - public function __construct(string $id, string $user, string $remote, ?string $displayName = null) { - $this->id = $id; - $this->user = $user; - $this->remote = $remote; - $this->displayName = $displayName; + public function __construct( + protected string $id, + protected string $user, + protected string $remote, + protected ?string $displayName = null, + ) { } /** @@ -63,12 +30,18 @@ class CloudId implements ICloudId { } public function getDisplayId(): string { + if ($this->displayName === null) { + /** @var CloudIdManager $cloudIdManager */ + $cloudIdManager = \OCP\Server::get(ICloudIdManager::class); + $this->displayName = $cloudIdManager->getDisplayNameFromContact($this->getId()); + } + + $atHost = str_replace(['http://', 'https://'], '', $this->getRemote()); + if ($this->displayName) { - $atPos = strrpos($this->getId(), '@'); - $atHost = substr($this->getId(), $atPos); - return $this->displayName . $atHost; + return $this->displayName . '@' . $atHost; } - return str_replace('https://', '', str_replace('http://', '', $this->getId())); + return $this->getUser() . '@' . $atHost; } /** diff --git a/lib/private/Federation/CloudIdManager.php b/lib/private/Federation/CloudIdManager.php index 86b5ed621ba..c599d9046a6 100644 --- a/lib/private/Federation/CloudIdManager.php +++ b/lib/private/Federation/CloudIdManager.php @@ -3,30 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017, Robin Appelman <robin@icewind.nl> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Guillaume Virlet <github@virlet.org> - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @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\Federation; @@ -36,6 +14,7 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; use OCP\Federation\ICloudId; use OCP\Federation\ICloudIdManager; +use OCP\Federation\ICloudIdResolver; use OCP\ICache; use OCP\ICacheFactory; use OCP\IURLGenerator; @@ -43,27 +22,21 @@ use OCP\IUserManager; use OCP\User\Events\UserChangedEvent; class CloudIdManager implements ICloudIdManager { - /** @var IManager */ - private $contactsManager; - /** @var IURLGenerator */ - private $urlGenerator; - /** @var IUserManager */ - private $userManager; private ICache $memCache; - /** @var array[] */ + private ICache $displayNameCache; private array $cache = []; + /** @var ICloudIdResolver[] */ + private array $cloudIdResolvers = []; public function __construct( - IManager $contactsManager, - IURLGenerator $urlGenerator, - IUserManager $userManager, ICacheFactory $cacheFactory, - IEventDispatcher $eventDispatcher + IEventDispatcher $eventDispatcher, + private IManager $contactsManager, + private IURLGenerator $urlGenerator, + private IUserManager $userManager, ) { - $this->contactsManager = $contactsManager; - $this->urlGenerator = $urlGenerator; - $this->userManager = $userManager; $this->memCache = $cacheFactory->createDistributed('cloud_id_'); + $this->displayNameCache = $cacheFactory->createDistributed('cloudid_name_'); $eventDispatcher->addListener(UserChangedEvent::class, [$this, 'handleUserEvent']); $eventDispatcher->addListener(CardUpdatedEvent::class, [$this, 'handleCardEvent']); } @@ -81,7 +54,7 @@ class CloudIdManager implements ICloudIdManager { if ($event instanceof CardUpdatedEvent) { $data = $event->getCardData()['carddata']; foreach (explode("\r\n", $data) as $line) { - if (str_starts_with($line, "CLOUD;")) { + if (str_starts_with($line, 'CLOUD;')) { $parts = explode(':', $line, 2); if (isset($parts[1])) { $key = $parts[1]; @@ -101,12 +74,18 @@ class CloudIdManager implements ICloudIdManager { public function resolveCloudId(string $cloudId): ICloudId { // TODO magic here to get the url and user instead of just splitting on @ + foreach ($this->cloudIdResolvers as $resolver) { + if ($resolver->isValidCloudId($cloudId)) { + return $resolver->resolveCloudId($cloudId); + } + } + if (!$this->isValidCloudId($cloudId)) { throw new \InvalidArgumentException('Invalid cloud id'); } // Find the first character that is not allowed in user names - $id = $this->fixRemoteURL($cloudId); + $id = $this->stripShareLinkFragments($cloudId); $posSlash = strpos($id, '/'); $posColon = strpos($id, ':'); @@ -126,16 +105,28 @@ class CloudIdManager implements ICloudIdManager { $user = substr($id, 0, $lastValidAtPos); $remote = substr($id, $lastValidAtPos + 1); - $this->userManager->validateUserId($user); + // We accept slightly more chars when working with federationId than with a local userId. + // We remove those eventual chars from the UserId before using + // the IUserManager API to confirm its format. + $this->userManager->validateUserId(str_replace('=', '-', $user)); if (!empty($user) && !empty($remote)) { - return new CloudId($id, $user, $remote, $this->getDisplayNameFromContact($id)); + $remote = $this->ensureDefaultProtocol($remote); + return new CloudId($id, $user, $remote, null); } } throw new \InvalidArgumentException('Invalid cloud id'); } - protected function getDisplayNameFromContact(string $cloudId): ?string { + public function getDisplayNameFromContact(string $cloudId): ?string { + $cachedName = $this->displayNameCache->get($cloudId); + if ($cachedName !== null) { + if ($cachedName === $cloudId) { + return null; + } + return $cachedName; + } + $addressBookEntries = $this->contactsManager->search($cloudId, ['CLOUD'], [ 'limit' => 1, 'enumeration' => false, @@ -146,17 +137,20 @@ class CloudIdManager implements ICloudIdManager { if (isset($entry['CLOUD'])) { foreach ($entry['CLOUD'] as $cloudID) { if ($cloudID === $cloudId) { - // Warning, if user decides to make his full name local only, + // Warning, if user decides to make their full name local only, // no FN is found on federated servers if (isset($entry['FN'])) { + $this->displayNameCache->set($cloudId, $entry['FN'], 15 * 60); return $entry['FN']; } else { - return $cloudID; + $this->displayNameCache->set($cloudId, $cloudId, 15 * 60); + return null; } } } } } + $this->displayNameCache->set($cloudId, $cloudId, 15 * 60); return null; } @@ -174,8 +168,9 @@ class CloudIdManager implements ICloudIdManager { // note that for remote id's we don't strip the protocol for the remote we use to construct the CloudId // this way if a user has an explicit non-https cloud id this will be preserved // we do still use the version without protocol for looking up the display name - $remote = $this->fixRemoteURL($remote); + $remote = $this->stripShareLinkFragments($remote); $host = $this->removeProtocolFromUrl($remote); + $remote = $this->ensureDefaultProtocol($remote); $key = $user . '@' . ($isLocal ? 'local' : $host); $cached = $this->cache[$key] ?? $this->memCache->get($key); @@ -188,7 +183,7 @@ class CloudIdManager implements ICloudIdManager { $localUser = $this->userManager->get($user); $displayName = $localUser ? $localUser->getDisplayName() : ''; } else { - $displayName = $this->getDisplayNameFromContact($user . '@' . $host); + $displayName = null; } // For the visible cloudID we only strip away https @@ -220,6 +215,14 @@ class CloudIdManager implements ICloudIdManager { return $url; } + protected function ensureDefaultProtocol(string $remote): string { + if (!str_contains($remote, '://')) { + $remote = 'https://' . $remote; + } + + return $remote; + } + /** * Strips away a potential file names and trailing slashes: * - http://localhost @@ -232,7 +235,7 @@ class CloudIdManager implements ICloudIdManager { * @param string $remote * @return string */ - protected function fixRemoteURL(string $remote): string { + protected function stripShareLinkFragments(string $remote): string { $remote = str_replace('\\', '/', $remote); if ($fileNamePosition = strpos($remote, '/index.php')) { $remote = substr($remote, 0, $fileNamePosition); @@ -247,6 +250,26 @@ class CloudIdManager implements ICloudIdManager { * @return bool */ public function isValidCloudId(string $cloudId): bool { - return str_contains($cloudId, '@'); + foreach ($this->cloudIdResolvers as $resolver) { + if ($resolver->isValidCloudId($cloudId)) { + return true; + } + } + + return strpos($cloudId, '@') !== false; + } + + public function createCloudId(string $id, string $user, string $remote, ?string $displayName = null): ICloudId { + return new CloudId($id, $user, $remote, $displayName); + } + + public function registerCloudIdResolver(ICloudIdResolver $resolver): void { + array_unshift($this->cloudIdResolvers, $resolver); + } + + public function unregisterCloudIdResolver(ICloudIdResolver $resolver): void { + if (($key = array_search($resolver, $this->cloudIdResolvers)) !== false) { + array_splice($this->cloudIdResolvers, $key, 1); + } } } |