aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Federation
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Federation')
-rw-r--r--lib/private/Federation/CloudFederationFactory.php23
-rw-r--r--lib/private/Federation/CloudFederationNotification.php22
-rw-r--r--lib/private/Federation/CloudFederationProviderManager.php227
-rw-r--r--lib/private/Federation/CloudFederationShare.php47
-rw-r--r--lib/private/Federation/CloudId.php65
-rw-r--r--lib/private/Federation/CloudIdManager.php206
6 files changed, 314 insertions, 276 deletions
diff --git a/lib/private/Federation/CloudFederationFactory.php b/lib/private/Federation/CloudFederationFactory.php
index 010ffdafe47..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;
@@ -27,7 +11,6 @@ use OCP\Federation\ICloudFederationNotification;
use OCP\Federation\ICloudFederationShare;
class CloudFederationFactory implements ICloudFederationFactory {
-
/**
* get a CloudFederationShare Object to prepare a share you want to send
*
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 c25d4a40363..81b5d717a56 100644
--- a/lib/private/Federation/CloudFederationProviderManager.php
+++ b/lib/private/Federation/CloudFederationProviderManager.php
@@ -1,29 +1,16 @@
<?php
+
+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>
- *
- * @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;
@@ -31,7 +18,13 @@ 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;
use Psr\Log\LoggerInterface;
/**
@@ -42,42 +35,20 @@ use Psr\Log\LoggerInterface;
* @package OC\Federation
*/
class CloudFederationProviderManager implements ICloudFederationProviderManager {
-
/** @var array list of available cloud federation providers */
- private $cloudFederationProvider;
-
- /** @var IAppManager */
- private $appManager;
-
- /** @var IClientService */
- private $httpClientService;
-
- /** @var ICloudIdManager */
- private $cloudIdManager;
-
- private LoggerInterface $logger;
-
- /** @var array cache OCM end-points */
- private $ocmEndPoints = [];
-
- private $supportedAPIVersion = '1.0-proposal1';
-
- /**
- * CloudFederationProviderManager constructor.
- *
- * @param IAppManager $appManager
- * @param IClientService $httpClientService
- * @param ICloudIdManager $cloudIdManager
- */
- public function __construct(IAppManager $appManager,
- IClientService $httpClientService,
- ICloudIdManager $cloudIdManager,
- LoggerInterface $logger) {
- $this->cloudFederationProvider = [];
- $this->appManager = $appManager;
- $this->httpClientService = $httpClientService;
- $this->cloudIdManager = $cloudIdManager;
- $this->logger = $logger;
+ private array $cloudFederationProvider = [];
+
+ public function __construct(
+ private IConfig $config,
+ private IAppManager $appManager,
+ private IAppConfig $appConfig,
+ private IClientService $httpClientService,
+ private ICloudIdManager $cloudIdManager,
+ private IOCMDiscoveryService $discoveryService,
+ private readonly ISignatureManager $signatureManager,
+ private readonly OCMSignatoryManager $signatoryManager,
+ private LoggerInterface $logger,
+ ) {
}
@@ -129,32 +100,28 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager
}
}
+ /**
+ * @deprecated 29.0.0 - Use {@see sendCloudShare()} instead and handle errors manually
+ */
public function sendShare(ICloudFederationShare $share) {
$cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith());
- $ocmEndPoint = $this->getOCMEndPoint($cloudID->getRemote());
- if (empty($ocmEndPoint)) {
- return false;
- }
-
- $client = $this->httpClientService->newClient();
try {
- $response = $client->post($ocmEndPoint . '/shares', [
- 'body' => json_encode($share->getShare()),
- 'headers' => ['content-type' => 'application/json'],
- '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 : [];
}
} catch (\Exception $e) {
+ $this->logger->debug($e->getMessage(), ['exception' => $e]);
+
// if flat re-sharing is not supported by the remote server
// we re-throw the exception and fall back to the old behaviour.
// (flat re-shares has been introduced in Nextcloud 9.1)
if ($e->getCode() === Http::STATUS_INTERNAL_SERVER_ERROR) {
- $this->logger->debug($e->getMessage(), ['exception' => $e]);
throw $e;
}
}
@@ -163,25 +130,38 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager
}
/**
+ * @param ICloudFederationShare $share
+ * @return IResponse
+ * @throws OCMProviderException
+ */
+ public function sendCloudShare(ICloudFederationShare $share): IResponse {
+ $cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith());
+ $client = $this->httpClientService->newClient();
+ try {
+ 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 {
+ return $client->getResponseFromThrowable($e);
+ } catch (\Throwable $e) {
+ throw new OCMProviderException($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ }
+
+ /**
* @param string $url
* @param ICloudFederationNotification $notification
* @return array|false
+ * @deprecated 29.0.0 - Use {@see sendCloudNotification()} instead and handle errors manually
*/
public function sendNotification($url, ICloudFederationNotification $notification) {
- $ocmEndPoint = $this->getOCMEndPoint($url);
-
- if (empty($ocmEndPoint)) {
- return false;
- }
-
- $client = $this->httpClientService->newClient();
try {
- $response = $client->post($ocmEndPoint . '/notifications', [
- 'body' => json_encode($notification->getMessage()),
- 'headers' => ['content-type' => 'application/json'],
- '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 : [];
@@ -195,6 +175,26 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager
}
/**
+ * @param string $url
+ * @param ICloudFederationNotification $notification
+ * @return IResponse
+ * @throws OCMProviderException
+ */
+ public function sendCloudNotification(string $url, ICloudFederationNotification $notification): IResponse {
+ $client = $this->httpClientService->newClient();
+ try {
+ 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 {
+ return $client->getResponseFromThrowable($e);
+ } catch (\Throwable $e) {
+ throw new OCMProviderException($e->getMessage(), $e->getCode(), $e);
+ }
+ }
+ }
+
+ /**
* check if the new cloud federation API is ready to be used
*
* @return bool
@@ -202,36 +202,53 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager
public function isReady() {
return $this->appManager->isEnabledForUser('cloud_federation_api');
}
+
/**
- * check if server supports the new OCM api and ask for the correct end-point
+ * @param string $cloudId
+ * @param string $uri
+ * @param string $payload
*
- * @param string $url full base URL of the cloud server
- * @return string
+ * @return IResponse
+ * @throws OCMProviderException
*/
- protected function getOCMEndPoint($url) {
- if (isset($this->ocmEndPoints[$url])) {
- return $this->ocmEndPoints[$url];
- }
-
- $client = $this->httpClientService->newClient();
- try {
- $response = $client->get($url . '/ocm-provider/', ['timeout' => 10, 'connect_timeout' => 10]);
- } catch (\Exception $e) {
- $this->ocmEndPoints[$url] = '';
- return '';
- }
+ 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));
+ }
- $result = $response->getBody();
- $result = json_decode($result, true);
+ /**
+ * @param string $uri
+ * @param string $payload
+ *
+ * @return array
+ */
+ private function prepareOcmPayload(string $uri, string $payload): array {
+ $payload = array_merge($this->getDefaultRequestOptions(), ['body' => $payload]);
- $supportedVersion = isset($result['apiVersion']) && $result['apiVersion'] === $this->supportedAPIVersion;
+ if ($this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_ENFORCED, lazy: true)
+ && $this->signatoryManager->getRemoteSignatory($this->signatureManager->extractIdentityFromUri($uri)) === null) {
+ return $payload;
+ }
- if (isset($result['endPoint']) && $supportedVersion) {
- $this->ocmEndPoints[$url] = $result['endPoint'];
- return $result['endPoint'];
+ if (!$this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_DISABLED, lazy: true)) {
+ $signedPayload = $this->signatureManager->signOutgoingRequestIClientPayload(
+ $this->signatoryManager,
+ $payload,
+ 'post', $uri
+ );
}
- $this->ocmEndPoints[$url] = '';
- return '';
+ 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 0f79ba521ea..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' => []
];
@@ -57,16 +42,16 @@ class CloudFederationShare implements ICloudFederationShare {
* @param string $sharedSecret
*/
public function __construct($shareWith = '',
- $name = '',
- $description = '',
- $providerId = '',
- $owner = '',
- $ownerDisplayName = '',
- $sharedBy = '',
- $sharedByDisplayName = '',
- $shareType = '',
- $resourceType = '',
- $sharedSecret = ''
+ $name = '',
+ $description = '',
+ $providerId = '',
+ $owner = '',
+ $ownerDisplayName = '',
+ $sharedBy = '',
+ $sharedByDisplayName = '',
+ $shareType = '',
+ $resourceType = '',
+ $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 77bb9437ba2..c599d9046a6 100644
--- a/lib/private/Federation/CloudIdManager.php
+++ b/lib/private/Federation/CloudIdManager.php
@@ -3,51 +3,67 @@
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;
+use OCA\DAV\Events\CardUpdatedEvent;
use OCP\Contacts\IManager;
+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;
use OCP\IUserManager;
+use OCP\User\Events\UserChangedEvent;
class CloudIdManager implements ICloudIdManager {
- /** @var IManager */
- private $contactsManager;
- /** @var IURLGenerator */
- private $urlGenerator;
- /** @var IUserManager */
- private $userManager;
-
- public function __construct(IManager $contactsManager, IURLGenerator $urlGenerator, IUserManager $userManager) {
- $this->contactsManager = $contactsManager;
- $this->urlGenerator = $urlGenerator;
- $this->userManager = $userManager;
+ private ICache $memCache;
+ private ICache $displayNameCache;
+ private array $cache = [];
+ /** @var ICloudIdResolver[] */
+ private array $cloudIdResolvers = [];
+
+ public function __construct(
+ ICacheFactory $cacheFactory,
+ IEventDispatcher $eventDispatcher,
+ private IManager $contactsManager,
+ private IURLGenerator $urlGenerator,
+ private IUserManager $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']);
+ }
+
+ public function handleUserEvent(Event $event): void {
+ if ($event instanceof UserChangedEvent && $event->getFeature() === 'displayName') {
+ $userId = $event->getUser()->getUID();
+ $key = $userId . '@local';
+ unset($this->cache[$key]);
+ $this->memCache->remove($key);
+ }
+ }
+
+ public function handleCardEvent(Event $event): void {
+ if ($event instanceof CardUpdatedEvent) {
+ $data = $event->getCardData()['carddata'];
+ foreach (explode("\r\n", $data) as $line) {
+ if (str_starts_with($line, 'CLOUD;')) {
+ $parts = explode(':', $line, 2);
+ if (isset($parts[1])) {
+ $key = $parts[1];
+ unset($this->cache[$key]);
+ $this->memCache->remove($key);
+ }
+ }
+ }
+ }
}
/**
@@ -58,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, ':');
@@ -82,14 +104,29 @@ class CloudIdManager implements ICloudIdManager {
if ($lastValidAtPos !== false) {
$user = substr($id, 0, $lastValidAtPos);
$remote = substr($id, $lastValidAtPos + 1);
+
+ // 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,
@@ -100,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;
}
@@ -120,35 +160,69 @@ class CloudIdManager implements ICloudIdManager {
* @return CloudId
*/
public function getCloudId(string $user, ?string $remote): ICloudId {
- if ($remote === null) {
- $remote = rtrim($this->removeProtocolFromUrl($this->urlGenerator->getAbsoluteURL('/')), '/');
- $fixedRemote = $this->fixRemoteURL($remote);
+ $isLocal = $remote === null;
+ if ($isLocal) {
+ $remote = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
+ }
+
+ // 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->stripShareLinkFragments($remote);
+ $host = $this->removeProtocolFromUrl($remote);
+ $remote = $this->ensureDefaultProtocol($remote);
+
+ $key = $user . '@' . ($isLocal ? 'local' : $host);
+ $cached = $this->cache[$key] ?? $this->memCache->get($key);
+ if ($cached) {
+ $this->cache[$key] = $cached; // put items from memcache into local cache
+ return new CloudId($cached['id'], $cached['user'], $cached['remote'], $cached['displayName']);
+ }
+
+ if ($isLocal) {
$localUser = $this->userManager->get($user);
- $displayName = !is_null($localUser) ? $localUser->getDisplayName() : '';
+ $displayName = $localUser ? $localUser->getDisplayName() : '';
} else {
- // TODO check what the correct url is for remote (asking the remote)
- $fixedRemote = $this->fixRemoteURL($remote);
- $host = $this->removeProtocolFromUrl($fixedRemote);
- $displayName = $this->getDisplayNameFromContact($user . '@' . $host);
+ $displayName = null;
}
- $id = $user . '@' . $remote;
- return new CloudId($id, $user, $fixedRemote, $displayName);
+
+ // For the visible cloudID we only strip away https
+ $id = $user . '@' . $this->removeProtocolFromUrl($remote, true);
+
+ $data = [
+ 'id' => $id,
+ 'user' => $user,
+ 'remote' => $remote,
+ 'displayName' => $displayName,
+ ];
+ $this->cache[$key] = $data;
+ $this->memCache->set($key, $data, 15 * 60);
+ return new CloudId($id, $user, $remote, $displayName);
}
/**
* @param string $url
* @return string
*/
- private function removeProtocolFromUrl($url) {
- if (strpos($url, 'https://') === 0) {
- return substr($url, strlen('https://'));
- } elseif (strpos($url, 'http://') === 0) {
- return substr($url, strlen('http://'));
+ public function removeProtocolFromUrl(string $url, bool $httpsOnly = false): string {
+ if (str_starts_with($url, 'https://')) {
+ return substr($url, 8);
+ }
+ if (!$httpsOnly && str_starts_with($url, 'http://')) {
+ return substr($url, 7);
}
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
@@ -161,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);
@@ -176,6 +250,26 @@ class CloudIdManager implements ICloudIdManager {
* @return bool
*/
public function isValidCloudId(string $cloudId): bool {
+ 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);
+ }
+ }
}