diff --git a/apps/files_sharing/lib/Listener/SharesUpdatedListener.php b/apps/files_sharing/lib/Listener/SharesUpdatedListener.php index a6ea34d295f9b..9d90d3f3388bd 100644 --- a/apps/files_sharing/lib/Listener/SharesUpdatedListener.php +++ b/apps/files_sharing/lib/Listener/SharesUpdatedListener.php @@ -8,6 +8,7 @@ namespace OCA\Files_Sharing\Listener; +use OC\User\NoUserException; use OCA\Files_Sharing\AppInfo\Application; use OCA\Files_Sharing\Config\ConfigLexicon; use OCA\Files_Sharing\Event\UserShareAccessUpdatedEvent; @@ -136,7 +137,14 @@ public function handle(Event $event): void { private function markOrRun(IUser $user, callable $callback): void { $start = floatval($this->clock->now()->format('U.u')); if ($this->cutOffMarkTime === -1.0 || $this->updatedTime < $this->cutOffMarkTime) { - $callback(); + try { + $callback(); + } catch (NoUserException $e) { + // A share recipient may reference a user id that no backend can resolve anymore + // (e.g. with LazyUser::getUID()) - like remnant / incorrectly removed user. + // Skip this recipient instead of aborting the share operation. + $this->logger->debug('Skipping share mount update for unresolvable user ' . $user->getUID(), ['exception' => $e]); + } } else { $this->markUserForRefresh($user); } diff --git a/apps/files_sharing/tests/SharesUpdatedListenerTest.php b/apps/files_sharing/tests/SharesUpdatedListenerTest.php index 3598f6cfb5b09..f803b04d443af 100644 --- a/apps/files_sharing/tests/SharesUpdatedListenerTest.php +++ b/apps/files_sharing/tests/SharesUpdatedListenerTest.php @@ -7,6 +7,7 @@ namespace OCA\Files_Sharing\Tests; +use OC\User\NoUserException; use OCA\Files_Sharing\Config\ConfigLexicon; use OCA\Files_Sharing\Event\UserShareAccessUpdatedEvent; use OCA\Files_Sharing\Listener\SharesUpdatedListener; @@ -115,6 +116,33 @@ public function testShareAddedFilterOwner() { $this->sharesUpdatedListener->handle($event); } + public function testShareAddedSkipsUnresolvableUser(): void { + $share = $this->createMock(IShare::class); + $user1 = $this->createUser('user1', ''); + $user2 = $this->createUser('user2', ''); + + $this->manager->method('getUsersForShare') + ->willReturn([$user1, $user2]); + + $event = new ShareCreatedEvent($share); + + // user1 is an orphaned recipient that no backend can resolve + $this->shareRecipientUpdater + ->expects($this->exactly(2)) + ->method('updateForAddedShare') + ->willReturnCallback(function (IUser $user) use ($user1): void { + if ($user === $user1) { + throw new NoUserException('Backends provided no user object'); + } + }); + + // the failure is logged, not thrown + $this->logger->expects($this->once())->method('debug'); + + // must not throw: user2 is still processed + $this->sharesUpdatedListener->handle($event); + } + public function testShareAccessUpdated() { $user1 = $this->createUser('user1', ''); $user2 = $this->createUser('user2', '');