diff --git a/core/AppInfo/Application.php b/core/AppInfo/Application.php index 50f8caba53a84..6d14ac467b9b6 100644 --- a/core/AppInfo/Application.php +++ b/core/AppInfo/Application.php @@ -38,6 +38,8 @@ use OC\Core\Sharing\Recipient\TeamShareRecipientType; use OC\Core\Sharing\Recipient\TokenShareRecipientType; use OC\Core\Sharing\Recipient\UserShareRecipientType; +use OC\DirectEditing\Listeners\UserDeletedTokenCleanupListener as UserDeletedDirectEditingTokenCleanupListener; +use OC\DirectEditing\Listeners\UserDisabledTokenCleanupListener as UserDisabledDirectEditingTokenCleanupListener; use OC\OCM\OCMDiscoveryHandler; use OC\OCM\OCMJwksHandler; use OC\TagManager; @@ -55,6 +57,7 @@ use OCP\Server; use OCP\User\Events\BeforeUserDeletedEvent; use OCP\User\Events\PasswordUpdatedEvent; +use OCP\User\Events\UserChangedEvent; use OCP\User\Events\UserDeletedEvent; use OCP\Util; @@ -115,6 +118,10 @@ public function register(IRegistrationContext $context): void { $context->registerEventListener(RestrictInteractionEvent::class, RestrictInteractionListener::class); + // Direct Editing + $context->registerEventListener(UserDeletedEvent::class, UserDeletedDirectEditingTokenCleanupListener::class); + $context->registerEventListener(UserChangedEvent::class, UserDisabledDirectEditingTokenCleanupListener::class); + $registry = Server::get(ISharingRegistry::class); $registry->registerRecipientType(Server::get(EmailShareRecipientType::class)); diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 7f6c8757b0d16..b23819d093969 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1782,6 +1782,8 @@ 'OC\\Diagnostics\\Query' => $baseDir . '/lib/private/Diagnostics/Query.php', 'OC\\Diagnostics\\QueryLogger' => $baseDir . '/lib/private/Diagnostics/QueryLogger.php', 'OC\\Diagnostics\\TLogSlowOperation' => $baseDir . '/lib/private/Diagnostics/TLogSlowOperation.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 3d2026f49ff4c..1c629facd2da4 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1823,6 +1823,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Diagnostics\\Query' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/Query.php', 'OC\\Diagnostics\\QueryLogger' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/QueryLogger.php', 'OC\\Diagnostics\\TLogSlowOperation' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/TLogSlowOperation.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 e9b26ace570ed..d3c73c9bb725e 100644 --- a/lib/private/DirectEditing/Manager.php +++ b/lib/private/DirectEditing/Manager.php @@ -218,6 +218,13 @@ public function cleanup(): int { return $query->executeStatement(); } + 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)