Skip to content

fix(files_sharing): skip unresolvable share recipients - #62368

Open
Antreesy wants to merge 1 commit into
masterfrom
fix/18415/talk-share-fail-for-orphan-user
Open

fix(files_sharing): skip unresolvable share recipients#62368
Antreesy wants to merge 1 commit into
masterfrom
fix/18415/talk-share-fail-for-orphan-user

Conversation

@Antreesy

@Antreesy Antreesy commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

in SharesUpdatedListener, a share recipient may reference a user id that no backend can resolve anymore (e.g. with LazyUser::getUID()) - like remnant / incorrectly removed user.

Example: Talk application supplies LazyUsers from oc_talk_attendees table, which can get desynced from the user backend (e.g. LDAP entries)

Skip this recipient instead of aborting the share operation resolve the issue. As listener is running as a post-effect, and share is already created (at least to the room -> to the group), it should be a safe reason to catch and skip?

To test:

  1. Easiest path: create a rogue entry in oc_talk_attendees, with non-existing id, omit capability 'conversation-subfolders' to utilize legacy share mechanism (each file is separate share), share a file to the room => POST request should fail with 500
  2. Complicated path: find a way for LDAP to clear mapping / execute similar flow to leave a rogur entry, then same as in opt.1

TODO

  • Sanity check
  • Possible side-effects?
  • For backports - should use OC\User\NoUserException ?

Checklist

AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@Antreesy Antreesy added this to the Nextcloud 35 milestone Jul 21, 2026
@Antreesy Antreesy self-assigned this Jul 21, 2026
@Antreesy
Antreesy requested a review from a team as a code owner July 21, 2026 14:48
@Antreesy
Antreesy requested review from leftybournes and salmart-dev and removed request for a team July 21, 2026 14:48
@@ -135,7 +136,17 @@ private function markOrRun(IUser $user, callable $callback): void {
$elapsed = $now - $this->firstRun;

if ($this->cutOffMarkTime === -1.0 || $elapsed < $this->cutOffMarkTime) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought === on float was not possible 🤔 Should this be < 0? (unrelated to this PR)

// 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->warning(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think debug level is enough. Also, no need to specify 'app' in the logger context, that’s automatically injected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the plausible ticket scenario (deleted/fake/unresolvable user is missing a share), agree that debug is probably enough

Assisted-by: ClaudeCode:claude-opus-4-8
Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
@Antreesy
Antreesy force-pushed the fix/18415/talk-share-fail-for-orphan-user branch from d2ed18f to 847501c Compare August 3, 2026 09:48
@come-nc

come-nc commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

I opened #62791 for another issue but of the same kind.
I’m not sure whether it also fixes this issue, because I’m not sure what the $listener() call is.

@Antreesy

Antreesy commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

I’m not sure whether it also fixes this issue, because I’m not sure what the $listener() call is.

Tested with my reproduction steps, sadly it doesn't get caught on your branch:

{"reqId":"UgIT9iJaKsrzp4ZpNtzC","level":3,"time":"2026-08-03T17:01:00+00:00","remoteAddr":"192.168.21.9","user":"admin","app":"no app in context","method":"POST","url":"/ocs/v2.php/apps/files_sharing/api/v1/shares","scriptName":"/ocs/v2.php","message":"Error while sending 'share created' event","userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36","version":"35.0.0.1","exception":{"Exception":"OC\\User\\NoUserException","Message":"Backends provided no user object","Code":0,"Trace":[{"function":"getUserFolder","class":"OC\\Files\\Node\\Root","type":"->","args":["bob2"]},{"file":"/var/www/html/lib/private/Files/Node/LazyFolder.php","line":67,"function":"call_user_func_array","args":[[{"__class__":"OC\\Files\\Node\\Root"},"getUserFolder"],["bob2"]]},{"file":"/var/www/html/lib/private/Files/Node/LazyRoot.php","line":41,"function":"__call","class":"OC\\Files\\Node\\LazyFolder","type":"->","args":["getUserFolder",["bob2"]]},{"file":"/var/www/html/apps/files_sharing/lib/ShareTargetValidator.php","line":70,"function":"getUserFolder","class":"OC\\Files\\Node\\LazyRoot","type":"->","args":["bob2"]},{"file":"/var/www/html/apps/files_sharing/lib/ShareRecipientUpdater.php","line":77,"function":"verifyMountPoint","class":"OCA\\Files_Sharing\\ShareTargetValidator","type":"->","args":["*** sensitive parameters replaced ***","*** sensitive parameters replaced ***",{"__class__":"Closure"},["*** sensitive parameters replaced ***"]]},{"file":"/var/www/html/apps/files_sharing/lib/Listener/SharesUpdatedListener.php","line":98,"function":"updateForAddedShare","class":"OCA\\Files_Sharing\\ShareRecipientUpdater","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/apps/files_sharing/lib/Listener/SharesUpdatedListener.php","line":138,"function":"{closure:OCA\\Files_Sharing\\Listener\\SharesUpdatedListener::handle():97}","class":"OCA\\Files_Sharing\\Listener\\SharesUpdatedListener","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/apps/files_sharing/lib/Listener/SharesUpdatedListener.php","line":97,"function":"markOrRun","class":"OCA\\Files_Sharing\\Listener\\SharesUpdatedListener","type":"->","args":["*** sensitive parameters replaced ***",{"__class__":"Closure"}]},{"file":"/var/www/html/lib/private/EventDispatcher/ServiceEventListener.php","line":57,"function":"handle","class":"OCA\\Files_Sharing\\Listener\\SharesUpdatedListener","type":"->","args":[{"__class__":"OCP\\Share\\Events\\ShareCreatedEvent"}]},{"file":"/var/www/html/3rdparty/symfony/event-dispatcher/EventDispatcher.php","line":220,"function":"__invoke","class":"OC\\EventDispatcher\\ServiceEventListener","type":"->","args":[{"__class__":"OCP\\Share\\Events\\ShareCreatedEvent"},"OCP\\Share\\Events\\ShareCreatedEvent",{"__class__":"Symfony\\Component\\EventDispatcher\\EventDispatcher"}]},{"file":"/var/www/html/3rdparty/symfony/event-dispatcher/EventDispatcher.php","line":56,"function":"callListeners","class":"Symfony\\Component\\EventDispatcher\\EventDispatcher","type":"->","args":[[{"__class__":"Closure"},{"__class__":"Closure"},{"__class__":"Closure"},{"__class__":"Closure"},{"__class__":"Closure"},"And 5 more entries, set log level to debug to see all entries"],"OCP\\Share\\Events\\ShareCreatedEvent",{"__class__":"OCP\\Share\\Events\\ShareCreatedEvent"}]},{"file":"/var/www/html/lib/private/EventDispatcher/EventDispatcher.php","line":74,"function":"dispatch","class":"Symfony\\Component\\EventDispatcher\\EventDispatcher","type":"->","args":[{"__class__":"OCP\\Share\\Events\\ShareCreatedEvent"},"OCP\\Share\\Events\\ShareCreatedEvent"]},{"file":"/var/www/html/lib/private/EventDispatcher/EventDispatcher.php","line":87,"function":"dispatch","class":"OC\\EventDispatcher\\EventDispatcher","type":"->","args":["OCP\\Share\\Events\\ShareCreatedEvent",{"__class__":"OCP\\Share\\Events\\ShareCreatedEvent"}]},{"file":"/var/www/html/lib/private/Share20/Manager.php","line":1897,"function":"dispatchTyped","class":"OC\\EventDispatcher\\EventDispatcher","type":"->","args":[{"__class__":"OCP\\Share\\Events\\ShareCreatedEvent"}]},{"file":"/var/www/html/lib/private/Share20/Manager.php","line":637,"function":"dispatchEvent","class":"OC\\Share20\\Manager","type":"->","args":[{"__class__":"OCP\\Share\\Events\\ShareCreatedEvent"},"share created"]},{"file":"/var/www/html/apps/files_sharing/lib/Controller/ShareAPIController.php","line":813,"function":"createShare","class":"OC\\Share20\\Manager","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/lib/private/AppFramework/Http/Dispatcher.php","line":172,"function":"createShare","class":"OCA\\Files_Sharing\\Controller\\ShareAPIController","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/lib/private/AppFramework/Http/Dispatcher.php","line":80,"function":"executeController","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->","args":[{"__class__":"OCA\\Files_Sharing\\Controller\\ShareAPIController"},"createShare"]},{"file":"/var/www/html/lib/private/AppFramework/App.php","line":138,"function":"dispatch","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->","args":[{"__class__":"OCA\\Files_Sharing\\Controller\\ShareAPIController"},"createShare"]},{"file":"/var/www/html/lib/private/Route/Router.php","line":324,"function":"main","class":"OC\\AppFramework\\App","type":"::","args":["OCA\\Files_Sharing\\Controller\\ShareAPIController","createShare",{"__class__":"OC\\AppFramework\\DependencyInjection\\DIContainer"},{"_route":"ocs.files_sharing.shareapi.createshare"}]},{"file":"/var/www/html/ocs/v1.php","line":78,"function":"match","class":"OC\\Route\\Router","type":"->","args":["/ocsapp/apps/files_sharing/api/v1/shares"]},{"file":"/var/www/html/ocs/v2.php","line":10,"args":["/var/www/html/ocs/v1.php"],"function":"require_once"}],"File":"/var/www/html/lib/private/Files/Node/Root.php","Line":325,"message":"Error while sending 'share created' event","exception":"{\"class\":\"OC\\User\\NoUserException\",\"message\":\"Backends provided no user object\",\"code\":0,\"file\":\"/var/www/html/lib/private/Files/Node/Root.php:325\",\"trace\":\"#0 [internal function]: OC\\Files\\Node\\Root->getUserFolder('bob2')\\n#1 /var/www/html/lib/private/Files/Node/LazyFolder.php(67): call_user_func_array(Array, Array)\\n#2 /var/www/html/lib/private/Files/Node/LazyRoot.php(41): OC\\Files\\Node\\LazyFolder->__call('getUserFolder', Array)\\n#3 /var/www/html/apps/files_sharing/lib/ShareTargetValidator.php(70): OC\\Files\\Node\\LazyRoot->getUserFolder('bob2')\\n#4 /var/www/html/apps/files_sharing/lib/ShareRecipientUpdater.php(77): OCA\\Files_Sharing\\ShareTargetValidator->verifyMountPoint(Object(OC\\User\\LazyUser), Object(OC\\Share20\\Share), Object(Closure), Array)\\n#5 /var/www/html/apps/files_sharing/lib/Listener/SharesUpdatedListener.php(98): OCA\\Files_Sharing\\ShareRecipientUpdater->updateForAddedShare(Object(OC\\User\\LazyUser), Object(OC\\Share20\\Share))\\n#6 /var/www/html/apps/files_sharing/lib/Listener/SharesUpdatedListener.php(138): OCA\\Files_Sharing\\Listener\\SharesUpdatedListener->{closure:OCA\\Files_Sharing\\Listener\\SharesUpdatedListener::handle():97}()\\n#7 /var/www/html/apps/files_sharing/lib/Listener/SharesUpdatedListener.php(97): OCA\\Files_Sharing\\Listener\\SharesUpdatedListener->markOrRun(Object(OC\\User\\LazyUser), Object(Closure))\\n#8 /var/www/html/lib/private/EventDispatcher/ServiceEventListener.php(57): OCA\\Files_Sharing\\Listener\\SharesUpdatedListener->handle(Object(OCP\\Share\\Events\\ShareCreatedEvent))\\n#9 /var/www/html/3rdparty/symfony/event-dispatcher/EventDispatcher.php(220): OC\\EventDispatcher\\ServiceEventListener->__invoke(Object(OCP\\Share\\Events\\ShareCreatedEvent), 'OCP\\\\Share\\\\Event...', Object(Symfony\\Component\\EventDispatcher\\EventDispatcher))\\n#10 /var/www/html/3rdparty/symfony/event-dispatcher/EventDispatcher.php(56): Symfony\\Component\\EventDispatcher\\EventDispatcher->callListeners(Array, 'OCP\\\\Share\\\\Event...', Object(OCP\\Share\\Events\\ShareCreatedEvent))\\n#11 /var/www/html/lib/private/EventDispatcher/EventDispatcher.php(74): Symfony\\Component\\EventDispatcher\\EventDispatcher->dispatch(Object(OCP\\Share\\Events\\ShareCreatedEvent), 'OCP\\\\Share\\\\Event...')\\n#12 /var/www/html/lib/private/EventDispatcher/EventDispatcher.php(87): OC\\EventDispatcher\\EventDispatcher->dispatch('OCP\\\\Share\\\\Event...', Object(OCP\\Share\\Events\\ShareCreatedEvent))\\n#13 /var/www/html/lib/private/Share20/Manager.php(1897): OC\\EventDispatcher\\EventDispatcher->dispatchTyped(Object(OCP\\Share\\Events\\ShareCreatedEvent))\\n#14 /var/www/html/lib/private/Share20/Manager.php(637): OC\\Share20\\Manager->dispatchEvent(Object(OCP\\Share\\Events\\ShareCreatedEvent), 'share created')\\n#15 /var/www/html/apps/files_sharing/lib/Controller/ShareAPIController.php(813): OC\\Share20\\Manager->createShare(Object(OC\\Share20\\Share))\\n#16 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(172): OCA\\Files_Sharing\\Controller\\ShareAPIController->createShare('/Talk/vertical ...', 19, 10, '3579626860', NULL, '', NULL, NULL, '', '', NULL, NULL)\\n#17 /var/www/html/lib/private/AppFramework/Http/Dispatcher.php(80): OC\\AppFramework\\Http\\Dispatcher->executeController(Object(OCA\\Files_Sharing\\Controller\\ShareAPIController), 'createShare')\\n#18 /var/www/html/lib/private/AppFramework/App.php(138): OC\\AppFramework\\Http\\Dispatcher->dispatch(Object(OCA\\Files_Sharing\\Controller\\ShareAPIController), 'createShare')\\n#19 /var/www/html/lib/private/Route/Router.php(324): OC\\AppFramework\\App::main('OCA\\\\Files_Shari...', 'createShare', Object(OC\\AppFramework\\DependencyInjection\\DIContainer), Array)\\n#20 /var/www/html/ocs/v1.php(78): OC\\Route\\Router->match('/ocsapp/apps/fi...')\\n#21 /var/www/html/ocs/v2.php(10): require_once('/var/www/html/o...')\\n#22 {main}\"}","CustomMessage":"Error while sending 'share created' event"}}

@nickvergessen

Copy link
Copy Markdown
Member

Would be good to merge for the maintenance release RC this week?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Talk fails to upload files to the conversation, if one of participants is gone (orphan attendee from former LDAP user)

4 participants