diff options
author | Côme Chilliet <91878298+come-nc@users.noreply.github.com> | 2025-02-13 10:24:05 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-02-13 10:24:05 +0100 |
commit | ee48cafd200233203a1444dba797ef3eb89a35ca (patch) | |
tree | 1f37b64545f4ab80ffb14c442753539777e9cd98 | |
parent | 6e1d9a26209ec5524fbc2fb9c7cbb53315e64d72 (diff) | |
parent | 355fef6ff92b3ce1206bbbed17947e588ff89436 (diff) | |
download | nextcloud-server-chore/mail-bisect-ee48cafd200233203a1444dba797ef3eb89a35ca.tar.gz nextcloud-server-chore/mail-bisect-ee48cafd200233203a1444dba797ef3eb89a35ca.zip |
Merge pull request #50689 from nextcloud/fix/migrate-dav-to-eventschore/mail-bisect-ee48cafd200233203a1444dba797ef3eb89a35ca
fix(dav): Migrate from hooks to user events
-rw-r--r-- | apps/dav/composer/composer/autoload_classmap.php | 2 | ||||
-rw-r--r-- | apps/dav/composer/composer/autoload_static.php | 2 | ||||
-rw-r--r-- | apps/dav/lib/AppInfo/Application.php | 37 | ||||
-rw-r--r-- | apps/dav/lib/HookManager.php | 155 | ||||
-rw-r--r-- | apps/dav/lib/Listener/UserEventsListener.php | 151 | ||||
-rw-r--r-- | apps/dav/tests/unit/CardDAV/CardDavBackendTest.php | 11 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/HookManagerTest.php | 222 | ||||
-rw-r--r-- | apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php | 140 | ||||
-rw-r--r-- | build/psalm-baseline.xml | 6 | ||||
-rw-r--r-- | tests/lib/User/UserTest.php | 6 |
10 files changed, 326 insertions, 406 deletions
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index a70aba9f842..09eb4ba17bd 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -284,7 +284,6 @@ return array( 'OCA\\DAV\\Files\\RootCollection' => $baseDir . '/../lib/Files/RootCollection.php', 'OCA\\DAV\\Files\\Sharing\\FilesDropPlugin' => $baseDir . '/../lib/Files/Sharing/FilesDropPlugin.php', 'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => $baseDir . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php', - 'OCA\\DAV\\HookManager' => $baseDir . '/../lib/HookManager.php', 'OCA\\DAV\\Listener\\ActivityUpdaterListener' => $baseDir . '/../lib/Listener/ActivityUpdaterListener.php', 'OCA\\DAV\\Listener\\AddMissingIndicesListener' => $baseDir . '/../lib/Listener/AddMissingIndicesListener.php', 'OCA\\DAV\\Listener\\AddressbookListener' => $baseDir . '/../lib/Listener/AddressbookListener.php', @@ -299,6 +298,7 @@ return array( 'OCA\\DAV\\Listener\\OutOfOfficeListener' => $baseDir . '/../lib/Listener/OutOfOfficeListener.php', 'OCA\\DAV\\Listener\\SubscriptionListener' => $baseDir . '/../lib/Listener/SubscriptionListener.php', 'OCA\\DAV\\Listener\\TrustedServerRemovedListener' => $baseDir . '/../lib/Listener/TrustedServerRemovedListener.php', + 'OCA\\DAV\\Listener\\UserEventsListener' => $baseDir . '/../lib/Listener/UserEventsListener.php', 'OCA\\DAV\\Listener\\UserPreferenceListener' => $baseDir . '/../lib/Listener/UserPreferenceListener.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndex' => $baseDir . '/../lib/Migration/BuildCalendarSearchIndex.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndexBackgroundJob' => $baseDir . '/../lib/Migration/BuildCalendarSearchIndexBackgroundJob.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index 2d00105b548..a2048fcdf05 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -299,7 +299,6 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Files\\RootCollection' => __DIR__ . '/..' . '/../lib/Files/RootCollection.php', 'OCA\\DAV\\Files\\Sharing\\FilesDropPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/FilesDropPlugin.php', 'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php', - 'OCA\\DAV\\HookManager' => __DIR__ . '/..' . '/../lib/HookManager.php', 'OCA\\DAV\\Listener\\ActivityUpdaterListener' => __DIR__ . '/..' . '/../lib/Listener/ActivityUpdaterListener.php', 'OCA\\DAV\\Listener\\AddMissingIndicesListener' => __DIR__ . '/..' . '/../lib/Listener/AddMissingIndicesListener.php', 'OCA\\DAV\\Listener\\AddressbookListener' => __DIR__ . '/..' . '/../lib/Listener/AddressbookListener.php', @@ -314,6 +313,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Listener\\OutOfOfficeListener' => __DIR__ . '/..' . '/../lib/Listener/OutOfOfficeListener.php', 'OCA\\DAV\\Listener\\SubscriptionListener' => __DIR__ . '/..' . '/../lib/Listener/SubscriptionListener.php', 'OCA\\DAV\\Listener\\TrustedServerRemovedListener' => __DIR__ . '/..' . '/../lib/Listener/TrustedServerRemovedListener.php', + 'OCA\\DAV\\Listener\\UserEventsListener' => __DIR__ . '/..' . '/../lib/Listener/UserEventsListener.php', 'OCA\\DAV\\Listener\\UserPreferenceListener' => __DIR__ . '/..' . '/../lib/Listener/UserPreferenceListener.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndex' => __DIR__ . '/..' . '/../lib/Migration/BuildCalendarSearchIndex.php', 'OCA\\DAV\\Migration\\BuildCalendarSearchIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/Migration/BuildCalendarSearchIndexBackgroundJob.php', diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php index ae587c98965..a01125a1103 100644 --- a/apps/dav/lib/AppInfo/Application.php +++ b/apps/dav/lib/AppInfo/Application.php @@ -20,7 +20,6 @@ use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider; use OCA\DAV\CalDAV\Reminder\NotificationProviderManager; use OCA\DAV\CalDAV\Reminder\Notifier; use OCA\DAV\Capabilities; - use OCA\DAV\CardDAV\ContactsManager; use OCA\DAV\CardDAV\PhotoCache; use OCA\DAV\CardDAV\SyncService; @@ -47,7 +46,6 @@ use OCA\DAV\Events\CardDeletedEvent; use OCA\DAV\Events\CardUpdatedEvent; use OCA\DAV\Events\SubscriptionCreatedEvent; use OCA\DAV\Events\SubscriptionDeletedEvent; -use OCA\DAV\HookManager; use OCA\DAV\Listener\ActivityUpdaterListener; use OCA\DAV\Listener\AddMissingIndicesListener; use OCA\DAV\Listener\AddressbookListener; @@ -62,6 +60,7 @@ use OCA\DAV\Listener\ClearPhotoCacheListener; use OCA\DAV\Listener\OutOfOfficeListener; use OCA\DAV\Listener\SubscriptionListener; use OCA\DAV\Listener\TrustedServerRemovedListener; +use OCA\DAV\Listener\UserEventsListener; use OCA\DAV\Listener\UserPreferenceListener; use OCA\DAV\Search\ContactsSearchProvider; use OCA\DAV\Search\EventsSearchProvider; @@ -81,15 +80,21 @@ use OCP\Config\BeforePreferenceDeletedEvent; use OCP\Config\BeforePreferenceSetEvent; use OCP\Contacts\IManager as IContactsManager; use OCP\DB\Events\AddMissingIndicesEvent; -use OCP\EventDispatcher\GenericEvent; use OCP\EventDispatcher\IEventDispatcher; use OCP\Federation\Events\TrustedServerRemovedEvent; use OCP\Files\AppData\IAppDataFactory; -use OCP\IUser; use OCP\Server; +use OCP\User\Events\BeforeUserDeletedEvent; +use OCP\User\Events\BeforeUserIdUnassignedEvent; use OCP\User\Events\OutOfOfficeChangedEvent; use OCP\User\Events\OutOfOfficeClearedEvent; use OCP\User\Events\OutOfOfficeScheduledEvent; +use OCP\User\Events\UserChangedEvent; +use OCP\User\Events\UserCreatedEvent; +use OCP\User\Events\UserDeletedEvent; +use OCP\User\Events\UserFirstTimeLoggedInEvent; +use OCP\User\Events\UserIdAssignedEvent; +use OCP\User\Events\UserIdUnassignedEvent; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; use Throwable; @@ -187,6 +192,15 @@ class Application extends App implements IBootstrap { $context->registerEventListener(OutOfOfficeClearedEvent::class, OutOfOfficeListener::class); $context->registerEventListener(OutOfOfficeScheduledEvent::class, OutOfOfficeListener::class); + $context->registerEventListener(UserFirstTimeLoggedInEvent::class, UserEventsListener::class); + $context->registerEventListener(UserIdAssignedEvent::class, UserEventsListener::class); + $context->registerEventListener(BeforeUserIdUnassignedEvent::class, UserEventsListener::class); + $context->registerEventListener(UserIdUnassignedEvent::class, UserEventsListener::class); + $context->registerEventListener(BeforeUserDeletedEvent::class, UserEventsListener::class); + $context->registerEventListener(UserDeletedEvent::class, UserEventsListener::class); + $context->registerEventListener(UserCreatedEvent::class, UserEventsListener::class); + $context->registerEventListener(UserChangedEvent::class, UserEventsListener::class); + $context->registerNotifierService(Notifier::class); $context->registerCalendarProvider(CalendarProvider::class); @@ -209,18 +223,10 @@ class Application extends App implements IBootstrap { $context->injectFn([$this, 'registerCalendarReminders']); } - public function registerHooks(HookManager $hm, + public function registerHooks( IEventDispatcher $dispatcher, - IAppContainer $container) { - $hm->setup(); - - // first time login event setup - $dispatcher->addListener(IUser::class . '::firstLogin', function ($event) use ($hm): void { - if ($event instanceof GenericEvent) { - $hm->firstLogin($event->getSubject()); - } - }); - + IAppContainer $container, + ): void { $dispatcher->addListener(UserUpdatedEvent::class, function (UserUpdatedEvent $event) use ($container): void { /** @var SyncService $syncService */ $syncService = Server::get(SyncService::class); @@ -240,7 +246,6 @@ class Application extends App implements IBootstrap { // Here we should recalculate if reminders should be sent to new or old sharees }); - } public function registerContactsManager(IContactsManager $cm, IAppContainer $container): void { diff --git a/apps/dav/lib/HookManager.php b/apps/dav/lib/HookManager.php deleted file mode 100644 index 9f4bc95d89b..00000000000 --- a/apps/dav/lib/HookManager.php +++ /dev/null @@ -1,155 +0,0 @@ -<?php - -/** - * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2016 ownCloud, Inc. - * SPDX-License-Identifier: AGPL-3.0-only - */ -namespace OCA\DAV; - -use OCA\DAV\CalDAV\CalDavBackend; -use OCA\DAV\CardDAV\CardDavBackend; -use OCA\DAV\CardDAV\SyncService; -use OCP\Defaults; -use OCP\IUser; -use OCP\IUserManager; -use OCP\Util; -use Psr\Log\LoggerInterface; - -class HookManager { - - /** @var IUser[] */ - private $usersToDelete = []; - - /** @var array */ - private $calendarsToDelete = []; - - /** @var array */ - private $subscriptionsToDelete = []; - - /** @var array */ - private $addressBooksToDelete = []; - - public function __construct( - private IUserManager $userManager, - private SyncService $syncService, - private CalDavBackend $calDav, - private CardDavBackend $cardDav, - private Defaults $themingDefaults, - ) { - } - - public function setup() { - Util::connectHook('OC_User', - 'post_createUser', - $this, - 'postCreateUser'); - \OC::$server->getUserManager()->listen('\OC\User', 'assignedUserId', function ($uid): void { - $this->postCreateUser(['uid' => $uid]); - }); - Util::connectHook('OC_User', - 'pre_deleteUser', - $this, - 'preDeleteUser'); - \OC::$server->getUserManager()->listen('\OC\User', 'preUnassignedUserId', [$this, 'preUnassignedUserId']); - Util::connectHook('OC_User', - 'post_deleteUser', - $this, - 'postDeleteUser'); - \OC::$server->getUserManager()->listen('\OC\User', 'postUnassignedUserId', function ($uid): void { - $this->postDeleteUser(['uid' => $uid]); - }); - \OC::$server->getUserManager()->listen('\OC\User', 'postUnassignedUserId', [$this, 'postUnassignedUserId']); - Util::connectHook('OC_User', 'changeUser', $this, 'changeUser'); - } - - public function postCreateUser($params) { - $user = $this->userManager->get($params['uid']); - if ($user instanceof IUser) { - $this->syncService->updateUser($user); - } - } - - public function preDeleteUser($params) { - $uid = $params['uid']; - $userPrincipalUri = 'principals/users/' . $uid; - $this->usersToDelete[$uid] = $this->userManager->get($uid); - $this->calendarsToDelete = $this->calDav->getUsersOwnCalendars($userPrincipalUri); - $this->subscriptionsToDelete = $this->calDav->getSubscriptionsForUser($userPrincipalUri); - $this->addressBooksToDelete = $this->cardDav->getUsersOwnAddressBooks($userPrincipalUri); - } - - public function preUnassignedUserId($uid) { - $this->usersToDelete[$uid] = $this->userManager->get($uid); - } - - public function postDeleteUser($params) { - $uid = $params['uid']; - if (isset($this->usersToDelete[$uid])) { - $this->syncService->deleteUser($this->usersToDelete[$uid]); - } - - foreach ($this->calendarsToDelete as $calendar) { - $this->calDav->deleteCalendar( - $calendar['id'], - true // Make sure the data doesn't go into the trashbin, a new user with the same UID would later see it otherwise - ); - } - - foreach ($this->subscriptionsToDelete as $subscription) { - $this->calDav->deleteSubscription( - $subscription['id'], - ); - } - $this->calDav->deleteAllSharesByUser('principals/users/' . $uid); - - foreach ($this->addressBooksToDelete as $addressBook) { - $this->cardDav->deleteAddressBook($addressBook['id']); - } - } - - public function postUnassignedUserId($uid) { - if (isset($this->usersToDelete[$uid])) { - $this->syncService->deleteUser($this->usersToDelete[$uid]); - } - } - - public function changeUser($params) { - $user = $params['user']; - $feature = $params['feature']; - // This case is already covered by the account manager firing up a signal - // later on - if ($feature !== 'eMailAddress' && $feature !== 'displayName') { - $this->syncService->updateUser($user); - } - } - - /** - * @return void - */ - public function firstLogin(?IUser $user = null) { - if (!is_null($user)) { - $principal = 'principals/users/' . $user->getUID(); - if ($this->calDav->getCalendarsForUserCount($principal) === 0) { - try { - $this->calDav->createCalendar($principal, CalDavBackend::PERSONAL_CALENDAR_URI, [ - '{DAV:}displayname' => CalDavBackend::PERSONAL_CALENDAR_NAME, - '{http://apple.com/ns/ical/}calendar-color' => $this->themingDefaults->getColorPrimary(), - 'components' => 'VEVENT' - ]); - } catch (\Exception $e) { - \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]); - } - } - if ($this->cardDav->getAddressBooksForUserCount($principal) === 0) { - try { - $this->cardDav->createAddressBook($principal, CardDavBackend::PERSONAL_ADDRESSBOOK_URI, [ - '{DAV:}displayname' => CardDavBackend::PERSONAL_ADDRESSBOOK_NAME, - ]); - } catch (\Exception $e) { - \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]); - } - } - } - } -} diff --git a/apps/dav/lib/Listener/UserEventsListener.php b/apps/dav/lib/Listener/UserEventsListener.php new file mode 100644 index 00000000000..c8811f26d9b --- /dev/null +++ b/apps/dav/lib/Listener/UserEventsListener.php @@ -0,0 +1,151 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OCA\DAV\Listener; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CardDAV\CardDavBackend; +use OCA\DAV\CardDAV\SyncService; +use OCP\Defaults; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\IUser; +use OCP\IUserManager; +use OCP\User\Events\BeforeUserDeletedEvent; +use OCP\User\Events\BeforeUserIdUnassignedEvent; +use OCP\User\Events\UserChangedEvent; +use OCP\User\Events\UserCreatedEvent; +use OCP\User\Events\UserDeletedEvent; +use OCP\User\Events\UserFirstTimeLoggedInEvent; +use OCP\User\Events\UserIdAssignedEvent; +use OCP\User\Events\UserIdUnassignedEvent; +use Psr\Log\LoggerInterface; + +/** @template-implements IEventListener<UserFirstTimeLoggedInEvent|UserIdAssignedEvent|BeforeUserIdUnassignedEvent|UserIdUnassignedEvent|BeforeUserDeletedEvent|UserDeletedEvent|UserCreatedEvent|UserChangedEvent> */ +class UserEventsListener implements IEventListener { + + /** @var IUser[] */ + private array $usersToDelete = []; + + private array $calendarsToDelete = []; + private array $subscriptionsToDelete = []; + private array $addressBooksToDelete = []; + + public function __construct( + private IUserManager $userManager, + private SyncService $syncService, + private CalDavBackend $calDav, + private CardDavBackend $cardDav, + private Defaults $themingDefaults, + ) { + } + + public function handle(Event $event): void { + if ($event instanceof UserCreatedEvent) { + $this->postCreateUser($event->getUser()); + } elseif ($event instanceof UserIdAssignedEvent) { + $user = $this->userManager->get($event->getUserId()); + if ($user !== null) { + $this->postCreateUser($user); + } + } elseif ($event instanceof BeforeUserDeletedEvent) { + $this->preDeleteUser($event->getUser()); + } elseif ($event instanceof BeforeUserIdUnassignedEvent) { + $this->preUnassignedUserId($event->getUserId()); + } elseif ($event instanceof UserDeletedEvent) { + $this->postDeleteUser($event->getUid()); + } elseif ($event instanceof UserIdUnassignedEvent) { + $this->postDeleteUser($event->getUserId()); + } elseif ($event instanceof UserChangedEvent) { + $this->changeUser($event->getUser(), $event->getFeature()); + } elseif ($event instanceof UserFirstTimeLoggedInEvent) { + $this->firstLogin($event->getUser()); + } + } + + public function postCreateUser(IUser $user): void { + $this->syncService->updateUser($user); + } + + public function preDeleteUser(IUser $user): void { + $uid = $user->getUID(); + $userPrincipalUri = 'principals/users/' . $uid; + $this->usersToDelete[$uid] = $user; + $this->calendarsToDelete[$uid] = $this->calDav->getUsersOwnCalendars($userPrincipalUri); + $this->subscriptionsToDelete[$uid] = $this->calDav->getSubscriptionsForUser($userPrincipalUri); + $this->addressBooksToDelete[$uid] = $this->cardDav->getUsersOwnAddressBooks($userPrincipalUri); + } + + public function preUnassignedUserId(string $uid): void { + $user = $this->userManager->get($uid); + if ($user !== null) { + $this->usersToDelete[$uid] = $user; + } + } + + public function postDeleteUser(string $uid): void { + if (isset($this->usersToDelete[$uid])) { + $this->syncService->deleteUser($this->usersToDelete[$uid]); + } + + foreach ($this->calendarsToDelete[$uid] as $calendar) { + $this->calDav->deleteCalendar( + $calendar['id'], + true // Make sure the data doesn't go into the trashbin, a new user with the same UID would later see it otherwise + ); + } + + foreach ($this->subscriptionsToDelete[$uid] as $subscription) { + $this->calDav->deleteSubscription( + $subscription['id'], + ); + } + $this->calDav->deleteAllSharesByUser('principals/users/' . $uid); + + foreach ($this->addressBooksToDelete[$uid] as $addressBook) { + $this->cardDav->deleteAddressBook($addressBook['id']); + } + + unset($this->calendarsToDelete[$uid]); + unset($this->subscriptionsToDelete[$uid]); + unset($this->addressBooksToDelete[$uid]); + } + + public function changeUser(IUser $user, string $feature): void { + // This case is already covered by the account manager firing up a signal + // later on + if ($feature !== 'eMailAddress' && $feature !== 'displayName') { + $this->syncService->updateUser($user); + } + } + + public function firstLogin(IUser $user): void { + $principal = 'principals/users/' . $user->getUID(); + if ($this->calDav->getCalendarsForUserCount($principal) === 0) { + try { + $this->calDav->createCalendar($principal, CalDavBackend::PERSONAL_CALENDAR_URI, [ + '{DAV:}displayname' => CalDavBackend::PERSONAL_CALENDAR_NAME, + '{http://apple.com/ns/ical/}calendar-color' => $this->themingDefaults->getColorPrimary(), + 'components' => 'VEVENT' + ]); + } catch (\Exception $e) { + \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]); + } + } + if ($this->cardDav->getAddressBooksForUserCount($principal) === 0) { + try { + $this->cardDav->createAddressBook($principal, CardDavBackend::PERSONAL_ADDRESSBOOK_URI, [ + '{DAV:}displayname' => CardDavBackend::PERSONAL_ADDRESSBOOK_NAME, + ]); + } catch (\Exception $e) { + \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]); + } + } + } +} diff --git a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php index 96968909d1c..8f89283e94d 100644 --- a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php +++ b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php @@ -526,7 +526,8 @@ class CardDavBackendTest extends TestCase { $query = $this->db->getQueryBuilder(); $query->select('*') - ->from('cards_properties'); + ->from('cards_properties') + ->orderBy('name'); $qResult = $query->execute(); $result = $qResult->fetchAll(); @@ -534,13 +535,13 @@ class CardDavBackendTest extends TestCase { $this->assertSame(2, count($result)); - $this->assertSame('UID', $result[0]['name']); - $this->assertSame($cardUri, $result[0]['value']); + $this->assertSame('FN', $result[0]['name']); + $this->assertSame('John Doe', $result[0]['value']); $this->assertSame($bookId, (int)$result[0]['addressbookid']); $this->assertSame($cardId, (int)$result[0]['cardid']); - $this->assertSame('FN', $result[1]['name']); - $this->assertSame('John Doe', $result[1]['value']); + $this->assertSame('UID', $result[1]['name']); + $this->assertSame($cardUri, $result[1]['value']); $this->assertSame($bookId, (int)$result[1]['addressbookid']); $this->assertSame($cardId, (int)$result[1]['cardid']); diff --git a/apps/dav/tests/unit/DAV/HookManagerTest.php b/apps/dav/tests/unit/DAV/HookManagerTest.php deleted file mode 100644 index 746727cd2b3..00000000000 --- a/apps/dav/tests/unit/DAV/HookManagerTest.php +++ /dev/null @@ -1,222 +0,0 @@ -<?php - -/** - * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2016 ownCloud, Inc. - * SPDX-License-Identifier: AGPL-3.0-only - */ -namespace OCA\DAV\Tests\unit\DAV; - -use OCA\DAV\CalDAV\CalDavBackend; -use OCA\DAV\CardDAV\CardDavBackend; -use OCA\DAV\CardDAV\SyncService; -use OCA\DAV\HookManager; -use OCP\Defaults; -use OCP\IL10N; -use OCP\IUser; -use OCP\IUserManager; -use PHPUnit\Framework\MockObject\MockObject; -use Test\TestCase; - -class HookManagerTest extends TestCase { - /** @var IL10N */ - private $l10n; - - protected function setUp(): void { - parent::setUp(); - $this->l10n = $this->createMock(IL10N::class); - $this->l10n - ->expects($this->any()) - ->method('t') - ->willReturnCallback(function ($text, $parameters = []) { - return vsprintf($text, $parameters); - }); - } - - public function test(): void { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - $user->expects($this->once())->method('getUID')->willReturn('newUser'); - - /** @var IUserManager | MockObject $userManager */ - $userManager = $this->getMockBuilder(IUserManager::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var SyncService | MockObject $syncService */ - $syncService = $this->getMockBuilder(SyncService::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var Defaults | MockObject $syncService */ - $defaults = $this->getMockBuilder(Defaults::class) - ->disableOriginalConstructor() - ->getMock(); - - $defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca'); - - /** @var CalDavBackend | MockObject $cal */ - $cal = $this->getMockBuilder(CalDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $cal->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0); - $cal->expects($this->once())->method('createCalendar')->with( - 'principals/users/newUser', - 'personal', [ - '{DAV:}displayname' => 'Personal', - '{http://apple.com/ns/ical/}calendar-color' => '#745bca', - 'components' => 'VEVENT' - ]); - - /** @var CardDavBackend | MockObject $card */ - $card = $this->getMockBuilder(CardDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $card->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0); - $card->expects($this->once())->method('createAddressBook')->with( - 'principals/users/newUser', - 'contacts', ['{DAV:}displayname' => 'Contacts']); - - $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults); - $hm->firstLogin($user); - } - - public function testWithExisting(): void { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - $user->expects($this->once())->method('getUID')->willReturn('newUser'); - - /** @var IUserManager | MockObject $userManager */ - $userManager = $this->getMockBuilder(IUserManager::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var SyncService | MockObject $syncService */ - $syncService = $this->getMockBuilder(SyncService::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var Defaults | MockObject $syncService */ - $defaults = $this->getMockBuilder(Defaults::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var CalDavBackend | MockObject $cal */ - $cal = $this->getMockBuilder(CalDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $cal->expects($this->once())->method('getCalendarsForUserCount')->willReturn(1); - $cal->expects($this->never())->method('createCalendar'); - - /** @var CardDavBackend | MockObject $card */ - $card = $this->getMockBuilder(CardDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $card->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(1); - $card->expects($this->never())->method('createAddressBook'); - - $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults); - $hm->firstLogin($user); - } - - public function testWithBirthdayCalendar(): void { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - $user->expects($this->once())->method('getUID')->willReturn('newUser'); - - /** @var IUserManager | MockObject $userManager */ - $userManager = $this->getMockBuilder(IUserManager::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var SyncService | MockObject $syncService */ - $syncService = $this->getMockBuilder(SyncService::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var Defaults | MockObject $syncService */ - $defaults = $this->getMockBuilder(Defaults::class) - ->disableOriginalConstructor() - ->getMock(); - $defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca'); - - /** @var CalDavBackend | MockObject $cal */ - $cal = $this->getMockBuilder(CalDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $cal->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0); - $cal->expects($this->once())->method('createCalendar')->with( - 'principals/users/newUser', - 'personal', [ - '{DAV:}displayname' => 'Personal', - '{http://apple.com/ns/ical/}calendar-color' => '#745bca', - 'components' => 'VEVENT' - ]); - - /** @var CardDavBackend | MockObject $card */ - $card = $this->getMockBuilder(CardDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $card->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0); - $card->expects($this->once())->method('createAddressBook')->with( - 'principals/users/newUser', - 'contacts', ['{DAV:}displayname' => 'Contacts']); - - $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults); - $hm->firstLogin($user); - } - - public function testDeleteCalendar(): void { - $user = $this->getMockBuilder(IUser::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var IUserManager | MockObject $userManager */ - $userManager = $this->getMockBuilder(IUserManager::class) - ->disableOriginalConstructor() - ->getMock(); - $userManager->expects($this->once())->method('get')->willReturn($user); - - /** @var SyncService | MockObject $syncService */ - $syncService = $this->getMockBuilder(SyncService::class) - ->disableOriginalConstructor() - ->getMock(); - $syncService->expects($this->once()) - ->method('deleteUser'); - - /** @var Defaults | MockObject $syncService */ - $defaults = $this->getMockBuilder(Defaults::class) - ->disableOriginalConstructor() - ->getMock(); - - /** @var CalDavBackend | MockObject $cal */ - $cal = $this->getMockBuilder(CalDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $cal->expects($this->once())->method('getUsersOwnCalendars')->willReturn([ - ['id' => 'personal'] - ]); - $cal->expects($this->once())->method('getSubscriptionsForUser')->willReturn([ - ['id' => 'some-subscription'] - ]); - $cal->expects($this->once())->method('deleteCalendar')->with('personal'); - $cal->expects($this->once())->method('deleteSubscription')->with('some-subscription'); - $cal->expects($this->once())->method('deleteAllSharesByUser'); - - /** @var CardDavBackend | MockObject $card */ - $card = $this->getMockBuilder(CardDavBackend::class) - ->disableOriginalConstructor() - ->getMock(); - $card->expects($this->once())->method('getUsersOwnAddressBooks')->willReturn([ - ['id' => 'personal'] - ]); - $card->expects($this->once())->method('deleteAddressBook'); - - $hm = new HookManager($userManager, $syncService, $cal, $card, $defaults); - $hm->preDeleteUser(['uid' => 'newUser']); - $hm->postDeleteUser(['uid' => 'newUser']); - } -} diff --git a/apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php b/apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php new file mode 100644 index 00000000000..ce91a2a7fa6 --- /dev/null +++ b/apps/dav/tests/unit/DAV/Listener/UserEventsListenerTest.php @@ -0,0 +1,140 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace OCA\DAV\Tests\unit\DAV\Listener; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CardDAV\CardDavBackend; +use OCA\DAV\CardDAV\SyncService; +use OCA\DAV\Listener\UserEventsListener; +use OCP\Defaults; +use OCP\IUser; +use OCP\IUserManager; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class UserEventsListenerTest extends TestCase { + private IUserManager&MockObject $userManager; + private SyncService&MockObject $syncService; + private CalDavBackend&MockObject $calDavBackend; + private CardDavBackend&MockObject $cardDavBackend; + private Defaults&MockObject $defaults; + + private UserEventsListener $userEventsListener; + + protected function setUp(): void { + parent::setUp(); + $this->userManager = $this->createMock(IUserManager::class); + $this->syncService = $this->createMock(SyncService::class); + $this->calDavBackend = $this->createMock(CalDavBackend::class); + $this->cardDavBackend = $this->createMock(CardDavBackend::class); + $this->defaults = $this->createMock(Defaults::class); + $this->userEventsListener = new UserEventsListener( + $this->userManager, + $this->syncService, + $this->calDavBackend, + $this->cardDavBackend, + $this->defaults, + ); + } + + public function test(): void { + $user = $this->getMockBuilder(IUser::class) + ->disableOriginalConstructor() + ->getMock(); + $user->expects($this->once())->method('getUID')->willReturn('newUser'); + + $this->defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca'); + + $this->calDavBackend->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0); + $this->calDavBackend->expects($this->once())->method('createCalendar')->with( + 'principals/users/newUser', + 'personal', [ + '{DAV:}displayname' => 'Personal', + '{http://apple.com/ns/ical/}calendar-color' => '#745bca', + 'components' => 'VEVENT' + ]); + + $this->cardDavBackend->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0); + $this->cardDavBackend->expects($this->once())->method('createAddressBook')->with( + 'principals/users/newUser', + 'contacts', ['{DAV:}displayname' => 'Contacts']); + + $this->userEventsListener->firstLogin($user); + } + + public function testWithExisting(): void { + $user = $this->getMockBuilder(IUser::class) + ->disableOriginalConstructor() + ->getMock(); + $user->expects($this->once())->method('getUID')->willReturn('newUser'); + + $this->calDavBackend->expects($this->once())->method('getCalendarsForUserCount')->willReturn(1); + $this->calDavBackend->expects($this->never())->method('createCalendar'); + + $this->cardDavBackend->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(1); + $this->cardDavBackend->expects($this->never())->method('createAddressBook'); + + $this->userEventsListener->firstLogin($user); + } + + public function testWithBirthdayCalendar(): void { + $user = $this->getMockBuilder(IUser::class) + ->disableOriginalConstructor() + ->getMock(); + $user->expects($this->once())->method('getUID')->willReturn('newUser'); + + $this->defaults->expects($this->once())->method('getColorPrimary')->willReturn('#745bca'); + + $this->calDavBackend->expects($this->once())->method('getCalendarsForUserCount')->willReturn(0); + $this->calDavBackend->expects($this->once())->method('createCalendar')->with( + 'principals/users/newUser', + 'personal', [ + '{DAV:}displayname' => 'Personal', + '{http://apple.com/ns/ical/}calendar-color' => '#745bca', + 'components' => 'VEVENT' + ]); + + $this->cardDavBackend->expects($this->once())->method('getAddressBooksForUserCount')->willReturn(0); + $this->cardDavBackend->expects($this->once())->method('createAddressBook')->with( + 'principals/users/newUser', + 'contacts', ['{DAV:}displayname' => 'Contacts']); + + $this->userEventsListener->firstLogin($user); + } + + public function testDeleteCalendar(): void { + $user = $this->getMockBuilder(IUser::class) + ->disableOriginalConstructor() + ->getMock(); + $user->expects($this->once())->method('getUID')->willReturn('newUser'); + + $this->syncService->expects($this->once()) + ->method('deleteUser'); + + $this->calDavBackend->expects($this->once())->method('getUsersOwnCalendars')->willReturn([ + ['id' => 'personal'] + ]); + $this->calDavBackend->expects($this->once())->method('getSubscriptionsForUser')->willReturn([ + ['id' => 'some-subscription'] + ]); + $this->calDavBackend->expects($this->once())->method('deleteCalendar')->with('personal'); + $this->calDavBackend->expects($this->once())->method('deleteSubscription')->with('some-subscription'); + $this->calDavBackend->expects($this->once())->method('deleteAllSharesByUser'); + + $this->cardDavBackend->expects($this->once())->method('getUsersOwnAddressBooks')->willReturn([ + ['id' => 'personal'] + ]); + $this->cardDavBackend->expects($this->once())->method('deleteAddressBook'); + + $this->userEventsListener->preDeleteUser($user); + $this->userEventsListener->postDeleteUser('newUser'); + } +} diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index 2824f99f79d..18df1f058da 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -543,12 +543,6 @@ <code><![CDATA[bool]]></code> </InvalidReturnType> </file> - <file src="apps/dav/lib/HookManager.php"> - <InvalidPropertyAssignmentValue> - <code><![CDATA[$this->usersToDelete]]></code> - <code><![CDATA[$this->usersToDelete]]></code> - </InvalidPropertyAssignmentValue> - </file> <file src="apps/dav/lib/Migration/BuildCalendarSearchIndexBackgroundJob.php"> <ParamNameMismatch> <code><![CDATA[$arguments]]></code> diff --git a/tests/lib/User/UserTest.php b/tests/lib/User/UserTest.php index e79aaadd593..44d28481ed5 100644 --- a/tests/lib/User/UserTest.php +++ b/tests/lib/User/UserTest.php @@ -929,6 +929,12 @@ class UserTest extends TestCase { $this->equalTo('enabled'), 'true' ); + /* dav event listener gets the manager list from config */ + $config->expects(self::any()) + ->method('getUserValue') + ->willReturnCallback( + fn ($user, $app, $key, $default) => ($key === 'enabled' ? 'false' : $default) + ); $user = new User('foo', $backend, $this->dispatcher, null, $config); $user->setEnabled(true); |