From 62f4bcdecf52a88017969341913d856b7c08359e Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Aug 2026 09:07:07 +0200 Subject: [PATCH] fix(DirectEditing): cleanup tokens on user deletion ... and for disabled users Signed-off-by: Max --- core/Application.php | 7 +++ lib/composer/composer/autoload_classmap.php | 2 + lib/composer/composer/autoload_static.php | 2 + .../UserDeletedTokenCleanupListener.php | 49 +++++++++++++++++ .../UserDisabledTokenCleanupListener.php | 54 +++++++++++++++++++ lib/private/DirectEditing/Manager.php | 7 +++ 6 files changed, 121 insertions(+) create mode 100644 lib/private/DirectEditing/Listeners/UserDeletedTokenCleanupListener.php create mode 100644 lib/private/DirectEditing/Listeners/UserDisabledTokenCleanupListener.php diff --git a/core/Application.php b/core/Application.php index 6beedf75ad8ed..0b111f0fe90b4 100644 --- a/core/Application.php +++ b/core/Application.php @@ -20,6 +20,8 @@ use OC\Core\Listener\BeforeTemplateRenderedListener; use OC\Core\Listener\PasswordUpdatedListener; use OC\Core\Notification\CoreNotifier; +use OC\DirectEditing\Listeners\UserDeletedTokenCleanupListener as UserDeletedDirectEditingTokenCleanupListener; +use OC\DirectEditing\Listeners\UserDisabledTokenCleanupListener as UserDisabledDirectEditingTokenCleanupListener; use OC\TagManager; use OCP\AppFramework\App; use OCP\AppFramework\Http\Events\BeforeLoginTemplateRenderedEvent; @@ -30,6 +32,7 @@ use OCP\Notification\IManager as INotificationManager; use OCP\User\Events\BeforeUserDeletedEvent; use OCP\User\Events\PasswordUpdatedEvent; +use OCP\User\Events\UserChangedEvent; use OCP\User\Events\UserDeletedEvent; use OCP\Util; @@ -302,5 +305,9 @@ public function __construct() { // Tags $eventDispatcher->addServiceListener(UserDeletedEvent::class, TagManager::class); + + // Direct Editing + $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedDirectEditingTokenCleanupListener::class); + $eventDispatcher->addServiceListener(UserChangedEvent::class, UserDisabledDirectEditingTokenCleanupListener::class); } } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 7dcb13ca9a7b6..52bde21f27b00 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1464,6 +1464,8 @@ 'OC\\Diagnostics\\EventLogger' => $baseDir . '/lib/private/Diagnostics/EventLogger.php', 'OC\\Diagnostics\\Query' => $baseDir . '/lib/private/Diagnostics/Query.php', 'OC\\Diagnostics\\QueryLogger' => $baseDir . '/lib/private/Diagnostics/QueryLogger.php', + 'OC\\DirectEditing\\Listeners\\UserDeletedTokenCleanupListener' => $baseDir . '/lib/private/DirectEditing/Listeners/UserDeletedTokenCleanupListener.php', + 'OC\\DirectEditing\\Listeners\\UserDisabledTokenCleanupListener' => $baseDir . '/lib/private/DirectEditing/Listeners/UserDisabledTokenCleanupListener.php', 'OC\\DirectEditing\\Manager' => $baseDir . '/lib/private/DirectEditing/Manager.php', 'OC\\DirectEditing\\Token' => $baseDir . '/lib/private/DirectEditing/Token.php', 'OC\\EmojiHelper' => $baseDir . '/lib/private/EmojiHelper.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index bd129614c0b9e..6e4964f4a7d32 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1497,6 +1497,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Diagnostics\\EventLogger' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/EventLogger.php', 'OC\\Diagnostics\\Query' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/Query.php', 'OC\\Diagnostics\\QueryLogger' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/QueryLogger.php', + 'OC\\DirectEditing\\Listeners\\UserDeletedTokenCleanupListener' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Listeners/UserDeletedTokenCleanupListener.php', + 'OC\\DirectEditing\\Listeners\\UserDisabledTokenCleanupListener' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Listeners/UserDisabledTokenCleanupListener.php', 'OC\\DirectEditing\\Manager' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Manager.php', 'OC\\DirectEditing\\Token' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Token.php', 'OC\\EmojiHelper' => __DIR__ . '/../../..' . '/lib/private/EmojiHelper.php', diff --git a/lib/private/DirectEditing/Listeners/UserDeletedTokenCleanupListener.php b/lib/private/DirectEditing/Listeners/UserDeletedTokenCleanupListener.php new file mode 100644 index 0000000000000..1fc7860a90687 --- /dev/null +++ b/lib/private/DirectEditing/Listeners/UserDeletedTokenCleanupListener.php @@ -0,0 +1,49 @@ + + */ +class UserDeletedTokenCleanupListener implements IEventListener { + public function __construct( + private Manager $manager, + private LoggerInterface $logger, + ) { + } + + #[\Override] + public function handle(Event $event): void { + if (!($event instanceof UserDeletedEvent)) { + // Unrelated + return; + } + + /** + * Catch any exception during this process as any failure here shouldn't block the + * user deletion. + */ + try { + $uid = $event->getUser()->getUID(); + $this->manager->invalidateTokensForUser($uid); + } catch (Throwable $e) { + $this->logger->error('Could not clean up direct editing tokens after user deletion: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } +} diff --git a/lib/private/DirectEditing/Listeners/UserDisabledTokenCleanupListener.php b/lib/private/DirectEditing/Listeners/UserDisabledTokenCleanupListener.php new file mode 100644 index 0000000000000..9284f1d437d67 --- /dev/null +++ b/lib/private/DirectEditing/Listeners/UserDisabledTokenCleanupListener.php @@ -0,0 +1,54 @@ + + */ +class UserDisabledTokenCleanupListener implements IEventListener { + public function __construct( + private Manager $manager, + private LoggerInterface $logger, + ) { + } + + #[\Override] + public function handle(Event $event): void { + if (!($event instanceof UserChangedEvent)) { + // Unrelated + return; + } + + if ($event->getFeature() !== 'enabled' || $event->getValue()) { + // not disabled + return; + } + + /** + * Catch any exception during this process + * as any failure here shouldn't block the disabling the user. + */ + try { + $uid = $event->getUser()->getUID(); + $this->manager->invalidateTokensForUser($uid); + } catch (Throwable $e) { + $this->logger->error('Could not clean up direct editing tokens when disabling user: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } +} diff --git a/lib/private/DirectEditing/Manager.php b/lib/private/DirectEditing/Manager.php index 67fc3c4081757..4df012396161d 100644 --- a/lib/private/DirectEditing/Manager.php +++ b/lib/private/DirectEditing/Manager.php @@ -224,6 +224,13 @@ public function cleanup(): int { return $query->execute(); } + public function invalidateTokensForUser(string $uid): int { + $query = $this->connection->getQueryBuilder(); + $query->delete(self::TABLE_TOKENS) + ->where($query->expr()->eq('user_id', $query->createNamedParameter($uid))); + return $query->executeStatement(); + } + public function refreshToken(string $token): bool { $query = $this->connection->getQueryBuilder(); $query->update(self::TABLE_TOKENS)