Merge pull request #48588 from nextcloud/backport/47896/stable29
[stable29] fix: Make user removal more resilientpull/48755/head
commit
658aa1a2dd
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\Repair;
|
||||
|
||||
use OC\User\BackgroundJobs\CleanupDeletedUsers;
|
||||
use OCP\BackgroundJob\IJobList;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\IRepairStep;
|
||||
|
||||
class AddCleanupDeletedUsersBackgroundJob implements IRepairStep {
|
||||
private IJobList $jobList;
|
||||
|
||||
public function __construct(IJobList $jobList) {
|
||||
$this->jobList = $jobList;
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return 'Add cleanup-deleted-users background job';
|
||||
}
|
||||
|
||||
public function run(IOutput $output) {
|
||||
$this->jobList->add(CleanupDeletedUsers::class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\User\BackgroundJobs;
|
||||
|
||||
use OC\User\Manager;
|
||||
use OC\User\PartiallyDeletedUsersBackend;
|
||||
use OC\User\User;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\BackgroundJob\IJob;
|
||||
use OCP\BackgroundJob\TimedJob;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IConfig;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class CleanupDeletedUsers extends TimedJob {
|
||||
public function __construct(
|
||||
ITimeFactory $time,
|
||||
private Manager $userManager,
|
||||
private IConfig $config,
|
||||
private LoggerInterface $logger,
|
||||
) {
|
||||
parent::__construct($time);
|
||||
$this->setTimeSensitivity(IJob::TIME_INSENSITIVE);
|
||||
$this->setInterval(24 * 3600);
|
||||
}
|
||||
|
||||
protected function run($argument): void {
|
||||
$backend = new PartiallyDeletedUsersBackend($this->config);
|
||||
$users = $backend->getUsers();
|
||||
|
||||
if (empty($users)) {
|
||||
$this->logger->debug('No failed deleted users found.');
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($users as $userId) {
|
||||
if ($this->userManager->userExists($userId)) {
|
||||
$this->logger->info('Skipping user {userId}, marked as deleted, as they still exists in user backend.', ['userId' => $userId]);
|
||||
$backend->unmarkUser($userId);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$user = new User(
|
||||
$userId,
|
||||
$backend,
|
||||
\OCP\Server::get(IEventDispatcher::class),
|
||||
$this->userManager,
|
||||
$this->config,
|
||||
);
|
||||
$user->delete();
|
||||
$this->logger->info('Cleaned up deleted user {userId}', ['userId' => $userId]);
|
||||
} catch (\Throwable $error) {
|
||||
$this->logger->warning('Could not cleanup deleted user {userId}', ['userId' => $userId, 'exception' => $error]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\User;
|
||||
|
||||
use OCP\IConfig;
|
||||
use OCP\IUserBackend;
|
||||
use OCP\User\Backend\IGetHomeBackend;
|
||||
|
||||
/**
|
||||
* This is a "fake" backend for users that were deleted,
|
||||
* but not properly removed from Nextcloud (e.g. an exception occurred).
|
||||
* This backend is only needed because some APIs in user-deleted-events require a "real" user with backend.
|
||||
*/
|
||||
class PartiallyDeletedUsersBackend extends Backend implements IGetHomeBackend, IUserBackend {
|
||||
|
||||
public function __construct(
|
||||
private IConfig $config,
|
||||
) {
|
||||
}
|
||||
|
||||
public function deleteUser($uid): bool {
|
||||
// fake true, deleting failed users is automatically handled by User::delete()
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getBackendName(): string {
|
||||
return 'deleted users';
|
||||
}
|
||||
|
||||
public function userExists($uid) {
|
||||
return $this->config->getUserValue($uid, 'core', 'deleted') === 'true';
|
||||
}
|
||||
|
||||
public function getHome(string $uid): string|false {
|
||||
return $this->config->getUserValue($uid, 'core', 'deleted.home-path') ?: false;
|
||||
}
|
||||
|
||||
public function getUsers($search = '', $limit = null, $offset = null) {
|
||||
return $this->config->getUsersForUserValue('core', 'deleted', 'true');
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmark a user as deleted.
|
||||
* This typically the case if the user deletion failed in the backend but before the backend deleted the user,
|
||||
* meaning the user still exists so we unmark them as it still can be accessed (and deleted) normally.
|
||||
*/
|
||||
public function unmarkUser(string $userId): void {
|
||||
$this->config->deleteUserValue($userId, 'core', 'deleted');
|
||||
$this->config->deleteUserValue($userId, 'core', 'deleted.home-path');
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue