Merge pull request #54026 from nextcloud/enh/add-cloud-id-chars

feat: add ICloudIdResolver
pull/53966/head
Stephan Orbaugh 2025-07-28 15:49:12 +07:00 committed by GitHub
commit 4eda352397
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 120 additions and 32 deletions

@ -34,11 +34,11 @@ class AddressHandlerTest extends \Test\TestCase {
$this->contactsManager = $this->createMock(IManager::class);
$this->cloudIdManager = new CloudIdManager(
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class),
$this->contactsManager,
$this->urlGenerator,
$this->createMock(IUserManager::class),
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class)
);
$this->addressHandler = new AddressHandler($this->urlGenerator, $this->il10n, $this->cloudIdManager);

@ -64,11 +64,11 @@ class MountPublicLinkControllerTest extends \Test\TestCase {
$this->clientService = $this->createMock(IClientService::class);
$this->contactsManager = $this->createMock(IContactsManager::class);
$this->cloudIdManager = new CloudIdManager(
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class),
$this->contactsManager,
$this->createMock(IURLGenerator::class),
$this->userManager,
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class)
);
$this->controller = new MountPublicLinkController(

@ -74,11 +74,11 @@ class FederatedShareProviderTest extends \Test\TestCase {
$this->addressHandler = $this->createMock(AddressHandler::class);
$this->contactsManager = $this->createMock(IContactsManager::class);
$this->cloudIdManager = new CloudIdManager(
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class),
$this->contactsManager,
$this->createMock(IURLGenerator::class),
$this->userManager,
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class)
);
$this->gsConfig = $this->createMock(\OCP\GlobalScale\IConfig::class);

@ -54,11 +54,11 @@ class CacheTest extends TestCase {
$this->contactsManager = $this->createMock(IManager::class);
$this->cloudIdManager = new CloudIdManager(
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class),
$this->contactsManager,
$this->createMock(IURLGenerator::class),
$this->createMock(IUserManager::class),
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class)
);
$this->remoteUser = $this->getUniqueID('remoteuser');

@ -90,11 +90,11 @@ class ManagerTest extends TestCase {
$this->testMountProvider = new MountProvider(Server::get(IDBConnection::class), function () {
return $this->manager;
}, new CloudIdManager(
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class),
$this->contactsManager,
$this->createMock(IURLGenerator::class),
$this->userManager,
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class)
));
$group1 = $this->createMock(IGroup::class);

@ -376,6 +376,7 @@ return array(
'OCP\\Federation\\ICloudFederationShare' => $baseDir . '/lib/public/Federation/ICloudFederationShare.php',
'OCP\\Federation\\ICloudId' => $baseDir . '/lib/public/Federation/ICloudId.php',
'OCP\\Federation\\ICloudIdManager' => $baseDir . '/lib/public/Federation/ICloudIdManager.php',
'OCP\\Federation\\ICloudIdResolver' => $baseDir . '/lib/public/Federation/ICloudIdResolver.php',
'OCP\\Files' => $baseDir . '/lib/public/Files.php',
'OCP\\FilesMetadata\\AMetadataEvent' => $baseDir . '/lib/public/FilesMetadata/AMetadataEvent.php',
'OCP\\FilesMetadata\\Event\\MetadataBackgroundEvent' => $baseDir . '/lib/public/FilesMetadata/Event/MetadataBackgroundEvent.php',

@ -417,6 +417,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Federation\\ICloudFederationShare' => __DIR__ . '/../../..' . '/lib/public/Federation/ICloudFederationShare.php',
'OCP\\Federation\\ICloudId' => __DIR__ . '/../../..' . '/lib/public/Federation/ICloudId.php',
'OCP\\Federation\\ICloudIdManager' => __DIR__ . '/../../..' . '/lib/public/Federation/ICloudIdManager.php',
'OCP\\Federation\\ICloudIdResolver' => __DIR__ . '/../../..' . '/lib/public/Federation/ICloudIdResolver.php',
'OCP\\Files' => __DIR__ . '/../../..' . '/lib/public/Files.php',
'OCP\\FilesMetadata\\AMetadataEvent' => __DIR__ . '/../../..' . '/lib/public/FilesMetadata/AMetadataEvent.php',
'OCP\\FilesMetadata\\Event\\MetadataBackgroundEvent' => __DIR__ . '/../../..' . '/lib/public/FilesMetadata/Event/MetadataBackgroundEvent.php',

@ -14,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;
@ -21,27 +22,19 @@ 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;
private ICache $displayNameCache;
/** @var array[] */
private array $cache = [];
/** @var ICloudIdResolver[] */
private array $cloudIdResolvers = [];
public function __construct(
IManager $contactsManager,
IURLGenerator $urlGenerator,
IUserManager $userManager,
ICacheFactory $cacheFactory,
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']);
@ -81,6 +74,12 @@ 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');
}
@ -251,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);
}
}
}

@ -1160,11 +1160,11 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerService(ICloudIdManager::class, function (ContainerInterface $c) {
return new CloudIdManager(
$c->get(ICacheFactory::class),
$c->get(IEventDispatcher::class),
$c->get(\OCP\Contacts\IManager::class),
$c->get(IURLGenerator::class),
$c->get(IUserManager::class),
$c->get(ICacheFactory::class),
$c->get(IEventDispatcher::class),
);
});

@ -8,11 +8,14 @@ declare(strict_types=1);
*/
namespace OCP\Federation;
use OCP\AppFramework\Attribute\Consumable;
/**
* Interface for resolving federated cloud ids
*
* @since 12.0.0
*/
#[Consumable(since: '12.0.0')]
interface ICloudIdManager {
/**
* @param string $cloudId
@ -55,4 +58,28 @@ interface ICloudIdManager {
* @since 30.0.0 - Optional parameter $httpsOnly was added
*/
public function removeProtocolFromUrl(string $url, bool $httpsOnly = false): string;
/**
* @param string $id The remote cloud id
* @param string $user The user id on the remote server
* @param string $remote The base address of the remote server
* @param ?string $displayName The displayname of the remote user
*
* @since 32.0.0
*/
public function createCloudId(string $id, string $user, string $remote, ?string $displayName = null): ICloudId;
/**
* @param $resolver The cloud id resolver to register
*
* @since 32.0.0
*/
public function registerCloudIdResolver(ICloudIdResolver $resolver): void;
/**
* @param $resolver The cloud id resolver to unregister
*
* @since 32.0.0
*/
public function unregisterCloudIdResolver(ICloudIdResolver $resolver): void;
}

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCP\Federation;
use OCP\AppFramework\Attribute\Consumable;
use OCP\AppFramework\Attribute\Implementable;
/**
* Interface for resolving federated cloud ids
*
* @since 32.0.0
*/
#[Consumable(since: '32.0.0')]
#[Implementable(since: '32.0.0')]
interface ICloudIdResolver {
/**
* @param string $cloudId
* @return ICloudId
* @throws \InvalidArgumentException
*
* @since 32.0.0
*/
public function resolveCloudId(string $cloudId): ICloudId;
/**
* Check if the input is a correctly formatted cloud id
*
* @param string $cloudId
* @return bool
*
* @since 32.0.0
*/
public function isValidCloudId(string $cloudId): bool;
}

@ -64,11 +64,11 @@ class MailPluginTest extends TestCase {
$this->userSession = $this->createMock(IUserSession::class);
$this->mailer = $this->createMock(IMailer::class);
$this->cloudIdManager = new CloudIdManager(
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class),
$this->contactsManager,
$this->createMock(IURLGenerator::class),
$this->createMock(IUserManager::class),
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class)
);
$this->searchResult = new SearchResult();

@ -49,11 +49,11 @@ class RemotePluginTest extends TestCase {
$this->config = $this->createMock(IConfig::class);
$this->contactsManager = $this->createMock(IManager::class);
$this->cloudIdManager = new CloudIdManager(
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class),
$this->contactsManager,
$this->createMock(IURLGenerator::class),
$this->createMock(IUserManager::class),
$this->createMock(ICacheFactory::class),
$this->createMock(IEventDispatcher::class)
);
$this->searchResult = new SearchResult();
}

@ -47,11 +47,11 @@ class CloudIdManagerTest extends TestCase {
->willReturn(new ArrayCache(''));
$this->cloudIdManager = new CloudIdManager(
$this->cacheFactory,
$this->createMock(IEventDispatcher::class),
$this->contactsManager,
$this->urlGenerator,
$this->userManager,
$this->cacheFactory,
$this->createMock(IEventDispatcher::class)
);
$this->overwriteService(ICloudIdManager::class, $this->cloudIdManager);
}