Skip to content

fix: mail a group's member once, however many groups they are in - #824

Merged
blaipr merged 1 commit into
mainfrom
fix/a-group-member-is-mailed-once
Aug 18, 2026
Merged

fix: mail a group's member once, however many groups they are in#824
blaipr merged 1 commit into
mainfrom
fix/a-group-member-is-mailed-once

Conversation

@blaipr

@blaipr blaipr commented Aug 18, 2026

Copy link
Copy Markdown
Member

getUserEmailForGroup() addresses the mail carrying a newly issued temporary master password. A user belongs to the group either by their own userGroupId or through UserToUserGroup, so the query joins the membership table and matches on both:

LEFT JOIN UserToUserGroup ON UserToUserGroup.userId = User.id
WHERE ... AND (User.userGroupId = :userGroupId OR UserToUserGroup.userGroupId = :userGroupId2)

The join carries one row per membership the user holds, and User.userGroupId = :userGroupId is true on every one of those rows — so somebody in the group directly comes back once for each other group they belong to. sendByEmailForGroup() mails what it is handed.

Demonstrated on a server: a user whose own group is the one being mailed, who also belongs to two others, returns 2 rows for 1 user. They receive the master password twice.

One DISTINCT.

Test

The existing repository test asserted the bind values and nothing else, so it passed with the duplication in place.

The new one runs against a real database, because the defect is in what the join returns and a mocked repository returns whatever the test tells it to. It asserts both directions:

  • the member of several groups is addressed once;
  • members by either route — own group, or UserToUserGroup — are still addressed at all.

The second matters because a fix that simply dropped people would satisfy the first on its own. Removing the DISTINCT fails the first with a recipient is a person, not a membership.

Scope

getUserEmailForGroup() has one caller, TemporaryMasterPass::sendByEmailForGroup(), itself reached only from ConfigEncryption\SaveTempController. Nothing else is affected.

3973 unit + 974 integration pass; PHPStan level 6 on src and PHPCS clean.

`getUserEmailForGroup()` addresses the mail carrying a newly issued temporary
master password. A user belongs to the group either by their own `userGroupId`
or through `UserToUserGroup`, and the join carries one row per membership they
hold — so `User.userGroupId = :id`, true on every one of those rows, returned
somebody in the group directly once for each *other* group they were in.
`sendByEmailForGroup()` mails what it is handed.

A user whose own group is the one being mailed and who belongs to two others
came back as two rows for one person, and received the master password twice.

The existing repository test checked the bind values and nothing else, so it
passed either way. The new one runs against a real database and asserts both
directions: the member of several groups is addressed once, and members by
either route are still addressed at all — a fix that dropped people would
satisfy the first assertion on its own.
@blaipr
blaipr merged commit 81d9ce9 into main Aug 18, 2026
8 checks passed
@blaipr
blaipr deleted the fix/a-group-member-is-mailed-once branch August 18, 2026 22:06
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