Skip to content

[Step 2]: Rework team layout persist - #6779

Open
RobertJoonas wants to merge 6 commits into
refactor-teams-layout-persist-1from
refactor-teams-layout-persist-2
Open

RobertJoonas wants to merge 6 commits into
refactor-teams-layout-persist-1from
refactor-teams-layout-persist-2

Conversation

@RobertJoonas

Copy link
Copy Markdown
Contributor

Changes

  • Put "who may give which role to whom" in a dedicated TeamRolePermissions module with its own tests. It exposes two functions: ensure_can_grant_for_member and ensure_can_grant_for_invitation. The functions now share most of the role permissions logic.
    • Changing your own role now follows the exact same rules as changing someone else's role with the same role. On master, the only difference between the two is the ability to keep your own role -- changing yourself from owner -> owner is refused, but valid (no-op) when changing someone else from owner -> owner
    • Role validation lived in each caller, and the invitation check accepted strings. An admin passing "owner" as a string would have got past the owners-only rule (not reachable in practice). Roles are now validated in one place, and only atoms are accepted.
    • An admin couldn't change an owner's role, or invite an owner, but could change a pending owner invitation to another role. The new function denies that change -- if an admin can't grant the owner role, it shouldn't be able to change an existing owner role.
    • The one intended difference between invitations and memberships is kept: admins may change pending billing invitations, but not billing members.
  • Create a new Invitations.UpdateRole function (not used yet; will be in step 3) which changes the role of a pending team invitation without sending an e-mail and without resetting its expiry. Updating the role of an existing invitation currently works with an upsert. This makes it an explicit update action.
    • The function now also refuses to update guest invitations, because turning them into team invitations would break the link in the guest's e-mail -- a bug that will be fixed when using it in the TeamManagement UI (step 3).

Tests

  • Automated tests have been added

Changelog

  • This PR does not make a user-facing change

Documentation

  • This change does not need a documentation update

Dark mode

  • This PR does not change the UI

@RobertJoonas
RobertJoonas changed the base branch from master to refactor-teams-layout-persist-1 October 14, 2026 17:34
Comment thread lib/plausible/teams/team_role_permissions.ex
Comment thread lib/plausible/teams/team_role_permissions.ex
Comment thread lib/plausible/teams/team_role_permissions.ex
Comment thread lib/plausible/teams/invitations/update_role.ex Outdated
@RobertJoonas
RobertJoonas added this pull request to stack #6783 October 15, 2026 11:42
Currently, invitations and memberships both maintain their own separate
version of "who can grant what role to who" logic. This commit brings
those two closer together and makes sure that the shared functionality
is not duplicated.

While this was meant to be a refactor, a few bugs / inconsistencies
were discovered and fixed while moving the two under the same roof:

- An Admin couldn't change an Owner's role, but could change a pending
  Owner invitation to any other role. And then again, couldn't create
  a new invitation with the Owner role. FIX: Prohibit demoting Owner
  role even on pending invitations.

- Changing your own role was handled separately, which rejected keeping
  your own Owner or Admin role, but the same no-op was allowed when
  changing someone else's role. FIX: No separate "can_grant_to_self?"
  function anymore. Assigning to self is the same as assigning to
  someone else with the same role as you. The no-op role changes are
  allowed in both cases.

- Role validation lived in each caller, and the invitation check
  compared roles to atoms while also accepting strings. While it's
  impossible in practice currently, an Admin passing "owner" as string
  would get past the owners-only rule. FIX: Move role validation deeper
  and accept only atoms.

The only intended difference between invitations and memberships was
kept: an Admin may change pending billing invitations, but not billing
members, since billing members might already have access to the
subscription, which admins don't.
Currently, team management changes a pending invitation's role by inviting
the same email again. Under the hood, that's a write action (not an update)
which due to a unique(team_id, email) conflict overwrites the existing row.

This commit makes it an explicit update action, and it's not purely cosmetic.
Since recent commits, the backend now rejects a new invitation if a pending
one already exists (instead of overwriting it). So this is actually needed.
@RobertJoonas
RobertJoonas force-pushed the refactor-teams-layout-persist-2 branch from 149236f to fa3228a Compare October 15, 2026 12:39
@RobertJoonas
RobertJoonas force-pushed the refactor-teams-layout-persist-2 branch from 89118c4 to 5d9d734 Compare October 15, 2026 17:38

This branch has not been deployed

No deployments
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.

3 participants