aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php
blob: a619021d192e3e04fdf3509cdb60c1c29da9d31d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<?php

declare(strict_types=1);

/**
 * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */
namespace OC\Authentication\Listeners;

use OC\Files\Cache\Cache;
use OC\Files\Storage\Wrapper\Wrapper;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Config\IMountProviderCollection;
use OCP\Files\Storage\IStorage;
use OCP\User\Events\BeforeUserDeletedEvent;
use OCP\User\Events\UserDeletedEvent;
use Psr\Log\LoggerInterface;

/** @template-implements IEventListener<BeforeUserDeletedEvent|UserDeletedEvent> */
class UserDeletedFilesCleanupListener implements IEventListener {
	/** @var array<string,IStorage> */
	private $homeStorageCache = [];

	public function __construct(
		private IMountProviderCollection $mountProviderCollection,
		private LoggerInterface $logger,
	) {
	}

	public function handle(Event $event): void {
		$user = $event->getUser();

		// since we can't reliably get the user home storage after the user is deleted
		// but the user deletion might get canceled during the before event
		// we only cache the user home storage during the before event and then do the
		// action deletion during the after event

		if ($event instanceof BeforeUserDeletedEvent) {
			$this->logger->debug('Prepare deleting storage for user {userId}', ['userId' => $user->getUID()]);

			$userHome = $this->mountProviderCollection->getHomeMountForUser($user);
			$storage = $userHome->getStorage();
			if (!$storage) {
				throw new \Exception('Account has no home storage');
			}

			// remove all wrappers, so we do the delete directly on the home storage bypassing any wrapper
			while ($storage->instanceOfStorage(Wrapper::class)) {
				/** @var Wrapper $storage */
				$storage = $storage->getWrapperStorage();
			}

			$this->homeStorageCache[$event->getUser()->getUID()] = $storage;
		}
		if ($event instanceof UserDeletedEvent) {
			if (!isset($this->homeStorageCache[$user->getUID()])) {
				throw new \Exception('UserDeletedEvent fired without matching BeforeUserDeletedEvent');
			}
			$storage = $this->homeStorageCache[$user->getUID()];
			$cache = $storage->getCache();
			$storage->rmdir('');
			$this->logger->debug('Deleted storage for user {userId}', ['userId' => $user->getUID()]);

			if ($cache instanceof Cache) {
				$cache->clear();
			} else {
				throw new \Exception('Home storage has invalid cache');
			}
		}
	}
}