Skip to content

fix: Don't re-add a user to auto groups while they are being deleted - #96

Merged
jo23sh merged 1 commit into
masterfrom
fix/no-readd-during-user-deletion
Sep 1, 2026
Merged

fix: Don't re-add a user to auto groups while they are being deleted#96
jo23sh merged 1 commit into
masterfrom
fix/no-readd-during-user-deletion

Conversation

@jo23sh

@jo23sh jo23sh commented Sep 1, 2026

Copy link
Copy Markdown
Owner

The bug

Deleting a user removes them from every group before deleting the user record, so each removal fires UserRemovedEvent while userExists() is still true. The modification hook then puts the user straight back into the auto groups — and that row outlives the user record, leaving a membership in oc_group_user for a user that no longer exists.

Nextcloud logs this for every such row, on every user listing:

Found one enabled account that is removed from its backend,
but still exists in Nextcloud database

Found on an instance where an hourly API consumer was producing 11 of them per run. The accounts had been deleted through the GUI, months apart, and were gone from oc_users, oc_accounts and oc_preferences — only the group rows remained, all in the configured auto group.

Why the existing guard misses it

f5458a2 added a userExists() check, but it was written for CleanupDeletedUsers, where the user really is gone by the time the event fires. During an interactive deletion the user still exists, so the guard passes.

Dropping UserRemovedEvent isn't an option — putting someone back when an admin removes them by hand is what modification_hook is for.

The fix

Track the deletion from BeforeUserDeletedEvent, which fires before the group removals begin, and bail out for that uid.

The flag is never cleared, deliberately: PHP rebuilds the container every request, so it cannot grow over time, and clearing it on UserDeletedEvent would only be correct if every group removal fires before that event — if one fires after, the bug is back. A stale entry costs nothing.

Tests

  • testUserBeingDeletedIsIgnoredWhileStillPresent — the case the old guard cannot cover
  • testOtherUsersAreUnaffectedByAPendingDeletion — the flag is per uid, not a global switch

Only php -l was run locally (no PHP on the machine this was written on); relying on CI for phpunit.

@jo23sh jo23sh self-assigned this Sep 1, 2026
Deleting a user removes them from every group before deleting the user record,
so each removal fires UserRemovedEvent while userExists() is still true. The
modification hook then put the user straight back into the auto groups, and that
row outlived the user record — leaving a membership in oc_group_user for a user
that no longer exists.

Nextcloud logs "Found one enabled account that is removed from its backend, but
still exists in Nextcloud database" for each such row on every user listing. On
the instance where this was found, an hourly API consumer was producing 11 of
them per run, and the accounts had been deleted through the GUI months apart.

The existing userExists() guard cannot catch this: it was written for
CleanupDeletedUsers, where the user really is gone by the time the event fires.
During an interactive deletion the user still exists. So the deletion is now
tracked from BeforeUserDeletedEvent, which fires before the group removals begin,
and the hook bails out for that uid.

The flag is never cleared, deliberately. PHP rebuilds the container every
request, so it cannot grow over time, and clearing it on UserDeletedEvent would
only be correct if every group removal fires before that event — if one fires
after, the bug is back. A stale entry costs nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jo23sh
jo23sh force-pushed the fix/no-readd-during-user-deletion branch from 4c4e9dc to e1041de Compare September 1, 2026 06:12
@jo23sh
jo23sh merged commit f74be48 into master Sep 1, 2026
8 checks passed
@jo23sh
jo23sh deleted the fix/no-readd-during-user-deletion branch September 1, 2026 06:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant