{{ message }}
Repository navigation
fix(teams): exempt a member's own membership from the privacy policy - #13786
Merged
Merged
Conversation
Contributor
The membership privacy policy is documented as controlling what members can see about *other* members, but it was applied to every membership in the list, including the requester's own. New projects are created with `membershipsUserId` disabled, so a standard user listing team memberships got an empty `userId` on every row - including their own - and could no longer tell which membership was theirs. That also left them unable to leave a team, since removing a membership needs its ID. Treat the requester's own membership like a privileged read in both `GET /v1/teams/:teamId/memberships` and `GET /v1/teams/:teamId/memberships/:membershipId`, matching how `Memberships/Delete.php` and `Memberships/Update.php` already identify the caller's own membership. None of these fields are secret from the member anyway - `GET /v1/account` returns all of them. Fixes #13767
HarshMN2345
force-pushed
the
fix/membership-privacy-self
branch
from
September 21, 2026 06:00
04daa29 to
2f4091f
Compare
✨ Benchmark resultsComparing
Per-scenario breakdown & investigation detailsMetrics below reflect the current branch (after). Δ P95 compares against the base.
Top API waits (after) |
Meldiron
approved these changes
Sep 21, 2026
Meldiron
left a comment
Contributor
There was a problem hiding this comment.
looks good, lets just confirm we really need this
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What does this PR do?
The membership privacy policy is documented as controlling what a member can see about other members:
But
GET /v1/teams/:teamId/membershipsandGET /v1/teams/:teamId/memberships/:membershipIdapplied it to every membership in the response, the requester's own included.New projects are created with every flag disabled (
membershipsUserId => falseinProjects/Http/Projects/Create.php) — the?? truefallback in the endpoints only covers projects that predate the policy. So on a fresh project a standard user listing team memberships getsuserId: ""on every row, including their own, and has no way to tell which membership is theirs.That is not just cosmetic.
DELETE /v1/teams/:teamId/memberships/:membershipIdexplicitly supports self-removal, but finding your ownmembershipIdrequires matching onuserId— so a member could not leave a team. The same applies to requests authenticated with a user JWT from a function, which is neither privileged nor a key.This exempts the requester's own membership from the policy, treating it like a privileged read. It reuses the
userInternalId === $user->getSequence()check thatMemberships/Delete.phpandMemberships/Update.phpalready use to recognise the caller's own membership. Nothing is actually concealed by the old behaviour —GET /v1/accountreturns every one of these fields about yourself.Other members' details are unaffected: the policy still governs them exactly as before, and API keys / admin mode are unchanged.
Test Plan
tests/e2e/Services/Teamsandtests/e2e/Services/Project.Three existing tests asserted the old behaviour on a team where the viewer was the only member, so they now cover the distinction between self and other rather than conflating them:
TeamsCustomClientTest::testGetMembershipPrivacy— invites a second member. Policy assertions target that member; the viewer's own row is asserted visible in all three policy states.PoliciesMembershipPrivacyIntegrationTest::testMembershipPrivacyIntegration— step 4 now asserts user2's row is hidden from user1 and user1's own row is not.testMembershipUserAccessedAtDefault/testMembershipUserAccessedAtPolicy—createTeamWithMembernow creates a signed-in viewer plus one other member, so theuserAccessedAtpolicy is exercised against the other member.Manual reproduction from the issue:
teams.listMemberships(teamId).userIdis""on the user's own membership. After: it is their user ID, while any other member's row still follows the project policy.composer lintandcomposer analyzepass on the four changed files.Related PRs and Issues
Checklist