Skip to content

Commit 5b84219

Browse files
committed
fix(DirectEditing): cleanup tokens on user deletion
... and for disabled users Signed-off-by: Max <max@nextcloud.com>
1 parent cbdff85 commit 5b84219

6 files changed

Lines changed: 121 additions & 0 deletions

File tree

core/AppInfo/Application.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
use OC\Core\Sharing\Recipient\TeamShareRecipientType;
3939
use OC\Core\Sharing\Recipient\TokenShareRecipientType;
4040
use OC\Core\Sharing\Recipient\UserShareRecipientType;
41+
use OC\DirectEditing\Listeners\UserDeletedTokenCleanupListener as UserDeletedDirectEditingTokenCleanupListener;
42+
use OC\DirectEditing\Listeners\UserDisabledTokenCleanupListener as UserDisabledDirectEditingTokenCleanupListener;
4143
use OC\OCM\OCMDiscoveryHandler;
4244
use OC\OCM\OCMJwksHandler;
4345
use OC\TagManager;
@@ -55,6 +57,7 @@
5557
use OCP\Server;
5658
use OCP\User\Events\BeforeUserDeletedEvent;
5759
use OCP\User\Events\PasswordUpdatedEvent;
60+
use OCP\User\Events\UserChangedEvent;
5861
use OCP\User\Events\UserDeletedEvent;
5962
use OCP\Util;
6063

@@ -115,6 +118,10 @@ public function register(IRegistrationContext $context): void {
115118

116119
$context->registerEventListener(RestrictInteractionEvent::class, RestrictInteractionListener::class);
117120

121+
// Direct Editing
122+
$context->registerEventListener(UserDeletedEvent::class, UserDeletedDirectEditingTokenCleanupListener::class);
123+
$context->registerEventListener(UserChangedEvent::class, UserDisabledDirectEditingTokenCleanupListener::class);
124+
118125
$registry = Server::get(ISharingRegistry::class);
119126

120127
$registry->registerRecipientType(Server::get(EmailShareRecipientType::class));

lib/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1782,6 +1782,8 @@
17821782
'OC\\Diagnostics\\Query' => $baseDir . '/lib/private/Diagnostics/Query.php',
17831783
'OC\\Diagnostics\\QueryLogger' => $baseDir . '/lib/private/Diagnostics/QueryLogger.php',
17841784
'OC\\Diagnostics\\TLogSlowOperation' => $baseDir . '/lib/private/Diagnostics/TLogSlowOperation.php',
1785+
'OC\\DirectEditing\\Listeners\\UserDeletedTokenCleanupListener' => $baseDir . '/lib/private/DirectEditing/Listeners/UserDeletedTokenCleanupListener.php',
1786+
'OC\\DirectEditing\\Listeners\\UserDisabledTokenCleanupListener' => $baseDir . '/lib/private/DirectEditing/Listeners/UserDisabledTokenCleanupListener.php',
17851787
'OC\\DirectEditing\\Manager' => $baseDir . '/lib/private/DirectEditing/Manager.php',
17861788
'OC\\DirectEditing\\Token' => $baseDir . '/lib/private/DirectEditing/Token.php',
17871789
'OC\\EmojiHelper' => $baseDir . '/lib/private/EmojiHelper.php',

lib/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,6 +1823,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
18231823
'OC\\Diagnostics\\Query' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/Query.php',
18241824
'OC\\Diagnostics\\QueryLogger' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/QueryLogger.php',
18251825
'OC\\Diagnostics\\TLogSlowOperation' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/TLogSlowOperation.php',
1826+
'OC\\DirectEditing\\Listeners\\UserDeletedTokenCleanupListener' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Listeners/UserDeletedTokenCleanupListener.php',
1827+
'OC\\DirectEditing\\Listeners\\UserDisabledTokenCleanupListener' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Listeners/UserDisabledTokenCleanupListener.php',
18261828
'OC\\DirectEditing\\Manager' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Manager.php',
18271829
'OC\\DirectEditing\\Token' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Token.php',
18281830
'OC\\EmojiHelper' => __DIR__ . '/../../..' . '/lib/private/EmojiHelper.php',
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OC\DirectEditing\Listeners;
11+
12+
use OC\DirectEditing\Manager;
13+
use OCP\EventDispatcher\Event;
14+
use OCP\EventDispatcher\IEventListener;
15+
use OCP\User\Events\UserDeletedEvent;
16+
use Psr\Log\LoggerInterface;
17+
use Throwable;
18+
19+
/**
20+
* @template-implements IEventListener<UserDeletedEvent>
21+
*/
22+
class UserDeletedTokenCleanupListener implements IEventListener {
23+
public function __construct(
24+
private Manager $manager,
25+
private LoggerInterface $logger,
26+
) {
27+
}
28+
29+
#[\Override]
30+
public function handle(Event $event): void {
31+
if (!($event instanceof UserDeletedEvent)) {
32+
// Unrelated
33+
return;
34+
}
35+
36+
/**
37+
* Catch any exception during this process as any failure here shouldn't block the
38+
* user deletion.
39+
*/
40+
try {
41+
$uid = $event->getUser()->getUID();
42+
$this->manager->invalidateTokensForUser($uid);
43+
} catch (Throwable $e) {
44+
$this->logger->error('Could not clean up direct editing tokens after user deletion: ' . $e->getMessage(), [
45+
'exception' => $e,
46+
]);
47+
}
48+
}
49+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OC\DirectEditing\Listeners;
11+
12+
use OC\DirectEditing\Manager;
13+
use OCP\EventDispatcher\Event;
14+
use OCP\EventDispatcher\IEventListener;
15+
use OCP\User\Events\UserChangedEvent;
16+
use Psr\Log\LoggerInterface;
17+
use Throwable;
18+
19+
/**
20+
* @template-implements IEventListener<UserChangedEvent>
21+
*/
22+
class UserDisabledTokenCleanupListener implements IEventListener {
23+
public function __construct(
24+
private Manager $manager,
25+
private LoggerInterface $logger,
26+
) {
27+
}
28+
29+
#[\Override]
30+
public function handle(Event $event): void {
31+
if (!($event instanceof UserChangedEvent)) {
32+
// Unrelated
33+
return;
34+
}
35+
36+
if ($event->getFeature() !== 'enabled' || $event->getValue()) {
37+
// not disabled
38+
return;
39+
}
40+
41+
/**
42+
* Catch any exception during this process
43+
* as any failure here shouldn't block the disabling the user.
44+
*/
45+
try {
46+
$uid = $event->getUser()->getUID();
47+
$this->manager->invalidateTokensForUser($uid);
48+
} catch (Throwable $e) {
49+
$this->logger->error('Could not clean up direct editing tokens when disabling user: ' . $e->getMessage(), [
50+
'exception' => $e,
51+
]);
52+
}
53+
}
54+
}

lib/private/DirectEditing/Manager.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,13 @@ public function cleanup(): int {
218218
return $query->executeStatement();
219219
}
220220

221+
public function invalidateTokensForUser(string $uid): int {
222+
$query = $this->connection->getQueryBuilder();
223+
$query->delete(self::TABLE_TOKENS)
224+
->where($query->expr()->eq('user_id', $query->createNamedParameter($uid)));
225+
return $query->executeStatement();
226+
}
227+
221228
public function refreshToken(string $token): bool {
222229
$query = $this->connection->getQueryBuilder();
223230
$query->update(self::TABLE_TOKENS)

0 commit comments

Comments
 (0)