Skip to content

Say which address change actually fires the event - #219

Merged
nursoda merged 1 commit into
mainfrom
docs/say-which-address-fires-the-event
Sep 4, 2026
Merged

Say which address change actually fires the event#219
nursoda merged 1 commit into
mainfrom
docs/say-which-address-fires-the-event

Conversation

@nursoda

@nursoda nursoda commented Aug 27, 2026

Copy link
Copy Markdown
Member

The class docblock of EMailDeleted claimed that every path clearing the address used for delivery goes through IUser::setSystemEMailAddress() and therefore reaches this listener. It does not, and the claim contradicted the app's own threat model.

Two paths change the delivery address without dispatching anything usable:

  • Deleting the additional address a user had picked as their notification address calls IUser::setPrimaryEMailAddress('') (apps/provisioning_api/lib/Controller/UsersController.php, branch COLLECTION_EMAIL), which only removes the settings/primary_email user value.
  • occ user:setting <uid> settings primary_email writes or deletes that user value directly — only email and display_name are special-cased in core/Command/User/Setting.php.

After either, getEMailAddress() falls back to the system address, or returns null when the account has none. The account data does emit UserUpdatedEvent on the first path, but before the primary address is reset, so getEMailAddress() read from it still yields the old address. That event is no hook for this, which is why the listener ignores it — a behaviour EMailDeletedTest already pins.

No behaviour changes. There is nothing to react to, and a listener that never runs leaves the provider enabled. That is stricter than disabling it, never a downgrade to password-only.

The docblock now limits itself to the system address, names the two paths that get past it, and points at the threat model entry "A code outlives a changed notification address", so the two texts do not drift apart again.

Merge after #214. That entry reaches main with it; this branch touches no file #214 touches, so there is no conflict, only an order.

Every claim above was verified against a local checkout of nextcloud/server.

🤖 Generated with Claude Code, verified, tweaked and approved by @nursoda.

@nursoda
nursoda requested a review from seyfahni as a code owner August 27, 2026 02:33
@nursoda nursoda added the documentation Improvements or additions to documentation: instructions, concepts, analyses label Aug 27, 2026
@nursoda nursoda self-assigned this Aug 27, 2026
@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@nursoda

nursoda commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

More PRs upcoming on this issue.

The class docblock claimed that every path clearing the address used for
delivery goes through setSystemEMailAddress() and therefore reaches this
listener. It does not. Deleting the additional address a user had picked as
their notification address calls setPrimaryEMailAddress(''), and
`occ user:setting <uid> settings primary_email` writes or deletes that user
value directly — only `email` and `display_name` are special-cased there. Both
drop settings/primary_email without dispatching anything, so delivery falls back
to the system address, or stops altogether when the account has none.

The account data does emit UserUpdatedEvent on the first path, but before the
primary address is reset, so getEMailAddress() read from it still yields the old
address. That event is therefore no hook for this, which is why the listener
ignores it — a behaviour the test suite already pins.

Behaviour is unchanged: there is nothing to react to, and a listener that never
runs leaves the provider enabled, which is stricter than disabling it. The claim
was the error — it contradicted the threat model's own "A code outlives a
changed notification address" entry. The docblock now limits itself to the
system address, names the two paths that get past it, and points at that entry
so the two do not drift apart again.

Signed-off-by: Olav Seyfarth <olav@seyfarth.de>
@nursoda
nursoda merged commit ebe298a into main Sep 4, 2026
40 checks passed
@nursoda
nursoda deleted the docs/say-which-address-fires-the-event branch September 4, 2026 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation: instructions, concepts, analyses

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants