SetupManager.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Files;
  8. use OC\Files\Config\MountProviderCollection;
  9. use OC\Files\Mount\HomeMountPoint;
  10. use OC\Files\Mount\MountPoint;
  11. use OC\Files\Storage\Common;
  12. use OC\Files\Storage\Home;
  13. use OC\Files\Storage\Storage;
  14. use OC\Files\Storage\Wrapper\Availability;
  15. use OC\Files\Storage\Wrapper\Encoding;
  16. use OC\Files\Storage\Wrapper\PermissionsMask;
  17. use OC\Files\Storage\Wrapper\Quota;
  18. use OC\Lockdown\Filesystem\NullStorage;
  19. use OC\Share\Share;
  20. use OC\Share20\ShareDisableChecker;
  21. use OC_App;
  22. use OC_Hook;
  23. use OC_Util;
  24. use OCA\Files_External\Config\ExternalMountPoint;
  25. use OCA\Files_Sharing\External\Mount;
  26. use OCA\Files_Sharing\ISharedMountPoint;
  27. use OCA\Files_Sharing\SharedMount;
  28. use OCP\Constants;
  29. use OCP\Diagnostics\IEventLogger;
  30. use OCP\EventDispatcher\IEventDispatcher;
  31. use OCP\Files\Config\ICachedMountInfo;
  32. use OCP\Files\Config\IHomeMountProvider;
  33. use OCP\Files\Config\IMountProvider;
  34. use OCP\Files\Config\IUserMountCache;
  35. use OCP\Files\Events\BeforeFileSystemSetupEvent;
  36. use OCP\Files\Events\InvalidateMountCacheEvent;
  37. use OCP\Files\Events\Node\FilesystemTornDownEvent;
  38. use OCP\Files\Mount\IMountManager;
  39. use OCP\Files\Mount\IMountPoint;
  40. use OCP\Files\NotFoundException;
  41. use OCP\Files\Storage\IStorage;
  42. use OCP\Group\Events\UserAddedEvent;
  43. use OCP\Group\Events\UserRemovedEvent;
  44. use OCP\ICache;
  45. use OCP\ICacheFactory;
  46. use OCP\IConfig;
  47. use OCP\IUser;
  48. use OCP\IUserManager;
  49. use OCP\IUserSession;
  50. use OCP\Lockdown\ILockdownManager;
  51. use OCP\Share\Events\ShareCreatedEvent;
  52. use Psr\Log\LoggerInterface;
  53. class SetupManager {
  54. private bool $rootSetup = false;
  55. // List of users for which at least one mount is setup
  56. private array $setupUsers = [];
  57. // List of users for which all mounts are setup
  58. private array $setupUsersComplete = [];
  59. /** @var array<string, string[]> */
  60. private array $setupUserMountProviders = [];
  61. private ICache $cache;
  62. private bool $listeningForProviders;
  63. private array $fullSetupRequired = [];
  64. private bool $setupBuiltinWrappersDone = false;
  65. private bool $forceFullSetup = false;
  66. public function __construct(
  67. private IEventLogger $eventLogger,
  68. private MountProviderCollection $mountProviderCollection,
  69. private IMountManager $mountManager,
  70. private IUserManager $userManager,
  71. private IEventDispatcher $eventDispatcher,
  72. private IUserMountCache $userMountCache,
  73. private ILockdownManager $lockdownManager,
  74. private IUserSession $userSession,
  75. ICacheFactory $cacheFactory,
  76. private LoggerInterface $logger,
  77. private IConfig $config,
  78. private ShareDisableChecker $shareDisableChecker,
  79. ) {
  80. $this->cache = $cacheFactory->createDistributed('setupmanager::');
  81. $this->listeningForProviders = false;
  82. $this->forceFullSetup = $this->config->getSystemValueBool('debug.force-full-fs-setup');
  83. $this->setupListeners();
  84. }
  85. private function isSetupStarted(IUser $user): bool {
  86. return in_array($user->getUID(), $this->setupUsers, true);
  87. }
  88. public function isSetupComplete(IUser $user): bool {
  89. return in_array($user->getUID(), $this->setupUsersComplete, true);
  90. }
  91. private function setupBuiltinWrappers() {
  92. if ($this->setupBuiltinWrappersDone) {
  93. return;
  94. }
  95. $this->setupBuiltinWrappersDone = true;
  96. // load all filesystem apps before, so no setup-hook gets lost
  97. OC_App::loadApps(['filesystem']);
  98. $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
  99. Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
  100. if ($storage->instanceOfStorage(Common::class)) {
  101. $options = array_merge($mount->getOptions(), ['mount_point' => $mountPoint]);
  102. $storage->setMountOptions($options);
  103. }
  104. return $storage;
  105. });
  106. $reSharingEnabled = Share::isResharingAllowed();
  107. $user = $this->userSession->getUser();
  108. $sharingEnabledForUser = $user ? !$this->shareDisableChecker->sharingDisabledForUser($user->getUID()) : true;
  109. Filesystem::addStorageWrapper(
  110. 'sharing_mask',
  111. function ($mountPoint, IStorage $storage, IMountPoint $mount) use ($reSharingEnabled, $sharingEnabledForUser) {
  112. $sharingEnabledForMount = $mount->getOption('enable_sharing', true);
  113. $isShared = $mount instanceof ISharedMountPoint;
  114. if (!$sharingEnabledForMount || !$sharingEnabledForUser || (!$reSharingEnabled && $isShared)) {
  115. return new PermissionsMask([
  116. 'storage' => $storage,
  117. 'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE,
  118. ]);
  119. }
  120. return $storage;
  121. }
  122. );
  123. // install storage availability wrapper, before most other wrappers
  124. Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
  125. $externalMount = $mount instanceof ExternalMountPoint || $mount instanceof Mount;
  126. if ($externalMount && !$storage->isLocal()) {
  127. return new Availability(['storage' => $storage]);
  128. }
  129. return $storage;
  130. });
  131. Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
  132. if ($mount->getOption('encoding_compatibility', false) && !$mount instanceof SharedMount) {
  133. return new Encoding(['storage' => $storage]);
  134. }
  135. return $storage;
  136. });
  137. $quotaIncludeExternal = $this->config->getSystemValue('quota_include_external_storage', false);
  138. Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage, IMountPoint $mount) use ($quotaIncludeExternal) {
  139. // set up quota for home storages, even for other users
  140. // which can happen when using sharing
  141. if ($mount instanceof HomeMountPoint) {
  142. $user = $mount->getUser();
  143. return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) {
  144. return OC_Util::getUserQuota($user);
  145. }, 'root' => 'files', 'include_external_storage' => $quotaIncludeExternal]);
  146. }
  147. return $storage;
  148. });
  149. Filesystem::addStorageWrapper('readonly', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
  150. /*
  151. * Do not allow any operations that modify the storage
  152. */
  153. if ($mount->getOption('readonly', false)) {
  154. return new PermissionsMask([
  155. 'storage' => $storage,
  156. 'mask' => Constants::PERMISSION_ALL & ~(
  157. Constants::PERMISSION_UPDATE |
  158. Constants::PERMISSION_CREATE |
  159. Constants::PERMISSION_DELETE
  160. ),
  161. ]);
  162. }
  163. return $storage;
  164. });
  165. Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
  166. }
  167. /**
  168. * Setup the full filesystem for the specified user
  169. */
  170. public function setupForUser(IUser $user): void {
  171. if ($this->isSetupComplete($user)) {
  172. return;
  173. }
  174. $this->setupUsersComplete[] = $user->getUID();
  175. $this->eventLogger->start('fs:setup:user:full', 'Setup full filesystem for user');
  176. if (!isset($this->setupUserMountProviders[$user->getUID()])) {
  177. $this->setupUserMountProviders[$user->getUID()] = [];
  178. }
  179. $previouslySetupProviders = $this->setupUserMountProviders[$user->getUID()];
  180. $this->setupForUserWith($user, function () use ($user) {
  181. $this->mountProviderCollection->addMountForUser($user, $this->mountManager, function (
  182. IMountProvider $provider,
  183. ) use ($user) {
  184. return !in_array(get_class($provider), $this->setupUserMountProviders[$user->getUID()]);
  185. });
  186. });
  187. $this->afterUserFullySetup($user, $previouslySetupProviders);
  188. $this->eventLogger->end('fs:setup:user:full');
  189. }
  190. /**
  191. * part of the user setup that is run only once per user
  192. */
  193. private function oneTimeUserSetup(IUser $user) {
  194. if ($this->isSetupStarted($user)) {
  195. return;
  196. }
  197. $this->setupUsers[] = $user->getUID();
  198. $this->setupRoot();
  199. $this->eventLogger->start('fs:setup:user:onetime', 'Onetime filesystem for user');
  200. $this->setupBuiltinWrappers();
  201. $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
  202. // TODO remove hook
  203. OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user->getUID()]);
  204. $event = new BeforeFileSystemSetupEvent($user);
  205. $this->eventDispatcher->dispatchTyped($event);
  206. Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
  207. $userDir = '/' . $user->getUID() . '/files';
  208. Filesystem::initInternal($userDir);
  209. if ($this->lockdownManager->canAccessFilesystem()) {
  210. $this->eventLogger->start('fs:setup:user:home', 'Setup home filesystem for user');
  211. // home mounts are handled separate since we need to ensure this is mounted before we call the other mount providers
  212. $homeMount = $this->mountProviderCollection->getHomeMountForUser($user);
  213. $this->mountManager->addMount($homeMount);
  214. if ($homeMount->getStorageRootId() === -1) {
  215. $this->eventLogger->start('fs:setup:user:home:scan', 'Scan home filesystem for user');
  216. $homeMount->getStorage()->mkdir('');
  217. $homeMount->getStorage()->getScanner()->scan('');
  218. $this->eventLogger->end('fs:setup:user:home:scan');
  219. }
  220. $this->eventLogger->end('fs:setup:user:home');
  221. } else {
  222. $this->mountManager->addMount(new MountPoint(
  223. new NullStorage([]),
  224. '/' . $user->getUID()
  225. ));
  226. $this->mountManager->addMount(new MountPoint(
  227. new NullStorage([]),
  228. '/' . $user->getUID() . '/files'
  229. ));
  230. $this->setupUsersComplete[] = $user->getUID();
  231. }
  232. $this->listenForNewMountProviders();
  233. $this->eventLogger->end('fs:setup:user:onetime');
  234. }
  235. /**
  236. * Final housekeeping after a user has been fully setup
  237. */
  238. private function afterUserFullySetup(IUser $user, array $previouslySetupProviders): void {
  239. $this->eventLogger->start('fs:setup:user:full:post', 'Housekeeping after user is setup');
  240. $userRoot = '/' . $user->getUID() . '/';
  241. $mounts = $this->mountManager->getAll();
  242. $mounts = array_filter($mounts, function (IMountPoint $mount) use ($userRoot) {
  243. return str_starts_with($mount->getMountPoint(), $userRoot);
  244. });
  245. $allProviders = array_map(function (IMountProvider $provider) {
  246. return get_class($provider);
  247. }, $this->mountProviderCollection->getProviders());
  248. $newProviders = array_diff($allProviders, $previouslySetupProviders);
  249. $mounts = array_filter($mounts, function (IMountPoint $mount) use ($previouslySetupProviders) {
  250. return !in_array($mount->getMountProvider(), $previouslySetupProviders);
  251. });
  252. $this->userMountCache->registerMounts($user, $mounts, $newProviders);
  253. $cacheDuration = $this->config->getSystemValueInt('fs_mount_cache_duration', 5 * 60);
  254. if ($cacheDuration > 0) {
  255. $this->cache->set($user->getUID(), true, $cacheDuration);
  256. $this->fullSetupRequired[$user->getUID()] = false;
  257. }
  258. $this->eventLogger->end('fs:setup:user:full:post');
  259. }
  260. /**
  261. * @param IUser $user
  262. * @param IMountPoint $mounts
  263. * @return void
  264. * @throws \OCP\HintException
  265. * @throws \OC\ServerNotAvailableException
  266. */
  267. private function setupForUserWith(IUser $user, callable $mountCallback): void {
  268. $this->oneTimeUserSetup($user);
  269. if ($this->lockdownManager->canAccessFilesystem()) {
  270. $mountCallback();
  271. }
  272. $this->eventLogger->start('fs:setup:user:post-init-mountpoint', 'post_initMountPoints legacy hook');
  273. \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', ['user' => $user->getUID()]);
  274. $this->eventLogger->end('fs:setup:user:post-init-mountpoint');
  275. $userDir = '/' . $user->getUID() . '/files';
  276. $this->eventLogger->start('fs:setup:user:setup-hook', 'setup legacy hook');
  277. OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user->getUID(), 'user_dir' => $userDir]);
  278. $this->eventLogger->end('fs:setup:user:setup-hook');
  279. }
  280. /**
  281. * Set up the root filesystem
  282. */
  283. public function setupRoot(): void {
  284. //setting up the filesystem twice can only lead to trouble
  285. if ($this->rootSetup) {
  286. return;
  287. }
  288. $this->setupBuiltinWrappers();
  289. $this->rootSetup = true;
  290. $this->eventLogger->start('fs:setup:root', 'Setup root filesystem');
  291. $rootMounts = $this->mountProviderCollection->getRootMounts();
  292. foreach ($rootMounts as $rootMountProvider) {
  293. $this->mountManager->addMount($rootMountProvider);
  294. }
  295. $this->eventLogger->end('fs:setup:root');
  296. }
  297. /**
  298. * Get the user to setup for a path or `null` if the root needs to be setup
  299. *
  300. * @param string $path
  301. * @return IUser|null
  302. */
  303. private function getUserForPath(string $path) {
  304. if (str_starts_with($path, '/__groupfolders')) {
  305. return null;
  306. } elseif (substr_count($path, '/') < 2) {
  307. if ($user = $this->userSession->getUser()) {
  308. return $user;
  309. } else {
  310. return null;
  311. }
  312. } elseif (str_starts_with($path, '/appdata_' . \OC_Util::getInstanceId()) || str_starts_with($path, '/files_external/')) {
  313. return null;
  314. } else {
  315. [, $userId] = explode('/', $path);
  316. }
  317. return $this->userManager->get($userId);
  318. }
  319. /**
  320. * Set up the filesystem for the specified path
  321. */
  322. public function setupForPath(string $path, bool $includeChildren = false): void {
  323. $user = $this->getUserForPath($path);
  324. if (!$user) {
  325. $this->setupRoot();
  326. return;
  327. }
  328. if ($this->isSetupComplete($user)) {
  329. return;
  330. }
  331. if ($this->fullSetupRequired($user)) {
  332. $this->setupForUser($user);
  333. return;
  334. }
  335. // for the user's home folder, and includes children we need everything always
  336. if (rtrim($path) === '/' . $user->getUID() . '/files' && $includeChildren) {
  337. $this->setupForUser($user);
  338. return;
  339. }
  340. if (!isset($this->setupUserMountProviders[$user->getUID()])) {
  341. $this->setupUserMountProviders[$user->getUID()] = [];
  342. }
  343. $setupProviders = &$this->setupUserMountProviders[$user->getUID()];
  344. $currentProviders = [];
  345. try {
  346. $cachedMount = $this->userMountCache->getMountForPath($user, $path);
  347. } catch (NotFoundException $e) {
  348. $this->setupForUser($user);
  349. return;
  350. }
  351. $this->oneTimeUserSetup($user);
  352. $this->eventLogger->start('fs:setup:user:path', "Setup $path filesystem for user");
  353. $this->eventLogger->start('fs:setup:user:path:find', "Find mountpoint for $path");
  354. $mounts = [];
  355. if (!in_array($cachedMount->getMountProvider(), $setupProviders)) {
  356. $currentProviders[] = $cachedMount->getMountProvider();
  357. if ($cachedMount->getMountProvider()) {
  358. $setupProviders[] = $cachedMount->getMountProvider();
  359. $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()]);
  360. } else {
  361. $this->logger->debug('mount at ' . $cachedMount->getMountPoint() . ' has no provider set, performing full setup');
  362. $this->eventLogger->end('fs:setup:user:path:find');
  363. $this->setupForUser($user);
  364. $this->eventLogger->end('fs:setup:user:path');
  365. return;
  366. }
  367. }
  368. if ($includeChildren) {
  369. $subCachedMounts = $this->userMountCache->getMountsInPath($user, $path);
  370. $this->eventLogger->end('fs:setup:user:path:find');
  371. $needsFullSetup = array_reduce($subCachedMounts, function (bool $needsFullSetup, ICachedMountInfo $cachedMountInfo) {
  372. return $needsFullSetup || $cachedMountInfo->getMountProvider() === '';
  373. }, false);
  374. if ($needsFullSetup) {
  375. $this->logger->debug('mount has no provider set, performing full setup');
  376. $this->setupForUser($user);
  377. $this->eventLogger->end('fs:setup:user:path');
  378. return;
  379. } else {
  380. foreach ($subCachedMounts as $cachedMount) {
  381. if (!in_array($cachedMount->getMountProvider(), $setupProviders)) {
  382. $currentProviders[] = $cachedMount->getMountProvider();
  383. $setupProviders[] = $cachedMount->getMountProvider();
  384. $mounts = array_merge($mounts, $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()]));
  385. }
  386. }
  387. }
  388. } else {
  389. $this->eventLogger->end('fs:setup:user:path:find');
  390. }
  391. if (count($mounts)) {
  392. $this->userMountCache->registerMounts($user, $mounts, $currentProviders);
  393. $this->setupForUserWith($user, function () use ($mounts) {
  394. array_walk($mounts, [$this->mountManager, 'addMount']);
  395. });
  396. } elseif (!$this->isSetupStarted($user)) {
  397. $this->oneTimeUserSetup($user);
  398. }
  399. $this->eventLogger->end('fs:setup:user:path');
  400. }
  401. private function fullSetupRequired(IUser $user): bool {
  402. if ($this->forceFullSetup) {
  403. return true;
  404. }
  405. // we perform a "cached" setup only after having done the full setup recently
  406. // this is also used to trigger a full setup after handling events that are likely
  407. // to change the available mounts
  408. if (!isset($this->fullSetupRequired[$user->getUID()])) {
  409. $this->fullSetupRequired[$user->getUID()] = !$this->cache->get($user->getUID());
  410. }
  411. return $this->fullSetupRequired[$user->getUID()];
  412. }
  413. /**
  414. * @param string $path
  415. * @param string[] $providers
  416. */
  417. public function setupForProvider(string $path, array $providers): void {
  418. $user = $this->getUserForPath($path);
  419. if (!$user) {
  420. $this->setupRoot();
  421. return;
  422. }
  423. if ($this->isSetupComplete($user)) {
  424. return;
  425. }
  426. if ($this->fullSetupRequired($user)) {
  427. $this->setupForUser($user);
  428. return;
  429. }
  430. $this->eventLogger->start('fs:setup:user:providers', 'Setup filesystem for ' . implode(', ', $providers));
  431. $this->oneTimeUserSetup($user);
  432. // home providers are always used
  433. $providers = array_filter($providers, function (string $provider) {
  434. return !is_subclass_of($provider, IHomeMountProvider::class);
  435. });
  436. if (in_array('', $providers)) {
  437. $this->setupForUser($user);
  438. return;
  439. }
  440. $setupProviders = $this->setupUserMountProviders[$user->getUID()] ?? [];
  441. $providers = array_diff($providers, $setupProviders);
  442. if (count($providers) === 0) {
  443. if (!$this->isSetupStarted($user)) {
  444. $this->oneTimeUserSetup($user);
  445. }
  446. $this->eventLogger->end('fs:setup:user:providers');
  447. return;
  448. } else {
  449. $this->setupUserMountProviders[$user->getUID()] = array_merge($setupProviders, $providers);
  450. $mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, $providers);
  451. }
  452. $this->userMountCache->registerMounts($user, $mounts, $providers);
  453. $this->setupForUserWith($user, function () use ($mounts) {
  454. array_walk($mounts, [$this->mountManager, 'addMount']);
  455. });
  456. $this->eventLogger->end('fs:setup:user:providers');
  457. }
  458. public function tearDown() {
  459. $this->setupUsers = [];
  460. $this->setupUsersComplete = [];
  461. $this->setupUserMountProviders = [];
  462. $this->fullSetupRequired = [];
  463. $this->rootSetup = false;
  464. $this->mountManager->clear();
  465. $this->eventDispatcher->dispatchTyped(new FilesystemTornDownEvent());
  466. }
  467. /**
  468. * Get mounts from mount providers that are registered after setup
  469. */
  470. private function listenForNewMountProviders() {
  471. if (!$this->listeningForProviders) {
  472. $this->listeningForProviders = true;
  473. $this->mountProviderCollection->listen('\OC\Files\Config', 'registerMountProvider', function (
  474. IMountProvider $provider,
  475. ) {
  476. foreach ($this->setupUsers as $userId) {
  477. $user = $this->userManager->get($userId);
  478. if ($user) {
  479. $mounts = $provider->getMountsForUser($user, Filesystem::getLoader());
  480. array_walk($mounts, [$this->mountManager, 'addMount']);
  481. }
  482. }
  483. });
  484. }
  485. }
  486. private function setupListeners() {
  487. // note that this event handling is intentionally pessimistic
  488. // clearing the cache to often is better than not enough
  489. $this->eventDispatcher->addListener(UserAddedEvent::class, function (UserAddedEvent $event) {
  490. $this->cache->remove($event->getUser()->getUID());
  491. });
  492. $this->eventDispatcher->addListener(UserRemovedEvent::class, function (UserRemovedEvent $event) {
  493. $this->cache->remove($event->getUser()->getUID());
  494. });
  495. $this->eventDispatcher->addListener(ShareCreatedEvent::class, function (ShareCreatedEvent $event) {
  496. $this->cache->remove($event->getShare()->getSharedWith());
  497. });
  498. $this->eventDispatcher->addListener(InvalidateMountCacheEvent::class, function (InvalidateMountCacheEvent $event,
  499. ) {
  500. if ($user = $event->getUser()) {
  501. $this->cache->remove($user->getUID());
  502. } else {
  503. $this->cache->clear();
  504. }
  505. });
  506. $genericEvents = [
  507. 'OCA\Circles\Events\CreatingCircleEvent',
  508. 'OCA\Circles\Events\DestroyingCircleEvent',
  509. 'OCA\Circles\Events\AddingCircleMemberEvent',
  510. 'OCA\Circles\Events\RemovingCircleMemberEvent',
  511. ];
  512. foreach ($genericEvents as $genericEvent) {
  513. $this->eventDispatcher->addListener($genericEvent, function ($event) {
  514. $this->cache->clear();
  515. });
  516. }
  517. }
  518. }