Fix a regression in accepting site transfers with pending invitations (#4852)

This commit is contained in:
Adrian Gruntkowski 2024-11-25 09:49:48 +01:00 committed by GitHub
parent 333238f085
commit 405c33f7cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 17 additions and 126 deletions

View File

@ -75,7 +75,7 @@ defmodule Plausible.Teams.Invitations do
:team, :team,
:owner, :owner,
guest_memberships: [team_membership: :user], guest_memberships: [team_membership: :user],
guest_invitations: :team_invitation guest_invitations: [team_invitation: :inviter]
]) ])
{:ok, _} = {:ok, _} =
@ -84,19 +84,6 @@ defmodule Plausible.Teams.Invitations do
end) end)
end end
def accept(invitation_id, user, now \\ NaiveDateTime.utc_now(:second)) do
case find_for_user(invitation_id, user) do
{:ok, %Teams.Invitation{} = team_invitation} ->
do_accept(team_invitation, user, now)
{:ok, %Teams.SiteTransfer{} = site_transfer} ->
do_transfer(site_transfer, user, now)
{:error, _} = error ->
error
end
end
def accept_invitation_sync(site_invitation, user) do def accept_invitation_sync(site_invitation, user) do
site_invitation = site_invitation =
Repo.preload( Repo.preload(
@ -150,7 +137,7 @@ defmodule Plausible.Teams.Invitations do
:team, :team,
:owner, :owner,
guest_memberships: [team_membership: :user], guest_memberships: [team_membership: :user],
guest_invitations: :team_invitation guest_invitations: [team_invitation: :inviter]
]) ])
{:ok, site_transfer} = {:ok, site_transfer} =
@ -209,7 +196,7 @@ defmodule Plausible.Teams.Invitations do
Plausible.Mailer.send(email) Plausible.Mailer.send(email)
end end
defp do_accept(team_invitation, user, now, opts \\ []) do defp do_accept(team_invitation, user, now, opts) do
send_email? = Keyword.get(opts, :send_email?, true) send_email? = Keyword.get(opts, :send_email?, true)
guest_invitations = Keyword.get(opts, :guest_invitations, team_invitation.guest_invitations) guest_invitations = Keyword.get(opts, :guest_invitations, team_invitation.guest_invitations)
@ -234,34 +221,6 @@ defmodule Plausible.Teams.Invitations do
end) end)
end end
defp do_transfer(site_transfer, new_owner, now) do
# That's probably the most involved flow of all so far
# - if new owner does not have a team yet, create one
# - ensure the new team can take ownership of the site
# - move site to and create guest memberships in the new team
# - create editor guest membership for the old owner
# - remove old guest memberships
site_transfer = Repo.preload(site_transfer, [:initiator, site: :team])
with :ok <- ensure_transfer_valid(site_transfer.site.team, new_owner, :owner),
{:ok, team} <- Teams.get_or_create(new_owner),
:ok <- ensure_can_take_ownership(site_transfer.site, team) do
site = Repo.preload(site_transfer.site, guest_memberships: [team_membership: :user])
{:ok, _} =
Repo.transaction(fn ->
:ok = transfer_site_ownership(site, team, now)
Repo.delete!(site_transfer)
end)
send_transfer_accepted_email(site_transfer)
{:ok, team_membership} = Teams.Memberships.get(team, new_owner)
{:ok, team_membership}
end
end
defp transfer_site_ownership(site, team, now) do defp transfer_site_ownership(site, team, now) do
prior_team = site.team prior_team = site.team
@ -275,7 +234,7 @@ defmodule Plausible.Teams.Invitations do
old_team_invitation = old_guest_invitation.team_invitation old_team_invitation = old_guest_invitation.team_invitation
{:ok, new_team_invitation} = {:ok, new_team_invitation} =
create_team_invitation(team, old_team_invitation.email, now) create_team_invitation(team, old_team_invitation.email, old_team_invitation.inviter)
{:ok, _new_guest_invitation} = {:ok, _new_guest_invitation} =
create_guest_invitation(new_team_invitation, site, old_guest_invitation.role) create_guest_invitation(new_team_invitation, site, old_guest_invitation.role)
@ -330,8 +289,6 @@ defmodule Plausible.Teams.Invitations do
) )
end end
# TODO: Update site lock status with SiteLocker
:ok :ok
end end
@ -371,7 +328,7 @@ defmodule Plausible.Teams.Invitations do
end end
end end
defp send_transfer_accepted_email(site_transfer) do def send_transfer_accepted_email(site_transfer) do
PlausibleWeb.Email.ownership_transfer_accepted( PlausibleWeb.Email.ownership_transfer_accepted(
site_transfer.email, site_transfer.email,
site_transfer.initiator.email, site_transfer.initiator.email,
@ -380,38 +337,6 @@ defmodule Plausible.Teams.Invitations do
|> Plausible.Mailer.send() |> Plausible.Mailer.send()
end end
defp find_for_user(invitation_id, user) do
with {:error, :invitation_not_found} <- find_invitation(invitation_id, user) do
find_site_transfer(invitation_id, user)
end
end
defp find_invitation(invitation_id, user) do
invitation =
Teams.Invitation
|> Repo.get_by(invitation_id: invitation_id, email: user.email)
|> Repo.preload([:team, :inviter, guest_invitations: :site])
if invitation do
{:ok, invitation}
else
{:error, :invitation_not_found}
end
end
defp find_site_transfer(transfer_id, user) do
site_transfer =
Teams.SiteTransfer
|> Repo.get_by(transfer_id: transfer_id, email: user.email)
|> Repo.preload([:initiator, site: :team])
if site_transfer do
{:ok, site_transfer}
else
{:error, :invitation_not_found}
end
end
@doc false @doc false
def check_invitation_permissions(site, inviter, invitation_role, opts) do def check_invitation_permissions(site, inviter, invitation_role, opts) do
check_permissions? = Keyword.get(opts, :check_permissions, true) check_permissions? = Keyword.get(opts, :check_permissions, true)

View File

@ -36,6 +36,18 @@ defmodule Plausible.Site.Memberships.AcceptInvitationTest do
assert_no_emails_delivered() assert_no_emails_delivered()
end end
test "transfers ownership with pending invites" do
existing_owner = new_user()
site = new_site(owner: existing_owner)
invite_guest(site, "some@example.com", role: :viewer, inviter: existing_owner)
new_owner = new_user() |> subscribe_to_growth_plan()
assert {:ok, _new_membership} =
AcceptInvitation.transfer_ownership(site, new_owner)
end
@tag :teams @tag :teams
test "syncs ownership transfers provided memberships exist already" do test "syncs ownership transfers provided memberships exist already" do
site = insert(:site, memberships: []) site = insert(:site, memberships: [])
@ -310,52 +322,6 @@ defmodule Plausible.Site.Memberships.AcceptInvitationTest do
assert_guest_membership(team, site, invitee, :editor) assert_guest_membership(team, site, invitee, :editor)
end end
@tag :teams
test "converts an invitation into a membership (TEAMS)" do
inviter = insert(:user)
invitee = insert(:user)
team = insert(:team)
site = insert(:site, team: team, members: [inviter])
insert(:team_membership, team: team, user: inviter, role: :owner)
_invitation =
insert(:invitation,
site_id: site.id,
inviter: inviter,
email: invitee.email,
role: :admin
)
team_invitation =
insert(:team_invitation,
team: team,
inviter: inviter,
email: invitee.email,
role: :guest
)
insert(:guest_invitation, team_invitation: team_invitation, site: site, role: :editor)
assert {:ok, team_membership} =
Plausible.Teams.Invitations.accept(team_invitation.invitation_id, invitee)
assert [guest_membership] =
Repo.preload(team_membership, :guest_memberships).guest_memberships
assert guest_membership.site_id == site.id
assert team_membership.user_id == invitee.id
assert guest_membership.role == :editor
assert team_membership.role == :guest
assert team_membership.team_id == team.id
refute Repo.reload(team_invitation)
assert_email_delivered_with(
to: [nil: inviter.email],
subject: @subject_prefix <> "#{invitee.email} accepted your invitation to #{site.domain}"
)
end
test "does not degrade role when trying to invite self as an owner" do test "does not degrade role when trying to invite self as an owner" do
user = insert(:user) user = insert(:user)