mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
feat(portal): Allow completely deleting accounts (#4557)
We need "ON DELETE CASCADE" everywhere to fully erase account-related data. As a safeguard measure the account can only be deleted after its subscription in Stripe is cancelled.
This commit is contained in:
@@ -9,6 +9,10 @@ defmodule Domain.Accounts.Account.Query do
|
||||
where(queryable, [accounts: accounts], is_nil(accounts.deleted_at))
|
||||
end
|
||||
|
||||
def disabled(queryable \\ all()) do
|
||||
where(queryable, [accounts: accounts], not is_nil(accounts.disabled_at))
|
||||
end
|
||||
|
||||
def not_disabled(queryable \\ not_deleted()) do
|
||||
where(queryable, [accounts: accounts], is_nil(accounts.disabled_at))
|
||||
end
|
||||
|
||||
@@ -83,4 +83,17 @@ defmodule Domain.Ops do
|
||||
|> Domain.Billing.EventHandler.handle_event()
|
||||
end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
To delete an account you need to disable it first by cancelling its subscription in Stripe.
|
||||
"""
|
||||
def delete_disabled_account(id) do
|
||||
Domain.Accounts.Account.Query.not_deleted()
|
||||
|> Domain.Accounts.Account.Query.disabled()
|
||||
|> Domain.Accounts.Account.Query.by_id(id)
|
||||
|> Domain.Repo.one!()
|
||||
|> Domain.Repo.delete()
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
defmodule Domain.Repo.Migrations.AddAccountDeletionCascade do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
execute("""
|
||||
ALTER TABLE "actor_group_memberships"
|
||||
DROP CONSTRAINT "actor_group_memberships_account_id_fkey",
|
||||
ADD CONSTRAINT "actor_group_memberships_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "actors"
|
||||
DROP CONSTRAINT "actors_account_id_fkey",
|
||||
ADD CONSTRAINT "actors_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "auth_identities"
|
||||
DROP CONSTRAINT "auth_identities_account_id_fkey",
|
||||
ADD CONSTRAINT "auth_identities_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "auth_providers"
|
||||
DROP CONSTRAINT "auth_providers_account_id_fkey",
|
||||
ADD CONSTRAINT "auth_providers_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "clients"
|
||||
DROP CONSTRAINT "devices_account_id_fkey",
|
||||
ADD CONSTRAINT "devices_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "configurations"
|
||||
DROP CONSTRAINT "configurations_account_id_fkey",
|
||||
ADD CONSTRAINT "configurations_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "gateway_groups"
|
||||
DROP CONSTRAINT "gateway_groups_account_id_fkey",
|
||||
ADD CONSTRAINT "gateway_groups_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "gateways"
|
||||
DROP CONSTRAINT "gateways_account_id_fkey",
|
||||
ADD CONSTRAINT "gateways_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "network_addresses"
|
||||
DROP CONSTRAINT "network_addresses_account_id_fkey",
|
||||
ADD CONSTRAINT "network_addresses_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "policies"
|
||||
DROP CONSTRAINT "policies_account_id_fkey",
|
||||
ADD CONSTRAINT "policies_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "relay_groups"
|
||||
DROP CONSTRAINT "relay_groups_account_id_fkey",
|
||||
ADD CONSTRAINT "relay_groups_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "relays"
|
||||
DROP CONSTRAINT "relays_account_id_fkey",
|
||||
ADD CONSTRAINT "relays_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "resource_connections"
|
||||
DROP CONSTRAINT "resource_connections_account_id_fkey",
|
||||
ADD CONSTRAINT "resource_connections_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
|
||||
execute("""
|
||||
ALTER TABLE "resources"
|
||||
DROP CONSTRAINT "resources_account_id_fkey",
|
||||
ADD CONSTRAINT "resources_account_id_fkey"
|
||||
FOREIGN KEY ("account_id")
|
||||
REFERENCES "accounts" ("id") ON DELETE CASCADE;
|
||||
""")
|
||||
end
|
||||
end
|
||||
@@ -80,4 +80,38 @@ defmodule Domain.OpsTest do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete_disabled_account/1" do
|
||||
test "doesn't delete an account that is not disabled" do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
|
||||
assert_raise Ecto.NoResultsError, fn ->
|
||||
delete_disabled_account(account.id)
|
||||
end
|
||||
end
|
||||
|
||||
test "deletes account along with all related entities" do
|
||||
account = Fixtures.Accounts.create_account()
|
||||
Fixtures.Actors.create_group(account: account)
|
||||
Fixtures.Actors.create_actor(account: account)
|
||||
Fixtures.Auth.create_identity(account: account)
|
||||
Fixtures.Clients.create_client(account: account)
|
||||
Fixtures.Flows.create_activity(account: account)
|
||||
Fixtures.Gateways.create_gateway(account: account)
|
||||
Fixtures.Policies.create_policy(account: account)
|
||||
Fixtures.Relays.create_relay(account: account)
|
||||
Fixtures.Resources.create_resource(account: account)
|
||||
Fixtures.Tokens.create_token(account: account)
|
||||
|
||||
Fixtures.Accounts.disable_account(account)
|
||||
|
||||
assert delete_disabled_account(account.id) == :ok
|
||||
|
||||
assert_raise Ecto.NoResultsError, fn ->
|
||||
assert delete_disabled_account(account.id) == :ok
|
||||
end
|
||||
|
||||
refute Repo.one(Domain.Accounts.Account)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user