From 27565ea5c84181c6cfb7d79901f906dd223cbc55 Mon Sep 17 00:00:00 2001 From: Brian Manifold Date: Sat, 18 Oct 2025 10:02:26 -0700 Subject: [PATCH] refactor(portal): remove soft delete elements from portal code (#10607) Why: * In previous commits, the portal code had been updated to use hard deletion rather than soft deletion of data. The fields used in the soft deletion were still kept in the DB and the code to allow for zero downtime rollout and an easy rollback if necessary. To continue with that work the portal code has now been updated to remove any reference to the soft deleted fields (e.g. deleted_at, persistent_id, etc...). While the code has been updated the actual data in the DB will need to remain for now, to once again allow for a zero downtime rollout. Once this commit has been deployed to production another PR can follow to remove the columns from the necessary tables in the DB. Related: #8187 --- .../actor_group_membership_controller.ex | 4 +- .../lib/api/controllers/policy_controller.ex | 6 +- .../api/controllers/resource_controller.ex | 6 +- .../api/test/api/gateway/channel_test.exs | 5 +- .../apps/api/test/api/relay/channel_test.exs | 3 +- elixir/apps/domain/lib/domain/accounts.ex | 15 +- .../domain/lib/domain/accounts/account.ex | 44 +- .../lib/domain/accounts/account/query.ex | 9 +- elixir/apps/domain/lib/domain/actors.ex | 189 +------- elixir/apps/domain/lib/domain/actors/actor.ex | 15 +- .../lib/domain/actors/actor/changeset.ex | 7 - .../domain/lib/domain/actors/actor/query.ex | 61 +-- elixir/apps/domain/lib/domain/actors/group.ex | 9 +- .../lib/domain/actors/group/changeset.ex | 6 +- .../domain/lib/domain/actors/group/query.ex | 72 +-- .../domain/lib/domain/actors/group/sync.ex | 14 +- .../lib/domain/actors/membership/query.ex | 6 +- elixir/apps/domain/lib/domain/auth.ex | 133 +----- .../domain/auth/adapters/openid_connect.ex | 8 +- .../apps/domain/lib/domain/auth/identity.ex | 8 +- .../lib/domain/auth/identity/changeset.ex | 10 - .../domain/lib/domain/auth/identity/query.ex | 31 +- .../domain/lib/domain/auth/identity/sync.ex | 29 +- .../apps/domain/lib/domain/auth/provider.ex | 7 +- .../lib/domain/auth/provider/changeset.ex | 10 +- .../domain/lib/domain/auth/provider/query.ex | 8 +- elixir/apps/domain/lib/domain/clients.ex | 25 +- .../apps/domain/lib/domain/clients/client.ex | 3 - .../lib/domain/clients/client/changeset.ex | 3 +- .../domain/lib/domain/clients/client/query.ex | 25 +- elixir/apps/domain/lib/domain/gateways.ex | 39 +- .../domain/lib/domain/gateways/gateway.ex | 3 - .../lib/domain/gateways/gateway/changeset.ex | 10 +- .../lib/domain/gateways/gateway/query.ex | 20 - .../apps/domain/lib/domain/gateways/group.ex | 11 +- .../lib/domain/gateways/group/changeset.ex | 7 - .../domain/lib/domain/gateways/group/query.ex | 16 - elixir/apps/domain/lib/domain/ops.ex | 2 +- elixir/apps/domain/lib/domain/policies.ex | 138 +----- .../apps/domain/lib/domain/policies/policy.ex | 10 - .../lib/domain/policies/policy/changeset.ex | 1 - .../lib/domain/policies/policy/query.ex | 39 +- elixir/apps/domain/lib/domain/relays.ex | 48 +- elixir/apps/domain/lib/domain/relays/group.ex | 7 +- .../lib/domain/relays/group/changeset.ex | 7 - .../domain/lib/domain/relays/group/query.ex | 20 +- elixir/apps/domain/lib/domain/relays/relay.ex | 2 - .../lib/domain/relays/relay/changeset.ex | 15 +- .../domain/lib/domain/relays/relay/query.ex | 11 - elixir/apps/domain/lib/domain/resources.ex | 128 ++---- .../domain/lib/domain/resources/resource.ex | 15 +- .../domain/resources/resource/changeset.ex | 9 - .../lib/domain/resources/resource/query.ex | 28 +- elixir/apps/domain/lib/domain/tokens.ex | 126 +----- elixir/apps/domain/lib/domain/tokens/token.ex | 2 - .../lib/domain/tokens/token/changeset.ex | 7 - .../domain/lib/domain/tokens/token/query.ex | 17 - ...0251013204955_remove_soft_deleted_data.exs | 66 +++ ...251016183541_add_default_persistent_id.exs | 31 ++ ...eate_unique_indexes_without_deleted_at.exs | 169 +++++++ .../apps/domain/test/domain/accounts_test.exs | 7 +- .../apps/domain/test/domain/actors_test.exs | 373 +-------------- .../jobs/sync_directory_test.exs | 161 ------- .../jumpcloud/jobs/sync_directory_test.exs | 122 ----- .../jobs/sync_directory_test.exs | 122 ----- .../okta/jobs/sync_directory_test.exs | 148 ------ elixir/apps/domain/test/domain/auth_test.exs | 427 +++--------------- .../replication_connection_test.exs | 5 +- .../domain/changes/hooks/accounts_test.exs | 31 +- .../hooks/actor_group_memberships_test.exs | 2 +- .../domain/changes/hooks/clients_test.exs | 17 - .../changes/hooks/gateway_groups_test.exs | 3 +- .../domain/changes/hooks/gateways_test.exs | 37 +- .../domain/changes/hooks/policies_test.exs | 80 +--- .../domain/changes/hooks/resources_test.exs | 75 +-- .../test/domain/changes/hooks/tokens_test.exs | 48 +- .../apps/domain/test/domain/clients_test.exs | 2 +- .../apps/domain/test/domain/gateways_test.exs | 2 +- .../apps/domain/test/domain/policies_test.exs | 208 --------- .../apps/domain/test/domain/relays_test.exs | 3 - .../domain/test/domain/repo/filter_test.exs | 6 +- elixir/apps/domain/test/domain/repo_test.exs | 33 +- .../domain/test/domain/resources_test.exs | 186 -------- .../apps/domain/test/domain/tokens_test.exs | 25 +- .../domain/test/support/fixtures/accounts.ex | 2 +- .../domain/test/support/fixtures/actors.ex | 9 - .../apps/domain/test/support/fixtures/auth.ex | 5 +- .../domain/test/support/fixtures/tokens.ex | 5 +- .../web/lib/web/live/actors/components.ex | 5 +- elixir/apps/web/lib/web/live/actors/edit.ex | 7 +- elixir/apps/web/lib/web/live/actors/groups.ex | 7 +- .../actors/service_accounts/new_identity.ex | 1 - elixir/apps/web/lib/web/live/actors/show.ex | 19 +- .../lib/web/live/actors/users/new_identity.ex | 1 - elixir/apps/web/lib/web/live/clients/edit.ex | 3 +- elixir/apps/web/lib/web/live/clients/show.ex | 9 +- elixir/apps/web/lib/web/live/gateways/show.ex | 3 +- elixir/apps/web/lib/web/live/groups/edit.ex | 1 - .../web/lib/web/live/groups/edit_actors.ex | 1 - elixir/apps/web/lib/web/live/groups/index.ex | 4 - elixir/apps/web/lib/web/live/groups/show.ex | 17 +- elixir/apps/web/lib/web/live/policies/edit.ex | 5 +- .../apps/web/lib/web/live/policies/index.ex | 10 +- elixir/apps/web/lib/web/live/policies/show.ex | 80 +--- .../web/lib/web/live/relay_groups/edit.ex | 7 +- .../lib/web/live/relay_groups/new_token.ex | 7 +- .../web/lib/web/live/relay_groups/show.ex | 9 +- elixir/apps/web/lib/web/live/relays/show.ex | 3 +- .../web/lib/web/live/resources/components.ex | 2 +- .../apps/web/lib/web/live/resources/edit.ex | 1 - .../apps/web/lib/web/live/resources/show.ex | 83 +--- .../lib/web/live/settings/api_clients/edit.ex | 3 +- .../lib/web/live/settings/api_clients/show.ex | 7 +- .../settings/identity_providers/components.ex | 11 - .../google_workspace/show.ex | 46 +- .../identity_providers/jumpcloud/show.ex | 47 +- .../microsoft_entra/show.ex | 46 +- .../settings/identity_providers/mock/show.ex | 44 +- .../settings/identity_providers/okta/show.ex | 48 +- .../identity_providers/openid_connect/show.ex | 48 +- .../identity_providers/system/show.ex | 7 +- elixir/apps/web/lib/web/live/sites/edit.ex | 3 +- .../web/lib/web/live/sites/gateways/index.ex | 4 +- .../apps/web/lib/web/live/sites/new_token.ex | 6 +- elixir/apps/web/lib/web/live/sites/show.ex | 31 +- .../google_workspace/show_test.exs | 17 - .../jumpcloud/show_test.exs | 21 - .../microsoft_entra/show_test.exs | 17 - .../identity_providers/okta/show_test.exs | 17 - .../openid_connect/show_test.exs | 17 - .../identity_providers/system/show_test.exs | 17 - scripts/tests/lib.sh | 4 +- 132 files changed, 689 insertions(+), 3976 deletions(-) create mode 100644 elixir/apps/domain/priv/repo/migrations/20251013204955_remove_soft_deleted_data.exs create mode 100644 elixir/apps/domain/priv/repo/migrations/20251016183541_add_default_persistent_id.exs create mode 100644 elixir/apps/domain/priv/repo/migrations/20251016220755_recreate_unique_indexes_without_deleted_at.exs diff --git a/elixir/apps/api/lib/api/controllers/actor_group_membership_controller.ex b/elixir/apps/api/lib/api/controllers/actor_group_membership_controller.ex index ff3b9e429..34fc14c5a 100644 --- a/elixir/apps/api/lib/api/controllers/actor_group_membership_controller.ex +++ b/elixir/apps/api/lib/api/controllers/actor_group_membership_controller.ex @@ -65,7 +65,7 @@ defmodule API.ActorGroupMembershipController do ) do subject = conn.assigns.subject preload = [:memberships] - filter = [deleted?: false, editable?: true] + filter = [editable?: true] with {:ok, group} <- Actors.fetch_group_by_id(actor_group_id, subject, preload: preload, filter: filter), @@ -105,7 +105,7 @@ defmodule API.ActorGroupMembershipController do remove = Map.get(params, "remove", []) subject = conn.assigns.subject preload = [:memberships] - filter = [deleted?: false, editable?: true] + filter = [editable?: true] with {:ok, group} <- Actors.fetch_group_by_id(actor_group_id, subject, preload: preload, filter: filter), diff --git a/elixir/apps/api/lib/api/controllers/policy_controller.ex b/elixir/apps/api/lib/api/controllers/policy_controller.ex index 516157a7e..db2dfffb6 100644 --- a/elixir/apps/api/lib/api/controllers/policy_controller.ex +++ b/elixir/apps/api/lib/api/controllers/policy_controller.ex @@ -43,7 +43,7 @@ defmodule API.PolicyController do # Show a specific Policy def show(conn, %{"id" => id}) do - with {:ok, policy} <- Policies.fetch_policy_by_id_or_persistent_id(id, conn.assigns.subject) do + with {:ok, policy} <- Policies.fetch_policy_by_id(id, conn.assigns.subject) do render(conn, :show, policy: policy) end end @@ -91,7 +91,7 @@ defmodule API.PolicyController do def update(conn, %{"id" => id, "policy" => params}) do subject = conn.assigns.subject - with {:ok, policy} <- Policies.fetch_policy_by_id_or_persistent_id(id, subject) do + with {:ok, policy} <- Policies.fetch_policy_by_id(id, subject) do case Policies.update_policy(policy, params, subject) do {:ok, policy} -> render(conn, :show, policy: policy) @@ -124,7 +124,7 @@ defmodule API.PolicyController do def delete(conn, %{"id" => id}) do subject = conn.assigns.subject - with {:ok, policy} <- Policies.fetch_policy_by_id_or_persistent_id(id, subject), + with {:ok, policy} <- Policies.fetch_policy_by_id(id, subject), {:ok, policy} <- Policies.delete_policy(policy, subject) do render(conn, :show, policy: policy) end diff --git a/elixir/apps/api/lib/api/controllers/resource_controller.ex b/elixir/apps/api/lib/api/controllers/resource_controller.ex index 06ac1d8d3..dc59305df 100644 --- a/elixir/apps/api/lib/api/controllers/resource_controller.ex +++ b/elixir/apps/api/lib/api/controllers/resource_controller.ex @@ -43,7 +43,7 @@ defmodule API.ResourceController do def show(conn, %{"id" => id}) do with {:ok, resource} <- - Resources.fetch_resource_by_id_or_persistent_id(id, conn.assigns.subject) do + Resources.fetch_resource_by_id(id, conn.assigns.subject) do render(conn, :show, resource: resource) end end @@ -92,7 +92,7 @@ defmodule API.ResourceController do subject = conn.assigns.subject attrs = set_param_defaults(params) - with {:ok, resource} <- Resources.fetch_active_resource_by_id_or_persistent_id(id, subject) do + with {:ok, resource} <- Resources.fetch_resource_by_id(id, subject) do case Resources.update_resource(resource, attrs, subject) do {:ok, updated_resource} -> render(conn, :show, resource: updated_resource) @@ -124,7 +124,7 @@ defmodule API.ResourceController do def delete(conn, %{"id" => id}) do subject = conn.assigns.subject - with {:ok, resource} <- Resources.fetch_resource_by_id_or_persistent_id(id, subject), + with {:ok, resource} <- Resources.fetch_resource_by_id(id, subject), {:ok, resource} <- Resources.delete_resource(resource, subject) do render(conn, :show, resource: resource) end diff --git a/elixir/apps/api/test/api/gateway/channel_test.exs b/elixir/apps/api/test/api/gateway/channel_test.exs index f503c773f..8a2fa4413 100644 --- a/elixir/apps/api/test/api/gateway/channel_test.exs +++ b/elixir/apps/api/test/api/gateway/channel_test.exs @@ -102,8 +102,9 @@ defmodule API.Gateway.ChannelTest do capture_log(fn -> send(socket.channel_pid, %Changes.Change{lsn: 50}) - # Wait for the channel to process and emit the log - Process.sleep(1) + # Force the channel to process the message before continuing + # :sys.get_state/1 is synchronous and will wait for all pending messages to be handled + :sys.get_state(socket.channel_pid) end) assert message =~ "[warning] Out of order or duplicate change received; ignoring" diff --git a/elixir/apps/api/test/api/relay/channel_test.exs b/elixir/apps/api/test/api/relay/channel_test.exs index eb1d85541..51e681755 100644 --- a/elixir/apps/api/test/api/relay/channel_test.exs +++ b/elixir/apps/api/test/api/relay/channel_test.exs @@ -60,7 +60,8 @@ defmodule API.Relay.ChannelTest do describe "handle_in/3 for unknown messages" do test "it doesn't crash", %{socket: socket} do ref = push(socket, "unknown_message", %{}) - assert_reply ref, :error, %{reason: :unknown_message} + + assert_reply ref, :error, %{reason: :unknown_message}, 1000 end end end diff --git a/elixir/apps/domain/lib/domain/accounts.ex b/elixir/apps/domain/lib/domain/accounts.ex index b66b4046a..75718eb01 100644 --- a/elixir/apps/domain/lib/domain/accounts.ex +++ b/elixir/apps/domain/lib/domain/accounts.ex @@ -11,7 +11,7 @@ defmodule Domain.Accounts do def all_accounts_by_ids!(ids) do if Enum.all?(ids, &Repo.valid_uuid?/1) do - Account.Query.not_deleted() + Account.Query.all() |> Account.Query.by_id({:in, ids}) |> Repo.all() else @@ -43,7 +43,7 @@ defmodule Domain.Accounts do def fetch_account_by_id(id, %Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_own_account_permission()), true <- Repo.valid_uuid?(id) do - Account.Query.not_deleted() + Account.Query.all() |> Account.Query.by_id(id) |> Authorizer.for_subject(subject) |> Repo.fetch(Account.Query, opts) @@ -58,13 +58,13 @@ defmodule Domain.Accounts do def fetch_account_by_id_or_slug("", _opts), do: {:error, :not_found} def fetch_account_by_id_or_slug(id_or_slug, opts) do - Account.Query.not_deleted() + Account.Query.all() |> Account.Query.by_id_or_slug(id_or_slug) |> Repo.fetch(Account.Query, opts) end def fetch_account_by_id!(id) do - Account.Query.not_deleted() + Account.Query.all() |> Account.Query.by_id(id) |> Repo.one!() end @@ -80,7 +80,7 @@ defmodule Domain.Accounts do def update_account(%Account{} = account, attrs, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_own_account_permission()) do - Account.Query.not_deleted() + Account.Query.all() |> Account.Query.by_id(account.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Account.Query, @@ -118,8 +118,7 @@ defmodule Domain.Accounts do Map.fetch!(account.features || %Features{}, feature) || false end - # TODO: HARD-DELETE - Update after `deleted_at` is removed from DB - def account_active?(%{deleted_at: nil, disabled_at: nil}), do: true + def account_active?(%{disabled_at: nil}), do: true def account_active?(_account), do: false def ensure_has_access_to(%Auth.Subject{} = subject, %Account{} = account) do @@ -134,7 +133,7 @@ defmodule Domain.Accounts do slug_candidate = Domain.NameGenerator.generate_slug() queryable = - Account.Query.not_deleted() + Account.Query.all() |> Account.Query.by_slug(slug_candidate) if Repo.exists?(queryable) do diff --git a/elixir/apps/domain/lib/domain/accounts/account.ex b/elixir/apps/domain/lib/domain/accounts/account.ex index 9d922049d..e9cd460a1 100644 --- a/elixir/apps/domain/lib/domain/accounts/account.ex +++ b/elixir/apps/domain/lib/domain/accounts/account.ex @@ -25,41 +25,31 @@ defmodule Domain.Accounts.Account do # We mention all schemas here to leverage Ecto compile-time reference checks, # because later we will have to shard data by account_id. - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :actors, Domain.Actors.Actor, where: [deleted_at: nil] - has_many :actor_group_memberships, Domain.Actors.Membership, where: [deleted_at: nil] - has_many :actor_groups, Domain.Actors.Group, where: [deleted_at: nil] + has_many :actors, Domain.Actors.Actor + has_many :actor_group_memberships, Domain.Actors.Membership + has_many :actor_groups, Domain.Actors.Group - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :auth_providers, Domain.Auth.Provider, where: [deleted_at: nil] - has_many :auth_identities, Domain.Auth.Identity, where: [deleted_at: nil] + has_many :auth_providers, Domain.Auth.Provider + has_many :auth_identities, Domain.Auth.Identity - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :network_addresses, Domain.Network.Address, where: [deleted_at: nil] + has_many :network_addresses, Domain.Network.Address - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :policies, Domain.Policies.Policy, where: [deleted_at: nil] + has_many :policies, Domain.Policies.Policy - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :flows, Domain.Flows.Flow, where: [deleted_at: nil] + has_many :flows, Domain.Flows.Flow - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :resources, Domain.Resources.Resource, where: [deleted_at: nil] - has_many :resource_connections, Domain.Resources.Connection, where: [deleted_at: nil] + has_many :resources, Domain.Resources.Resource + has_many :resource_connections, Domain.Resources.Connection - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :clients, Domain.Clients.Client, where: [deleted_at: nil] + has_many :clients, Domain.Clients.Client - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :gateways, Domain.Gateways.Gateway, where: [deleted_at: nil] - has_many :gateway_groups, Domain.Gateways.Group, where: [deleted_at: nil] + has_many :gateways, Domain.Gateways.Gateway + has_many :gateway_groups, Domain.Gateways.Group - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :relays, Domain.Relays.Relay, where: [deleted_at: nil] - has_many :relay_groups, Domain.Relays.Group, where: [deleted_at: nil] + has_many :relays, Domain.Relays.Relay + has_many :relay_groups, Domain.Relays.Group - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :tokens, Domain.Tokens.Token, where: [deleted_at: nil] + has_many :tokens, Domain.Tokens.Token field :warning, :string field :warning_delivery_attempts, :integer, default: 0 @@ -68,8 +58,6 @@ defmodule Domain.Accounts.Account do field :disabled_reason, :string field :disabled_at, :utc_datetime_usec - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/accounts/account/query.ex b/elixir/apps/domain/lib/domain/accounts/account/query.ex index eb513243f..6b5e47de3 100644 --- a/elixir/apps/domain/lib/domain/accounts/account/query.ex +++ b/elixir/apps/domain/lib/domain/accounts/account/query.ex @@ -5,16 +5,11 @@ defmodule Domain.Accounts.Account.Query do from(accounts in Domain.Accounts.Account, as: :accounts) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def not_deleted(queryable \\ all()) 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 + def not_disabled(queryable \\ all()) do where(queryable, [accounts: accounts], is_nil(accounts.disabled_at)) end @@ -100,7 +95,7 @@ defmodule Domain.Accounts.Account.Query do @impl Domain.Repo.Query def preloads, do: [ - clients: {Domain.Clients.Client.Query.not_deleted(), Domain.Clients.Client.Query.preloads()} + clients: {Domain.Clients.Client.Query.all(), Domain.Clients.Client.Query.preloads()} ] @impl Domain.Repo.Query diff --git a/elixir/apps/domain/lib/domain/actors.ex b/elixir/apps/domain/lib/domain/actors.ex index ad06b2aeb..91aabdf27 100644 --- a/elixir/apps/domain/lib/domain/actors.ex +++ b/elixir/apps/domain/lib/domain/actors.ex @@ -2,7 +2,7 @@ defmodule Domain.Actors do alias Domain.Actors.Membership alias Web.Clients alias Domain.Repo - alias Domain.{Accounts, Auth, Tokens, Clients, Policies, Billing} + alias Domain.{Accounts, Auth, Tokens, Clients, Billing} alias Domain.Actors.{Authorizer, Actor, Group} require Ecto.Query require Logger @@ -12,7 +12,7 @@ defmodule Domain.Actors do def fetch_groups_count_grouped_by_provider_id(%Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do groups = - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.group_by_provider_id() |> Authorizer.for_subject(subject) |> Repo.all() @@ -48,7 +48,7 @@ defmodule Domain.Actors do def list_groups(%Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - Group.Query.not_deleted() + Group.Query.all() |> Authorizer.for_subject(subject) |> Repo.list(Group.Query, opts) end @@ -56,7 +56,7 @@ defmodule Domain.Actors do def list_groups_for(%Actor{} = actor, %Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.by_actor_id(actor.id) |> Authorizer.for_subject(subject) |> Repo.list(Group.Query, opts) @@ -66,7 +66,7 @@ defmodule Domain.Actors do def all_groups!(%Auth.Subject{} = subject, opts \\ []) do {preload, _opts} = Keyword.pop(opts, :preload, []) - Group.Query.not_deleted() + Group.Query.all() |> Authorizer.for_subject(subject) |> Repo.all() |> Repo.preload(preload) @@ -75,7 +75,7 @@ defmodule Domain.Actors do def all_editable_groups!(%Auth.Subject{} = subject, opts \\ []) do {preload, _opts} = Keyword.pop(opts, :preload, []) - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.editable() |> Authorizer.for_subject(subject) |> Repo.all() @@ -90,7 +90,7 @@ defmodule Domain.Actors do def list_editable_groups(%Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.editable() |> Authorizer.for_subject(subject) |> Repo.list(Group.Query, opts) @@ -101,7 +101,7 @@ defmodule Domain.Actors do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do ids = groups |> Enum.map(& &1.id) |> Enum.uniq() - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.by_id({:in, ids}) |> Group.Query.preload_few_actors_for_each_group(limit) |> Authorizer.for_subject(subject) @@ -114,7 +114,7 @@ defmodule Domain.Actors do ids = actors |> Enum.map(& &1.id) |> Enum.uniq() {:ok, peek} = - Actor.Query.not_deleted() + Actor.Query.all() |> Actor.Query.by_id({:in, ids}) |> Actor.Query.preload_few_groups_for_each_actor(limit) |> Authorizer.for_subject(subject) @@ -141,7 +141,7 @@ defmodule Domain.Actors do ids = actors |> Enum.map(& &1.id) |> Enum.uniq() {:ok, peek} = - Actor.Query.not_deleted() + Actor.Query.all() |> Actor.Query.by_id({:in, ids}) |> Actor.Query.preload_few_clients_for_each_actor(limit) |> Authorizer.for_subject(subject) @@ -224,7 +224,7 @@ defmodule Domain.Actors do def update_group(%Group{provider_id: nil} = group, attrs, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.by_id(group.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Group.Query, @@ -256,93 +256,6 @@ defmodule Domain.Actors do {:error, :synced_group} end - # TODO: HARD-DELETE - Remove this once the `deleted_at` field in the DB is gone - def soft_delete_group(%Group{provider_id: nil} = group, %Auth.Subject{} = subject) do - queryable = - Group.Query.not_deleted() - |> Group.Query.by_id(group.id) - - case soft_delete_groups(queryable, subject) do - {:ok, [group]} -> - {:ok, _policies} = Policies.soft_delete_policies_for(group, subject) - - # TODO: Hard delete - # Consider using a trigger or transaction to handle the side effects of soft-deletions to ensure consistency - {_count, _memberships} = - Membership.Query.all() - |> Membership.Query.by_group_id(group.id) - |> Repo.delete_all() - - {:ok, group} - - {:ok, []} -> - {:error, :not_found} - - {:error, reason} -> - {:error, reason} - end - end - - # TODO: HARD-DELETE - Remove this once the `deleted_at` field in the DB is gone - def soft_delete_group(%Group{}, %Auth.Subject{}) do - {:error, :synced_group} - end - - # TODO: HARD-DELETE - Remove this once the `deleted_at` field in the DB is gone - def soft_delete_groups_for(%Auth.Provider{} = provider, %Auth.Subject{} = subject) do - queryable = - Group.Query.not_deleted() - |> Group.Query.by_provider_id(provider.id) - |> Group.Query.by_account_id(provider.account_id) - - # TODO: Hard delete - # Consider using a trigger or transaction to handle the side effects of soft-deletions to ensure consistency - {_count, _memberships} = - Membership.Query.by_group_provider_id(provider.id) - |> Repo.delete_all() - - with {:ok, groups} <- soft_delete_groups(queryable, subject) do - {:ok, _policies} = Policies.soft_delete_policies_for(provider, subject) - {:ok, groups} - end - end - - # TODO: HARD-DELETE - Remove this once the `deleted_at` field in the DB is gone - defp soft_delete_groups(queryable, subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - {_count, groups} = - queryable - |> Authorizer.for_subject(subject) - |> Group.Query.delete() - |> Repo.update_all([]) - - {:ok, groups} - end - end - - @doc false - # used in sync workers - # TODO: HARD-DELETE - Remove this once the `deleted_at` field in the DB is gone - def soft_delete_groups(queryable) do - {_count, groups} = - queryable - |> Group.Query.delete() - |> Repo.update_all([]) - - :ok = - Enum.each(groups, fn group -> - {:ok, _policies} = Domain.Policies.soft_delete_policies_for(group) - end) - - # TODO: Hard delete - # Consider using a trigger or transaction to handle the side effects of soft-deletions to ensure consistency - {_count, _memberships} = - Membership.Query.by_group_id({:in, Enum.map(groups, & &1.id)}) - |> Repo.delete_all() - - {:ok, groups} - end - def group_synced?(%Group{provider_id: nil}), do: false def group_synced?(%Group{}), do: true @@ -350,11 +263,7 @@ defmodule Domain.Actors do def group_managed?(%Group{}), do: false def group_editable?(%Group{} = group), - do: not group_soft_deleted?(group) and not group_synced?(group) and not group_managed?(group) - - # TODO: HARD-DELETE - Remove this once the `deleted_at` field in the DB is gone - def group_soft_deleted?(%Group{deleted_at: nil}), do: false - def group_soft_deleted?(%Group{}), do: true + do: not group_synced?(group) and not group_managed?(group) # Actors @@ -379,18 +288,6 @@ defmodule Domain.Actors do |> Repo.aggregate(:count) end - def count_synced_actors_for_provider(%Auth.Provider{} = provider) do - Actor.Query.not_deleted() - |> Actor.Query.by_deleted_identity_provider_id(provider.id) - |> Actor.Query.by_stale_for_provider(provider.id) - |> Repo.all() - - Actor.Query.not_deleted() - |> Actor.Query.by_deleted_identity_provider_id(provider.id) - |> Actor.Query.by_stale_for_provider(provider.id) - |> Repo.aggregate(:count) - end - def fetch_actor_by_id(id, %Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()), true <- Repo.valid_uuid?(id) do @@ -441,7 +338,7 @@ defmodule Domain.Actors do def list_actors(%Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - Actor.Query.not_deleted() + Actor.Query.all() |> Authorizer.for_subject(subject) |> Repo.list(Actor.Query, opts) end @@ -520,7 +417,7 @@ defmodule Domain.Actors do def update_actor(%Actor{} = actor, attrs, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - Actor.Query.not_deleted() + Actor.Query.all() |> Actor.Query.by_id(actor.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Actor.Query, @@ -569,7 +466,7 @@ defmodule Domain.Actors do [] group_ids -> - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.not_editable() |> Group.Query.by_id({:in, group_ids}) |> Repo.all() @@ -578,7 +475,7 @@ defmodule Domain.Actors do def disable_actor(%Actor{} = actor, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - Actor.Query.not_deleted() + Actor.Query.all() |> Actor.Query.by_id(actor.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Actor.Query, @@ -596,7 +493,7 @@ defmodule Domain.Actors do def enable_actor(%Actor{} = actor, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - Actor.Query.not_deleted() + Actor.Query.all() |> Actor.Query.by_id(actor.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Actor.Query, with: &Actor.Changeset.enable_actor/1) @@ -616,35 +513,6 @@ defmodule Domain.Actors do end end - # TODO: HARD-DELETE - Remove this after `deleted_at` column is removed from DB - def soft_delete_actor(%Actor{} = actor, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - Actor.Query.not_deleted() - |> Actor.Query.by_id(actor.id) - |> Authorizer.for_subject(subject) - |> Repo.fetch_and_update(Actor.Query, - with: fn actor -> - if actor.type != :account_admin_user or other_enabled_admins_exist?(actor) do - :ok = Auth.soft_delete_identities_for(actor, subject) - :ok = Clients.delete_clients_for(actor, subject) - - # TODO: Hard delete - # Consider using a trigger or transaction to handle the side effects of soft-deletions to ensure consistency - {_count, _memberships} = - Membership.Query.by_actor_id(actor.id) - |> Repo.delete_all() - - {:ok, _tokens} = Tokens.delete_tokens_for(actor, subject) - - Actor.Changeset.delete_actor(actor) - else - :cant_delete_the_last_admin - end - end - ) - end - end - def delete_unsynced_actors(%Auth.Provider{} = provider, synced_at) do {count, _deleted_actors} = Actor.Query.all() @@ -678,34 +546,13 @@ defmodule Domain.Actors do {:ok, %{deleted_memberships: count}} end - def delete_stale_synced_actors_for_provider( - %Auth.Provider{} = provider, - %Auth.Subject{} = subject - ) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - Actor.Query.not_deleted() - |> Authorizer.for_subject(subject) - |> Actor.Query.by_deleted_identity_provider_id(provider.id) - |> Actor.Query.by_stale_for_provider(provider.id) - |> Repo.all() - |> Enum.each(fn actor -> - {:ok, _actor} = delete_actor(actor, subject) - end) - end - end - def actor_synced?(%Actor{last_synced_at: nil}), do: false def actor_synced?(%Actor{}), do: true - # TODO: HARD-DELETE - Remove this once the `deleted_at` field in the DB is gone - def actor_deleted?(%Actor{deleted_at: nil}), do: false - def actor_deleted?(%Actor{}), do: true - def actor_disabled?(%Actor{disabled_at: nil}), do: false def actor_disabled?(%Actor{}), do: true - # TODO: HARD-DELETE - Update this once the `deleted_at` field in the DB is gone - def actor_active?(%Actor{disabled_at: nil, deleted_at: nil}), do: true + def actor_active?(%Actor{disabled_at: nil}), do: true def actor_active?(%Actor{}), do: false defp other_enabled_admins_exist?(%Actor{ diff --git a/elixir/apps/domain/lib/domain/actors/actor.ex b/elixir/apps/domain/lib/domain/actors/actor.ex index 4b4b25a84..d631927cf 100644 --- a/elixir/apps/domain/lib/domain/actors/actor.ex +++ b/elixir/apps/domain/lib/domain/actors/actor.ex @@ -7,20 +7,13 @@ defmodule Domain.Actors.Actor do field :name, :string - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from DB - has_many :identities, Domain.Auth.Identity, where: [deleted_at: nil] + has_many :identities, Domain.Auth.Identity - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from DB - has_many :clients, Domain.Clients.Client, - where: [deleted_at: nil], - preload_order: [desc: :last_seen_at] + has_many :clients, Domain.Clients.Client, preload_order: [desc: :last_seen_at] - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from DB - has_many :tokens, Domain.Tokens.Token, where: [deleted_at: nil] + has_many :tokens, Domain.Tokens.Token has_many :memberships, Domain.Actors.Membership, on_replace: :delete - # TODO: where doesn't work on join tables so soft-deleted records will be preloaded, - # ref https://github.com/firezone/firezone/issues/2162 has_many :groups, through: [:memberships, :group] belongs_to :account, Domain.Accounts.Account @@ -29,8 +22,6 @@ defmodule Domain.Actors.Actor do field :last_synced_at, :utc_datetime_usec field :disabled_at, :utc_datetime_usec - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/actors/actor/changeset.ex b/elixir/apps/domain/lib/domain/actors/actor/changeset.ex index c8ecc6b38..908ea717d 100644 --- a/elixir/apps/domain/lib/domain/actors/actor/changeset.ex +++ b/elixir/apps/domain/lib/domain/actors/actor/changeset.ex @@ -85,13 +85,6 @@ defmodule Domain.Actors.Actor.Changeset do |> put_change(:disabled_at, nil) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def delete_actor(%Actor{} = actor) do - actor - |> change() - |> put_default_value(:deleted_at, DateTime.utc_now()) - end - defp validate_granted_permissions(changeset, subject) do validate_change(changeset, :type, fn :type, granted_actor_type -> if Auth.can_grant_role?(subject, granted_actor_type) do diff --git a/elixir/apps/domain/lib/domain/actors/actor/query.ex b/elixir/apps/domain/lib/domain/actors/actor/query.ex index fd2411c6c..28f785789 100644 --- a/elixir/apps/domain/lib/domain/actors/actor/query.ex +++ b/elixir/apps/domain/lib/domain/actors/actor/query.ex @@ -5,13 +5,7 @@ defmodule Domain.Actors.Actor.Query do from(actors in Domain.Actors.Actor, as: :actors) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def not_deleted do - all() - |> where([actors: actors], is_nil(actors.deleted_at)) - end - - def not_disabled(queryable \\ not_deleted()) do + def not_disabled(queryable \\ all()) do where(queryable, [actors: actors], is_nil(actors.disabled_at)) end @@ -47,34 +41,6 @@ defmodule Domain.Actors.Actor.Query do where(queryable, [actors: actors], actors.last_synced_at != ^synced_at) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed in DB - def by_deleted_identity_provider_id(queryable, provider_id) do - queryable - |> join(:inner, [actors: actors], identities in ^Domain.Auth.Identity.Query.deleted(), - on: identities.actor_id == actors.id, - as: :deleted_identities - ) - |> where( - [deleted_identities: deleted_identities], - deleted_identities.provider_id == ^provider_id - ) - end - - # TODO: HARD-DELETE - Update after `deleted_at` column is removed in DB - def by_stale_for_provider(queryable, provider_id) do - subquery = - Domain.Auth.Identity.Query.all() - |> where( - [identities: identities], - identities.actor_id == parent_as(:actors).id and - (identities.provider_id != ^provider_id or - is_nil(identities.deleted_at)) - ) - - queryable - |> where([actors: actors], not exists(subquery)) - end - def by_type(queryable, {:in, types}) do where(queryable, [actors: actors], actors.type in ^types) end @@ -98,7 +64,7 @@ defmodule Domain.Actors.Actor.Query do def with_joined_clients(queryable, limit) do subquery = - Domain.Clients.Client.Query.not_deleted() + Domain.Clients.Client.Query.all() |> where([clients: clients], clients.actor_id == parent_as(:actors).id) |> order_by([clients: clients], desc: clients.last_seen_at) |> limit(^limit) @@ -138,13 +104,6 @@ defmodule Domain.Actors.Actor.Query do subquery = Domain.Actors.Membership.Query.all() |> where([memberships: memberships], memberships.actor_id == parent_as(:actors).id) - # we need second join to exclude soft deleted actors before applying a limit - |> join( - :inner, - [memberships: memberships], - groups in ^Domain.Actors.Group.Query.not_deleted(), - on: groups.id == memberships.group_id - ) |> select([memberships: memberships], memberships.group_id) |> limit(^limit) @@ -168,7 +127,7 @@ defmodule Domain.Actors.Actor.Query do queryable, :left, [memberships: memberships], - groups in ^Domain.Actors.Group.Query.not_deleted(), + groups in ^Domain.Actors.Group.Query.all(), on: groups.id == memberships.group_id, as: :groups ) @@ -179,7 +138,7 @@ defmodule Domain.Actors.Actor.Query do queryable, :left, [actors: actors], - clients in ^Domain.Clients.Client.Query.not_deleted(), + clients in ^Domain.Clients.Client.Query.all(), on: clients.actor_id == actors.id, as: :clients ) @@ -191,7 +150,7 @@ defmodule Domain.Actors.Actor.Query do queryable, :left, [actors: actors], - identities in ^Domain.Auth.Identity.Query.not_deleted(), + identities in ^Domain.Auth.Identity.Query.all(), on: identities.actor_id == actors.id, as: ^binding ) @@ -277,11 +236,6 @@ defmodule Domain.Actors.Actor.Query do name: :group_id, type: {:string, :uuid}, fun: &filter_by_group_id/2 - }, - %Domain.Repo.Filter{ - name: :deleted?, - type: :boolean, - fun: &filter_deleted/1 } ] @@ -347,9 +301,4 @@ defmodule Domain.Actors.Actor.Query do {queryable, dynamic(exists(subquery))} end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed in DB - def filter_deleted(queryable) do - {queryable, dynamic([actors: actors], not is_nil(actors.deleted_at))} - end end diff --git a/elixir/apps/domain/lib/domain/actors/group.ex b/elixir/apps/domain/lib/domain/actors/group.ex index 83a7fb0fb..3e1712b17 100644 --- a/elixir/apps/domain/lib/domain/actors/group.ex +++ b/elixir/apps/domain/lib/domain/actors/group.ex @@ -11,15 +11,10 @@ defmodule Domain.Actors.Group do field :last_synced_at, :utc_datetime_usec - # TODO: HARD-DELETE - Remove `where` after `deleted_at` column is removed from DB - has_many :policies, Domain.Policies.Policy, - foreign_key: :actor_group_id, - where: [deleted_at: nil] + has_many :policies, Domain.Policies.Policy, foreign_key: :actor_group_id has_many :memberships, Domain.Actors.Membership, on_replace: :delete - # TODO: where doesn't work on join tables so soft-deleted records will be preloaded, - # ref https://github.com/firezone/firezone/issues/2162 has_many :actors, through: [:memberships, :actor] field :created_by, Ecto.Enum, values: ~w[actor identity provider system]a @@ -27,8 +22,6 @@ defmodule Domain.Actors.Group do belongs_to :account, Domain.Accounts.Account - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/actors/group/changeset.ex b/elixir/apps/domain/lib/domain/actors/group/changeset.ex index 715475180..1aa43b4a3 100644 --- a/elixir/apps/domain/lib/domain/actors/group/changeset.ex +++ b/elixir/apps/domain/lib/domain/actors/group/changeset.ex @@ -11,8 +11,7 @@ defmodule Domain.Actors.Group.Changeset do "WHERE provider_id IS NOT NULL AND provider_identifier IS NOT NULL"} end - # TODO: Update after `deleted_at` is removed from the DB - def upsert_on_conflict, do: {:replace, ~w[name updated_at deleted_at]a} + def upsert_on_conflict, do: {:replace, ~w[name updated_at]a} def create(%Accounts.Account{} = account, attrs, %Auth.Subject{} = subject) do %Actors.Group{memberships: []} @@ -36,7 +35,6 @@ defmodule Domain.Actors.Group.Changeset do |> put_subject_trail(:created_by, :system) end - # TODO: Update after `deleted_at` is removed from the DB def create(%Auth.Provider{} = provider, attrs) do %Actors.Group{memberships: []} |> cast(attrs, ~w[name provider_identifier last_synced_at]a) @@ -45,8 +43,6 @@ defmodule Domain.Actors.Group.Changeset do |> changeset() |> put_change(:provider_id, provider.id) |> put_change(:account_id, provider.account_id) - # resurrect synced groups - |> put_change(:deleted_at, nil) |> put_subject_trail(:created_by, :provider) end diff --git a/elixir/apps/domain/lib/domain/actors/group/query.ex b/elixir/apps/domain/lib/domain/actors/group/query.ex index 558b014f5..5148ce766 100644 --- a/elixir/apps/domain/lib/domain/actors/group/query.ex +++ b/elixir/apps/domain/lib/domain/actors/group/query.ex @@ -6,12 +6,6 @@ defmodule Domain.Actors.Group.Query do from(groups in Domain.Actors.Group, as: :groups) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def not_deleted do - all() - |> where([groups: groups], is_nil(groups.deleted_at)) - end - def not_editable(queryable) do where(queryable, [groups: groups], not is_nil(groups.provider_id) or groups.type != :static) end @@ -69,17 +63,6 @@ defmodule Domain.Actors.Group.Query do where(queryable, [groups: groups], groups.last_synced_at != ^synced_at) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def delete(queryable) do - queryable - |> Ecto.Query.select([groups: groups], groups) - |> Ecto.Query.update([groups: groups], - set: [ - deleted_at: fragment("COALESCE(?, timezone('UTC', NOW()))", groups.deleted_at) - ] - ) - end - def group_by_provider_id(queryable) do queryable |> group_by([groups: groups], groups.provider_id) @@ -112,13 +95,6 @@ defmodule Domain.Actors.Group.Query do subquery = Domain.Actors.Membership.Query.all() |> where([memberships: memberships], memberships.group_id == parent_as(:groups).id) - # we need second join to exclude soft deleted actors before applying a limit - |> join( - :inner, - [memberships: memberships], - actors in ^Domain.Actors.Actor.Query.not_deleted(), - on: actors.id == memberships.actor_id - ) |> select([memberships: memberships], memberships.actor_id) |> limit(^limit) @@ -142,7 +118,7 @@ defmodule Domain.Actors.Group.Query do queryable, :left, [memberships: memberships], - actors in ^Domain.Actors.Actor.Query.not_deleted(), + actors in ^Domain.Actors.Actor.Query.all(), on: actors.id == memberships.actor_id, as: :actors ) @@ -180,43 +156,21 @@ defmodule Domain.Actors.Group.Query do {:ok, %{upserted_groups: count}} end - # TODO: Update after `deleted_at` is removed from DB # TODO: IDP Sync # See: https://github.com/firezone/firezone/issues/8750 - # We use CTE here which should be very performant even for very large inserts and deletions + # We use CTE here which should be very performant even for very large inserts def update_everyone_group_memberships(account_id) do - # Delete memberships for actors and groups that are soft-deleted - delete_memberships = - from( - agm in Domain.Actors.Membership, - where: - agm.account_id == ^account_id and - (exists( - from(a in Domain.Actors.Actor, - where: a.id == parent_as(:agm).actor_id and not is_nil(a.deleted_at) - ) - ) or - exists( - from(g in Domain.Actors.Group, - where: g.id == parent_as(:agm).group_id and not is_nil(g.deleted_at) - ) - )) - ) - |> from(as: :agm) - - # Insert memberships for the cross join of non-deleted user actors and managed groups + # Insert memberships for the cross join of user actors and managed groups insert_with_cte_fn = fn repo, _changes -> current_memberships_cte = from( a in Domain.Actors.Actor, cross_join: g in Domain.Actors.Group, where: - is_nil(a.deleted_at) and - a.account_id == ^account_id and + a.account_id == ^account_id and a.type in [:account_user, :account_admin_user] and g.type == :managed and - g.account_id == ^account_id and - is_nil(g.deleted_at), + g.account_id == ^account_id, select: %{ actor_id: a.id, group_id: g.id @@ -244,7 +198,6 @@ defmodule Domain.Actors.Group.Query do end Ecto.Multi.new() - |> Ecto.Multi.delete_all(:delete_memberships, delete_memberships) |> Ecto.Multi.run(:insert_memberships, insert_with_cte_fn) end @@ -273,11 +226,6 @@ defmodule Domain.Actors.Group.Query do values: &Domain.Auth.all_third_party_providers!/1, fun: &filter_by_provider_id/2 }, - %Domain.Repo.Filter{ - name: :deleted?, - type: :boolean, - fun: &filter_deleted/1 - }, %Domain.Repo.Filter{ name: :editable?, type: :boolean, @@ -293,19 +241,11 @@ defmodule Domain.Actors.Group.Query do {queryable, dynamic([groups: groups], groups.provider_id == ^provider_id)} end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def filter_deleted(queryable) do - {queryable, dynamic([groups: groups], not is_nil(groups.deleted_at))} - end - - # TODO: Update after `deleted_at` is removed from DB def filter_editable(queryable) do {queryable, dynamic( [groups: groups], - is_nil(groups.provider_id) and - is_nil(groups.deleted_at) and - groups.type == :static + is_nil(groups.provider_id) and groups.type == :static )} end end diff --git a/elixir/apps/domain/lib/domain/actors/group/sync.ex b/elixir/apps/domain/lib/domain/actors/group/sync.ex index d871f53fa..ab33f4151 100644 --- a/elixir/apps/domain/lib/domain/actors/group/sync.ex +++ b/elixir/apps/domain/lib/domain/actors/group/sync.ex @@ -45,21 +45,15 @@ defmodule Domain.Actors.Group.Sync do {:ok, groups} end - # TODO: Update after `deleted_at` is removed from DB defp plan_groups_update(groups, provider_identifiers) do identifiers_set = MapSet.new(provider_identifiers) {upsert, delete} = Enum.reduce(groups, {provider_identifiers, []}, fn group, {upsert, delete} -> - cond do - MapSet.member?(identifiers_set, group.provider_identifier) -> - {upsert, delete} - - !is_nil(group.deleted_at) -> - {upsert, delete} - - true -> - {upsert -- [group.provider_identifier], [group.provider_identifier] ++ delete} + if MapSet.member?(identifiers_set, group.provider_identifier) do + {upsert, delete} + else + {upsert -- [group.provider_identifier], [group.provider_identifier] ++ delete} end end) diff --git a/elixir/apps/domain/lib/domain/actors/membership/query.ex b/elixir/apps/domain/lib/domain/actors/membership/query.ex index 03c776019..a14b9c070 100644 --- a/elixir/apps/domain/lib/domain/actors/membership/query.ex +++ b/elixir/apps/domain/lib/domain/actors/membership/query.ex @@ -84,14 +84,14 @@ defmodule Domain.Actors.Membership.Query do end def with_joined_actors(queryable \\ all()) do - join(queryable, :inner, [memberships: memberships], actors in ^Actor.Query.not_deleted(), + join(queryable, :inner, [memberships: memberships], actors in ^Actor.Query.all(), on: actors.id == memberships.actor_id, as: :actors ) end def with_joined_groups(queryable \\ all()) do - join(queryable, :inner, [memberships: memberships], groups in ^Group.Query.not_deleted(), + join(queryable, :inner, [memberships: memberships], groups in ^Group.Query.all(), on: groups.id == memberships.group_id, as: :groups ) @@ -139,13 +139,11 @@ defmodule Domain.Actors.Membership.Query do ai.provider_identifier = mi.user_provider_identifier AND ai.account_id = $#{account_id} AND ai.provider_id = $#{provider_id} - AND ai.deleted_at IS NULL ) JOIN actor_groups ag ON ( ag.provider_identifier = mi.group_provider_identifier AND ag.account_id = $#{account_id} AND ag.provider_id = $#{provider_id} - AND ag.deleted_at IS NULL ) ) INSERT INTO actor_group_memberships (id, actor_id, group_id, account_id, last_synced_at) diff --git a/elixir/apps/domain/lib/domain/auth.ex b/elixir/apps/domain/lib/domain/auth.ex index 257ac6cc1..c5e69127c 100644 --- a/elixir/apps/domain/lib/domain/auth.ex +++ b/elixir/apps/domain/lib/domain/auth.ex @@ -163,7 +163,7 @@ defmodule Domain.Auth do def list_providers(%Subject{} = subject, opts \\ []) do with :ok <- ensure_has_permissions(subject, Authorizer.manage_providers_permission()) do - Provider.Query.not_deleted() + Provider.Query.all() |> Authorizer.for_subject(Provider, subject) |> Repo.list(Provider.Query, opts) end @@ -177,7 +177,7 @@ defmodule Domain.Auth do end def all_third_party_providers!(%Subject{} = subject) do - Provider.Query.not_deleted() + Provider.Query.all() |> Provider.Query.by_account_id(subject.account.id) |> Provider.Query.by_adapter({:not_in, [:email, :userpass]}) |> Authorizer.for_subject(Provider, subject) @@ -185,7 +185,7 @@ defmodule Domain.Auth do end def all_providers!(%Subject{} = subject) do - Provider.Query.not_deleted() + Provider.Query.all() |> Provider.Query.by_account_id(subject.account.id) |> Authorizer.for_subject(Provider, subject) |> Repo.all() @@ -314,30 +314,10 @@ defmodule Domain.Auth do end end - def soft_delete_provider(%Provider{} = provider, %Subject{} = subject) do - provider - |> mutate_provider(subject, fn provider -> - if other_active_providers_exist?(provider) do - :ok = soft_delete_identities_for(provider, subject) - {:ok, _groups} = Actors.soft_delete_groups_for(provider, subject) - Provider.Changeset.soft_delete_provider(provider) - else - :cant_delete_the_last_provider - end - end) - |> case do - {:ok, provider} -> - Adapters.ensure_deprovisioned(provider) - - {:error, reason} -> - {:error, reason} - end - end - defp mutate_provider(%Provider{} = provider, %Subject{} = subject, callback) when is_function(callback, 1) do with :ok <- ensure_has_permissions(subject, Authorizer.manage_providers_permission()) do - Provider.Query.not_deleted() + Provider.Query.all() |> Provider.Query.by_id(provider.id) |> Authorizer.for_subject(Provider, subject) |> Repo.fetch_and_update(Provider.Query, with: callback) @@ -367,7 +347,7 @@ defmodule Domain.Auth do # Identities def max_last_seen_at_by_actor_ids(actor_ids) do - Identity.Query.not_deleted() + Identity.Query.all() |> Identity.Query.by_actor_id({:in, actor_ids}) |> Identity.Query.max_last_seen_at_grouped_by_actor_id() |> Repo.all() @@ -392,7 +372,7 @@ defmodule Domain.Auth do def fetch_identity_by_id(id, %Subject{} = subject, opts \\ []) do with :ok <- ensure_has_permissions(subject, Authorizer.manage_identities_permission()), true <- Repo.valid_uuid?(id) do - Identity.Query.not_deleted() + Identity.Query.all() |> Identity.Query.by_id(id) |> Authorizer.for_subject(Identity, subject) |> Repo.fetch(Identity.Query, opts) @@ -406,7 +386,7 @@ defmodule Domain.Auth do def fetch_identities_count_grouped_by_provider_id(%Subject{} = subject) do with :ok <- ensure_has_permissions(subject, Authorizer.manage_identities_permission()) do identities = - Identity.Query.not_deleted() + Identity.Query.all() |> Identity.Query.group_by_provider_id() |> Authorizer.for_subject(Identity, subject) |> Repo.all() @@ -419,14 +399,14 @@ defmodule Domain.Auth do end def all_identities_for(%Actors.Actor{} = actor, opts \\ []) do - Identity.Query.not_deleted() + Identity.Query.all() |> Identity.Query.by_actor_id(actor.id) |> Repo.all(opts) end def list_identities_for(%Actors.Actor{} = actor, %Subject{} = subject, opts \\ []) do with :ok <- ensure_has_permissions(subject, Authorizer.manage_identities_permission()) do - Identity.Query.not_deleted() + Identity.Query.all() |> Identity.Query.by_actor_id(actor.id) |> Authorizer.for_subject(Identity, subject) |> Repo.list(Identity.Query, opts) @@ -451,14 +431,11 @@ defmodule Domain.Auth do end # used by IdP adapters - # TODO: HARD-DELETE - Remove `unsafe_fragment` after `deleted_at` column is removed from DB def upsert_identity(%Actors.Actor{} = actor, %Provider{} = provider, attrs) do Identity.Changeset.create_identity(actor, provider, attrs) |> Adapters.identity_changeset(provider) |> Repo.insert( - conflict_target: - {:unsafe_fragment, - ~s/(account_id, provider_id, provider_identifier) WHERE deleted_at IS NULL/}, + conflict_target: {:unsafe_fragment, "(account_id, provider_id, provider_identifier)"}, on_conflict: {:replace, [:provider_state]}, returning: true ) @@ -516,78 +493,6 @@ defmodule Domain.Auth do end end - # TODO: HARD-DELETE - # This function should not be necessary after hard delete because deleting a provider - # will delete all of it's identities using a cascading delete in the DB - def delete_identities_for(%Provider{} = provider, %Subject{} = subject) do - with :ok <- ensure_has_permissions(subject, Authorizer.manage_identities_permission()) do - {num_deleted, _} = - Identity.Query.all() - |> Identity.Query.by_provider_id(provider.id) - |> Identity.Query.by_account_id(provider.account_id) - |> Authorizer.for_subject(Identity, subject) - |> Repo.delete_all() - - {:ok, num_deleted} - end - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed in DB - def soft_delete_identity(%Identity{} = identity, %Subject{} = subject) do - required_permissions = - {:one_of, - [ - Authorizer.manage_identities_permission(), - Authorizer.manage_own_identities_permission() - ]} - - with :ok <- ensure_has_permissions(subject, required_permissions) do - Identity.Query.not_deleted() - |> Identity.Query.by_id(identity.id) - |> Authorizer.for_subject(Identity, subject) - |> Repo.fetch_and_update(Identity.Query, - with: fn identity -> - {:ok, _tokens} = Tokens.soft_delete_tokens_for(identity, subject) - Identity.Changeset.delete_identity(identity) - end - ) - end - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed in DB - def soft_delete_identities_for(%Actors.Actor{} = actor, %Subject{} = subject) do - Identity.Query.not_deleted() - |> Identity.Query.by_actor_id(actor.id) - |> Identity.Query.by_account_id(actor.account_id) - |> soft_delete_identities(actor, subject) - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed in DB - def soft_delete_identities_for(%Provider{} = provider, %Subject{} = subject) do - Identity.Query.not_deleted() - |> Identity.Query.by_provider_id(provider.id) - |> Identity.Query.by_account_id(provider.account_id) - |> soft_delete_identities(provider, subject) - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed in DB - defp soft_delete_identities(queryable, assoc, subject) do - with :ok <- ensure_has_permissions(subject, Authorizer.manage_identities_permission()) do - {:ok, _tokens} = Tokens.soft_delete_tokens_for(assoc, subject) - - {_count, nil} = - queryable - |> Authorizer.for_subject(Identity, subject) - |> Repo.update_all(set: [deleted_at: DateTime.utc_now(), provider_state: %{}]) - - :ok - end - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed in DB - def identity_soft_deleted?(%{deleted_at: nil}), do: false - def identity_soft_deleted?(_identity), do: true - # Sign Up / In / Off @doc """ @@ -604,18 +509,6 @@ defmodule Domain.Auth do {:error, :unauthorized} end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed in DB - def sign_in( - %Provider{deleted_at: deleted_at}, - _id_or_provider_identifier, - _token_nonce, - _secret, - %Context{} - ) - when not is_nil(deleted_at) do - {:error, :unauthorized} - end - def sign_in( %Provider{} = provider, id_or_provider_identifier, @@ -648,12 +541,6 @@ defmodule Domain.Auth do {:error, :unauthorized} end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed in DB - def sign_in(%Provider{deleted_at: deleted_at}, _token_nonce, _payload, %Context{}) - when not is_nil(deleted_at) do - {:error, :unauthorized} - end - def sign_in(%Provider{} = provider, token_nonce, payload, %Context{} = context) do with {:ok, identity, expires_at} <- Adapters.verify_and_update_identity(provider, payload), identity = Repo.preload(identity, :actor), diff --git a/elixir/apps/domain/lib/domain/auth/adapters/openid_connect.ex b/elixir/apps/domain/lib/domain/auth/adapters/openid_connect.ex index 796f5b0cc..e46f8426a 100644 --- a/elixir/apps/domain/lib/domain/auth/adapters/openid_connect.ex +++ b/elixir/apps/domain/lib/domain/auth/adapters/openid_connect.ex @@ -201,7 +201,7 @@ defmodule Domain.Auth.Adapters.OpenIDConnect do with {:ok, _provider_identifier, _email, adapter_state} <- fetch_state(provider, token_params, identifier_claim) do - Provider.Query.not_deleted() + Provider.Query.all() |> Provider.Query.by_id(provider.id) |> Repo.fetch_and_update(Provider.Query, with: fn provider -> @@ -215,7 +215,7 @@ defmodule Domain.Auth.Adapters.OpenIDConnect do ) else {:error, :expired_token} -> - Provider.Query.not_deleted() + Provider.Query.all() |> Provider.Query.by_id(provider.id) |> Repo.fetch_and_update(Provider.Query, with: fn provider -> @@ -228,7 +228,7 @@ defmodule Domain.Auth.Adapters.OpenIDConnect do {:error, :expired} {:error, :invalid_token} -> - Provider.Query.not_deleted() + Provider.Query.all() |> Provider.Query.by_id(provider.id) |> Repo.fetch_and_update(Provider.Query, with: fn provider -> @@ -253,7 +253,7 @@ defmodule Domain.Auth.Adapters.OpenIDConnect do with {:ok, _provider_identifier, _email, identity_state} <- fetch_state(identity.provider, token_params, identifier_claim) do - Identity.Query.not_deleted() + Identity.Query.all() |> Identity.Query.by_id(identity.id) |> Repo.fetch_and_update(Identity.Query, with: &Identity.Changeset.update_identity_provider_state(&1, identity_state) diff --git a/elixir/apps/domain/lib/domain/auth/identity.ex b/elixir/apps/domain/lib/domain/auth/identity.ex index e7d1fe349..ac74a4f8c 100644 --- a/elixir/apps/domain/lib/domain/auth/identity.ex +++ b/elixir/apps/domain/lib/domain/auth/identity.ex @@ -23,14 +23,10 @@ defmodule Domain.Auth.Identity do field :created_by, Ecto.Enum, values: ~w[system provider identity]a field :created_by_subject, :map - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :clients, Domain.Clients.Client, where: [deleted_at: nil] + has_many :clients, Domain.Clients.Client - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :tokens, Domain.Tokens.Token, foreign_key: :identity_id, where: [deleted_at: nil] + has_many :tokens, Domain.Tokens.Token, foreign_key: :identity_id - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps(updated_at: false) end end diff --git a/elixir/apps/domain/lib/domain/auth/identity/changeset.ex b/elixir/apps/domain/lib/domain/auth/identity/changeset.ex index b11496f2d..6d9e53a6a 100644 --- a/elixir/apps/domain/lib/domain/auth/identity/changeset.ex +++ b/elixir/apps/domain/lib/domain/auth/identity/changeset.ex @@ -60,7 +60,6 @@ defmodule Domain.Auth.Identity.Changeset do Actors.Actor.Changeset.sync(actor, attrs) end ) - |> put_change(:deleted_at, nil) |> changeset() end @@ -82,15 +81,6 @@ defmodule Domain.Auth.Identity.Changeset do |> put_change(:provider_virtual_state, virtual_state) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def delete_identity(%Identity{} = identity) do - identity - |> change() - |> put_change(:provider_state, %{}) - |> put_change(:provider_virtual_state, %{}) - |> put_default_value(:deleted_at, DateTime.utc_now()) - end - defp maybe_put_email_from_identifier(changeset) do identifier = get_field(changeset, :provider_identifier) email = get_field(changeset, :email) diff --git a/elixir/apps/domain/lib/domain/auth/identity/query.ex b/elixir/apps/domain/lib/domain/auth/identity/query.ex index 420136217..7acf712ce 100644 --- a/elixir/apps/domain/lib/domain/auth/identity/query.ex +++ b/elixir/apps/domain/lib/domain/auth/identity/query.ex @@ -6,26 +6,11 @@ defmodule Domain.Auth.Identity.Query do from(identities in Domain.Auth.Identity, as: :identities) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def not_deleted do - all() - |> where([identities: identities], is_nil(identities.deleted_at)) - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def deleted do - all() - |> where([identities: identities], not is_nil(identities.deleted_at)) - end - - # TODO: Update after `deleted_at` is removed from DB - def not_disabled(queryable \\ not_deleted()) do + def not_disabled(queryable \\ all()) do queryable |> with_assoc(:inner, :actor) - |> where([actor: actor], is_nil(actor.deleted_at)) |> where([actor: actor], is_nil(actor.disabled_at)) |> with_assoc(:inner, :provider) - |> where([provider: provider], is_nil(provider.deleted_at)) |> where([provider: provider], is_nil(provider.disabled_at)) end @@ -152,18 +137,6 @@ defmodule Domain.Auth.Identity.Query do }) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def delete(queryable) do - queryable - |> Ecto.Query.select([identities: identities], identities) - |> Ecto.Query.update([identities: identities], - set: [ - deleted_at: fragment("COALESCE(?, timezone('UTC', NOW()))", identities.deleted_at), - provider_state: ^%{} - ] - ) - end - def with_preloaded_assoc(queryable, type \\ :left, assoc) do queryable |> with_assoc(type, assoc) @@ -211,7 +184,6 @@ defmodule Domain.Auth.Identity.Query do WHERE ai.account_id = $#{account_id} AND ai.provider_id = $#{provider_id} AND ai.provider_identifier IN (SELECT provider_identifier FROM input_data) - AND ai.deleted_at IS NULL ), actors_to_create AS ( SELECT @@ -271,7 +243,6 @@ defmodule Domain.Auth.Identity.Query do FROM all_actor_mappings aam LEFT JOIN existing_identities ei ON ei.provider_identifier = aam.provider_identifier ON CONFLICT (account_id, provider_id, provider_identifier) - WHERE deleted_at IS NULL DO UPDATE SET email = EXCLUDED.email, provider_state = COALESCE(auth_identities.provider_state, '{}'::jsonb) || diff --git a/elixir/apps/domain/lib/domain/auth/identity/sync.ex b/elixir/apps/domain/lib/domain/auth/identity/sync.ex index b2cbe496e..5209429bf 100644 --- a/elixir/apps/domain/lib/domain/auth/identity/sync.ex +++ b/elixir/apps/domain/lib/domain/auth/identity/sync.ex @@ -50,7 +50,6 @@ defmodule Domain.Auth.Identity.Sync do {:ok, identities} end - # TODO: Update after `deleted_at` is removed from DB defp plan_identities_update(identities, provider_identifiers) do {insert, update, delete} = Enum.reduce( @@ -59,15 +58,10 @@ defmodule Domain.Auth.Identity.Sync do fn identity, {insert, update, delete} -> insert = insert -- [identity.provider_identifier] - cond do - identity.provider_identifier in provider_identifiers -> - {insert, [identity.provider_identifier] ++ update, delete} - - not is_nil(identity.deleted_at) -> - {insert, update, delete} - - true -> - {insert, update, [identity.provider_identifier] ++ delete} + if identity.provider_identifier in provider_identifiers do + {insert, [identity.provider_identifier] ++ update, delete} + else + {insert, update, [identity.provider_identifier] ++ delete} end end ) @@ -146,7 +140,6 @@ defmodule Domain.Auth.Identity.Sync do end) end - # TODO: Update after `deleted_at` is removed from DB defp update_identities_and_actors( identities, attrs_by_provider_identifier, @@ -159,19 +152,7 @@ defmodule Domain.Auth.Identity.Sync do end) |> Repo.preload(:actor) |> Enum.reduce(%{}, fn identity, acc -> - acc_identity = Map.get(acc, identity.provider_identifier) - - # make sure that deleted identities have the least priority in case of conflicts - cond do - is_nil(acc_identity) -> - Map.put(acc, identity.provider_identifier, identity) - - is_nil(acc_identity.deleted_at) -> - acc - - true -> - Map.put(acc, identity.provider_identifier, identity) - end + Map.put(acc, identity.provider_identifier, identity) end) provider_identifiers_to_update diff --git a/elixir/apps/domain/lib/domain/auth/provider.ex b/elixir/apps/domain/lib/domain/auth/provider.ex index 41e739677..619184bbf 100644 --- a/elixir/apps/domain/lib/domain/auth/provider.ex +++ b/elixir/apps/domain/lib/domain/auth/provider.ex @@ -14,9 +14,8 @@ defmodule Domain.Auth.Provider do belongs_to :account, Domain.Accounts.Account - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from DB - has_many :actor_groups, Domain.Actors.Group, where: [deleted_at: nil] - has_many :identities, Domain.Auth.Identity, where: [deleted_at: nil] + has_many :actor_groups, Domain.Actors.Group + has_many :identities, Domain.Auth.Identity field :created_by, Ecto.Enum, values: ~w[system identity actor]a field :created_by_subject, :map @@ -29,8 +28,6 @@ defmodule Domain.Auth.Provider do field :disabled_at, :utc_datetime_usec - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec field :assigned_default_at, :utc_datetime_usec timestamps() end diff --git a/elixir/apps/domain/lib/domain/auth/provider/changeset.ex b/elixir/apps/domain/lib/domain/auth/provider/changeset.ex index ef365c904..b79f7b232 100644 --- a/elixir/apps/domain/lib/domain/auth/provider/changeset.ex +++ b/elixir/apps/domain/lib/domain/auth/provider/changeset.ex @@ -4,10 +4,9 @@ defmodule Domain.Auth.Provider.Changeset do alias Domain.Auth.{Subject, Provider, Adapters} @create_fields ~w[id name adapter provisioner adapter_config adapter_state disabled_at assigned_default_at]a - # TODO: HARD-DELETE - Update after `deleted_at` is removed from DB @update_fields ~w[name adapter_config last_syncs_failed last_sync_error sync_disabled_at sync_error_emailed_at - adapter_state provisioner disabled_at deleted_at]a + adapter_state provisioner disabled_at]a @required_fields ~w[name adapter adapter_config provisioner]a def create(account, attrs, %Subject{} = subject) do @@ -131,11 +130,4 @@ defmodule Domain.Auth.Provider.Changeset do |> change() |> put_change(:disabled_at, nil) end - - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def soft_delete_provider(%Provider{} = provider) do - provider - |> change() - |> put_default_value(:deleted_at, DateTime.utc_now()) - end end diff --git a/elixir/apps/domain/lib/domain/auth/provider/query.ex b/elixir/apps/domain/lib/domain/auth/provider/query.ex index c0b4a2711..922121943 100644 --- a/elixir/apps/domain/lib/domain/auth/provider/query.ex +++ b/elixir/apps/domain/lib/domain/auth/provider/query.ex @@ -5,13 +5,7 @@ defmodule Domain.Auth.Provider.Query do from(provider in Domain.Auth.Provider, as: :providers) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from the DB - def not_deleted do - all() - |> where([providers: providers], is_nil(providers.deleted_at)) - end - - def not_disabled(queryable \\ not_deleted()) do + def not_disabled(queryable \\ all()) do where(queryable, [providers: providers], is_nil(providers.disabled_at)) end diff --git a/elixir/apps/domain/lib/domain/clients.ex b/elixir/apps/domain/lib/domain/clients.ex index 4b88a6569..744326164 100644 --- a/elixir/apps/domain/lib/domain/clients.ex +++ b/elixir/apps/domain/lib/domain/clients.ex @@ -18,13 +18,13 @@ defmodule Domain.Clients do end def count_by_account_id(account_id) do - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_account_id(account_id) |> Repo.aggregate(:count) end def count_1m_active_users_for_account(%Accounts.Account{} = account) do - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_account_id(account.id) |> Client.Query.by_last_seen_within(1, "month") |> Client.Query.select_distinct_actor_id() @@ -34,13 +34,13 @@ defmodule Domain.Clients do end def count_by_actor_id(actor_id) do - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_actor_id(actor_id) |> Repo.aggregate(:count) end def count_incompatible_for(account, gateway_version) do - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_account_id(account.id) |> Client.Query.by_last_seen_within(1, "week") |> Client.Query.by_incompatible_for(gateway_version) @@ -49,7 +49,7 @@ defmodule Domain.Clients do end def fetch_client_by_id(id, preload: :identity) do - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_id(id) |> Repo.fetch(Client.Query, preload: :identity) end @@ -75,7 +75,7 @@ defmodule Domain.Clients do end def fetch_client_by_id!(id, opts \\ []) do - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_id(id) |> Repo.fetch!(Client.Query, opts) end @@ -89,7 +89,7 @@ defmodule Domain.Clients do ]} with :ok <- Auth.ensure_has_permissions(subject, required_permissions) do - Client.Query.not_deleted() + Client.Query.all() |> Authorizer.for_subject(subject) |> Repo.list(Client.Query, opts) end @@ -109,7 +109,7 @@ defmodule Domain.Clients do with :ok <- Auth.ensure_has_permissions(subject, required_permissions), true <- Repo.valid_uuid?(actor_id) do - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_actor_id(actor_id) |> Authorizer.for_subject(subject) |> Repo.list(Client.Query, opts) @@ -196,7 +196,7 @@ defmodule Domain.Clients do def update_client(%Client{} = client, attrs, %Auth.Subject{} = subject) do with :ok <- Authorizer.ensure_has_access_to(client, subject) do - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_id(client.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Client.Query, @@ -209,7 +209,7 @@ defmodule Domain.Clients do def verify_client(%Client{} = client, %Auth.Subject{} = subject) do with :ok <- Authorizer.ensure_has_access_to(client, subject), :ok <- Auth.ensure_has_permissions(subject, Authorizer.verify_clients_permission()) do - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_id(client.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Client.Query, @@ -222,7 +222,7 @@ defmodule Domain.Clients do def remove_client_verification(%Client{} = client, %Auth.Subject{} = subject) do with :ok <- Authorizer.ensure_has_access_to(client, subject), :ok <- Auth.ensure_has_permissions(subject, Authorizer.verify_clients_permission()) do - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_id(client.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Client.Query, @@ -240,7 +240,7 @@ defmodule Domain.Clients do def delete_clients_for(%Actors.Actor{} = actor, %Auth.Subject{} = subject) do queryable = - Client.Query.not_deleted() + Client.Query.all() |> Client.Query.by_actor_id(actor.id) |> Client.Query.by_account_id(actor.account_id) @@ -252,7 +252,6 @@ defmodule Domain.Clients do end end - # TODO: HARD-DELETE # We don't necessarily want to delete associated tokens when deleting a client because # that token could be a multi-owner token in the case of a headless client. # Instead we need to introduce the concept of ephemeral clients/gateways and permanent ones. diff --git a/elixir/apps/domain/lib/domain/clients/client.ex b/elixir/apps/domain/lib/domain/clients/client.ex index 74ce9e034..3c26298da 100644 --- a/elixir/apps/domain/lib/domain/clients/client.ex +++ b/elixir/apps/domain/lib/domain/clients/client.ex @@ -28,7 +28,6 @@ defmodule Domain.Clients.Client do verified_at: DateTime.t() | nil, verified_by: :system | :actor | :identity | nil, verified_by_subject: map() | nil, - deleted_at: DateTime.t() | nil, inserted_at: DateTime.t(), updated_at: DateTime.t() } @@ -70,8 +69,6 @@ defmodule Domain.Clients.Client do field :verified_by, Ecto.Enum, values: [:system, :actor, :identity] field :verified_by_subject, :map - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/clients/client/changeset.ex b/elixir/apps/domain/lib/domain/clients/client/changeset.ex index 76d0dffcb..9b6d4dcde 100644 --- a/elixir/apps/domain/lib/domain/clients/client/changeset.ex +++ b/elixir/apps/domain/lib/domain/clients/client/changeset.ex @@ -12,9 +12,8 @@ defmodule Domain.Clients.Client.Changeset do # WireGuard base64-encoded string length @key_length 44 - # TODO: Update or remove after `deleted_at` is removed from DB def upsert_conflict_target, - do: {:unsafe_fragment, ~s/(account_id, actor_id, external_id) WHERE deleted_at IS NULL/} + do: {:unsafe_fragment, ~s/(account_id, actor_id, external_id)/} def upsert_on_conflict do Clients.Client.Query.all() diff --git a/elixir/apps/domain/lib/domain/clients/client/query.ex b/elixir/apps/domain/lib/domain/clients/client/query.ex index f5de0782b..d6290233a 100644 --- a/elixir/apps/domain/lib/domain/clients/client/query.ex +++ b/elixir/apps/domain/lib/domain/clients/client/query.ex @@ -5,12 +5,6 @@ defmodule Domain.Clients.Client.Query do from(clients in Domain.Clients.Client, as: :clients) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def not_deleted do - all() - |> where([clients: clients], is_nil(clients.deleted_at)) - end - def by_id(queryable, id) do where(queryable, [clients: clients], clients.id == ^id) end @@ -45,7 +39,7 @@ defmodule Domain.Clients.Client.Query do |> distinct(true) end - def count_clients_by_actor_id(queryable \\ not_deleted()) do + def count_clients_by_actor_id(queryable \\ all()) do queryable |> group_by([clients: clients], clients.actor_id) |> select([clients: clients], %{ @@ -67,28 +61,13 @@ defmodule Domain.Clients.Client.Query do ) end - def returning_not_deleted(queryable) do - select(queryable, [clients: clients], clients) - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from the DB - def soft_delete(queryable) do - queryable - |> Ecto.Query.select([clients: clients], clients) - |> Ecto.Query.update([clients: clients], - set: [ - deleted_at: fragment("COALESCE(?, timezone('UTC', NOW()))", clients.deleted_at) - ] - ) - end - def with_joined_actor(queryable) do with_named_binding(queryable, :actor, fn queryable, binding -> join( queryable, :inner, [clients: clients], - actor in ^Domain.Actors.Actor.Query.not_deleted(), + actor in ^Domain.Actors.Actor.Query.all(), on: clients.actor_id == actor.id, as: ^binding ) diff --git a/elixir/apps/domain/lib/domain/gateways.ex b/elixir/apps/domain/lib/domain/gateways.ex index d7fd9d583..5f75969c1 100644 --- a/elixir/apps/domain/lib/domain/gateways.ex +++ b/elixir/apps/domain/lib/domain/gateways.ex @@ -21,14 +21,14 @@ defmodule Domain.Gateways do end def count_groups_for_account(%Accounts.Account{} = account) do - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.by_account_id(account.id) |> Group.Query.by_managed_by(:account) |> Repo.aggregate(:count) end def fetch_gateway_by_id(id) do - Gateway.Query.not_deleted() + Gateway.Query.all() |> Gateway.Query.by_id(id) |> Repo.fetch(Gateway.Query, []) end @@ -54,7 +54,7 @@ defmodule Domain.Gateways do end def fetch_internet_group(%Accounts.Account{} = account) do - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.by_managed_by(:system) |> Group.Query.by_account_id(account.id) |> Group.Query.by_name("Internet") @@ -63,21 +63,21 @@ defmodule Domain.Gateways do def list_groups(%Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_gateways_permission()) do - Group.Query.not_deleted() + Group.Query.all() |> Authorizer.for_subject(subject) |> Repo.list(Group.Query, opts) end end def all_groups!(%Auth.Subject{} = subject) do - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.by_managed_by(:account) |> Authorizer.for_subject(subject) |> Repo.all() end def all_groups_for_account!(%Accounts.Account{} = account) do - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.by_managed_by(:account) |> Group.Query.by_account_id(account.id) |> Repo.all() @@ -124,7 +124,7 @@ defmodule Domain.Gateways do def update_group(%Group{managed_by: :account} = group, attrs, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_gateways_permission()) do - Group.Query.not_deleted() + Group.Query.all() |> Group.Query.by_id(group.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update( @@ -167,7 +167,7 @@ defmodule Domain.Gateways do def authenticate(encoded_token, %Auth.Context{} = context) when is_binary(encoded_token) do with {:ok, token} <- Tokens.use_token(encoded_token, context), - queryable = Group.Query.not_deleted() |> Group.Query.by_id(token.gateway_group_id), + queryable = Group.Query.all() |> Group.Query.by_id(token.gateway_group_id), {:ok, group} <- Repo.fetch(queryable, Group.Query, []) do {:ok, group, token} else @@ -197,14 +197,14 @@ defmodule Domain.Gateways do end def fetch_gateway_by_id!(id, opts \\ []) do - Gateway.Query.not_deleted() + Gateway.Query.all() |> Gateway.Query.by_id(id) |> Repo.fetch!(Gateway.Query, opts) end def list_gateways(%Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_gateways_permission()) do - Gateway.Query.not_deleted() + Gateway.Query.all() |> Authorizer.for_subject(subject) |> Repo.list(Gateway.Query, opts) end @@ -213,7 +213,7 @@ defmodule Domain.Gateways do def all_gateways_for_account!(%Account{} = account, opts \\ []) do {preload, _opts} = Keyword.pop(opts, :preload, []) - Gateway.Query.not_deleted() + Gateway.Query.all() |> Gateway.Query.by_account_id(account.id) |> Repo.all() |> Repo.preload(preload) @@ -265,7 +265,7 @@ defmodule Domain.Gateways do |> Map.keys() gateways = - Gateway.Query.not_deleted() + Gateway.Query.all() |> Gateway.Query.by_ids(connected_gateway_ids) |> Gateway.Query.by_account_id(subject.account.id) |> Gateway.Query.by_resource_id(resource_id) @@ -314,7 +314,7 @@ defmodule Domain.Gateways do def update_gateway(%Gateway{} = gateway, attrs, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_gateways_permission()) do - Gateway.Query.not_deleted() + Gateway.Query.all() |> Gateway.Query.by_id(gateway.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Gateway.Query, @@ -324,19 +324,6 @@ defmodule Domain.Gateways do end end - # TODO: HARD-DELETE - Remove this once deleted_at field is gone - def soft_delete_gateway(%Gateway{} = gateway, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_gateways_permission()) do - Gateway.Query.not_deleted() - |> Gateway.Query.by_id(gateway.id) - |> Authorizer.for_subject(subject) - |> Repo.fetch_and_update(Gateway.Query, - with: &Gateway.Changeset.soft_delete/1, - preload: [:online?] - ) - end - end - def delete_gateway(%Gateway{} = gateway, %Auth.Subject{} = subject) do with :ok <- Authorizer.ensure_has_access_to(gateway, subject) do Repo.delete(gateway) diff --git a/elixir/apps/domain/lib/domain/gateways/gateway.ex b/elixir/apps/domain/lib/domain/gateways/gateway.ex index 47bdf4189..0a04eb222 100644 --- a/elixir/apps/domain/lib/domain/gateways/gateway.ex +++ b/elixir/apps/domain/lib/domain/gateways/gateway.ex @@ -20,7 +20,6 @@ defmodule Domain.Gateways.Gateway do online?: boolean(), account_id: Ecto.UUID.t(), group_id: Ecto.UUID.t(), - deleted_at: DateTime.t(), inserted_at: DateTime.t(), updated_at: DateTime.t() } @@ -50,8 +49,6 @@ defmodule Domain.Gateways.Gateway do belongs_to :account, Domain.Accounts.Account belongs_to :group, Domain.Gateways.Group - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/gateways/gateway/changeset.ex b/elixir/apps/domain/lib/domain/gateways/gateway/changeset.ex index e352ba372..96a475aed 100644 --- a/elixir/apps/domain/lib/domain/gateways/gateway/changeset.ex +++ b/elixir/apps/domain/lib/domain/gateways/gateway/changeset.ex @@ -27,9 +27,8 @@ defmodule Domain.Gateways.Gateway.Changeset do # WireGuard base64-encoded string length @key_length 44 - # TODO: Update or remove after `deleted_at` is removed from DB def upsert_conflict_target, - do: {:unsafe_fragment, ~s/(account_id, group_id, external_id) WHERE deleted_at IS NULL/} + do: {:unsafe_fragment, ~s/(account_id, group_id, external_id)/} def upsert_on_conflict, do: {:replace, @conflict_replace_fields} @@ -72,13 +71,6 @@ defmodule Domain.Gateways.Gateway.Changeset do |> validate_required(@required_fields) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def soft_delete(%Gateways.Gateway{} = gateway) do - gateway - |> change() - |> put_default_value(:deleted_at, DateTime.utc_now()) - end - defp changeset(changeset) do changeset |> trim_change(:name) diff --git a/elixir/apps/domain/lib/domain/gateways/gateway/query.ex b/elixir/apps/domain/lib/domain/gateways/gateway/query.ex index cbbfb224a..5066f73e9 100644 --- a/elixir/apps/domain/lib/domain/gateways/gateway/query.ex +++ b/elixir/apps/domain/lib/domain/gateways/gateway/query.ex @@ -5,12 +5,6 @@ defmodule Domain.Gateways.Gateway.Query do from(gateways in Domain.Gateways.Gateway, as: :gateways) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def not_deleted do - all() - |> where([gateways: gateways], is_nil(gateways.deleted_at)) - end - def by_id(queryable, id) do where(queryable, [gateways: gateways], gateways.id == ^id) end @@ -37,10 +31,6 @@ defmodule Domain.Gateways.Gateway.Query do |> where([connections: connections], connections.resource_id == ^resource_id) end - def returning_not_deleted(queryable) do - select(queryable, [gateways: gateways], gateways) - end - def with_joined_connections(queryable) do with_named_binding(queryable, :connections, fn queryable, binding -> queryable @@ -92,11 +82,6 @@ defmodule Domain.Gateways.Gateway.Query do name: :ids, type: {:list, {:string, :uuid}}, fun: &filter_by_ids/2 - }, - %Domain.Repo.Filter{ - name: :deleted?, - type: :boolean, - fun: &filter_deleted/1 } ] @@ -107,9 +92,4 @@ defmodule Domain.Gateways.Gateway.Query do def filter_by_ids(queryable, ids) do {queryable, dynamic([gateways: gateways], gateways.id in ^ids)} end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def filter_deleted(queryable) do - {queryable, dynamic([gateways: gateways], not is_nil(gateways.deleted_at))} - end end diff --git a/elixir/apps/domain/lib/domain/gateways/group.ex b/elixir/apps/domain/lib/domain/gateways/group.ex index fb069045d..00748cd97 100644 --- a/elixir/apps/domain/lib/domain/gateways/group.ex +++ b/elixir/apps/domain/lib/domain/gateways/group.ex @@ -8,7 +8,6 @@ defmodule Domain.Gateways.Group do account_id: Ecto.UUID.t(), created_by: :actor | :identity | :system, created_by_subject: map(), - deleted_at: DateTime.t() | nil, inserted_at: DateTime.t(), updated_at: DateTime.t() } @@ -19,21 +18,15 @@ defmodule Domain.Gateways.Group do field :managed_by, Ecto.Enum, values: ~w[account system]a belongs_to :account, Domain.Accounts.Account - # TODO: HARD-DELETE - Remove `where` after `deleted_at` column is remove - has_many :gateways, Domain.Gateways.Gateway, foreign_key: :group_id, where: [deleted_at: nil] + has_many :gateways, Domain.Gateways.Gateway, foreign_key: :group_id - # TODO: HARD-DELETE - Remove `where` after `deleted_at` column is remove - has_many :tokens, Domain.Tokens.Token, - foreign_key: :gateway_group_id, - where: [deleted_at: nil] + has_many :tokens, Domain.Tokens.Token, foreign_key: :gateway_group_id has_many :connections, Domain.Resources.Connection, foreign_key: :gateway_group_id field :created_by, Ecto.Enum, values: ~w[actor identity system]a field :created_by_subject, :map - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/gateways/group/changeset.ex b/elixir/apps/domain/lib/domain/gateways/group/changeset.ex index b689e6185..870071b0d 100644 --- a/elixir/apps/domain/lib/domain/gateways/group/changeset.ex +++ b/elixir/apps/domain/lib/domain/gateways/group/changeset.ex @@ -39,11 +39,4 @@ defmodule Domain.Gateways.Group.Changeset do |> validate_length(:name, min: 1, max: 64) |> unique_constraint(:name, name: :gateway_groups_account_id_name_managed_by_index) end - - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from the DB - def delete(%Gateways.Group{} = group) do - group - |> change() - |> put_default_value(:deleted_at, DateTime.utc_now()) - end end diff --git a/elixir/apps/domain/lib/domain/gateways/group/query.ex b/elixir/apps/domain/lib/domain/gateways/group/query.ex index 2f52b9ed3..b7a449d66 100644 --- a/elixir/apps/domain/lib/domain/gateways/group/query.ex +++ b/elixir/apps/domain/lib/domain/gateways/group/query.ex @@ -5,12 +5,6 @@ defmodule Domain.Gateways.Group.Query do from(groups in Domain.Gateways.Group, as: :groups) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def not_deleted do - all() - |> where([groups: groups], is_nil(groups.deleted_at)) - end - def by_id(queryable, id) do where(queryable, [groups: groups], groups.id == ^id) end @@ -45,11 +39,6 @@ defmodule Domain.Gateways.Group.Query do @impl Domain.Repo.Query def filters, do: [ - %Domain.Repo.Filter{ - name: :deleted?, - type: :boolean, - fun: &filter_deleted/1 - }, %Domain.Repo.Filter{ name: :managed_by, type: :string, @@ -57,11 +46,6 @@ defmodule Domain.Gateways.Group.Query do } ] - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def filter_deleted(queryable) do - {queryable, dynamic([groups: groups], not is_nil(groups.deleted_at))} - end - def filter_managed_by(queryable, managed_by) do {queryable, dynamic([groups: groups], groups.managed_by == ^managed_by)} end diff --git a/elixir/apps/domain/lib/domain/ops.ex b/elixir/apps/domain/lib/domain/ops.ex index 15c2f6b43..baae8edfe 100644 --- a/elixir/apps/domain/lib/domain/ops.ex +++ b/elixir/apps/domain/lib/domain/ops.ex @@ -109,7 +109,7 @@ defmodule Domain.Ops do 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.all() |> Domain.Accounts.Account.Query.disabled() |> Domain.Accounts.Account.Query.by_id(id) |> Domain.Repo.one!() diff --git a/elixir/apps/domain/lib/domain/policies.ex b/elixir/apps/domain/lib/domain/policies.ex index 648d270db..1298be625 100644 --- a/elixir/apps/domain/lib/domain/policies.ex +++ b/elixir/apps/domain/lib/domain/policies.ex @@ -1,6 +1,6 @@ defmodule Domain.Policies do alias Domain.Repo - alias Domain.{Auth, Actors, Cache.Cacheable, Clients, Resources} + alias Domain.{Auth, Cache.Cacheable, Clients} alias Domain.Policies.{Authorizer, Policy, Condition} require Logger @@ -24,26 +24,6 @@ defmodule Domain.Policies do end end - def fetch_policy_by_id_or_persistent_id(id, %Auth.Subject{} = subject, opts \\ []) do - required_permissions = - {:one_of, - [ - Authorizer.manage_policies_permission(), - Authorizer.view_available_policies_permission() - ]} - - with :ok <- Auth.ensure_has_permissions(subject, required_permissions), - true <- Repo.valid_uuid?(id) do - Policy.Query.all() - |> Policy.Query.by_id_or_persistent_id(id) - |> Authorizer.for_subject(subject) - |> Repo.fetch(Policy.Query, opts) - else - false -> {:error, :not_found} - other -> other - end - end - def list_policies(%Auth.Subject{} = subject, opts \\ []) do required_permissions = {:one_of, @@ -53,7 +33,7 @@ defmodule Domain.Policies do ]} with :ok <- Auth.ensure_has_permissions(subject, required_permissions) do - Policy.Query.not_deleted() + Policy.Query.all() |> Authorizer.for_subject(subject) |> Repo.list(Policy.Query, opts) end @@ -108,7 +88,7 @@ defmodule Domain.Policies do def update_policy(%Policy{} = policy, attrs, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_policies_permission()), :ok <- ensure_has_access_to(subject, policy) do - Policy.Query.not_deleted() + Policy.Query.all() |> Policy.Query.by_id(policy.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Policy.Query, @@ -119,7 +99,7 @@ defmodule Domain.Policies do def disable_policy(%Policy{} = policy, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_policies_permission()) do - Policy.Query.not_deleted() + Policy.Query.all() |> Policy.Query.by_id(policy.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Policy.Query, @@ -130,7 +110,7 @@ defmodule Domain.Policies do def enable_policy(%Policy{} = policy, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_policies_permission()) do - Policy.Query.not_deleted() + Policy.Query.all() |> Policy.Query.by_id(policy.id) |> Authorizer.for_subject(subject) |> Repo.fetch_and_update(Policy.Query, @@ -139,120 +119,12 @@ defmodule Domain.Policies do end end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def soft_delete_policy(%Policy{} = policy, %Auth.Subject{} = subject) do - Policy.Query.not_deleted() - |> Policy.Query.by_id(policy.id) - |> soft_delete_policies(subject) - |> case do - {:ok, [policy]} -> {:ok, policy} - {:ok, []} -> {:error, :not_found} - {:error, reason} -> {:error, reason} - end - end - def delete_policy(%Policy{} = policy, %Auth.Subject{} = subject) do with :ok <- Authorizer.ensure_has_access_to(policy, subject) do Repo.delete(policy) end end - # TODO: HARD-DELETE - Should not be needed after hard delete is implemented - def delete_policies_for(%Resources.Resource{} = resource, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_policies_permission()) do - {count, nil} = - Policy.Query.all() - |> Policy.Query.by_resource_id(resource.id) - |> Authorizer.for_subject(subject) - |> Repo.delete_all() - - {:ok, count} - end - end - - # TODO: HARD-DELETE - Should not be needed after hard delete is implemented - def delete_policies_for(%Actors.Group{} = actor_group, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_policies_permission()) do - {count, nil} = - Policy.Query.all() - |> Policy.Query.by_actor_group_id(actor_group.id) - |> Authorizer.for_subject(subject) - |> Repo.delete_all() - - {:ok, count} - end - end - - # TODO: HARD-DELETE - Should not be needed after hard delete is implemented - def delete_policies_for(%Auth.Provider{} = provider, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_policies_permission()) do - {count, nil} = - Policy.Query.all() - |> Policy.Query.by_actor_group_provider_id(provider.id) - |> Authorizer.for_subject(subject) - |> Repo.delete_all() - - {:ok, count} - end - end - - # TODO: HARD-DELETE - Should not be needed after hard delete is implemented - def delete_policies_for(%Actors.Group{} = actor_group) do - {count, nil} = - Policy.Query.all() - |> Policy.Query.by_actor_group_id(actor_group.id) - |> Repo.delete_all() - - {:ok, count} - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def soft_delete_policies_for(%Resources.Resource{} = resource, %Auth.Subject{} = subject) do - Policy.Query.not_deleted() - |> Policy.Query.by_resource_id(resource.id) - |> soft_delete_policies(subject) - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def soft_delete_policies_for(%Actors.Group{} = actor_group, %Auth.Subject{} = subject) do - Policy.Query.not_deleted() - |> Policy.Query.by_actor_group_id(actor_group.id) - |> soft_delete_policies(subject) - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def soft_delete_policies_for(%Auth.Provider{} = provider, %Auth.Subject{} = subject) do - Policy.Query.not_deleted() - |> Policy.Query.by_actor_group_provider_id(provider.id) - |> soft_delete_policies(subject) - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def soft_delete_policies_for(%Actors.Group{} = actor_group) do - Policy.Query.not_deleted() - |> Policy.Query.by_actor_group_id(actor_group.id) - |> soft_delete_policies() - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - defp soft_delete_policies(queryable, subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_policies_permission()) do - queryable - |> Authorizer.for_subject(subject) - |> soft_delete_policies() - end - end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - defp soft_delete_policies(queryable) do - {_count, policies} = - queryable - |> Policy.Query.delete() - |> Repo.update_all([]) - - {:ok, policies} - end - def filter_by_conforming_policies_for_client(policies, %Clients.Client{} = client) do Enum.filter(policies, fn policy -> policy.conditions diff --git a/elixir/apps/domain/lib/domain/policies/policy.ex b/elixir/apps/domain/lib/domain/policies/policy.ex index d76ad77d4..ae53d4de6 100644 --- a/elixir/apps/domain/lib/domain/policies/policy.ex +++ b/elixir/apps/domain/lib/domain/policies/policy.ex @@ -3,7 +3,6 @@ defmodule Domain.Policies.Policy do @type t :: %__MODULE__{ id: Ecto.UUID.t(), - persistent_id: Ecto.UUID.t(), description: String.t() | nil, conditions: [Domain.Policies.Condition.t()], actor_group_id: Ecto.UUID.t(), @@ -11,16 +10,12 @@ defmodule Domain.Policies.Policy do account_id: Ecto.UUID.t(), created_by: :actor | :identity, created_by_subject: map(), - replaced_by_policy_id: Ecto.UUID.t() | nil, disabled_at: DateTime.t() | nil, - deleted_at: DateTime.t() | nil, inserted_at: DateTime.t(), updated_at: DateTime.t() } schema "policies" do - field :persistent_id, Ecto.UUID - field :description, :string embeds_many :conditions, Domain.Policies.Condition, on_replace: :delete @@ -32,13 +27,8 @@ defmodule Domain.Policies.Policy do field :created_by, Ecto.Enum, values: ~w[actor identity]a field :created_by_subject, :map - belongs_to :replaced_by_policy, Domain.Policies.Policy - has_one :replaces_policy, Domain.Policies.Policy, foreign_key: :replaced_by_policy_id - field :disabled_at, :utc_datetime_usec - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/policies/policy/changeset.ex b/elixir/apps/domain/lib/domain/policies/policy/changeset.ex index af21366da..a9bf303a1 100644 --- a/elixir/apps/domain/lib/domain/policies/policy/changeset.ex +++ b/elixir/apps/domain/lib/domain/policies/policy/changeset.ex @@ -15,7 +15,6 @@ defmodule Domain.Policies.Policy.Changeset do |> changeset() |> put_change(:account_id, subject.account.id) |> put_subject_trail(:created_by, subject) - |> put_change(:persistent_id, Ecto.UUID.generate()) end def update(%Policy{} = policy, attrs) do diff --git a/elixir/apps/domain/lib/domain/policies/policy/query.ex b/elixir/apps/domain/lib/domain/policies/policy/query.ex index 15b43bff9..defa87d3e 100644 --- a/elixir/apps/domain/lib/domain/policies/policy/query.ex +++ b/elixir/apps/domain/lib/domain/policies/policy/query.ex @@ -5,17 +5,11 @@ defmodule Domain.Policies.Policy.Query do from(policies in Domain.Policies.Policy, as: :policies) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def not_deleted do - all() - |> where([policies: policies], is_nil(policies.deleted_at)) - end - - def not_disabled(queryable \\ not_deleted()) do + def not_disabled(queryable \\ all()) do where(queryable, [policies: policies], is_nil(policies.disabled_at)) end - def disabled(queryable \\ not_deleted()) do + def disabled(queryable \\ all()) do where(queryable, [policies: policies], not is_nil(policies.disabled_at)) end @@ -23,14 +17,6 @@ defmodule Domain.Policies.Policy.Query do where(queryable, [policies: policies], policies.id == ^id) end - def by_id_or_persistent_id(queryable, id) do - where(queryable, [policies: policies], policies.id == ^id) - |> or_where( - [policies: policies], - policies.persistent_id == ^id and is_nil(policies.replaced_by_policy_id) - ) - end - def by_account_id(queryable, account_id) do where(queryable, [policies: policies], policies.account_id == ^account_id) end @@ -85,17 +71,6 @@ defmodule Domain.Policies.Policy.Query do }) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def delete(queryable) do - queryable - |> Ecto.Query.select([policies: policies], policies) - |> Ecto.Query.update([policies: policies], - set: [ - deleted_at: fragment("COALESCE(?, timezone('UTC', NOW()))", policies.deleted_at) - ] - ) - end - def with_joined_actor_group(queryable) do with_named_binding(queryable, :actor_group, fn queryable, binding -> join(queryable, :inner, [policies: policies], actor_group in assoc(policies, ^binding), @@ -192,11 +167,6 @@ defmodule Domain.Policies.Policy.Query do {"Disabled", "disabled"} ], fun: &filter_by_status/2 - }, - %Domain.Repo.Filter{ - name: :deleted?, - type: :boolean, - fun: &filter_deleted/1 } ] @@ -252,9 +222,4 @@ defmodule Domain.Policies.Policy.Query do def filter_by_status(queryable, "disabled") do {queryable, dynamic([policies: policies], not is_nil(policies.disabled_at))} end - - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def filter_deleted(queryable) do - {queryable, dynamic([policies: policies], not is_nil(policies.deleted_at))} - end end diff --git a/elixir/apps/domain/lib/domain/relays.ex b/elixir/apps/domain/lib/domain/relays.ex index 2e9c4759d..9c1df4995 100644 --- a/elixir/apps/domain/lib/domain/relays.ex +++ b/elixir/apps/domain/lib/domain/relays.ex @@ -39,7 +39,7 @@ defmodule Domain.Relays do def list_groups(%Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_relays_permission()) do - Group.Query.not_deleted() + Group.Query.all() |> Authorizer.for_subject(subject) |> Repo.list(Group.Query, opts) end @@ -93,30 +93,6 @@ defmodule Domain.Relays do end end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def soft_delete_group(%Group{} = group, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_relays_permission()) do - Group.Query.not_deleted() - |> Group.Query.by_id(group.id) - |> Authorizer.for_subject(subject) - |> Group.Query.by_account_id(subject.account.id) - |> Repo.fetch_and_update(Group.Query, - with: fn group -> - {:ok, _tokens} = Tokens.soft_delete_tokens_for(group, subject) - - {_count, _} = - Relay.Query.not_deleted() - |> Relay.Query.by_group_id(group.id) - |> Repo.update_all(set: [deleted_at: DateTime.utc_now()]) - - Group.Changeset.delete(group) - end, - # TODO: Remove self-hosted relays - after_commit: &disconnect_relays_in_group/1 - ) - end - end - def create_token(%Group{account_id: nil} = group, attrs) do attrs = Map.merge(attrs, %{ @@ -153,7 +129,7 @@ defmodule Domain.Relays do def authenticate(encoded_token, %Auth.Context{} = context) when is_binary(encoded_token) do with {:ok, token} <- Tokens.use_token(encoded_token, context), - queryable = Group.Query.not_deleted() |> Group.Query.by_id(token.relay_group_id), + queryable = Group.Query.all() |> Group.Query.by_id(token.relay_group_id), {:ok, group} <- Repo.fetch(queryable, Group.Query, []) do {:ok, group, token} else @@ -176,14 +152,14 @@ defmodule Domain.Relays do end def fetch_relay_by_id!(id, opts \\ []) do - Relay.Query.not_deleted() + Relay.Query.all() |> Relay.Query.by_id(id) |> Repo.fetch!(Relay.Query, opts) end def list_relays(%Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_relays_permission()) do - Relay.Query.not_deleted() + Relay.Query.all() |> Authorizer.for_subject(subject) |> Repo.list(Relay.Query, opts) end @@ -257,7 +233,7 @@ defmodule Domain.Relays do connected_relay_ids = Map.keys(connected_relays) -- except_ids relays = - Relay.Query.not_deleted() + Relay.Query.all() |> Relay.Query.by_ids(connected_relay_ids) |> Relay.Query.global_or_by_account_id(account_id) # |> Relay.Query.by_last_seen_at_greater_than(5, "second", :ago) @@ -315,20 +291,6 @@ defmodule Domain.Relays do end end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def soft_delete_relay(%Relay{} = relay, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_relays_permission()) do - Relay.Query.not_deleted() - |> Relay.Query.by_id(relay.id) - |> Authorizer.for_subject(subject) - |> Repo.fetch_and_update(Relay.Query, - with: &Relay.Changeset.delete/1, - # TODO: Remove self-hosted relays - after_commit: &disconnect_relay/1 - ) - end - end - @doc """ Selects 3 nearest relays to the given location and then picks one of them randomly. """ diff --git a/elixir/apps/domain/lib/domain/relays/group.ex b/elixir/apps/domain/lib/domain/relays/group.ex index d4d82ba01..cc73f5268 100644 --- a/elixir/apps/domain/lib/domain/relays/group.ex +++ b/elixir/apps/domain/lib/domain/relays/group.ex @@ -5,15 +5,12 @@ defmodule Domain.Relays.Group do field :name, :string belongs_to :account, Domain.Accounts.Account - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :relays, Domain.Relays.Relay, foreign_key: :group_id, where: [deleted_at: nil] - has_many :tokens, Domain.Tokens.Token, foreign_key: :relay_group_id, where: [deleted_at: nil] + has_many :relays, Domain.Relays.Relay, foreign_key: :group_id + has_many :tokens, Domain.Tokens.Token, foreign_key: :relay_group_id field :created_by, Ecto.Enum, values: ~w[system identity]a field :created_by_subject, :map - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/relays/group/changeset.ex b/elixir/apps/domain/lib/domain/relays/group/changeset.ex index 0d7c8da2c..aad4d0f69 100644 --- a/elixir/apps/domain/lib/domain/relays/group/changeset.ex +++ b/elixir/apps/domain/lib/domain/relays/group/changeset.ex @@ -36,11 +36,4 @@ defmodule Domain.Relays.Group.Changeset do |> unique_constraint(:name, name: :relay_groups_name_index) |> unique_constraint(:name, name: :relay_groups_account_id_name_index) end - - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from the DB - def delete(%Relays.Group{} = group) do - group - |> change() - |> put_default_value(:deleted_at, DateTime.utc_now()) - end end diff --git a/elixir/apps/domain/lib/domain/relays/group/query.ex b/elixir/apps/domain/lib/domain/relays/group/query.ex index 4dfda8fc9..e167a53ce 100644 --- a/elixir/apps/domain/lib/domain/relays/group/query.ex +++ b/elixir/apps/domain/lib/domain/relays/group/query.ex @@ -5,12 +5,6 @@ defmodule Domain.Relays.Group.Query do from(groups in Domain.Relays.Group, as: :groups) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from the DB - def not_deleted do - all() - |> where([groups: groups], is_nil(groups.deleted_at)) - end - def by_id(queryable, id) do where(queryable, [groups: groups], groups.id == ^id) end @@ -47,17 +41,5 @@ defmodule Domain.Relays.Group.Query do ] @impl Domain.Repo.Query - def filters, - do: [ - %Domain.Repo.Filter{ - name: :deleted?, - type: :boolean, - fun: &filter_deleted/1 - } - ] - - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from the DB - def filter_deleted(queryable) do - {queryable, dynamic([groups: groups], not is_nil(groups.deleted_at))} - end + def filters, do: [] end diff --git a/elixir/apps/domain/lib/domain/relays/relay.ex b/elixir/apps/domain/lib/domain/relays/relay.ex index a600f3142..31c2d8820 100644 --- a/elixir/apps/domain/lib/domain/relays/relay.ex +++ b/elixir/apps/domain/lib/domain/relays/relay.ex @@ -25,8 +25,6 @@ defmodule Domain.Relays.Relay do belongs_to :account, Domain.Accounts.Account belongs_to :group, Domain.Relays.Group - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/relays/relay/changeset.ex b/elixir/apps/domain/lib/domain/relays/relay/changeset.ex index faf271ae2..5bc7f1339 100644 --- a/elixir/apps/domain/lib/domain/relays/relay/changeset.ex +++ b/elixir/apps/domain/lib/domain/relays/relay/changeset.ex @@ -21,16 +21,12 @@ defmodule Domain.Relays.Relay.Changeset do last_seen_at updated_at]a - # TODO: HARD-DELETE - Update or remove after `deleted_at` is removed from DB def upsert_conflict_target(%{account_id: nil}) do - {:unsafe_fragment, - ~s/(COALESCE(ipv4, ipv6), port) WHERE deleted_at IS NULL AND account_id IS NULL/} + {:unsafe_fragment, ~s/(COALESCE(ipv4, ipv6), port) WHERE account_id IS NULL/} end - # TODO: HARD-DELETE - Update or remove after `deleted_at` is removed from DB def upsert_conflict_target(%{account_id: _account_id}) do - {:unsafe_fragment, - ~s/(account_id, COALESCE(ipv4, ipv6), port) WHERE deleted_at IS NULL AND account_id IS NOT NULL/} + {:unsafe_fragment, ~s/(account_id, COALESCE(ipv4, ipv6), port) WHERE account_id IS NOT NULL/} end def upsert_on_conflict, do: {:replace, @conflict_replace_fields} @@ -59,13 +55,6 @@ defmodule Domain.Relays.Relay.Changeset do |> put_change(:group_id, group.id) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def delete(%Relays.Relay{} = relay) do - relay - |> change() - |> put_default_value(:deleted_at, DateTime.utc_now()) - end - def put_relay_version(changeset) do with {_data_or_changes, user_agent} when not is_nil(user_agent) <- fetch_field(changeset, :last_seen_user_agent), diff --git a/elixir/apps/domain/lib/domain/relays/relay/query.ex b/elixir/apps/domain/lib/domain/relays/relay/query.ex index b06a8d795..68851dad5 100644 --- a/elixir/apps/domain/lib/domain/relays/relay/query.ex +++ b/elixir/apps/domain/lib/domain/relays/relay/query.ex @@ -5,12 +5,6 @@ defmodule Domain.Relays.Relay.Query do from(relays in Domain.Relays.Relay, as: :relays) end - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def not_deleted do - all() - |> where([relays: relays], is_nil(relays.deleted_at)) - end - def by_id(queryable, id) do where(queryable, [relays: relays], relays.id == ^id) end @@ -55,11 +49,6 @@ defmodule Domain.Relays.Relay.Query do order_by(queryable, [relays: relays], asc_nulls_first: relays.account_id) end - # TODO: HARD-DELETE - Remove or possibly rename after `deleted_at` is removed from DB - def returning_not_deleted(queryable) do - select(queryable, [relays: relays], relays) - end - def with_preloaded_user(queryable) do with_named_binding(queryable, :user, fn queryable, binding -> queryable diff --git a/elixir/apps/domain/lib/domain/resources.ex b/elixir/apps/domain/lib/domain/resources.ex index 860b28447..a4304762a 100644 --- a/elixir/apps/domain/lib/domain/resources.ex +++ b/elixir/apps/domain/lib/domain/resources.ex @@ -24,6 +24,16 @@ defmodule Domain.Resources do end end + def fetch_resource_by_id!(id) do + if Repo.valid_uuid?(id) do + Resource.Query.all() + |> Resource.Query.by_id(id) + |> Repo.one!() + else + {:error, :not_found} + end + end + def fetch_internet_resource(%Accounts.Account{} = account) do Resource.Query.all() |> Resource.Query.by_account_id(account.id) @@ -41,80 +51,20 @@ defmodule Domain.Resources do end end - def fetch_resource_by_id_or_persistent_id(id, %Auth.Subject{} = subject, opts \\ []) do - required_permissions = - {:one_of, - [ - Authorizer.manage_resources_permission(), - Authorizer.view_available_resources_permission() - ]} - - with :ok <- Auth.ensure_has_permissions(subject, required_permissions), - true <- Repo.valid_uuid?(id) do - Resource.Query.all() - |> Resource.Query.by_id_or_persistent_id(id) - |> Authorizer.for_subject(Resource, subject) - |> Repo.fetch(Resource.Query, opts) - else - false -> {:error, :not_found} - other -> other - end - end - - def fetch_active_resource_by_id_or_persistent_id(id, %Auth.Subject{} = subject, opts \\ []) do - required_permissions = - {:one_of, - [ - Authorizer.manage_resources_permission(), - Authorizer.view_available_resources_permission() - ]} - - with :ok <- Auth.ensure_has_permissions(subject, required_permissions), - true <- Repo.valid_uuid?(id) do - Resource.Query.not_deleted() - |> Resource.Query.by_id_or_persistent_id(id) - |> Authorizer.for_subject(Resource, subject) - |> Repo.fetch(Resource.Query, opts) - else - false -> {:error, :not_found} - other -> other - end - end - - def fetch_resource_by_id!(id) do - if Repo.valid_uuid?(id) do - Resource.Query.not_deleted() - |> Resource.Query.by_id(id) - |> Repo.one!() - else - {:error, :not_found} - end - end - def fetch_all_resources_by_ids(ids) do - Resource.Query.not_deleted() + Resource.Query.all() |> Resource.Query.by_id({:in, ids}) |> Repo.all() |> Repo.preload(:gateway_groups) end - def fetch_resource_by_id_or_persistent_id!(id) do - if Repo.valid_uuid?(id) do - Resource.Query.not_deleted() - |> Resource.Query.by_id_or_persistent_id(id) - |> Repo.one!() - else - {:error, :not_found} - end - end - def all_authorized_resources(%Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.view_available_resources_permission()) do {preload, opts} = Keyword.pop(opts, :preload, []) resources = - Resource.Query.not_deleted() + Resource.Query.all() |> Resource.Query.by_account_id(subject.account.id) |> Resource.Query.by_authorized_actor_id(subject.actor.id) |> Resource.Query.with_at_least_one_gateway_group() @@ -126,32 +76,32 @@ defmodule Domain.Resources do end def all_resources!(%Auth.Subject{} = subject) do - Resource.Query.not_deleted() + Resource.Query.all() |> Resource.Query.by_account_id(subject.account.id) |> Resource.Query.filter_features(subject.account) |> Authorizer.for_subject(Resource, subject) |> Repo.all() end - def list_resources(%Auth.Subject{} = subject, opts \\ []) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_resources_permission()) do - Resource.Query.not_deleted() - |> Resource.Query.filter_features(subject.account) - |> Authorizer.for_subject(Resource, subject) - |> Repo.list(Resource.Query, opts) - end - end - def all_resources!(%Auth.Subject{} = subject, opts \\ []) do {preload, _opts} = Keyword.pop(opts, :preload, []) - Resource.Query.not_deleted() + Resource.Query.all() |> Resource.Query.filter_features(subject.account) |> Authorizer.for_subject(Resource, subject) |> Repo.all() |> Repo.preload(preload) end + def list_resources(%Auth.Subject{} = subject, opts \\ []) do + with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_resources_permission()) do + Resource.Query.all() + |> Resource.Query.filter_features(subject.account) + |> Authorizer.for_subject(Resource, subject) + |> Repo.list(Resource.Query, opts) + end + end + def count_resources_for_gateway(%Gateways.Gateway{} = gateway, %Auth.Subject{} = subject) do required_permissions = {:one_of, @@ -162,7 +112,7 @@ defmodule Domain.Resources do with :ok <- Auth.ensure_has_permissions(subject, required_permissions) do count = - Resource.Query.not_deleted() + Resource.Query.all() |> Authorizer.for_subject(Resource, subject) |> Resource.Query.by_gateway_group_id(gateway.group_id) |> Repo.aggregate(:count) @@ -181,7 +131,7 @@ defmodule Domain.Resources do with :ok <- Auth.ensure_has_permissions(subject, required_permissions) do resources = - Resource.Query.not_deleted() + Resource.Query.all() |> Resource.Query.by_account_id(subject.account.id) |> Resource.Query.by_gateway_group_id(gateway.group_id) |> Repo.all() @@ -195,7 +145,7 @@ defmodule Domain.Resources do ids = resources |> Enum.map(& &1.id) |> Enum.uniq() {:ok, peek} = - Resource.Query.not_deleted() + Resource.Query.all() |> Resource.Query.by_id({:in, ids}) |> Authorizer.for_subject(Resource, subject) |> Resource.Query.preload_few_actor_groups_for_each_resource(limit) @@ -249,7 +199,7 @@ defmodule Domain.Resources do def update_resource(%Resource{} = resource, attrs, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_resources_permission()) do - Resource.Query.not_deleted() + Resource.Query.all() |> Resource.Query.by_id(resource.id) |> Authorizer.for_subject(Resource, subject) |> Repo.fetch_and_update(Resource.Query, @@ -272,30 +222,6 @@ defmodule Domain.Resources do end end - # TODO: HARD-DELETE (shouldn't be needed) - def delete_connections_for(%Gateways.Group{} = gateway_group, %Auth.Subject{} = subject) do - Connection.Query.by_gateway_group_id(gateway_group.id) - |> delete_connections(subject) - end - - # TODO: HARD-DELETE (shouldn't be needed) - def delete_connections_for(%Resource{} = resource, %Auth.Subject{} = subject) do - Connection.Query.by_resource_id(resource.id) - |> delete_connections(subject) - end - - # TODO: HARD-DELETE (shouldn't be needed) - defp delete_connections(queryable, subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_resources_permission()) do - {count, nil} = - queryable - |> Authorizer.for_subject(Connection, subject) - |> Repo.delete_all() - - {:ok, count} - end - end - def connected?( resource_id, %Gateways.Gateway{} = gateway diff --git a/elixir/apps/domain/lib/domain/resources/resource.ex b/elixir/apps/domain/lib/domain/resources/resource.ex index 714f33ee7..0be85d7e6 100644 --- a/elixir/apps/domain/lib/domain/resources/resource.ex +++ b/elixir/apps/domain/lib/domain/resources/resource.ex @@ -8,7 +8,6 @@ defmodule Domain.Resources.Resource do @type t :: %__MODULE__{ id: Ecto.UUID.t(), - persistent_id: Ecto.UUID.t() | nil, address: String.t(), address_description: String.t() | nil, name: String.t(), @@ -18,15 +17,11 @@ defmodule Domain.Resources.Resource do account_id: Ecto.UUID.t(), created_by: String.t(), created_by_subject: map(), - replaced_by_resource_id: Ecto.UUID.t() | nil, - deleted_at: DateTime.t() | nil, inserted_at: DateTime.t(), updated_at: DateTime.t() } schema "resources" do - field :persistent_id, Ecto.UUID - field :address, :string field :address_description, :string field :name, :string @@ -41,12 +36,9 @@ defmodule Domain.Resources.Resource do belongs_to :account, Domain.Accounts.Account has_many :connections, Domain.Resources.Connection, on_replace: :delete - # TODO: where doesn't work on join tables so soft-deleted records will be preloaded, - # ref https://github.com/firezone/firezone/issues/2162 has_many :gateway_groups, through: [:connections, :gateway_group] - # TODO: HARD-DELETE - Remove `where` after `deleted_at` is removed from the DB - has_many :policies, Domain.Policies.Policy, where: [deleted_at: nil] + has_many :policies, Domain.Policies.Policy has_many :actor_groups, through: [:policies, :actor_group] # Warning: do not do Repo.preload/2 for this field, it will not work intentionally, @@ -56,11 +48,6 @@ defmodule Domain.Resources.Resource do field :created_by, Ecto.Enum, values: ~w[identity actor system]a field :created_by_subject, :map - belongs_to :replaced_by_resource, Domain.Resources.Resource - has_one :replaces_resource, Domain.Resources.Resource, foreign_key: :replaced_by_resource_id - - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/resources/resource/changeset.ex b/elixir/apps/domain/lib/domain/resources/resource/changeset.ex index cc57fd145..a4ae62ba4 100644 --- a/elixir/apps/domain/lib/domain/resources/resource/changeset.ex +++ b/elixir/apps/domain/lib/domain/resources/resource/changeset.ex @@ -31,7 +31,6 @@ defmodule Domain.Resources.Resource.Changeset do |> cast(attrs, @fields) |> changeset() |> validate_required(@required_fields) - |> put_change(:persistent_id, Ecto.UUID.generate()) |> put_change(:account_id, account.id) |> validate_address(account) |> cast_assoc(:connections, @@ -47,7 +46,6 @@ defmodule Domain.Resources.Resource.Changeset do |> changeset() |> validate_required(@required_fields) |> validate_address(account) - |> put_change(:persistent_id, Ecto.UUID.generate()) |> put_change(:account_id, account.id) |> put_subject_trail(:created_by, :system) |> cast_assoc(:connections, @@ -67,13 +65,6 @@ defmodule Domain.Resources.Resource.Changeset do ) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def delete(%Resource{} = resource) do - resource - |> change() - |> put_default_value(:deleted_at, DateTime.utc_now()) - end - defp validate_address(changeset, account) do if has_errors?(changeset, :type) do changeset diff --git a/elixir/apps/domain/lib/domain/resources/resource/query.ex b/elixir/apps/domain/lib/domain/resources/resource/query.ex index 2dce043bf..25bd3b642 100644 --- a/elixir/apps/domain/lib/domain/resources/resource/query.ex +++ b/elixir/apps/domain/lib/domain/resources/resource/query.ex @@ -5,12 +5,6 @@ defmodule Domain.Resources.Resource.Query do from(resources in Domain.Resources.Resource, as: :resources) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def not_deleted do - all() - |> where([resources: resources], is_nil(resources.deleted_at)) - end - def filter_features(queryable, %Domain.Accounts.Account{} = account) do if Domain.Accounts.internet_resource_enabled?(account) do queryable @@ -31,14 +25,6 @@ defmodule Domain.Resources.Resource.Query do where(queryable, [resources: resources], resources.type == ^type) end - def by_id_or_persistent_id(queryable, id) do - where(queryable, [resources: resources], resources.id == ^id) - |> or_where( - [resources: resources], - resources.persistent_id == ^id and is_nil(resources.replaced_by_resource_id) - ) - end - def by_account_id(queryable, account_id) do where(queryable, [resources: resources], resources.account_id == ^account_id) end @@ -85,7 +71,7 @@ defmodule Domain.Resources.Resource.Query do |> limit(^limit) actor_groups_subquery = - Domain.Actors.Group.Query.not_deleted() + Domain.Actors.Group.Query.all() |> where([groups: groups], groups.id in subquery(policies_subquery)) join( @@ -135,7 +121,7 @@ defmodule Domain.Resources.Resource.Query do |> join( :inner, [connections: connections], - gateway_group in ^Domain.Gateways.Group.Query.not_deleted(), + gateway_group in ^Domain.Gateways.Group.Query.all(), on: gateway_group.id == connections.gateway_group_id, as: ^binding ) @@ -167,11 +153,6 @@ defmodule Domain.Resources.Resource.Query do values: [], fun: &filter_by_gateway_group_id/2 }, - %Domain.Repo.Filter{ - name: :deleted?, - type: :boolean, - fun: &filter_deleted/1 - }, %Domain.Repo.Filter{ name: :type, type: {:list, :string}, @@ -193,11 +174,6 @@ defmodule Domain.Resources.Resource.Query do dynamic([connections: connections], connections.gateway_group_id == ^gateway_group_id)} end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def filter_deleted(queryable) do - {queryable, dynamic([resources: resources], not is_nil(resources.deleted_at))} - end - def filter_by_type(queryable, {:not_in, types}) do {queryable, dynamic([resources: resources], resources.type not in ^types)} end diff --git a/elixir/apps/domain/lib/domain/tokens.ex b/elixir/apps/domain/lib/domain/tokens.ex index ac8731064..14f8fc989 100644 --- a/elixir/apps/domain/lib/domain/tokens.ex +++ b/elixir/apps/domain/lib/domain/tokens.ex @@ -21,7 +21,7 @@ defmodule Domain.Tokens do end def fetch_token_by_id(id) do - Token.Query.not_deleted() + Token.Query.all() |> Token.Query.not_expired() |> Token.Query.by_id(id) |> Repo.fetch(Token.Query, []) @@ -48,7 +48,7 @@ defmodule Domain.Tokens do end def all_active_browser_session_tokens! do - Token.Query.not_deleted() + Token.Query.all() |> Token.Query.expires_in(15, :minute) |> Token.Query.by_type(:browser) |> Repo.all() @@ -56,7 +56,7 @@ defmodule Domain.Tokens do def list_subject_tokens(%Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_own_tokens_permission()) do - Token.Query.not_deleted() + Token.Query.all() |> Token.Query.by_actor_id(subject.actor.id) |> list_tokens(subject, opts) end @@ -64,7 +64,7 @@ defmodule Domain.Tokens do def list_tokens_for(%Actors.Actor{} = actor, %Auth.Subject{} = subject, opts \\ []) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_tokens_permission()) do - Token.Query.not_deleted() + Token.Query.all() |> Token.Query.by_actor_id(actor.id) |> list_tokens(subject, opts) end @@ -93,7 +93,7 @@ defmodule Domain.Tokens do (hardcoded token duration for Okta and Google Workspace). """ def update_token(%Token{} = token, attrs) do - Token.Query.not_deleted() + Token.Query.all() |> Token.Query.not_expired() |> Token.Query.by_id(token.id) |> Repo.fetch_and_update(Token.Query, with: &Token.Changeset.update(&1, attrs)) @@ -136,7 +136,7 @@ defmodule Domain.Tokens do end defp fetch_token_for_use(id, account_id, context_type) do - Token.Query.not_deleted() + Token.Query.all() |> Token.Query.not_expired() |> Token.Query.by_id(id) |> Token.Query.by_account_id(account_id) @@ -270,7 +270,7 @@ defmodule Domain.Tokens do def delete_all_tokens_by_type_and_assoc(:email, %Auth.Identity{} = identity) do {num_deleted, _} = - Token.Query.not_deleted() + Token.Query.all() |> Token.Query.by_type(:email) |> Token.Query.by_account_id(identity.account_id) |> Token.Query.by_identity_id(identity.id) @@ -288,118 +288,6 @@ defmodule Domain.Tokens do {:ok, num_deleted} end - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - def soft_delete_token(%Token{} = token, %Auth.Subject{} = subject) do - required_permissions = - {:one_of, - [ - Authorizer.manage_tokens_permission(), - Authorizer.manage_own_tokens_permission() - ]} - - with :ok <- Auth.ensure_has_permissions(subject, required_permissions) do - Token.Query.not_deleted() - |> Token.Query.by_id(token.id) - |> Authorizer.for_subject(subject) - |> soft_delete_tokens() - |> case do - {:ok, [token]} -> {:ok, token} - {:ok, []} -> {:error, :not_found} - end - end - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - def soft_delete_token_for(%Auth.Subject{} = subject) do - Token.Query.not_deleted() - |> Token.Query.by_id(subject.token_id) - |> Authorizer.for_subject(subject) - |> soft_delete_tokens() - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - def soft_delete_tokens_for(%Auth.Identity{} = identity) do - Token.Query.not_deleted() - |> Token.Query.by_identity_id(identity.id) - |> soft_delete_tokens() - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - def soft_delete_tokens_for(%Actors.Actor{} = actor, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_tokens_permission()) do - Token.Query.not_deleted() - |> Token.Query.by_actor_id(actor.id) - |> Authorizer.for_subject(subject) - |> soft_delete_tokens() - end - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - def soft_delete_tokens_for(%Auth.Identity{} = identity, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_tokens_permission()) do - Token.Query.not_deleted() - |> Token.Query.by_identity_id(identity.id) - |> Authorizer.for_subject(subject) - |> soft_delete_tokens() - end - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - def soft_delete_tokens_for(%Auth.Provider{} = provider, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_tokens_permission()) do - Token.Query.not_deleted() - |> Token.Query.by_provider_id(provider.id) - |> Authorizer.for_subject(subject) - |> soft_delete_tokens() - end - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - def soft_delete_tokens_for(%Relays.Group{} = group, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_tokens_permission()) do - Token.Query.not_deleted() - |> Token.Query.by_relay_group_id(group.id) - |> Authorizer.for_subject(subject) - |> soft_delete_tokens() - end - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - def soft_delete_tokens_for(%Gateways.Group{} = group, %Auth.Subject{} = subject) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_tokens_permission()) do - Token.Query.not_deleted() - |> Token.Query.by_gateway_group_id(group.id) - |> Authorizer.for_subject(subject) - |> soft_delete_tokens() - end - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - def soft_delete_all_tokens_by_type_and_assoc(:email, %Auth.Identity{} = identity) do - Token.Query.not_deleted() - |> Token.Query.by_type(:email) - |> Token.Query.by_account_id(identity.account_id) - |> Token.Query.by_identity_id(identity.id) - |> soft_delete_tokens() - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - def soft_delete_expired_tokens do - Token.Query.not_deleted() - |> Token.Query.expired() - |> soft_delete_tokens() - end - - # TODO: HARD-DELETE - Remove after `deleted_at` is remove from DB - defp soft_delete_tokens(queryable) do - {_count, tokens} = - queryable - |> Token.Query.delete() - |> Repo.update_all([]) - - {:ok, tokens} - end - defp fetch_config! do Domain.Config.fetch_env!(:domain, __MODULE__) end diff --git a/elixir/apps/domain/lib/domain/tokens/token.ex b/elixir/apps/domain/lib/domain/tokens/token.ex index 36c1bca53..f3f0e8f33 100644 --- a/elixir/apps/domain/lib/domain/tokens/token.ex +++ b/elixir/apps/domain/lib/domain/tokens/token.ex @@ -50,8 +50,6 @@ defmodule Domain.Tokens.Token do field :expires_at, :utc_datetime_usec - # TODO: HARD-DELETE - Remove field after soft deletion is removed - field :deleted_at, :utc_datetime_usec timestamps() end end diff --git a/elixir/apps/domain/lib/domain/tokens/token/changeset.ex b/elixir/apps/domain/lib/domain/tokens/token/changeset.ex index 7a60f7a06..b2a6e8f17 100644 --- a/elixir/apps/domain/lib/domain/tokens/token/changeset.ex +++ b/elixir/apps/domain/lib/domain/tokens/token/changeset.ex @@ -124,11 +124,4 @@ defmodule Domain.Tokens.Token.Changeset do |> put_change(:last_seen_at, DateTime.utc_now()) |> validate_required(~w[last_seen_user_agent last_seen_remote_ip]a) end - - # TODO: HARD-DELETE - Remove after `deleted_at` is removed from DB - def delete(%Token{} = token) do - token - |> change() - |> put_default_value(:deleted_at, DateTime.utc_now()) - end end diff --git a/elixir/apps/domain/lib/domain/tokens/token/query.ex b/elixir/apps/domain/lib/domain/tokens/token/query.ex index 87c0ebeeb..2d4724f3f 100644 --- a/elixir/apps/domain/lib/domain/tokens/token/query.ex +++ b/elixir/apps/domain/lib/domain/tokens/token/query.ex @@ -5,12 +5,6 @@ defmodule Domain.Tokens.Token.Query do from(tokens in Domain.Tokens.Token, as: :tokens) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def not_deleted do - all() - |> where([tokens: tokens], is_nil(tokens.deleted_at)) - end - def not_expired(queryable) do where( queryable, @@ -79,17 +73,6 @@ defmodule Domain.Tokens.Token.Query do where(queryable, [tokens: tokens], tokens.gateway_group_id == ^gateway_group_id) end - # TODO: HARD-DELETE - Remove after `deleted_at` column is removed from DB - def delete(queryable) do - queryable - |> Ecto.Query.select([tokens: tokens], tokens) - |> Ecto.Query.update([tokens: tokens], - set: [ - deleted_at: fragment("COALESCE(?, timezone('UTC', NOW()))", tokens.deleted_at) - ] - ) - end - def with_joined_account(queryable) do with_named_binding(queryable, :account, fn queryable, binding -> join(queryable, :inner, [tokens: tokens], account in assoc(tokens, ^binding), as: ^binding) diff --git a/elixir/apps/domain/priv/repo/migrations/20251013204955_remove_soft_deleted_data.exs b/elixir/apps/domain/priv/repo/migrations/20251013204955_remove_soft_deleted_data.exs new file mode 100644 index 000000000..0cb9f434c --- /dev/null +++ b/elixir/apps/domain/priv/repo/migrations/20251013204955_remove_soft_deleted_data.exs @@ -0,0 +1,66 @@ +defmodule Domain.Repo.Migrations.RemoveSoftDeletedData do + use Ecto.Migration + + def up do + # Delete all soft-deleted records from tables that have deleted_at column + # This is a data cleanup migration before removing the soft delete functionality + + # The order of execution was chosen to try and minimize ON DELETE CASCADE deletions + + execute(""" + DELETE FROM tokens WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM resources WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM auth_identities WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM clients WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM gateways WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM actors WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM actor_groups WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM auth_providers WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM gateway_groups WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM policies WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM relays WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM relay_groups WHERE deleted_at IS NOT NULL + """) + + execute(""" + DELETE FROM accounts WHERE deleted_at IS NOT NULL + """) + end + + def down do + # no-op Deleted data cannot be restored + end +end diff --git a/elixir/apps/domain/priv/repo/migrations/20251016183541_add_default_persistent_id.exs b/elixir/apps/domain/priv/repo/migrations/20251016183541_add_default_persistent_id.exs new file mode 100644 index 000000000..958b428ae --- /dev/null +++ b/elixir/apps/domain/priv/repo/migrations/20251016183541_add_default_persistent_id.exs @@ -0,0 +1,31 @@ +defmodule Domain.Repo.Migrations.AddDefaultPersistentId do + use Ecto.Migration + + def up do + # Add default UUID generation for resources.persistent_id + execute(""" + ALTER TABLE resources + ALTER COLUMN persistent_id SET DEFAULT gen_random_uuid() + """) + + # Add default UUID generation for policies.persistent_id + execute(""" + ALTER TABLE policies + ALTER COLUMN persistent_id SET DEFAULT gen_random_uuid() + """) + end + + def down do + # Remove default for resources.persistent_id + execute(""" + ALTER TABLE resources + ALTER COLUMN persistent_id DROP DEFAULT + """) + + # Remove default for policies.persistent_id + execute(""" + ALTER TABLE policies + ALTER COLUMN persistent_id DROP DEFAULT + """) + end +end diff --git a/elixir/apps/domain/priv/repo/migrations/20251016220755_recreate_unique_indexes_without_deleted_at.exs b/elixir/apps/domain/priv/repo/migrations/20251016220755_recreate_unique_indexes_without_deleted_at.exs new file mode 100644 index 000000000..640fcd8a7 --- /dev/null +++ b/elixir/apps/domain/priv/repo/migrations/20251016220755_recreate_unique_indexes_without_deleted_at.exs @@ -0,0 +1,169 @@ +defmodule Domain.Repo.Migrations.RecreateUniqueIndexesWithoutDeletedAt do + use Ecto.Migration + + @disable_ddl_transaction true + + def up do + # Clients + drop_if_exists( + index(:clients, [:account_id, :actor_id, :external_id], + name: :clients_account_id_actor_id_external_id_index, + concurrently: true + ) + ) + + create_if_not_exists( + unique_index(:clients, [:account_id, :actor_id, :external_id], + name: :clients_account_id_actor_id_external_id_index, + concurrently: true + ) + ) + + # Gateways + drop_if_exists( + index(:gateways, [:account_id, :group_id, :external_id], + name: :gateways_account_id_group_id_external_id_index, + concurrently: true + ) + ) + + create_if_not_exists( + unique_index(:gateways, [:account_id, :group_id, :external_id], + name: :gateways_account_id_group_id_external_id_index, + concurrently: true + ) + ) + + # Global Relays (requires raw SQL due to COALESCE) + execute("DROP INDEX CONCURRENTLY IF EXISTS global_relays_unique_address_index") + + execute(""" + CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS global_relays_unique_address_index + ON relays (COALESCE(ipv4, ipv6), port) + WHERE account_id IS NULL + """) + + # Account Relays (requires raw SQL due to COALESCE) + execute("DROP INDEX CONCURRENTLY IF EXISTS relays_unique_address_index") + + execute(""" + CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS relays_unique_address_index + ON relays (account_id, COALESCE(ipv4, ipv6), port) + WHERE account_id IS NOT NULL + """) + + # Auth Identities - provider_identifier unique index + drop_if_exists( + index(:auth_identities, [:account_id, :provider_id, :provider_identifier], + name: :auth_identities_account_id_provider_id_provider_identifier_idx, + concurrently: true + ) + ) + + create_if_not_exists( + unique_index(:auth_identities, [:account_id, :provider_id, :provider_identifier], + name: :auth_identities_account_id_provider_id_provider_identifier_idx, + concurrently: true + ) + ) + + # Auth Identities - email unique index + drop_if_exists( + index(:auth_identities, [:account_id, :provider_id, :email, :provider_identifier], + name: :auth_identities_acct_id_provider_id_email_prov_ident_unique_idx, + concurrently: true + ) + ) + + create_if_not_exists( + unique_index(:auth_identities, [:account_id, :provider_id, :email, :provider_identifier], + name: :auth_identities_acct_id_provider_id_email_prov_ident_unique_idx, + concurrently: true + ) + ) + end + + def down do + # Clients + drop_if_exists( + index(:clients, [:account_id, :actor_id, :external_id], + name: :clients_account_id_actor_id_external_id_index, + concurrently: true + ) + ) + + create_if_not_exists( + unique_index(:clients, [:account_id, :actor_id, :external_id], + name: :clients_account_id_actor_id_external_id_index, + where: "deleted_at IS NULL", + concurrently: true + ) + ) + + # Gateways + drop_if_exists( + index(:gateways, [:account_id, :group_id, :external_id], + name: :gateways_account_id_group_id_external_id_index, + concurrently: true + ) + ) + + create_if_not_exists( + unique_index(:gateways, [:account_id, :group_id, :external_id], + name: :gateways_account_id_group_id_external_id_index, + where: "deleted_at IS NULL", + concurrently: true + ) + ) + + # Global Relays (requires raw SQL due to COALESCE) + execute("DROP INDEX IF EXISTS global_relays_unique_address_index") + + execute(""" + CREATE UNIQUE INDEX IF NOT EXISTS global_relays_unique_address_index + ON relays (COALESCE(ipv4, ipv6), port) + WHERE deleted_at IS NULL AND account_id IS NULL + """) + + # Account Relays (requires raw SQL due to COALESCE) + execute("DROP INDEX IF EXISTS relays_unique_address_index") + + execute(""" + CREATE UNIQUE INDEX IF NOT EXISTS relays_unique_address_index + ON relays (account_id, COALESCE(ipv4, ipv6), port) + WHERE deleted_at IS NULL AND account_id IS NOT NULL + """) + + # Auth Identities - provider_identifier unique index + drop_if_exists( + index(:auth_identities, [:account_id, :provider_id, :provider_identifier], + name: :auth_identities_account_id_provider_id_provider_identifier_idx, + concurrently: true + ) + ) + + create_if_not_exists( + unique_index(:auth_identities, [:account_id, :provider_id, :provider_identifier], + name: :auth_identities_account_id_provider_id_provider_identifier_idx, + where: "deleted_at IS NULL", + concurrently: true + ) + ) + + # Auth Identities - email unique index + drop_if_exists( + index(:auth_identities, [:account_id, :provider_id, :email, :provider_identifier], + name: :auth_identities_acct_id_provider_id_email_prov_ident_unique_idx, + concurrently: true + ) + ) + + create_if_not_exists( + unique_index(:auth_identities, [:account_id, :provider_id, :email, :provider_identifier], + name: :auth_identities_acct_id_provider_id_email_prov_ident_unique_idx, + where: "deleted_at IS NULL", + concurrently: true + ) + ) + end +end diff --git a/elixir/apps/domain/test/domain/accounts_test.exs b/elixir/apps/domain/test/domain/accounts_test.exs index 96f42a43c..e7f660092 100644 --- a/elixir/apps/domain/test/domain/accounts_test.exs +++ b/elixir/apps/domain/test/domain/accounts_test.exs @@ -35,7 +35,7 @@ defmodule Domain.AccountsTest do end test "does not return deleted accounts" do - account = Fixtures.Accounts.create_account() |> Fixtures.Accounts.delete_account() + {:ok, account} = Fixtures.Accounts.create_account() |> Fixtures.Accounts.delete_account() accounts = all_accounts_by_ids!([account.id]) assert length(accounts) == 0 end @@ -783,11 +783,6 @@ defmodule Domain.AccountsTest do assert account_active?(account) == true end - test "returns false when account is deleted" do - account = Fixtures.Accounts.create_account() |> Fixtures.Accounts.delete_account() - assert account_active?(account) == false - end - test "returns false when account is disabled" do account = Fixtures.Accounts.create_account() |> Fixtures.Accounts.disable_account() assert account_active?(account) == false diff --git a/elixir/apps/domain/test/domain/actors_test.exs b/elixir/apps/domain/test/domain/actors_test.exs index 35bdf1364..776f6f048 100644 --- a/elixir/apps/domain/test/domain/actors_test.exs +++ b/elixir/apps/domain/test/domain/actors_test.exs @@ -84,19 +84,6 @@ defmodule Domain.ActorsTest do assert fetch_group_by_id(group.id, subject) == {:error, :not_found} end - # TODO: HARD-DELETE - This test is no longer relevant - # test "returns deleted groups", %{ - # account: account, - # subject: subject - # } do - # group = - # Fixtures.Actors.create_group(account: account) - # |> Fixtures.Actors.delete_group() - - # assert {:ok, fetched_group} = fetch_group_by_id(group.id, subject) - # assert fetched_group.id == group.id - # end - test "returns group by id", %{account: account, subject: subject} do group = Fixtures.Actors.create_group(account: account) assert {:ok, fetched_group} = fetch_group_by_id(group.id, subject) @@ -156,17 +143,6 @@ defmodule Domain.ActorsTest do assert {:ok, [], _metadata} = list_groups(subject) end - # TODO: HARD-DELETE - Is this test needed any more? - test "does not list deleted groups", %{ - account: account, - subject: subject - } do - Fixtures.Actors.create_group(account: account) - |> Fixtures.Actors.delete_group() - - assert {:ok, [], _metadata} = list_groups(subject) - end - test "returns all groups", %{ account: account, subject: subject @@ -218,17 +194,6 @@ defmodule Domain.ActorsTest do assert {:ok, [], _metadata} = list_editable_groups(subject) end - # TODO: HARD-DELETE - Is this test needed any more? - test "does not list deleted groups", %{ - account: account, - subject: subject - } do - Fixtures.Actors.create_group(account: account) - |> Fixtures.Actors.delete_group() - - assert {:ok, [], _metadata} = list_editable_groups(subject) - end - test "returns all editable groups", %{ account: account, subject: subject @@ -291,16 +256,6 @@ defmodule Domain.ActorsTest do assert {:ok, [], _metadata} = list_groups_for(actor, subject) end - # TODO: HARD-DELETE - Is this test needed any more? - test "does not list deleted groups", %{account: account, actor: actor, subject: subject} do - group = Fixtures.Actors.create_group(account: account) - Fixtures.Actors.create_membership(account: account, actor: actor, group: group) - - Fixtures.Actors.delete_group(group) - - assert {:ok, [], _metadata} = list_groups_for(actor, subject) - end - test "returns all groups for actor", %{ account: account, actor: actor, @@ -894,7 +849,7 @@ defmodule Domain.ActorsTest do sync_provider_groups(provider, attrs_list) assert Repo.aggregate(Actors.Group, :count) == 5 - assert Repo.aggregate(Actors.Group.Query.not_deleted(), :count) == 5 + assert Repo.aggregate(Actors.Group.Query.all(), :count) == 5 end test "ignores groups that are not synced from the provider", %{ @@ -927,53 +882,6 @@ defmodule Domain.ActorsTest do group_ids_by_provider_identifier: %{} }} end - - # TODO: HARD-DELETE - This test is no longer relevant - - # test "ignores synced groups that are soft deleted", %{ - # account: account, - # provider: provider - # } do - # deleted_group = - # Fixtures.Actors.create_group( - # account: account, - # provider: provider, - # provider_identifier: "G:GROUP_ID1", - # name: "ALREADY_DELETED" - # ) - - # Domain.Actors.Group.Query.not_deleted() - # |> Domain.Actors.Group.Query.by_account_id(account.id) - # |> Domain.Actors.Group.Query.by_provider_id(provider.id) - # |> Domain.Actors.Group.Query.by_provider_identifier( - # {:in, [deleted_group.provider_identifier]} - # ) - # |> Domain.Actors.delete_groups() - - # group2 = - # Fixtures.Actors.create_group( - # account: account, - # provider: provider, - # provider_identifier: "G:GROUP_ID2", - # name: "TO_BE_UPDATED" - # ) - - # attrs_list = [ - # %{"name" => "Group:Infrastructure", "provider_identifier" => "G:GROUP_ID2"}, - # %{"name" => "Group:Security", "provider_identifier" => "G:GROUP_ID3"}, - # %{"name" => "Group:Finance", "provider_identifier" => "G:GROUP_ID4"} - # ] - - # provider_identifiers = Enum.map(attrs_list, & &1["provider_identifier"]) - - # assert {:ok, sync_data} = sync_provider_groups(provider, attrs_list) - - # assert Enum.sort(Enum.map(sync_data.groups, & &1.name)) == - # Enum.sort([deleted_group.name, group2.name]) - - # assert sync_data.deleted == [] - # assert sync_data.plan == {provider_identifiers, []} - # end end describe "sync_provider_memberships/2" do @@ -1780,7 +1688,9 @@ defmodule Domain.ActorsTest do assert membership.actor_id == actor.id end - # TODO: HARD-DELETE - Is this test needed any more? + # This test isn't strictly necessary when using ON DELETE CASCADE, however + # leaving this test allows us to know if there is ever a change in the + # foreign key effects test "removes memberships when managed group is deleted", %{ account: account, actor: actor, @@ -1999,7 +1909,8 @@ defmodule Domain.ActorsTest do assert Repo.aggregate(Actors.Membership, :count) == 0 end - # TODO: HARD-DELETE - Should this test be put in policies? + # Leaving this test here for now. May want to consider moving this test + # to the policy tests rather than the actor group tests. test "cascade deletes policies that use this group", %{ account: account, subject: subject @@ -2053,22 +1964,6 @@ defmodule Domain.ActorsTest do subject: subject } end - - # TODO: HARD-DELETE - Is this test needed any more? - test "delete groups when provider is deleted", %{ - account: account, - provider: provider, - subject: subject - } do - group1 = Fixtures.Actors.create_group(account: account, provider: provider) - group2 = Fixtures.Actors.create_group(account: account, provider: provider) - - assert {:ok, _provider} = Auth.delete_provider(provider, subject) - - refute Repo.get(Domain.Auth.Provider, provider.id) - refute Repo.get(Actors.Group, group1.id) - refute Repo.get(Actors.Group, group2.id) - end end describe "group_synced?/1" do @@ -2118,23 +2013,6 @@ defmodule Domain.ActorsTest do end end - # TODO: HARD-DELETE - Remove after soft delete functionality is gone - describe "group_soft_deleted?/1" do - test "returns true for soft deleted groups" do - account = Fixtures.Accounts.create_account() - - group = - Fixtures.Actors.create_group(account: account) |> Fixtures.Actors.soft_delete_group() - - assert group_soft_deleted?(group) == true - end - - test "returns false for manually created groups" do - group = Fixtures.Actors.create_group() - assert group_soft_deleted?(group) == false - end - end - describe "count_users_for_account/1" do test "returns 0 when actors are in another account", %{} do account = Fixtures.Accounts.create_account() @@ -2237,77 +2115,6 @@ defmodule Domain.ActorsTest do end end - describe "count_synced_actors_for_provider/1" do - test "returns 0 when there are no actors" do - account = Fixtures.Accounts.create_account() - provider = Fixtures.Auth.create_userpass_provider(account: account) - assert count_synced_actors_for_provider(provider) == 0 - end - - test "returns 0 when there are no synced actors" do - account = Fixtures.Accounts.create_account() - provider = Fixtures.Auth.create_userpass_provider(account: account) - Fixtures.Actors.create_actor(type: :account_admin_user, account: account) - assert count_synced_actors_for_provider(provider) == 0 - end - - test "returns count of synced actors owned only by the given provider" do - account = Fixtures.Accounts.create_account() - - {provider, _bypass} = - Fixtures.Auth.start_and_create_openid_connect_provider(account: account) - - actor1 = - Fixtures.Actors.create_actor( - type: :account_admin_user, - account: account - ) - - Fixtures.Auth.create_identity(account: account, actor: actor1, provider: provider) - |> Fixtures.Auth.delete_identity() - - actor2 = - Fixtures.Actors.create_actor( - type: :account_admin_user, - account: account - ) - - Fixtures.Auth.create_identity(account: account, actor: actor2, provider: provider) - |> Fixtures.Auth.delete_identity() - - actor3 = - Fixtures.Actors.create_actor( - type: :account_admin_user, - account: account - ) - - Fixtures.Auth.create_identity(account: account, actor: actor3) - |> Fixtures.Auth.delete_identity() - - actor4 = - Fixtures.Actors.create_actor( - type: :account_admin_user, - account: account - ) - - Fixtures.Auth.create_identity(account: account, actor: actor4, provider: provider) - |> Fixtures.Auth.delete_identity() - - Fixtures.Auth.create_identity(account: account, actor: actor4) - |> Fixtures.Auth.delete_identity() - - actor5 = - Fixtures.Actors.create_actor( - type: :account_admin_user, - account: account - ) - - Fixtures.Auth.create_identity(account: account, actor: actor5, provider: provider) - - assert count_synced_actors_for_provider(provider) == 2 - end - end - describe "fetch_actor_by_id/3" do test "returns error when actor is not found" do subject = Fixtures.Auth.create_subject() @@ -2538,7 +2345,6 @@ defmodule Domain.ActorsTest do assert actor.name == attrs.name assert actor.type == attrs.type assert is_nil(actor.disabled_at) - assert is_nil(actor.deleted_at) end test "trims whitespace when creating an actor", %{ @@ -2551,7 +2357,6 @@ defmodule Domain.ActorsTest do assert actor.name == actor_name assert is_nil(actor.disabled_at) - assert is_nil(actor.deleted_at) end end @@ -2579,7 +2384,6 @@ defmodule Domain.ActorsTest do assert actor.name == attrs.name assert actor.type == attrs.type assert is_nil(actor.disabled_at) - assert is_nil(actor.deleted_at) end test "trims whitespace when creating an actor", %{ @@ -2593,7 +2397,6 @@ defmodule Domain.ActorsTest do assert actor.name == actor_name assert is_nil(actor.disabled_at) - assert is_nil(actor.deleted_at) end test "returns error when seats limit is exceeded (admins)", %{ @@ -3075,7 +2878,6 @@ defmodule Domain.ActorsTest do assert membership.actor_id == new_actor.id end - # TODO: HARD-DELETE - Move this test to Tokens since it has the FK constraint test "deletes token", %{ account: account, actor: actor, @@ -3087,7 +2889,6 @@ defmodule Domain.ActorsTest do refute Repo.get(Domain.Tokens.Token, subject.token_id) end - # TODO: HARD-DELETE - Move this test to AuthIdentities since it has the FK constraint test "deletes actor identities", %{ account: account, subject: subject @@ -3099,7 +2900,6 @@ defmodule Domain.ActorsTest do refute Repo.get(Domain.Auth.Identity, identity.id) end - # TODO: HARD-DELETE - Move this test to Clients since it has the FK constraint test "deletes actor clients", %{ account: account, subject: subject @@ -3109,7 +2909,7 @@ defmodule Domain.ActorsTest do assert {:ok, _actor} = delete_actor(actor_to_delete, subject) - assert Repo.aggregate(Domain.Clients.Client.Query.not_deleted(), :count) == 0 + assert Repo.aggregate(Domain.Clients.Client.Query.all(), :count) == 0 end test "deletes actor memberships", %{ @@ -3168,68 +2968,6 @@ defmodule Domain.ActorsTest do refute Repo.get(Domain.Actors.Actor, service_account_actor.id) end - # TODO: HARD-DELETE - Need to figure out if we care about this case - # test "returns error when trying to delete the last admin actor using a race condition" do - # for _ <- 0..50 do - # test_pid = self() - - # Task.async(fn -> - # allow_child_sandbox_access(test_pid) - - # Domain.Config.put_env_override(:outbound_email_adapter_configured?, true) - - # account = Fixtures.Accounts.create_account() - # provider = Fixtures.Auth.create_email_provider(account: account) - - # actor_one = - # Fixtures.Actors.create_actor( - # type: :account_admin_user, - # account: account, - # provider: provider - # ) - - # actor_two = - # Fixtures.Actors.create_actor( - # type: :account_admin_user, - # account: account, - # provider: provider - # ) - - # identity_one = - # Fixtures.Auth.create_identity( - # account: account, - # actor: actor_one, - # provider: provider - # ) - - # identity_two = - # Fixtures.Auth.create_identity( - # account: account, - # actor: actor_two, - # provider: provider - # ) - - # subject_one = Fixtures.Auth.create_subject(identity: identity_one) - # subject_two = Fixtures.Auth.create_subject(identity: identity_two) - - # for {actor, subject} <- [{actor_two, subject_one}, {actor_one, subject_two}] do - # Task.async(fn -> - # allow_child_sandbox_access(test_pid) - # delete_actor(actor, subject) - # end) - # end - # |> Task.await_many() - - # queryable = - # Actors.Actor.Query.not_deleted() - # |> Actors.Actor.Query.by_account_id(account.id) - - # assert Repo.aggregate(queryable, :count) == 1 - # end) - # end - # |> Task.await_many() - # end - test "does not allow to delete an actor twice", %{ account: account, subject: subject @@ -3271,78 +3009,6 @@ defmodule Domain.ActorsTest do end end - describe "delete_stale_synced_actors_for_provider/2" do - test "deletes actors synced with only the given provider" do - account = Fixtures.Accounts.create_account() - subject = Fixtures.Auth.create_subject(account: account) - - {provider, _bypass} = - Fixtures.Auth.start_and_create_openid_connect_provider(account: account) - - actor1 = - Fixtures.Actors.create_actor( - type: :account_admin_user, - account: account - ) - - Fixtures.Auth.create_identity(account: account, actor: actor1, provider: provider) - |> Fixtures.Auth.delete_identity() - - actor2 = - Fixtures.Actors.create_actor( - type: :account_admin_user, - account: account - ) - - Fixtures.Auth.create_identity(account: account, actor: actor2, provider: provider) - |> Fixtures.Auth.delete_identity() - - actor3 = - Fixtures.Actors.create_actor( - type: :account_admin_user, - account: account - ) - - Fixtures.Auth.create_identity(account: account, actor: actor3) - |> Fixtures.Auth.delete_identity() - - actor4 = - Fixtures.Actors.create_actor( - type: :account_admin_user, - account: account - ) - - Fixtures.Auth.create_identity(account: account, actor: actor4, provider: provider) - |> Fixtures.Auth.delete_identity() - - Fixtures.Auth.create_identity(account: account, actor: actor4) - |> Fixtures.Auth.delete_identity() - - assert delete_stale_synced_actors_for_provider(provider, subject) == :ok - not_deleted_actors = Repo.all(Actors.Actor.Query.not_deleted()) - not_deleted_actor_ids = not_deleted_actors |> Enum.map(& &1.id) |> Enum.sort() - - assert not_deleted_actor_ids == Enum.sort([actor4.id, actor3.id, subject.actor.id]) - end - - test "returns error when subject cannot delete actors" do - account = Fixtures.Accounts.create_account() - - {provider, _bypass} = - Fixtures.Auth.start_and_create_openid_connect_provider(account: account) - - subject = - Fixtures.Auth.create_subject(account: account) - |> Fixtures.Auth.remove_permissions() - - assert delete_stale_synced_actors_for_provider(provider, subject) == - {:error, - {:unauthorized, - reason: :missing_permissions, - missing_permissions: [Actors.Authorizer.manage_actors_permission()]}} - end - end - describe "actor_synced?/1" do test "returns true when actor is synced" do actor = Fixtures.Actors.create_actor() @@ -3359,23 +3025,6 @@ defmodule Domain.ActorsTest do end end - # TODO: HARD-DELETE - Remove after soft deletion functionality is removed - describe "actor_deleted?/1" do - test "returns true when actor is soft deleted" do - actor = - Fixtures.Actors.create_actor() - |> Fixtures.Actors.soft_delete() - - assert actor_deleted?(actor) == true - end - - test "returns false when actor is not deleted" do - actor = Fixtures.Actors.create_actor() - - assert actor_deleted?(actor) == false - end - end - describe "actor_disabled?/1" do test "returns true when actor is disabled" do actor = @@ -3662,12 +3311,4 @@ defmodule Domain.ActorsTest do {:ok, %{deleted_memberships: 0}} end end - - # TODO: HARD-DELETE - This may not be needed anymore - # defp allow_child_sandbox_access(parent_pid) do - # Ecto.Adapters.SQL.Sandbox.allow(Repo, parent_pid, self()) - # # Allow is async call we need to break current process execution - # # to allow sandbox to be enabled - # :timer.sleep(10) - # end end diff --git a/elixir/apps/domain/test/domain/auth/adapters/google_workspace/jobs/sync_directory_test.exs b/elixir/apps/domain/test/domain/auth/adapters/google_workspace/jobs/sync_directory_test.exs index 5baaf59a6..c0a452788 100644 --- a/elixir/apps/domain/test/domain/auth/adapters/google_workspace/jobs/sync_directory_test.exs +++ b/elixir/apps/domain/test/domain/auth/adapters/google_workspace/jobs/sync_directory_test.exs @@ -680,167 +680,6 @@ defmodule Domain.Auth.Adapters.GoogleWorkspace.Jobs.SyncDirectoryTest do refute Repo.reload(deleted_identity_token) end - test "resurrects deleted identities that reappear on the next sync", %{ - account: account, - provider: provider - } do - actor = Fixtures.Actors.create_actor(account: account) - provider_identifier = "USER_ID1" - - identity = - Fixtures.Auth.create_identity( - account: account, - provider: provider, - actor: actor, - provider_identifier: provider_identifier - ) - - inserted_at = identity.inserted_at - id = identity.id - - # Soft delete the identity - Repo.update_all(Domain.Auth.Identity, set: [deleted_at: DateTime.utc_now()]) - - assert Domain.Auth.all_identities_for(actor) == [] - - # Simulate a sync - bypass = Bypass.open() - - users = [ - %{ - "agreedToTerms" => true, - "archived" => false, - "creationTime" => "2023-06-10T17:32:06.000Z", - "id" => "USER_ID1", - "kind" => "admin#directory#user", - "lastLoginTime" => "2023-06-26T13:53:30.000Z", - "name" => %{ - "familyName" => "Manifold", - "fullName" => "Brian Manifold", - "givenName" => "Brian" - }, - "orgUnitPath" => "/Engineering", - "organizations" => [], - "phones" => [], - "primaryEmail" => "b@firez.xxx" - } - ] - - GoogleWorkspaceDirectory.override_endpoint_url("http://localhost:#{bypass.port}/") - - GoogleWorkspaceDirectory.mock_groups_list_endpoint( - bypass, - 200, - JSON.encode!(%{"groups" => []}) - ) - - GoogleWorkspaceDirectory.mock_organization_units_list_endpoint( - bypass, - 200, - JSON.encode!(%{"organizationUnits" => []}) - ) - - GoogleWorkspaceDirectory.mock_users_list_endpoint( - bypass, - 200, - JSON.encode!(%{"users" => users}) - ) - - GoogleWorkspaceDirectory.mock_token_endpoint(bypass) - - {:ok, pid} = Task.Supervisor.start_link() - assert execute(%{task_supervisor: pid}) == :ok - - # Assert that the identity has been resurrected - assert resurrected_identity = Repo.get(Domain.Auth.Identity, id) - assert resurrected_identity.inserted_at == inserted_at - assert resurrected_identity.id == id - assert resurrected_identity.deleted_at == nil - assert Domain.Auth.all_identities_for(actor) == [resurrected_identity] - end - - test "resurrects deleted groups that reappear on the next sync", %{ - account: account, - provider: provider - } do - actor_group = - Fixtures.Actors.create_group( - account: account, - provider: provider, - provider_identifier: "G:GROUP_ID1" - ) - - inserted_at = actor_group.inserted_at - id = actor_group.id - - # Soft delete the group - Repo.update_all(Domain.Actors.Group, set: [deleted_at: DateTime.utc_now()]) - - # Assert that the group and associated policy has been soft-deleted - assert Domain.Actors.Group.Query.not_deleted() |> Repo.all() == [] - - # Simulate a sync - bypass = Bypass.open() - - groups = [ - %{ - "kind" => "admin#directory#group", - "id" => "GROUP_ID1", - "etag" => "\"ET\"", - "email" => "i@fiez.xxx", - "name" => "Infrastructure", - "directMembersCount" => "5", - "description" => "Group to handle infrastructure alerts and management", - "adminCreated" => true, - "aliases" => [ - "pnr@firez.one" - ], - "nonEditableAliases" => [ - "i@ext.fiez.xxx" - ] - } - ] - - GoogleWorkspaceDirectory.override_endpoint_url("http://localhost:#{bypass.port}/") - - GoogleWorkspaceDirectory.mock_groups_list_endpoint( - bypass, - 200, - JSON.encode!(%{"groups" => groups}) - ) - - GoogleWorkspaceDirectory.mock_organization_units_list_endpoint( - bypass, - 200, - JSON.encode!(%{"organizationUnits" => []}) - ) - - GoogleWorkspaceDirectory.mock_users_list_endpoint( - bypass, - 200, - JSON.encode!(%{"users" => []}) - ) - - GoogleWorkspaceDirectory.mock_group_members_list_endpoint( - bypass, - "GROUP_ID1", - 200, - JSON.encode!(%{"members" => []}) - ) - - GoogleWorkspaceDirectory.mock_token_endpoint(bypass) - - {:ok, pid} = Task.Supervisor.start_link() - assert execute(%{task_supervisor: pid}) == :ok - - # Assert that the group has been resurrected - assert resurrected_group = Repo.get(Domain.Actors.Group, id) - assert resurrected_group.inserted_at == inserted_at - assert resurrected_group.id == id - assert resurrected_group.deleted_at == nil - assert Domain.Actors.Group.Query.not_deleted() |> Repo.all() == [resurrected_group] - end - test "persists the sync error on the provider", %{provider: provider} do error_message = "Admin SDK API has not been used in project XXXX before or it is disabled. " <> diff --git a/elixir/apps/domain/test/domain/auth/adapters/jumpcloud/jobs/sync_directory_test.exs b/elixir/apps/domain/test/domain/auth/adapters/jumpcloud/jobs/sync_directory_test.exs index 9cf1a9fa2..388627929 100644 --- a/elixir/apps/domain/test/domain/auth/adapters/jumpcloud/jobs/sync_directory_test.exs +++ b/elixir/apps/domain/test/domain/auth/adapters/jumpcloud/jobs/sync_directory_test.exs @@ -443,128 +443,6 @@ defmodule Domain.Auth.Adapters.JumpCloud.Jobs.SyncDirectoryTest do refute Repo.reload(deleted_identity_token) end - test "resurrects deleted identities that reappear on the next sync", %{ - bypass: bypass, - account: account, - provider: provider - } do - actor = Fixtures.Actors.create_actor(account: account) - provider_identifier = "USER_JDOE_ID" - - identity = - Fixtures.Auth.create_identity( - account: account, - provider: provider, - actor: actor, - provider_identifier: provider_identifier - ) - - inserted_at = identity.inserted_at - id = identity.id - - # Soft delete the identity - Repo.update_all(Domain.Auth.Identity, set: [deleted_at: DateTime.utc_now()]) - - assert Domain.Auth.all_identities_for(actor) == [] - - # Simulate a sync - - users = [ - %{ - "id" => "workos_user_jdoe_id", - "object" => "directory_user", - "custom_attributes" => %{}, - "directory_id" => "dir_123", - "organization_id" => "org_123", - "emails" => [ - %{ - "primary" => true, - "type" => "type", - "value" => "jdoe@example.local" - } - ], - "groups" => [], - "idp_id" => "USER_JDOE_ID", - "first_name" => "John", - "last_name" => "Doe", - "job_title" => "Software Eng", - "raw_attributes" => %{}, - "state" => "active", - "username" => "jdoe@example.local", - "created_at" => "2023-07-17T20:07:20.055Z", - "updated_at" => "2023-07-17T20:07:20.055Z" - } - ] - - WorkOSDirectory.override_base_url("http://localhost:#{bypass.port}/") - WorkOSDirectory.mock_list_directories_endpoint(bypass) - WorkOSDirectory.mock_list_groups_endpoint(bypass, []) - WorkOSDirectory.mock_list_users_endpoint(bypass, users) - - {:ok, pid} = Task.Supervisor.start_link() - assert execute(%{task_supervisor: pid}) == :ok - - # Assert that the identity has been resurrected - assert resurrected_identity = Repo.get(Domain.Auth.Identity, id) - assert resurrected_identity.inserted_at == inserted_at - assert resurrected_identity.id == id - assert resurrected_identity.deleted_at == nil - assert Domain.Auth.all_identities_for(actor) == [resurrected_identity] - end - - test "resurrects deleted groups that reappear on the next sync", %{ - bypass: bypass, - account: account, - provider: provider - } do - actor_group = - Fixtures.Actors.create_group( - account: account, - provider: provider, - provider_identifier: "G:GROUP_ENGINEERING_ID" - ) - - inserted_at = actor_group.inserted_at - id = actor_group.id - - # Soft delete the group - Repo.update_all(Domain.Actors.Group, set: [deleted_at: DateTime.utc_now()]) - - # Assert that the group and associated policy has been soft-deleted - assert Domain.Actors.Group.Query.not_deleted() |> Repo.all() == [] - - # Simulate a sync - - groups = [ - %{ - "id" => "GROUP_ENGINEERING_ID", - "object" => "directory_group", - "idp_id" => "engineering", - "directory_id" => "dir_123", - "organization_id" => "org_123", - "name" => "Engineering", - "created_at" => "2021-10-27 15:21:50.640958", - "updated_at" => "2021-12-13 12:15:45.531847", - "raw_attributes" => %{} - } - ] - - WorkOSDirectory.override_base_url("http://localhost:#{bypass.port}/") - WorkOSDirectory.mock_list_directories_endpoint(bypass) - WorkOSDirectory.mock_list_groups_endpoint(bypass, groups) - WorkOSDirectory.mock_list_users_endpoint(bypass, []) - - {:ok, pid} = Task.Supervisor.start_link() - assert execute(%{task_supervisor: pid}) == :ok - - # Assert that the group has been resurrected - assert resurrected_group = Repo.get(Domain.Actors.Group, id) - assert resurrected_group.inserted_at == inserted_at - assert resurrected_group.id == id - assert resurrected_group.deleted_at == nil - assert Domain.Actors.Group.Query.not_deleted() |> Repo.all() == [resurrected_group] - end - test "stops the sync retires on 401 error from WorkOS", %{provider: provider} do bypass = Bypass.open() WorkOSDirectory.override_base_url("http://localhost:#{bypass.port}") diff --git a/elixir/apps/domain/test/domain/auth/adapters/microsoft_entra/jobs/sync_directory_test.exs b/elixir/apps/domain/test/domain/auth/adapters/microsoft_entra/jobs/sync_directory_test.exs index a2b802789..8edf8e13e 100644 --- a/elixir/apps/domain/test/domain/auth/adapters/microsoft_entra/jobs/sync_directory_test.exs +++ b/elixir/apps/domain/test/domain/auth/adapters/microsoft_entra/jobs/sync_directory_test.exs @@ -141,128 +141,6 @@ defmodule Domain.Auth.Adapters.MicrosoftEntra.Jobs.SyncDirectoryTest do assert updated_provider.last_synced_at != provider.last_synced_at end - test "resurrects deleted identities that reappear on the next sync", %{ - account: account, - provider: provider - } do - actor = Fixtures.Actors.create_actor(account: account) - provider_identifier = "USER_JDOE_ID" - - identity = - Fixtures.Auth.create_identity( - account: account, - provider: provider, - actor: actor, - provider_identifier: provider_identifier - ) - - inserted_at = identity.inserted_at - id = identity.id - - # Soft delete the identity - Repo.update_all(Domain.Auth.Identity, set: [deleted_at: DateTime.utc_now()]) - - assert Domain.Auth.all_identities_for(actor) == [] - - # Simulate a sync - bypass = Bypass.open() - - users = [ - %{ - "id" => "USER_JDOE_ID", - "displayName" => "John Doe", - "givenName" => "John", - "surname" => "Doe", - "userPrincipalName" => "jdoe@example.local", - "mail" => "jdoe@example.local", - "accountEnabled" => true - } - ] - - MicrosoftEntraDirectory.override_endpoint_url("http://localhost:#{bypass.port}/") - - MicrosoftEntraDirectory.mock_groups_list_endpoint( - bypass, - 200, - JSON.encode!(%{"value" => []}) - ) - - MicrosoftEntraDirectory.mock_users_list_endpoint( - bypass, - 200, - JSON.encode!(%{"value" => users}) - ) - - {:ok, pid} = Task.Supervisor.start_link() - assert execute(%{task_supervisor: pid}) == :ok - - # Assert that the identity has been resurrected - assert resurrected_identity = Repo.get(Domain.Auth.Identity, id) - assert resurrected_identity.inserted_at == inserted_at - assert resurrected_identity.id == id - assert resurrected_identity.deleted_at == nil - assert Domain.Auth.all_identities_for(actor) == [resurrected_identity] - end - - test "resurrects deleted groups that reappear on the next sync", %{ - account: account, - provider: provider - } do - actor_group = - Fixtures.Actors.create_group( - account: account, - provider: provider, - provider_identifier: "G:GROUP_ALL_ID" - ) - - inserted_at = actor_group.inserted_at - id = actor_group.id - - # Soft delete the group - Repo.update_all(Domain.Actors.Group, set: [deleted_at: DateTime.utc_now()]) - - # Assert that the group and associated policy has been soft-deleted - assert Domain.Actors.Group.Query.not_deleted() |> Repo.all() == [] - - # Simulate a sync - bypass = Bypass.open() - - groups = [ - %{"id" => "GROUP_ALL_ID", "displayName" => "All"} - ] - - MicrosoftEntraDirectory.override_endpoint_url("http://localhost:#{bypass.port}/") - - MicrosoftEntraDirectory.mock_groups_list_endpoint( - bypass, - 200, - JSON.encode!(%{"value" => groups}) - ) - - MicrosoftEntraDirectory.mock_group_members_list_endpoint( - bypass, - "GROUP_ALL_ID", - 200, - JSON.encode!(%{"value" => []}) - ) - - MicrosoftEntraDirectory.mock_users_list_endpoint( - bypass, - 200, - JSON.encode!(%{"value" => []}) - ) - - {:ok, pid} = Task.Supervisor.start_link() - assert execute(%{task_supervisor: pid}) == :ok - - # Assert that the group has been resurrected - assert resurrected_group = Repo.get(Domain.Actors.Group, id) - assert resurrected_group.inserted_at == inserted_at - assert resurrected_group.id == id - assert resurrected_group.deleted_at == nil - assert Domain.Actors.Group.Query.not_deleted() |> Repo.all() == [resurrected_group] - end - test "does not crash on endpoint errors" do bypass = Bypass.open() Bypass.down(bypass) diff --git a/elixir/apps/domain/test/domain/auth/adapters/okta/jobs/sync_directory_test.exs b/elixir/apps/domain/test/domain/auth/adapters/okta/jobs/sync_directory_test.exs index 88758c185..a497e8129 100644 --- a/elixir/apps/domain/test/domain/auth/adapters/okta/jobs/sync_directory_test.exs +++ b/elixir/apps/domain/test/domain/auth/adapters/okta/jobs/sync_directory_test.exs @@ -745,154 +745,6 @@ defmodule Domain.Auth.Adapters.Okta.Jobs.SyncDirectoryTest do refute Repo.reload(deleted_identity_token) end - test "resurrects deleted identities that reappear on the next sync", %{ - bypass: bypass, - account: account, - provider: provider - } do - actor = Fixtures.Actors.create_actor(account: account) - provider_identifier = "USER_JDOE_ID" - - identity = - Fixtures.Auth.create_identity( - account: account, - provider: provider, - actor: actor, - provider_identifier: provider_identifier - ) - - inserted_at = identity.inserted_at - id = identity.id - - # Soft delete the identity - Repo.update_all(Domain.Auth.Identity, set: [deleted_at: DateTime.utc_now()]) - - assert Domain.Auth.all_identities_for(actor) == [] - - # Simulate a sync - - users = [ - %{ - "id" => "USER_JDOE_ID", - "status" => "ACTIVE", - "created" => "2023-12-21T18:30:05.000Z", - "activated" => nil, - "statusChanged" => "2023-12-21T20:04:06.000Z", - "lastLogin" => "2024-02-08T05:14:25.000Z", - "lastUpdated" => "2023-12-21T20:04:06.000Z", - "passwordChanged" => "2023-12-21T20:04:06.000Z", - "type" => %{"id" => "otye1rmouoEfu7KCV5d7"}, - "profile" => %{ - "firstName" => "John", - "lastName" => "Doe", - "mobilePhone" => nil, - "secondEmail" => nil, - "login" => "jdoe@example.com", - "email" => "jdoe@example.com" - }, - "_links" => %{ - "self" => %{ - "href" => "http://localhost:#{bypass.port}/api/v1/users/OT6AZkcmzkDXwkXcjTHY" - } - } - } - ] - - OktaDirectory.mock_groups_list_endpoint(bypass, 200, JSON.encode!([])) - OktaDirectory.mock_users_list_endpoint(bypass, 200, JSON.encode!(users)) - - {:ok, pid} = Task.Supervisor.start_link() - assert execute(%{task_supervisor: pid}) == :ok - - # Assert that the identity has been resurrected - assert resurrected_identity = Repo.get(Domain.Auth.Identity, id) - assert resurrected_identity.inserted_at == inserted_at - assert resurrected_identity.id == id - assert resurrected_identity.deleted_at == nil - assert Domain.Auth.all_identities_for(actor) == [resurrected_identity] - end - - test "resurrects deleted groups that reappear on the next sync", %{ - bypass: bypass, - account: account, - provider: provider - } do - actor_group = - Fixtures.Actors.create_group( - account: account, - provider: provider, - provider_identifier: "G:GROUP_DEVOPS_ID" - ) - - inserted_at = actor_group.inserted_at - id = actor_group.id - - # Soft delete the group - Repo.update_all(Domain.Actors.Group, set: [deleted_at: DateTime.utc_now()]) - - # Assert that the group and associated policy has been soft-deleted - assert Domain.Actors.Group.Query.not_deleted() |> Repo.all() == [] - - # Simulate a sync - - groups = [ - %{ - "id" => "GROUP_DEVOPS_ID", - "created" => "2024-02-07T04:32:03.000Z", - "lastUpdated" => "2024-02-07T04:32:03.000Z", - "lastMembershipUpdated" => "2024-02-07T04:32:38.000Z", - "objectClass" => [ - "okta:user_group" - ], - "type" => "OKTA_GROUP", - "profile" => %{ - "name" => "DevOps", - "description" => "" - }, - "_links" => %{ - "logo" => [ - %{ - "name" => "medium", - "href" => "http://localhost/md/image.png", - "type" => "image/png" - }, - %{ - "name" => "large", - "href" => "http://localhost/lg/image.png", - "type" => "image/png" - } - ], - "users" => %{ - "href" => "http://localhost:#{bypass.port}/api/v1/groups/00gezqhvv4IFj2Avg5d7/users" - }, - "apps" => %{ - "href" => "http://localhost:#{bypass.port}/api/v1/groups/00gezqhvv4IFj2Avg5d7/apps" - } - } - } - ] - - OktaDirectory.mock_users_list_endpoint(bypass, 200, JSON.encode!([])) - OktaDirectory.mock_groups_list_endpoint(bypass, 200, JSON.encode!(groups)) - - OktaDirectory.mock_group_members_list_endpoint( - bypass, - "GROUP_DEVOPS_ID", - 200, - JSON.encode!([]) - ) - - {:ok, pid} = Task.Supervisor.start_link() - assert execute(%{task_supervisor: pid}) == :ok - - # Assert that the group has been resurrected - assert resurrected_group = Repo.get(Domain.Actors.Group, id) - assert resurrected_group.inserted_at == inserted_at - assert resurrected_group.id == id - assert resurrected_group.deleted_at == nil - assert Domain.Actors.Group.Query.not_deleted() |> Repo.all() == [resurrected_group] - end - test "persists the sync error on the provider", %{provider: provider, bypass: bypass} do response = %{ "errorCode" => "E0000011", diff --git a/elixir/apps/domain/test/domain/auth_test.exs b/elixir/apps/domain/test/domain/auth_test.exs index 48fc8e2fa..61864ae3c 100644 --- a/elixir/apps/domain/test/domain/auth_test.exs +++ b/elixir/apps/domain/test/domain/auth_test.exs @@ -444,21 +444,6 @@ defmodule Domain.AuthTest do assert all_providers_pending_token_refresh_by_adapter!(:google_workspace) == [] end - test "ignores deleted providers" do - {provider, _bypass} = Fixtures.Auth.start_and_create_google_workspace_provider() - - Domain.Fixture.update!(provider, %{ - deleted_at: DateTime.utc_now(), - adapter_state: %{ - "access_token" => "OIDC_ACCESS_TOKEN", - "refresh_token" => "OIDC_REFRESH_TOKEN", - "expires_at" => DateTime.utc_now() - } - }) - - assert all_providers_pending_token_refresh_by_adapter!(:google_workspace) == [] - end - test "ignores non-custom provisioners" do {provider, _bypass} = Fixtures.Auth.start_and_create_google_workspace_provider() @@ -533,19 +518,6 @@ defmodule Domain.AuthTest do assert all_providers_pending_sync_by_adapter!(:google_workspace) == [] end - test "ignores deleted providers" do - {provider, _bypass} = Fixtures.Auth.start_and_create_google_workspace_provider() - - Domain.Fixture.update!(provider, %{ - deleted_at: DateTime.utc_now(), - adapter_state: %{ - "expires_at" => DateTime.utc_now() - } - }) - - assert all_providers_pending_sync_by_adapter!(:google_workspace) == [] - end - test "ignores non-custom provisioners" do {provider, _bypass} = Fixtures.Auth.start_and_create_google_workspace_provider() @@ -781,7 +753,6 @@ defmodule Domain.AuthTest do assert provider.created_by_subject == %{"email" => nil, "name" => "System"} assert is_nil(provider.disabled_at) - assert is_nil(provider.deleted_at) end test "trims whitespace when creating a provider", %{ @@ -961,7 +932,6 @@ defmodule Domain.AuthTest do assert provider.account_id == subject.account.id assert is_nil(provider.disabled_at) - assert is_nil(provider.deleted_at) end test "returns error when subject cannot manage providers", %{ @@ -1031,7 +1001,6 @@ defmodule Domain.AuthTest do assert is_nil(other_provider.disabled_at) end - # TODO: HARD-DELETE - This test should be moved to Tokens since it has the FK test "deletes tokens issued for provider identities", %{ account: account, subject: subject @@ -1209,8 +1178,7 @@ defmodule Domain.AuthTest do refute Repo.get(Auth.Provider, provider.id) - assert other_provider = Repo.get(Auth.Provider, other_provider.id) - assert is_nil(other_provider.deleted_at) + assert Repo.get(Auth.Provider, other_provider.id) end test "deletes provider identities and tokens", %{ @@ -1274,51 +1242,6 @@ defmodule Domain.AuthTest do assert delete_provider(provider, subject) == {:error, :cant_delete_the_last_provider} end - # TODO: HARD-DELETE - Need to figure out if we care about this case - # test "returns error when trying to delete the last provider using a race condition" do - # for _ <- 0..50 do - # test_pid = self() - - # Task.async(fn -> - # allow_child_sandbox_access(test_pid) - - # account = Fixtures.Accounts.create_account() - - # provider_one = Fixtures.Auth.create_email_provider(account: account) - # provider_two = Fixtures.Auth.create_userpass_provider(account: account) - - # actor = - # Fixtures.Actors.create_actor( - # type: :account_admin_user, - # account: account, - # provider: provider_one - # ) - - # identity = - # Fixtures.Auth.create_identity( - # account: account, - # actor: actor, - # provider: provider_one - # ) - - # subject = Fixtures.Auth.create_subject(identity: identity) - - # for provider <- [provider_two, provider_one] do - # Task.async(fn -> - # allow_child_sandbox_access(test_pid) - # delete_provider(provider, subject) - # end) - # end - # |> Task.await_many() - - # assert Auth.Provider.Query.not_deleted() - # |> Auth.Provider.Query.by_account_id(account.id) - # |> Repo.aggregate(:count) == 1 - # end) - # end - # |> Task.await_many() - # end - test "raises error when deleting stale provider structs", %{ subject: subject, account: account @@ -1740,162 +1663,87 @@ defmodule Domain.AuthTest do assert Enum.count(actor_ids_by_provider_identifier) == 2 end - test "does not re-create actors for deleted identities", %{ - account: account, - provider: provider - } do - identity = + test "deletes removed identities", %{account: account, provider: provider} do + provider_identifiers = ["USER_ID1", "USER_ID2", "USER_ID3", "USER_ID4", "USER_ID5"] + + deleted_identity_actor = Fixtures.Actors.create_actor(account: account) + + deleted_identity = Fixtures.Auth.create_identity( account: account, provider: provider, - provider_identifier: "USER_ID1", - actor: [type: :account_admin_user] + actor: deleted_identity_actor, + provider_identifier: Enum.at(provider_identifiers, 0) ) - |> Fixtures.Auth.delete_identity() + + deleted_identity_token = + Fixtures.Tokens.create_token( + account: account, + actor: deleted_identity_actor, + identity: deleted_identity + ) + + for n <- 1..4 do + Fixtures.Auth.create_identity( + account: account, + provider: provider, + provider_identifier: Enum.at(provider_identifiers, n) + ) + end attrs_list = [ %{ "actor" => %{ - "name" => "Brian Manifold", + "name" => "Joe Smith", "type" => "account_user" }, - "provider_identifier" => "USER_ID1" + "provider_identifier" => "USER_ID3" + }, + %{ + "actor" => %{ + "name" => "Jennie Smith", + "type" => "account_user" + }, + "provider_identifier" => "USER_ID4" + }, + %{ + "actor" => %{ + "name" => "Jane Doe", + "type" => "account_admin_user" + }, + "provider_identifier" => "USER_ID5" } ] assert {:ok, %{ - identities: [fetched_identity], - plan: {[], ["USER_ID1"], []}, - deleted_count: 0, + identities: [_id1, _id2, _id3, _id4, _id5], + plan: {[], upsert, delete}, + deleted_count: 2, inserted: [], actor_ids_by_provider_identifier: actor_ids_by_provider_identifier }} = sync_provider_identities(provider, attrs_list) - assert fetched_identity.actor_id == identity.actor_id - assert actor_ids_by_provider_identifier == %{"USER_ID1" => identity.actor_id} + assert Enum.sort(upsert) == ["USER_ID3", "USER_ID4", "USER_ID5"] - identity = Repo.get(Auth.Identity, identity.id) - assert identity.actor_id == identity.actor_id - refute identity.deleted_at + assert Enum.take(provider_identifiers, 2) + |> Enum.all?(&(&1 in delete)) - actor = Repo.get(Domain.Actors.Actor, identity.actor_id) - assert actor.name == "Brian Manifold" + refute Repo.get_by(Auth.Identity, provider_identifier: "USER_ID1") + refute Repo.get_by(Auth.Identity, provider_identifier: "USER_ID2") + + assert Auth.Identity.Query.all() + |> Auth.Identity.Query.by_provider_id(provider.id) + |> Repo.aggregate(:count) == 3 + + assert actor_ids_by_provider_identifier + |> Map.keys() + |> length() == 3 + + # Signs out users which identity has been deleted + refute Repo.reload(deleted_identity_token) end - test "does not attempt to delete identities that are already deleted", %{ - account: account, - provider: provider - } do - identity = - Fixtures.Auth.create_identity( - account: account, - provider: provider, - provider_identifier: "USER_ID1", - actor: [type: :account_admin_user] - ) - |> Fixtures.Auth.delete_identity() - - attrs_list = [] - - assert {:ok, - %{ - identities: [fetched_identity], - plan: {[], [], []}, - deleted_count: 0, - inserted: [], - actor_ids_by_provider_identifier: %{} - }} = sync_provider_identities(provider, attrs_list) - - assert fetched_identity.id == identity.id - - identity = Repo.get(Auth.Identity, identity.id) - assert identity.deleted_at - end - - # TODO: HARD-DELETE - Need to figure out if the flows message checking is necessary - # test "deletes removed identities", %{account: account, provider: provider} do - # provider_identifiers = ["USER_ID1", "USER_ID2", "USER_ID3", "USER_ID4", "USER_ID5"] - - # deleted_identity_actor = Fixtures.Actors.create_actor(account: account) - - # deleted_identity = - # Fixtures.Auth.create_identity( - # account: account, - # provider: provider, - # actor: deleted_identity_actor, - # provider_identifier: Enum.at(provider_identifiers, 0) - # ) - - # deleted_identity_token = - # Fixtures.Tokens.create_token( - # account: account, - # actor: deleted_identity_actor, - # identity: deleted_identity - # ) - - # for n <- 1..4 do - # Fixtures.Auth.create_identity( - # account: account, - # provider: provider, - # provider_identifier: Enum.at(provider_identifiers, n) - # ) - # end - - # attrs_list = [ - # %{ - # "actor" => %{ - # "name" => "Joe Smith", - # "type" => "account_user" - # }, - # "provider_identifier" => "USER_ID3" - # }, - # %{ - # "actor" => %{ - # "name" => "Jennie Smith", - # "type" => "account_user" - # }, - # "provider_identifier" => "USER_ID4" - # }, - # %{ - # "actor" => %{ - # "name" => "Jane Doe", - # "type" => "account_admin_user" - # }, - # "provider_identifier" => "USER_ID5" - # } - # ] - - # assert {:ok, - # %{ - # identities: [_id1, _id2, _id3, _id4, _id5], - # plan: {[], upsert, delete}, - # deleted: 2, - # inserted: [], - # actor_ids_by_provider_identifier: actor_ids_by_provider_identifier - # }} = sync_provider_identities(provider, attrs_list) - - # assert Enum.sort(upsert) == ["USER_ID3", "USER_ID4", "USER_ID5"] - - # assert Enum.take(provider_identifiers, 2) - # |> Enum.all?(&(&1 in delete)) - - # refute Repo.get_by(Auth.Identity, provider_identifier: "USER_ID1") - # refute Repo.get_by(Auth.Identity, provider_identifier: "USER_ID2") - - # assert Auth.Identity.Query.all() - # |> Auth.Identity.Query.by_provider_id(provider.id) - # |> Repo.aggregate(:count) == 3 - - # assert actor_ids_by_provider_identifier - # |> Map.keys() - # |> length() == 3 - - # # Signs out users which identity has been deleted - # deleted_identity_token = Repo.reload(deleted_identity_token) - # assert deleted_identity_token.deleted_at - # end - test "circuit breaker prevents mass deletions of identities", %{ account: account, provider: provider @@ -1919,7 +1767,7 @@ defmodule Domain.AuthTest do |> Auth.Identity.Query.by_provider_id(provider.id) |> Repo.aggregate(:count) == 5 - assert Auth.Identity.Query.not_deleted() + assert Auth.Identity.Query.all() |> Auth.Identity.Query.by_provider_id(provider.id) |> Repo.aggregate(:count) == 5 end @@ -1978,63 +1826,6 @@ defmodule Domain.AuthTest do assert Repo.aggregate(Auth.Identity, :count) == 0 assert Repo.aggregate(Domain.Actors.Actor, :count) == 0 end - - test "resolves provider identifier conflicts across actors", %{ - account: account, - provider: provider - } do - identity1 = - Fixtures.Auth.create_identity( - account: account, - provider: provider, - provider_identifier: "USER_ID1", - actor: [type: :account_admin_user] - ) - |> Fixtures.Auth.delete_identity() - - identity2 = - Fixtures.Auth.create_identity( - account: account, - provider: provider, - provider_identifier: "USER_ID1", - actor: [type: :account_admin_user] - ) - - attrs_list = [ - %{ - "actor" => %{ - "name" => "Brian Manifold", - "type" => "account_user" - }, - "provider_identifier" => "USER_ID1" - } - ] - - assert {:ok, - %{ - identities: [_identity1, _identity2], - plan: {[], update, []}, - deleted_count: 0, - inserted: [], - actor_ids_by_provider_identifier: actor_ids_by_provider_identifier - }} = sync_provider_identities(provider, attrs_list) - - assert length(update) == 2 - assert update == ["USER_ID1", "USER_ID1"] - - identity1 = Repo.get(Domain.Auth.Identity, identity1.id) |> Repo.preload(:actor) - assert identity1.deleted_at - assert identity1.actor.name != "Brian Manifold" - - identity2 = Repo.get(Domain.Auth.Identity, identity2.id) |> Repo.preload(:actor) - refute identity2.deleted_at - assert identity2.actor.name == "Brian Manifold" - - assert Map.get(actor_ids_by_provider_identifier, identity2.provider_identifier) == - identity2.actor.id - - assert Enum.count(actor_ids_by_provider_identifier) == 1 - end end describe "upsert_identity/3" do @@ -2087,7 +1878,6 @@ defmodule Domain.AuthTest do assert identity.provider_state == %{} assert identity.provider_virtual_state == %{} assert identity.account_id == provider.account_id - assert is_nil(identity.deleted_at) end test "trims whitespace when creating an identity", %{ @@ -2270,8 +2060,6 @@ defmodule Domain.AuthTest do "name" => subject.actor.name, "email" => subject.identity.email } - - assert is_nil(identity.deleted_at) end test "trims whitespace when creating an identity", %{ @@ -2389,7 +2177,6 @@ defmodule Domain.AuthTest do assert %{"password_hash" => _} = identity.provider_state assert %{password_hash: _} = identity.provider_virtual_state.changes assert identity.account_id == provider.account_id - assert is_nil(identity.deleted_at) end test "creates an identity when provider_identifier is an email address" do @@ -2423,7 +2210,6 @@ defmodule Domain.AuthTest do assert %{"password_hash" => _} = identity.provider_state assert %{password_hash: _} = identity.provider_virtual_state.changes assert identity.account_id == provider.account_id - assert is_nil(identity.deleted_at) end test "returns error when identifier is invalid" do @@ -2561,7 +2347,6 @@ defmodule Domain.AuthTest do refute Repo.get(Auth.Identity, identity.id) end - # TODO: HARD-DELETE - This test should be moved to tokens since it has the FK test "deletes token", %{ account: account, provider: provider, @@ -2682,29 +2467,6 @@ defmodule Domain.AuthTest do assert Repo.aggregate(by_actor_id_query, :count) == 0 end - test "removes all identities and flows that belong to a provider", %{ - account: account, - provider: provider, - subject: subject - } do - actor = Fixtures.Actors.create_actor(account: account, provider: provider) - Fixtures.Auth.create_identity(account: account, provider: provider, actor: actor) - Fixtures.Auth.create_identity(account: account, provider: provider, actor: actor) - Fixtures.Auth.create_identity(account: account, provider: provider, actor: actor) - - all_identities_query = Auth.Identity.Query.all() - assert Repo.aggregate(all_identities_query, :count) == 4 - assert {:ok, 4} = delete_identities_for(provider, subject) - - assert Repo.aggregate(all_identities_query, :count) == 0 - - by_provider_id_query = - Auth.Identity.Query.not_deleted() - |> Auth.Identity.Query.by_provider_id(provider.id) - - assert Repo.aggregate(by_provider_id_query, :count) == 0 - end - test "deletes tokens", %{ account: account, provider: provider, @@ -2753,22 +2515,6 @@ defmodule Domain.AuthTest do end end - # TODO: HARD-DELETE - Remove or update after soft deletion is removed - describe "identity_soft_deleted?/1" do - test "returns true when identity is deleted" do - identity = - Fixtures.Auth.create_identity() - |> Fixtures.Auth.delete_identity() - - assert identity_soft_deleted?(identity) == true - end - - test "returns false when identity is not deleted" do - identity = Fixtures.Auth.create_identity() - assert identity_soft_deleted?(identity) == false - end - end - # Authentication describe "sign_in/4" do @@ -3107,51 +2853,6 @@ defmodule Domain.AuthTest do assert sign_in(provider, identity.provider_identifier, nonce, secret, context) == {:error, :unauthorized} end - - # TODO: HARD-DELETE - Not sure this test is needed any more. I don't think this is a reachable state. - # test "returns error when actor is deleted", %{ - # account: account, - # provider: provider, - # user_agent: user_agent, - # remote_ip: remote_ip - # } do - # nonce = "test_nonce_for_firezone" - - # actor = - # Fixtures.Actors.create_actor(type: :account_admin_user, account: account) - # |> Fixtures.Actors.delete() - - # identity = Fixtures.Auth.create_identity(account: account, provider: provider, actor: actor) - # context = %Auth.Context{type: :browser, user_agent: user_agent, remote_ip: remote_ip} - # {:ok, identity} = Domain.Auth.Adapters.Email.request_sign_in_token(identity, context) - - # secret = identity.provider_virtual_state.nonce <> identity.provider_virtual_state.fragment - - # assert sign_in(provider, identity.provider_identifier, nonce, secret, context) == - # {:error, :unauthorized} - # end - - # TODO: HARD-DELETE - Not sure this test is needed any more. I don't think this is a reachable state. - # test "returns error when provider is deleted", %{ - # account: account, - # provider: provider, - # user_agent: user_agent, - # remote_ip: remote_ip - # } do - # nonce = "test_nonce_for_firezone" - - # actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account) - # identity = Fixtures.Auth.create_identity(account: account, provider: provider, actor: actor) - # subject = Fixtures.Auth.create_subject(identity: identity) - # {:ok, _provider} = delete_provider(provider, subject) - - # context = %Auth.Context{type: :browser, user_agent: user_agent, remote_ip: remote_ip} - # {:ok, identity} = Domain.Auth.Adapters.Email.request_sign_in_token(identity, context) - # secret = identity.provider_virtual_state.nonce <> identity.provider_virtual_state.fragment - - # assert sign_in(provider, identity.provider_identifier, nonce, secret, context) == - # {:error, :unauthorized} - # end end describe "sign_in/3" do @@ -4326,12 +4027,4 @@ defmodule Domain.AuthTest do refute can_grant_role?(subject, :account_admin_user) end end - - # TODO: HARD-DELETE - This may not be needed anymore - # defp allow_child_sandbox_access(parent_pid) do - # Ecto.Adapters.SQL.Sandbox.allow(Repo, parent_pid, self()) - # # Allow is async call we need to break current process execution - # # to allow sandbox to be enabled - # :timer.sleep(10) - # end end diff --git a/elixir/apps/domain/test/domain/change_logs/replication_connection_test.exs b/elixir/apps/domain/test/domain/change_logs/replication_connection_test.exs index 5bdfaf3d5..79b2b1bc9 100644 --- a/elixir/apps/domain/test/domain/change_logs/replication_connection_test.exs +++ b/elixir/apps/domain/test/domain/change_logs/replication_connection_test.exs @@ -273,14 +273,13 @@ defmodule Domain.ChangeLogs.ReplicationConnectionTest do } end - test "adds delete operation to flush buffer for non-soft-deleted records", %{account: account} do + test "adds delete operation to flush buffer for deleted records", %{account: account} do table = "resources" old_data = %{ "id" => Ecto.UUID.generate(), "account_id" => account.id, - "name" => "deleted resource", - "deleted_at" => nil + "name" => "deleted resource" } lsn = 12347 diff --git a/elixir/apps/domain/test/domain/changes/hooks/accounts_test.exs b/elixir/apps/domain/test/domain/changes/hooks/accounts_test.exs index f23cd6cf4..5ac252eae 100644 --- a/elixir/apps/domain/test/domain/changes/hooks/accounts_test.exs +++ b/elixir/apps/domain/test/domain/changes/hooks/accounts_test.exs @@ -30,26 +30,6 @@ defmodule Domain.Changes.Hooks.AccountsTest do assert account.id == account_id end - - test "sends delete when soft-deleted" do - account_id = "00000000-0000-0000-0000-000000000002" - :ok = PubSub.Account.subscribe(account_id) - - old_data = %{ - "id" => account_id, - "deleted_at" => nil - } - - data = %{ - "id" => account_id, - "deleted_at" => "2023-10-01T00:00:00Z" - } - - assert :ok == on_update(0, old_data, data) - assert_receive %Change{op: :delete, old_struct: %Accounts.Account{} = account, lsn: 0} - - assert account.id == account_id - end end describe "delete/1" do @@ -57,25 +37,18 @@ defmodule Domain.Changes.Hooks.AccountsTest do account_id = "00000000-0000-0000-0000-000000000003" :ok = PubSub.Account.subscribe(account_id) - old_data = %{ - "id" => account_id, - "deleted_at" => "2023-10-01T00:00:00Z" - } + old_data = %{"id" => account_id} assert :ok == on_delete(0, old_data) assert_receive %Change{op: :delete, old_struct: %Accounts.Account{} = account, lsn: 0} assert account.id == account_id - assert account.deleted_at == ~U[2023-10-01 00:00:00.000000Z] end test "deletes associated flows when account is deleted" do account = Fixtures.Accounts.create_account() flow = Fixtures.Flows.create_flow(account: account) - old_data = %{ - "id" => account.id, - "deleted_at" => "2023-10-01T00:00:00Z" - } + old_data = %{"id" => account.id} assert :ok == on_delete(0, old_data) assert Repo.get_by(Domain.Flows.Flow, id: flow.id) == nil diff --git a/elixir/apps/domain/test/domain/changes/hooks/actor_group_memberships_test.exs b/elixir/apps/domain/test/domain/changes/hooks/actor_group_memberships_test.exs index b77d1ad51..f9e554b8a 100644 --- a/elixir/apps/domain/test/domain/changes/hooks/actor_group_memberships_test.exs +++ b/elixir/apps/domain/test/domain/changes/hooks/actor_group_memberships_test.exs @@ -1,5 +1,5 @@ defmodule Domain.Changes.Hooks.ActorGroupMembershipsTest do - use API.ChannelCase, async: true + use Domain.DataCase, async: true import Domain.Changes.Hooks.ActorGroupMemberships alias Domain.{Actors, Changes.Change, Flows, PubSub} diff --git a/elixir/apps/domain/test/domain/changes/hooks/clients_test.exs b/elixir/apps/domain/test/domain/changes/hooks/clients_test.exs index 0d3f14797..9a8598b17 100644 --- a/elixir/apps/domain/test/domain/changes/hooks/clients_test.exs +++ b/elixir/apps/domain/test/domain/changes/hooks/clients_test.exs @@ -10,23 +10,6 @@ defmodule Domain.Changes.Hooks.ClientsTest do end describe "update/2" do - test "soft-delete broadcasts deleted client" do - client = Fixtures.Clients.create_client() - :ok = PubSub.Account.subscribe(client.account_id) - - old_data = %{"id" => client.id, "deleted_at" => nil, "account_id" => client.account_id} - - data = %{ - "id" => client.id, - "deleted_at" => DateTime.utc_now(), - "account_id" => client.account_id - } - - assert :ok == on_update(0, old_data, data) - assert_receive %Change{op: :delete, old_struct: %Clients.Client{} = deleted_client, lsn: 0} - assert deleted_client.id == client.id - end - test "update broadcasts updated client" do account = Fixtures.Accounts.create_account() client = Fixtures.Clients.create_client(account: account) diff --git a/elixir/apps/domain/test/domain/changes/hooks/gateway_groups_test.exs b/elixir/apps/domain/test/domain/changes/hooks/gateway_groups_test.exs index 886ee1ba7..9e30c8b43 100644 --- a/elixir/apps/domain/test/domain/changes/hooks/gateway_groups_test.exs +++ b/elixir/apps/domain/test/domain/changes/hooks/gateway_groups_test.exs @@ -25,8 +25,7 @@ defmodule Domain.Changes.Hooks.GatewayGroupsTest do old_data = %{ "id" => "00000000-0000-0000-0000-000000000001", "account_id" => account_id, - "name" => "Old Gateway Group", - "deleted_at" => nil + "name" => "Old Gateway Group" } data = Map.put(old_data, "name", "Updated Gateway Group") diff --git a/elixir/apps/domain/test/domain/changes/hooks/gateways_test.exs b/elixir/apps/domain/test/domain/changes/hooks/gateways_test.exs index 89665f2ff..0fd5c6622 100644 --- a/elixir/apps/domain/test/domain/changes/hooks/gateways_test.exs +++ b/elixir/apps/domain/test/domain/changes/hooks/gateways_test.exs @@ -10,38 +10,6 @@ defmodule Domain.Changes.Hooks.GatewaysTest do end describe "update/2" do - test "soft-delete broadcasts deleted gateway" do - account = Fixtures.Accounts.create_account() - gateway = Fixtures.Gateways.create_gateway(account: account) - - :ok = PubSub.Account.subscribe(account.id) - - old_data = %{"id" => gateway.id, "deleted_at" => nil, "account_id" => account.id} - data = Map.put(old_data, "deleted_at", "2023-01-01T00:00:00Z") - - assert :ok = on_update(0, old_data, data) - - assert_receive %Change{ - op: :delete, - old_struct: %Gateways.Gateway{} = deleted_gateway, - lsn: 0 - } - - assert deleted_gateway.id == gateway.id - end - - test "soft-delete deletes flows" do - account = Fixtures.Accounts.create_account() - gateway = Fixtures.Gateways.create_gateway(account: account) - - old_data = %{"id" => gateway.id, "deleted_at" => nil, "account_id" => account.id} - data = Map.put(old_data, "deleted_at", "2023-01-01T00:00:00Z") - - assert flow = Fixtures.Flows.create_flow(gateway: gateway, account: account) - assert :ok = on_update(0, old_data, data) - refute Repo.get_by(Domain.Flows.Flow, id: flow.id) - end - test "update returns :ok" do assert :ok = on_update(0, %{}, %{}) end @@ -57,8 +25,7 @@ defmodule Domain.Changes.Hooks.GatewaysTest do old_data = %{ "id" => gateway.id, "account_id" => account.id, - "name" => "Test Gateway", - "deleted_at" => nil + "name" => "Test Gateway" } assert :ok = on_delete(0, old_data) @@ -76,7 +43,7 @@ defmodule Domain.Changes.Hooks.GatewaysTest do account = Fixtures.Accounts.create_account() gateway = Fixtures.Gateways.create_gateway(account: account) - old_data = %{"id" => gateway.id, "account_id" => account.id, "deleted_at" => nil} + old_data = %{"id" => gateway.id, "account_id" => account.id} assert flow = Fixtures.Flows.create_flow(gateway: gateway, account: account) assert :ok = on_delete(0, old_data) diff --git a/elixir/apps/domain/test/domain/changes/hooks/policies_test.exs b/elixir/apps/domain/test/domain/changes/hooks/policies_test.exs index 4dd4e5079..448fecfd1 100644 --- a/elixir/apps/domain/test/domain/changes/hooks/policies_test.exs +++ b/elixir/apps/domain/test/domain/changes/hooks/policies_test.exs @@ -14,8 +14,7 @@ defmodule Domain.Changes.Hooks.PoliciesTest do "account_id" => account.id, "actor_group_id" => policy.actor_group_id, "resource_id" => policy.resource_id, - "disabled_at" => nil, - "deleted_at" => nil + "disabled_at" => nil } assert :ok == on_insert(0, data) @@ -40,8 +39,7 @@ defmodule Domain.Changes.Hooks.PoliciesTest do "account_id" => account.id, "actor_group_id" => policy.actor_group_id, "resource_id" => policy.resource_id, - "disabled_at" => nil, - "deleted_at" => nil + "disabled_at" => nil } data = Map.put(old_data, "disabled_at", "2023-10-01T00:00:00Z") @@ -74,8 +72,7 @@ defmodule Domain.Changes.Hooks.PoliciesTest do "account_id" => account.id, "actor_group_id" => policy.actor_group_id, "resource_id" => policy.resource_id, - "disabled_at" => "2023-09-01T00:00:00Z", - "deleted_at" => nil + "disabled_at" => "2023-09-01T00:00:00Z" } data = Map.put(old_data, "disabled_at", nil) @@ -89,62 +86,6 @@ defmodule Domain.Changes.Hooks.PoliciesTest do assert policy.resource_id == data["resource_id"] end - test "soft-delete broadcasts deleted policy" do - account = Fixtures.Accounts.create_account() - policy = Fixtures.Policies.create_policy(account: account) - :ok = PubSub.Account.subscribe(account.id) - - old_data = %{ - "id" => policy.id, - "account_id" => account.id, - "actor_group_id" => policy.actor_group_id, - "resource_id" => policy.resource_id, - "disabled_at" => nil, - "deleted_at" => nil - } - - data = Map.put(old_data, "deleted_at", "2023-10-01T00:00:00Z") - - assert :ok == on_update(0, old_data, data) - assert_receive %Change{op: :delete, old_struct: %Policies.Policy{} = policy, lsn: 0} - - assert policy.id == old_data["id"] - assert policy.account_id == old_data["account_id"] - assert policy.actor_group_id == old_data["actor_group_id"] - assert policy.resource_id == old_data["resource_id"] - end - - test "soft-delete deletes flows" do - account = Fixtures.Accounts.create_account() - resource = Fixtures.Resources.create_resource(account: account) - - policy = - Fixtures.Policies.create_policy( - account: account, - resource: resource - ) - - old_data = %{ - "id" => policy.id, - "account_id" => account.id, - "actor_group_id" => policy.actor_group_id, - "resource_id" => resource.id, - "deleted_at" => nil - } - - data = Map.put(old_data, "deleted_at", "2023-10-01T00:00:00Z") - - assert flow = - Fixtures.Flows.create_flow( - policy: policy, - resource: resource, - account: account - ) - - assert :ok = on_update(0, old_data, data) - refute Repo.get_by(Domain.Flows.Flow, id: flow.id) - end - test "non-breaking update broadcasts updated policy" do account = Fixtures.Accounts.create_account() policy = Fixtures.Policies.create_policy(account: account) @@ -156,8 +97,7 @@ defmodule Domain.Changes.Hooks.PoliciesTest do "account_id" => account.id, "actor_group_id" => policy.actor_group_id, "resource_id" => policy.resource_id, - "disabled_at" => nil, - "deleted_at" => nil + "disabled_at" => nil } data = Map.put(old_data, "description", "Updated description") @@ -186,8 +126,7 @@ defmodule Domain.Changes.Hooks.PoliciesTest do "id" => policy.id, "account_id" => account.id, "actor_group_id" => policy.actor_group_id, - "resource_id" => policy.resource_id, - "deleted_at" => nil + "resource_id" => policy.resource_id } data = Map.put(old_data, "resource_id", "00000000-0000-0000-0000-000000000001") @@ -210,8 +149,7 @@ defmodule Domain.Changes.Hooks.PoliciesTest do "id" => policy.id, "account_id" => account.id, "actor_group_id" => policy.actor_group_id, - "resource_id" => policy.resource_id, - "deleted_at" => nil + "resource_id" => policy.resource_id } data = Map.put(old_data, "actor_group_id", "00000000-0000-0000-0000-000000000001") @@ -233,8 +171,7 @@ defmodule Domain.Changes.Hooks.PoliciesTest do "resource_id" => policy.resource_id, "conditions" => [ %{"property" => "remote_ip", "operator" => "is_in", "values" => ["10.0.0.1"]} - ], - "deleted_at" => nil + ] } data = @@ -284,8 +221,7 @@ defmodule Domain.Changes.Hooks.PoliciesTest do "id" => policy.id, "account_id" => account.id, "actor_group_id" => policy.actor_group_id, - "resource_id" => policy.resource_id, - "deleted_at" => nil + "resource_id" => policy.resource_id } assert flow = Fixtures.Flows.create_flow(policy: policy, account: account) diff --git a/elixir/apps/domain/test/domain/changes/hooks/resources_test.exs b/elixir/apps/domain/test/domain/changes/hooks/resources_test.exs index 9b3f8b017..cfa86480f 100644 --- a/elixir/apps/domain/test/domain/changes/hooks/resources_test.exs +++ b/elixir/apps/domain/test/domain/changes/hooks/resources_test.exs @@ -18,8 +18,7 @@ defmodule Domain.Changes.Hooks.ResourcesTest do "type" => resource.type, "address" => resource.address, "filters" => filters, - "ip_stack" => resource.ip_stack, - "deleted_at" => nil + "ip_stack" => resource.ip_stack } assert :ok == on_insert(0, data) @@ -41,66 +40,6 @@ defmodule Domain.Changes.Hooks.ResourcesTest do end describe "update/2" do - test "soft-delete broadcasts deleted resource" do - account = Fixtures.Accounts.create_account() - filters = [%{"protocol" => "tcp", "ports" => ["80", "443"]}] - resource = Fixtures.Resources.create_resource(account: account, filters: filters) - - :ok = PubSub.Account.subscribe(account.id) - - old_data = %{ - "id" => resource.id, - "account_id" => account.id, - "address_description" => resource.address_description, - "type" => resource.type, - "address" => resource.address, - "filters" => filters, - "ip_stack" => resource.ip_stack, - "deleted_at" => nil - } - - data = Map.put(old_data, "deleted_at", "2023-10-01T00:00:00Z") - - assert :ok == on_update(0, old_data, data) - - assert_receive %Change{ - op: :delete, - old_struct: %Resources.Resource{} = deleted_resource, - lsn: 0 - } - - assert deleted_resource.id == resource.id - assert deleted_resource.account_id == resource.account_id - assert deleted_resource.type == resource.type - assert deleted_resource.address == resource.address - assert deleted_resource.filters == resource.filters - assert deleted_resource.ip_stack == resource.ip_stack - assert deleted_resource.address_description == resource.address_description - end - - test "soft-delete deletes flows" do - account = Fixtures.Accounts.create_account() - filters = [%{"protocol" => "tcp", "ports" => ["80", "443"]}] - resource = Fixtures.Resources.create_resource(account: account, filters: filters) - - old_data = %{ - "id" => resource.id, - "account_id" => account.id, - "address_description" => resource.address_description, - "type" => resource.type, - "address" => resource.address, - "filters" => filters, - "ip_stack" => resource.ip_stack, - "deleted_at" => nil - } - - data = Map.put(old_data, "deleted_at", "2023-10-01T00:00:00Z") - - assert flow = Fixtures.Flows.create_flow(resource: resource, account: account) - assert :ok = on_update(0, old_data, data) - refute Repo.get_by(Flows.Flow, id: flow.id) - end - test "regular update broadcasts updated resource" do account = Fixtures.Accounts.create_account() filters = [%{"protocol" => "tcp", "ports" => ["80", "443"]}] @@ -115,8 +54,7 @@ defmodule Domain.Changes.Hooks.ResourcesTest do "type" => resource.type, "address" => resource.address, "filters" => filters, - "ip_stack" => resource.ip_stack, - "deleted_at" => nil + "ip_stack" => resource.ip_stack } data = Map.put(old_data, "address", "new-address.example.com") @@ -151,8 +89,7 @@ defmodule Domain.Changes.Hooks.ResourcesTest do "type" => "dns", "address" => resource.address, "filters" => filters, - "ip_stack" => resource.ip_stack, - "deleted_at" => nil + "ip_stack" => resource.ip_stack } data = Map.put(old_data, "type", "cidr") @@ -178,8 +115,7 @@ defmodule Domain.Changes.Hooks.ResourcesTest do "type" => resource.type, "address" => resource.address, "filters" => filters, - "ip_stack" => resource.ip_stack, - "deleted_at" => nil + "ip_stack" => resource.ip_stack } assert :ok == on_delete(0, old_data) @@ -211,8 +147,7 @@ defmodule Domain.Changes.Hooks.ResourcesTest do "type" => resource.type, "address" => resource.address, "filters" => filters, - "ip_stack" => resource.ip_stack, - "deleted_at" => nil + "ip_stack" => resource.ip_stack } assert flow = Fixtures.Flows.create_flow(resource: resource, account: account) diff --git a/elixir/apps/domain/test/domain/changes/hooks/tokens_test.exs b/elixir/apps/domain/test/domain/changes/hooks/tokens_test.exs index 39220cc9f..b563e2713 100644 --- a/elixir/apps/domain/test/domain/changes/hooks/tokens_test.exs +++ b/elixir/apps/domain/test/domain/changes/hooks/tokens_test.exs @@ -14,48 +14,6 @@ defmodule Domain.Changes.Hooks.TokensTest do assert :ok = on_update(0, %{"type" => "email"}, %{"type" => "email"}) end - test "soft-delete broadcasts disconnect" do - account = Fixtures.Accounts.create_account() - token = Fixtures.Tokens.create_token(account: account) - - :ok = PubSub.subscribe("sessions:#{token.id}") - - old_data = %{ - "id" => token.id, - "account_id" => account.id, - "type" => token.type, - "deleted_at" => nil - } - - assert :ok == on_delete(0, old_data) - - assert_receive %Phoenix.Socket.Broadcast{ - topic: topic, - event: "disconnect" - } - - assert topic == "sessions:#{token.id}" - end - - test "soft-delete deletes flows" do - account = Fixtures.Accounts.create_account() - token = Fixtures.Tokens.create_token(account: account) - - old_data = %{ - "id" => token.id, - "account_id" => account.id, - "type" => token.type, - "deleted_at" => nil - } - - data = Map.put(old_data, "deleted_at", "2023-10-01T00:00:00Z") - - assert flow = Fixtures.Flows.create_flow(account: account, token: token) - assert flow.token_id == token.id - assert :ok = on_update(0, old_data, data) - refute Repo.get_by(Flows.Flow, id: flow.id) - end - test "regular update returns :ok" do assert :ok = on_update(0, %{}, %{}) end @@ -71,8 +29,7 @@ defmodule Domain.Changes.Hooks.TokensTest do old_data = %{ "id" => token.id, "account_id" => account.id, - "type" => token.type, - "deleted_at" => nil + "type" => token.type } assert :ok == on_delete(0, old_data) @@ -92,8 +49,7 @@ defmodule Domain.Changes.Hooks.TokensTest do old_data = %{ "id" => token.id, "account_id" => account.id, - "type" => token.type, - "deleted_at" => nil + "type" => token.type } assert flow = Fixtures.Flows.create_flow(account: account, token: token) diff --git a/elixir/apps/domain/test/domain/clients_test.exs b/elixir/apps/domain/test/domain/clients_test.exs index 64d0c8c56..e89a730b7 100644 --- a/elixir/apps/domain/test/domain/clients_test.exs +++ b/elixir/apps/domain/test/domain/clients_test.exs @@ -1306,7 +1306,7 @@ defmodule Domain.ClientsTest do Fixtures.Clients.create_client(actor: actor) query = - Clients.Client.Query.not_deleted() + Clients.Client.Query.all() |> Clients.Client.Query.by_actor_id(actor.id) assert Repo.aggregate(query, :count) == 3 diff --git a/elixir/apps/domain/test/domain/gateways_test.exs b/elixir/apps/domain/test/domain/gateways_test.exs index 2eafe8da4..512b44fb8 100644 --- a/elixir/apps/domain/test/domain/gateways_test.exs +++ b/elixir/apps/domain/test/domain/gateways_test.exs @@ -409,7 +409,7 @@ defmodule Domain.GatewaysTest do assert {:ok, _group} = delete_group(group, subject) - assert Resources.Resource.Query.not_deleted() + assert Resources.Resource.Query.all() |> Resources.Resource.Query.by_gateway_group_id(group.id) |> Repo.aggregate(:count) == 0 end diff --git a/elixir/apps/domain/test/domain/policies_test.exs b/elixir/apps/domain/test/domain/policies_test.exs index 871639ed3..54f6f3494 100644 --- a/elixir/apps/domain/test/domain/policies_test.exs +++ b/elixir/apps/domain/test/domain/policies_test.exs @@ -81,81 +81,6 @@ defmodule Domain.PoliciesTest do end end - describe "fetch_policy_by_id_or_persistent_id/3" do - test "returns error when policy does not exist", %{subject: subject} do - assert fetch_policy_by_id_or_persistent_id(Ecto.UUID.generate(), subject) == - {:error, :not_found} - end - - test "returns error when UUID is invalid", %{subject: subject} do - assert fetch_policy_by_id_or_persistent_id("foo", subject) == {:error, :not_found} - end - - test "returns policy when policy exists", %{account: account, subject: subject} do - policy = Fixtures.Policies.create_policy(account: account) - - assert {:ok, fetched_policy} = fetch_policy_by_id_or_persistent_id(policy.id, subject) - assert fetched_policy.id == policy.id - - assert {:ok, fetched_policy} = - fetch_policy_by_id_or_persistent_id(policy.persistent_id, subject) - - assert fetched_policy.id == policy.id - end - - test "does not return deleted policies", %{account: account, subject: subject} do - policy = Fixtures.Policies.create_policy(account: account) - delete_policy(policy, subject) - - assert {:error, :not_found} = fetch_policy_by_id_or_persistent_id(policy.id, subject) - end - - test "does not return policies in other accounts", %{subject: subject} do - policy = Fixtures.Policies.create_policy() - assert fetch_policy_by_id_or_persistent_id(policy.id, subject) == {:error, :not_found} - - assert fetch_policy_by_id_or_persistent_id(policy.persistent_id, subject) == - {:error, :not_found} - end - - test "returns error when subject has no permission to view policies", %{subject: subject} do - subject = Fixtures.Auth.remove_permissions(subject) - - assert fetch_policy_by_id_or_persistent_id(Ecto.UUID.generate(), subject) == - {:error, - {:unauthorized, - reason: :missing_permissions, - missing_permissions: [ - {:one_of, - [ - Policies.Authorizer.manage_policies_permission(), - Policies.Authorizer.view_available_policies_permission() - ]} - ]}} - end - - # TODO: add a test that soft-deleted assocs are not preloaded - test "associations are preloaded when opts given", %{account: account, subject: subject} do - policy = Fixtures.Policies.create_policy(account: account) - - {:ok, policy} = - fetch_policy_by_id_or_persistent_id(policy.id, subject, - preload: [:actor_group, :resource] - ) - - assert Ecto.assoc_loaded?(policy.actor_group) - assert Ecto.assoc_loaded?(policy.resource) - - {:ok, policy} = - fetch_policy_by_id_or_persistent_id(policy.persistent_id, subject, - preload: [:actor_group, :resource] - ) - - assert Ecto.assoc_loaded?(policy.actor_group) - assert Ecto.assoc_loaded?(policy.resource) - end - end - describe "list_policies/2" do test "returns empty list when there are no policies", %{subject: subject} do assert {:ok, [], _metadata} = list_policies(subject) @@ -754,139 +679,6 @@ defmodule Domain.PoliciesTest do end end - describe "delete_policies_for/1" do - setup %{resource: resource, actor_group: actor_group, account: account, subject: subject} do - policy = - Fixtures.Policies.create_policy( - account: account, - resource: resource, - actor_group: actor_group, - subject: subject - ) - - %{ - resource: resource, - actor_group: actor_group, - policy: policy - } - end - - test "deletes policies for actor group provider", %{ - actor_group: actor_group, - policy: policy - } do - assert {:ok, _count} = delete_policies_for(actor_group) - refute Repo.get(Policies.Policy, policy.id) - end - end - - describe "delete_policies_for/2" do - setup %{account: account, subject: subject} do - resource = Fixtures.Resources.create_resource(account: account) - actor_group = Fixtures.Actors.create_group(account: account) - - policy = - Fixtures.Policies.create_policy( - account: account, - resource: resource, - actor_group: actor_group, - subject: subject - ) - - %{ - resource: resource, - actor_group: actor_group, - policy: policy - } - end - - test "deletes policies for actor group", %{ - account: account, - policy: policy, - actor_group: actor_group, - subject: subject - } do - other_policy = Fixtures.Policies.create_policy(account: account, subject: subject) - - assert {:ok, 1} = delete_policies_for(actor_group, subject) - refute Repo.get(Policies.Policy, policy.id) - - assert is_nil(Repo.get(Policies.Policy, other_policy.id).deleted_at) - end - - test "deletes policies for actor group provider", %{ - account: account, - resource: resource, - policy: other_policy, - subject: subject - } do - Domain.Config.put_env_override(:outbound_email_adapter_configured?, true) - provider = Fixtures.Auth.create_email_provider(account: account) - actor_group = Fixtures.Actors.create_group(account: account, provider: provider) - - policy = - Fixtures.Policies.create_policy( - account: account, - resource: resource, - actor_group: actor_group, - subject: subject - ) - - assert {:ok, 1} = delete_policies_for(provider, subject) - refute Repo.get(Policies.Policy, policy.id) - - assert is_nil(Repo.get(Policies.Policy, other_policy.id).deleted_at) - end - - test "deletes policies for resource", %{ - account: account, - policy: policy, - resource: resource, - subject: subject - } do - other_policy = Fixtures.Policies.create_policy(account: account, subject: subject) - - assert {:ok, _deleted_policy} = delete_policies_for(resource, subject) - refute Repo.get(Policies.Policy, policy.id) - - assert is_nil(Repo.get(Policies.Policy, other_policy.id).deleted_at) - end - - test "returns error when subject has no permission to delete policies", %{ - resource: resource, - subject: subject - } do - subject = Fixtures.Auth.remove_permissions(subject) - - assert delete_policies_for(resource, subject) == - {:error, - {:unauthorized, - [ - reason: :missing_permissions, - missing_permissions: [ - %Domain.Auth.Permission{resource: Domain.Policies.Policy, action: :manage} - ] - ]}} - end - - test "does not do anything on state conflict", %{ - resource: resource, - actor_group: actor_group, - subject: subject - } do - assert {:ok, _count} = delete_policies_for(resource, subject) - assert delete_policies_for(actor_group, subject) == {:ok, 0} - assert delete_policies_for(resource, subject) == {:ok, 0} - end - - test "does not delete policies outside of account", %{ - resource: resource - } do - subject = Fixtures.Auth.create_subject() - assert delete_policies_for(resource, subject) == {:ok, 0} - end - end - describe "filter_by_conforming_policies_for_client/2" do test "returns empty list when there are no policies", %{} do client = Fixtures.Clients.create_client() diff --git a/elixir/apps/domain/test/domain/relays_test.exs b/elixir/apps/domain/test/domain/relays_test.exs index 37a2d4902..b84efddad 100644 --- a/elixir/apps/domain/test/domain/relays_test.exs +++ b/elixir/apps/domain/test/domain/relays_test.exs @@ -363,7 +363,6 @@ defmodule Domain.RelaysTest do assert delete_group(group, subject) == {:error, :unauthorized} end - # TODO: HARD-DELETE - This test should be moved to Tokens since it holds the FK test "deletes all tokens when group is deleted", %{account: account, subject: subject} do group = Fixtures.Relays.create_group(account: account) Fixtures.Relays.create_token(account: account, group: group) @@ -380,7 +379,6 @@ defmodule Domain.RelaysTest do assert length(tokens) == 0 end - # TODO: HARD-DELETE - This test should be moved to Tokens since it holds the FK test "deletes all relays when group is deleted", %{account: account, subject: subject} do group = Fixtures.Relays.create_group(account: account) Fixtures.Relays.create_relay(account: account, group: group) @@ -395,7 +393,6 @@ defmodule Domain.RelaysTest do assert length(relays) == 0 end - # TODO: HARD-DELETE - This test should be moved to Tokens since it holds the FK test "deletes associated tokens", %{ account: account, subject: subject diff --git a/elixir/apps/domain/test/domain/repo/filter_test.exs b/elixir/apps/domain/test/domain/repo/filter_test.exs index b93048b9b..eae5c6c71 100644 --- a/elixir/apps/domain/test/domain/repo/filter_test.exs +++ b/elixir/apps/domain/test/domain/repo/filter_test.exs @@ -29,7 +29,7 @@ defmodule Domain.Repo.FilterTest do type: :boolean, name: :bool, fun: fn queryable -> - {queryable, dynamic([accounts: accounts], is_nil(accounts.deleted_at))} + {queryable, dynamic([accounts: accounts], is_nil(accounts.disabled_at))} end } } @@ -41,7 +41,7 @@ defmodule Domain.Repo.FilterTest do |> inspect() == """ #Ecto.Query\ + where: is_nil(a0.disabled_at)>\ """ assert {queryable, dynamic} = build_dynamic(queryable, [bool: false], filters, nil) @@ -51,7 +51,7 @@ defmodule Domain.Repo.FilterTest do |> inspect() == """ #Ecto.Query\ + where: not is_nil(a0.disabled_at)>\ """ end diff --git a/elixir/apps/domain/test/domain/repo_test.exs b/elixir/apps/domain/test/domain/repo_test.exs index 280a53116..a710f0d7a 100644 --- a/elixir/apps/domain/test/domain/repo_test.exs +++ b/elixir/apps/domain/test/domain/repo_test.exs @@ -269,26 +269,27 @@ defmodule Domain.RepoTest do end end - test "allows to set custom order", %{ - account: account, - query_module: query_module, - queryable: queryable - } do - dt1 = ~U[2000-01-01 00:00:00.000000Z] - dt2 = ~U[2000-01-02 00:00:00.000000Z] + # TODO: BRIAN - Check if this test can be updated to use something other than deleted_at + # test "allows to set custom order", %{ + # account: account, + # query_module: query_module, + # queryable: queryable + # } do + # dt1 = ~U[2000-01-01 00:00:00.000000Z] + # dt2 = ~U[2000-01-02 00:00:00.000000Z] - Fixtures.Actors.create_actor(account: account) - |> Fixtures.Actors.update(deleted_at: dt1) + # Fixtures.Actors.create_actor(account: account) + # |> Fixtures.Actors.update(deleted_at: dt1) - Fixtures.Actors.create_actor(account: account) - |> Fixtures.Actors.update(deleted_at: dt2) + # Fixtures.Actors.create_actor(account: account) + # |> Fixtures.Actors.update(deleted_at: dt2) - assert {:ok, [%{deleted_at: ^dt1}, %{deleted_at: ^dt2}], _metadata} = - list(queryable, query_module, order_by: [{:actors, :asc, :deleted_at}]) + # assert {:ok, [%{deleted_at: ^dt1}, %{deleted_at: ^dt2}], _metadata} = + # list(queryable, query_module, order_by: [{:actors, :asc, :deleted_at}]) - assert {:ok, [%{deleted_at: ^dt2}, %{deleted_at: ^dt1}], _metadata} = - list(queryable, query_module, order_by: [{:actors, :desc, :deleted_at}]) - end + # assert {:ok, [%{deleted_at: ^dt2}, %{deleted_at: ^dt1}], _metadata} = + # list(queryable, query_module, order_by: [{:actors, :desc, :deleted_at}]) + # end test "allows to filter results" do query_module = Domain.Accounts.Account.Query diff --git a/elixir/apps/domain/test/domain/resources_test.exs b/elixir/apps/domain/test/domain/resources_test.exs index 2e32312b1..795798db9 100644 --- a/elixir/apps/domain/test/domain/resources_test.exs +++ b/elixir/apps/domain/test/domain/resources_test.exs @@ -103,122 +103,6 @@ defmodule Domain.ResourcesTest do end end - describe "fetch_resource_by_id_or_persistent_id/3" do - test "returns error when resource does not exist", %{subject: subject} do - assert fetch_resource_by_id_or_persistent_id(Ecto.UUID.generate(), subject) == - {:error, :not_found} - end - - test "returns error when UUID is invalid", %{subject: subject} do - assert fetch_resource_by_id_or_persistent_id("foo", subject) == {:error, :not_found} - end - - test "returns resource for account admin", %{account: account, subject: subject} do - resource = Fixtures.Resources.create_resource(account: account) - - assert {:ok, fetched_resource} = fetch_resource_by_id_or_persistent_id(resource.id, subject) - assert fetched_resource.id == resource.id - - assert {:ok, fetched_resource} = - fetch_resource_by_id_or_persistent_id(resource.persistent_id, subject) - - assert fetched_resource.id == resource.id - end - - test "returns authorized resource for account user", %{ - account: account - } do - actor_group = Fixtures.Actors.create_group(account: account) - actor = Fixtures.Actors.create_actor(type: :account_user, account: account) - Fixtures.Actors.create_membership(account: account, actor: actor, group: actor_group) - - identity = Fixtures.Auth.create_identity(account: account, actor: actor) - subject = Fixtures.Auth.create_subject(identity: identity) - - resource = Fixtures.Resources.create_resource(account: account) - - assert fetch_resource_by_id_or_persistent_id(resource.id, subject) == {:error, :not_found} - - assert fetch_resource_by_id_or_persistent_id(resource.persistent_id, subject) == - {:error, :not_found} - - policy = - Fixtures.Policies.create_policy( - account: account, - actor_group: actor_group, - resource: resource - ) - - assert {:ok, fetched_resource} = fetch_resource_by_id_or_persistent_id(resource.id, subject) - assert fetched_resource.id == resource.id - assert Enum.map(fetched_resource.authorized_by_policies, & &1.id) == [policy.id] - - assert {:ok, fetched_resource} = - fetch_resource_by_id_or_persistent_id(resource.persistent_id, subject) - - assert fetched_resource.id == resource.id - assert Enum.map(fetched_resource.authorized_by_policies, & &1.id) == [policy.id] - end - - test "does not return deleted resources", %{account: account, subject: subject} do - resource = Fixtures.Resources.create_resource(account: account) - delete_resource(resource, subject) - - assert {:error, :not_found} = fetch_resource_by_id_or_persistent_id(resource.id, subject) - - assert {:error, :not_found} = - fetch_resource_by_id_or_persistent_id(resource.persistent_id, subject) - end - - test "does not return resources in other accounts", %{subject: subject} do - resource = Fixtures.Resources.create_resource() - assert fetch_resource_by_id_or_persistent_id(resource.id, subject) == {:error, :not_found} - - assert fetch_resource_by_id_or_persistent_id(resource.persistent_id, subject) == - {:error, :not_found} - end - - test "returns error when subject has no permission to view resources", %{subject: subject} do - subject = Fixtures.Auth.remove_permissions(subject) - - assert fetch_resource_by_id_or_persistent_id(Ecto.UUID.generate(), subject) == - {:error, - {:unauthorized, - reason: :missing_permissions, - missing_permissions: [ - {:one_of, - [ - Resources.Authorizer.manage_resources_permission(), - Resources.Authorizer.view_available_resources_permission() - ]} - ]}} - end - - test "associations are preloaded when opts given", %{account: account, subject: subject} do - gateway_group = Fixtures.Gateways.create_group(account: account) - - resource = - Fixtures.Resources.create_resource( - account: account, - connections: [%{gateway_group_id: gateway_group.id}] - ) - - assert {:ok, resource} = - fetch_resource_by_id_or_persistent_id(resource.id, subject, preload: :connections) - - assert Ecto.assoc_loaded?(resource.connections) - assert length(resource.connections) == 1 - - assert {:ok, resource} = - fetch_resource_by_id_or_persistent_id(resource.persistent_id, subject, - preload: :connections - ) - - assert Ecto.assoc_loaded?(resource.connections) - assert length(resource.connections) == 1 - end - end - describe "all_authorized_resources/1" do test "returns empty list when there are no resources", %{subject: subject} do assert {:ok, []} = all_authorized_resources(subject) @@ -232,33 +116,6 @@ defmodule Domain.ResourcesTest do assert {:ok, []} = all_authorized_resources(subject) end - test "does not list deleted resources", %{ - account: account, - actor: actor, - subject: subject - } do - gateway_group = Fixtures.Gateways.create_group(account: account) - - resource = - Fixtures.Resources.create_resource( - account: account, - connections: [%{gateway_group_id: gateway_group.id}] - ) - - actor_group = Fixtures.Actors.create_group(account: account) - Fixtures.Actors.create_membership(account: account, actor: actor, group: actor_group) - - Fixtures.Policies.create_policy( - account: account, - actor_group: actor_group, - resource: resource - ) - - resource |> Ecto.Changeset.change(deleted_at: DateTime.utc_now()) |> Repo.update!() - - assert {:ok, []} = all_authorized_resources(subject) - end - test "does not list resources authorized by disabled policy", %{ account: account, actor: actor, @@ -1462,49 +1319,6 @@ defmodule Domain.ResourcesTest do end end - describe "delete_connections_for/2" do - setup %{account: account, subject: subject} do - group = Fixtures.Gateways.create_group(account: account, subject: subject) - - resource = - Fixtures.Resources.create_resource( - account: account, - connections: [%{gateway_group_id: group.id}] - ) - - %{ - group: group, - resource: resource - } - end - - test "does nothing on state conflict", %{ - group: group, - subject: subject - } do - assert delete_connections_for(group, subject) == {:ok, 1} - assert delete_connections_for(group, subject) == {:ok, 0} - end - - test "deletes connections for actor group", %{group: group, subject: subject} do - assert delete_connections_for(group, subject) == {:ok, 1} - assert Repo.aggregate(Resources.Connection.Query.by_gateway_group_id(group.id), :count) == 0 - end - - test "returns error when subject has no permission to manage resources", %{ - group: group, - subject: subject - } do - subject = Fixtures.Auth.remove_permissions(subject) - - assert delete_connections_for(group, subject) == - {:error, - {:unauthorized, - reason: :missing_permissions, - missing_permissions: [Resources.Authorizer.manage_resources_permission()]}} - end - end - describe "adapt_resource_for_version/2" do setup do account = Fixtures.Accounts.create_account() diff --git a/elixir/apps/domain/test/domain/tokens_test.exs b/elixir/apps/domain/test/domain/tokens_test.exs index 6e8d2eee5..3893c4a7d 100644 --- a/elixir/apps/domain/test/domain/tokens_test.exs +++ b/elixir/apps/domain/test/domain/tokens_test.exs @@ -424,7 +424,7 @@ defmodule Domain.TokensTest do end test "does not extend expiration of deleted tokens", %{token: token} do - token = Fixtures.Tokens.delete_token(token) + {:ok, token} = Fixtures.Tokens.delete_token(token) assert update_token(token, %{}) == {:error, :not_found} end end @@ -462,7 +462,7 @@ defmodule Domain.TokensTest do ] ]}} - refute Repo.get(Tokens.Token, token.id).deleted_at + assert Repo.get(Tokens.Token, token.id) end test "does not delete tokens that belong to other accounts", %{ @@ -531,22 +531,21 @@ defmodule Domain.TokensTest do assert {:ok, _token} = delete_token_for(subject) - refute Repo.get(Tokens.Token, token.id).deleted_at + assert Repo.get(Tokens.Token, token.id) end end - ## TODO: HARD-DELETE - This test should not be relevant after soft deletion is removed - # describe "delete_tokens_for/1" do - # test "deletes browser tokens for given identity", %{account: account} do - # actor = Fixtures.Actors.create_actor(account: account) - # identity = Fixtures.Auth.create_identity(account: account, actor: actor) - # token = Fixtures.Tokens.create_token(type: :browser, account: account, identity: identity) + describe "delete_tokens_for/1" do + test "deletes browser tokens for given identity", %{account: account} do + actor = Fixtures.Actors.create_actor(account: account) + identity = Fixtures.Auth.create_identity(account: account, actor: actor) + token = Fixtures.Tokens.create_token(type: :browser, account: account, identity: identity) - # assert {:ok, _num_tokens} = delete_tokens_for(identity) + assert {:ok, _num_tokens} = delete_tokens_for(identity) - # refute Repo.get(Tokens.Token, token.id) - # end - # end + refute Repo.get(Tokens.Token, token.id) + end + end describe "delete_tokens_for/2" do test "deletes browser tokens for given actor", %{account: account, subject: subject} do diff --git a/elixir/apps/domain/test/support/fixtures/accounts.ex b/elixir/apps/domain/test/support/fixtures/accounts.ex index 148702f89..826668d4c 100644 --- a/elixir/apps/domain/test/support/fixtures/accounts.ex +++ b/elixir/apps/domain/test/support/fixtures/accounts.ex @@ -41,7 +41,7 @@ defmodule Domain.Fixtures.Accounts do end def delete_account(%Accounts.Account{} = account) do - update_account(account, deleted_at: DateTime.utc_now()) + Repo.delete(account) end def disable_account(%Accounts.Account{} = account) do diff --git a/elixir/apps/domain/test/support/fixtures/actors.ex b/elixir/apps/domain/test/support/fixtures/actors.ex index e89073305..78bdd6369 100644 --- a/elixir/apps/domain/test/support/fixtures/actors.ex +++ b/elixir/apps/domain/test/support/fixtures/actors.ex @@ -76,11 +76,6 @@ defmodule Domain.Fixtures.Actors do group end - # TODO: HARD-DELETE - Remove after soft deletion functionality is removed - def soft_delete_group(group) do - update!(group, %{deleted_at: DateTime.utc_now()}) - end - def actor_attrs(attrs \\ %{}) do first_name = Enum.random(~w[Wade Dave Seth Riley Gilbert Jorge Dan Brian Roberto Ramon Juan]) last_name = Enum.random(~w[Robyn Traci Desiree Jon Bob Karl Joe Alberta Lynda Cara Brandi B]) @@ -166,8 +161,4 @@ defmodule Domain.Fixtures.Actors do {:ok, deleted_actor} = Domain.Actors.delete_actor(actor, subject) deleted_actor end - - def soft_delete(actor) do - update!(actor, %{deleted_at: DateTime.utc_now()}) - end end diff --git a/elixir/apps/domain/test/support/fixtures/auth.ex b/elixir/apps/domain/test/support/fixtures/auth.ex index 53104fabd..fee63869f 100644 --- a/elixir/apps/domain/test/support/fixtures/auth.ex +++ b/elixir/apps/domain/test/support/fixtures/auth.ex @@ -376,7 +376,8 @@ defmodule Domain.Fixtures.Auth do end def delete_provider(provider) do - update!(provider, deleted_at: DateTime.utc_now()) + {:ok, deleted_provider} = Repo.delete(provider) + deleted_provider end def fail_provider_sync(provider) do @@ -460,7 +461,7 @@ defmodule Domain.Fixtures.Auth do end def delete_identity(identity) do - update!(identity, deleted_at: DateTime.utc_now()) + Repo.delete!(identity) end def build_context(attrs \\ %{}) do diff --git a/elixir/apps/domain/test/support/fixtures/tokens.ex b/elixir/apps/domain/test/support/fixtures/tokens.ex index de06bd0a1..e321a9edf 100644 --- a/elixir/apps/domain/test/support/fixtures/tokens.ex +++ b/elixir/apps/domain/test/support/fixtures/tokens.ex @@ -1,6 +1,5 @@ defmodule Domain.Fixtures.Tokens do use Domain.Fixture - alias Domain.Tokens def remote_ip, do: Enum.random([unique_ipv4(), unique_ipv6()]) def user_agent, do: "iOS/12.5 (iPhone; #{unique_integer()}) connlib/0.7.412" @@ -198,9 +197,7 @@ defmodule Domain.Fixtures.Tokens do end def delete_token(token) do - token - |> Tokens.Token.Changeset.delete() - |> Domain.Repo.update!() + Domain.Repo.delete(token) end def expire_token(token) do diff --git a/elixir/apps/web/lib/web/live/actors/components.ex b/elixir/apps/web/lib/web/live/actors/components.ex index c15835552..eb7ff402a 100644 --- a/elixir/apps/web/lib/web/live/actors/components.ex +++ b/elixir/apps/web/lib/web/live/actors/components.ex @@ -29,10 +29,7 @@ defmodule Web.Actors.Components do Disabled - - Deleted - - + Active """ diff --git a/elixir/apps/web/lib/web/live/actors/edit.ex b/elixir/apps/web/lib/web/live/actors/edit.ex index d3b559034..c22329cb6 100644 --- a/elixir/apps/web/lib/web/live/actors/edit.ex +++ b/elixir/apps/web/lib/web/live/actors/edit.ex @@ -5,12 +5,7 @@ defmodule Web.Actors.Edit do def mount(%{"id" => id}, _session, socket) do with {:ok, actor} <- - Actors.fetch_actor_by_id(id, socket.assigns.subject, - preload: [:memberships], - filter: [ - deleted?: false - ] - ) do + Actors.fetch_actor_by_id(id, socket.assigns.subject, preload: [:memberships]) do changeset = Actors.change_actor(actor) socket = diff --git a/elixir/apps/web/lib/web/live/actors/groups.ex b/elixir/apps/web/lib/web/live/actors/groups.ex index 5c8e0583f..41a470370 100644 --- a/elixir/apps/web/lib/web/live/actors/groups.ex +++ b/elixir/apps/web/lib/web/live/actors/groups.ex @@ -4,12 +4,7 @@ defmodule Web.Actors.EditGroups do def mount(%{"id" => id}, _session, socket) do with {:ok, actor} <- - Actors.fetch_actor_by_id(id, socket.assigns.subject, - preload: [:memberships], - filter: [ - deleted?: false - ] - ) do + Actors.fetch_actor_by_id(id, socket.assigns.subject, preload: [:memberships]) do current_group_ids = Enum.map(actor.memberships, & &1.group_id) socket = diff --git a/elixir/apps/web/lib/web/live/actors/service_accounts/new_identity.ex b/elixir/apps/web/lib/web/live/actors/service_accounts/new_identity.ex index c0ea04ad5..48740b21b 100644 --- a/elixir/apps/web/lib/web/live/actors/service_accounts/new_identity.ex +++ b/elixir/apps/web/lib/web/live/actors/service_accounts/new_identity.ex @@ -7,7 +7,6 @@ defmodule Web.Actors.ServiceAccounts.NewIdentity do Actors.fetch_actor_by_id(id, socket.assigns.subject, preload: [:memberships], filter: [ - deleted?: false, types: ["service_account"] ] ) do diff --git a/elixir/apps/web/lib/web/live/actors/show.ex b/elixir/apps/web/lib/web/live/actors/show.ex index c73c7cdff..4eaa8f727 100644 --- a/elixir/apps/web/lib/web/live/actors/show.ex +++ b/elixir/apps/web/lib/web/live/actors/show.ex @@ -159,15 +159,14 @@ defmodule Web.Actors.Show do <:title> {actor_type(@actor.type)}: {@actor.name} (you) - (deleted) (disabled) - <:action :if={is_nil(@actor.deleted_at)}> + <:action> <.edit_button navigate={~p"/#{@account}/actors/#{@actor}/edit"}> Edit {actor_type(@actor.type)} - <:action :if={is_nil(@actor.deleted_at) and not Actors.actor_disabled?(@actor)}> + <:action :if={not Actors.actor_disabled?(@actor)}> <.button_with_confirmation id="disable_actor" style="warning" @@ -188,7 +187,7 @@ defmodule Web.Actors.Show do Disable {actor_type(@actor.type)} - <:action :if={is_nil(@actor.deleted_at) and Actors.actor_disabled?(@actor)}> + <:action :if={Actors.actor_disabled?(@actor)}> <.button_with_confirmation id="enable_actor" style="warning" @@ -244,7 +243,7 @@ defmodule Web.Actors.Show do Each authentication identity is associated with an identity provider and is used to identify the actor upon successful authentication. - <:action :if={is_nil(@actor.deleted_at) and Enum.any?(@available_providers)}> + <:action :if={Enum.any?(@available_providers)}> <.add_button :if={@actor.type != :service_account} navigate={~p"/#{@account}/actors/users/#{@actor}/new_identity"} @@ -316,7 +315,7 @@ defmodule Web.Actors.Show do
No authentication identities to display. - + <.link class={[link_style()]} navigate={~p"/#{@account}/actors/service_accounts/#{@actor}/new_identity"} @@ -325,7 +324,7 @@ defmodule Web.Actors.Show do to authenticate this service account. - + <.link class={[link_style()]} navigate={~p"/#{@account}/actors/users/#{@actor}/new_identity"} @@ -347,7 +346,7 @@ defmodule Web.Actors.Show do Authentication tokens are used to authenticate the actor. Revoke tokens to sign the actor out of all associated client sessions. - <:action :if={is_nil(@actor.deleted_at) and @actor.type == :service_account}> + <:action :if={@actor.type == :service_account}> <.add_button :if={@actor.type == :service_account} navigate={~p"/#{@account}/actors/service_accounts/#{@actor}/new_identity"} @@ -356,7 +355,7 @@ defmodule Web.Actors.Show do - <:action :if={is_nil(@actor.deleted_at)}> + <:action> <.button_with_confirmation id="revoke_all_tokens" style="danger" @@ -581,7 +580,7 @@ defmodule Web.Actors.Show do - <.danger_zone :if={is_nil(@actor.deleted_at)}> + <.danger_zone> <:action :if={not Actors.actor_synced?(@actor) or @identities == []}> <.button_with_confirmation id="delete_actor" diff --git a/elixir/apps/web/lib/web/live/actors/users/new_identity.ex b/elixir/apps/web/lib/web/live/actors/users/new_identity.ex index a75b58693..5c5eb7112 100644 --- a/elixir/apps/web/lib/web/live/actors/users/new_identity.ex +++ b/elixir/apps/web/lib/web/live/actors/users/new_identity.ex @@ -8,7 +8,6 @@ defmodule Web.Actors.Users.NewIdentity do Actors.fetch_actor_by_id(id, socket.assigns.subject, preload: [:memberships, :identities], filter: [ - deleted?: false, types: ["account_user", "account_admin_user"] ] ) do diff --git a/elixir/apps/web/lib/web/live/clients/edit.ex b/elixir/apps/web/lib/web/live/clients/edit.ex index d3d4bb697..05909ab7a 100644 --- a/elixir/apps/web/lib/web/live/clients/edit.ex +++ b/elixir/apps/web/lib/web/live/clients/edit.ex @@ -3,8 +3,7 @@ defmodule Web.Clients.Edit do alias Domain.Clients def mount(%{"id" => id}, _session, socket) do - with {:ok, client} <- Clients.fetch_client_by_id(id, socket.assigns.subject), - nil <- client.deleted_at do + with {:ok, client} <- Clients.fetch_client_by_id(id, socket.assigns.subject) do changeset = Clients.change_client(client) socket = diff --git a/elixir/apps/web/lib/web/live/clients/show.ex b/elixir/apps/web/lib/web/live/clients/show.ex index f1be14c9a..16ec7f5d8 100644 --- a/elixir/apps/web/lib/web/live/clients/show.ex +++ b/elixir/apps/web/lib/web/live/clients/show.ex @@ -73,10 +73,9 @@ defmodule Web.Clients.Show do <.section> <:title> Client Details - (deleted) - <:action :if={is_nil(@client.deleted_at)}> + <:action> <.edit_button navigate={~p"/#{@account}/clients/#{@client}/edit"}> Edit Client @@ -182,7 +181,7 @@ defmodule Web.Clients.Show do Information about the device that the Client is running on. - <:action :if={is_nil(@client.deleted_at) and not is_nil(@client.verified_at)}> + <:action :if={not is_nil(@client.verified_at)}> <.button_with_confirmation id="remove_client_verification" style="danger" @@ -203,7 +202,7 @@ defmodule Web.Clients.Show do Remove verification - <:action :if={is_nil(@client.deleted_at) and is_nil(@client.verified_at)}> + <:action :if={is_nil(@client.verified_at)}> <.button_with_confirmation id="verify_client" style="warning" @@ -331,7 +330,7 @@ defmodule Web.Clients.Show do - <.danger_zone :if={is_nil(@client.deleted_at)}> + <.danger_zone> <:action> <.button_with_confirmation id="delete_client" diff --git a/elixir/apps/web/lib/web/live/gateways/show.ex b/elixir/apps/web/lib/web/live/gateways/show.ex index 9e63f1756..b3b9717d2 100644 --- a/elixir/apps/web/lib/web/live/gateways/show.ex +++ b/elixir/apps/web/lib/web/live/gateways/show.ex @@ -39,7 +39,6 @@ defmodule Web.Gateways.Show do <.section> <:title> Gateway: {@gateway.name} - (deleted) <:content> <.vertical_table id="gateway"> @@ -106,7 +105,7 @@ defmodule Web.Gateways.Show do - <.danger_zone :if={is_nil(@gateway.deleted_at)}> + <.danger_zone> <:action> <.button_with_confirmation id="delete_gateway" diff --git a/elixir/apps/web/lib/web/live/groups/edit.ex b/elixir/apps/web/lib/web/live/groups/edit.ex index 14c027c79..66fdd32cc 100644 --- a/elixir/apps/web/lib/web/live/groups/edit.ex +++ b/elixir/apps/web/lib/web/live/groups/edit.ex @@ -7,7 +7,6 @@ defmodule Web.Groups.Edit do Actors.fetch_group_by_id(id, socket.assigns.subject, preload: [:memberships], filter: [ - deleted?: false, editable?: true ] ) do diff --git a/elixir/apps/web/lib/web/live/groups/edit_actors.ex b/elixir/apps/web/lib/web/live/groups/edit_actors.ex index 186e3c5f5..8c9e42123 100644 --- a/elixir/apps/web/lib/web/live/groups/edit_actors.ex +++ b/elixir/apps/web/lib/web/live/groups/edit_actors.ex @@ -8,7 +8,6 @@ defmodule Web.Groups.EditActors do Actors.fetch_group_by_id(id, socket.assigns.subject, preload: [:memberships], filter: [ - deleted?: false, editable?: true ] ) do diff --git a/elixir/apps/web/lib/web/live/groups/index.ex b/elixir/apps/web/lib/web/live/groups/index.ex index fcf216c3f..c44f95fb3 100644 --- a/elixir/apps/web/lib/web/live/groups/index.ex +++ b/elixir/apps/web/lib/web/live/groups/index.ex @@ -75,10 +75,6 @@ defmodule Web.Groups.Index do > <:col :let={group} field={{:groups, :name}} label="name" class="w-3/12"> <.group account={@account} group={group} /> - - - (deleted) - <:col :let={group} label="actors"> <.peek peek={Map.fetch!(@group_actors, group.id)}> diff --git a/elixir/apps/web/lib/web/live/groups/show.ex b/elixir/apps/web/lib/web/live/groups/show.ex index 7e057a6ac..8432213fb 100644 --- a/elixir/apps/web/lib/web/live/groups/show.ex +++ b/elixir/apps/web/lib/web/live/groups/show.ex @@ -83,9 +83,8 @@ defmodule Web.Groups.Show do <.section> <:title> Group: {@group.name} - (deleted) - <:action :if={is_nil(@group.deleted_at)}> + <:action> <.edit_button :if={Actors.group_editable?(@group)} navigate={~p"/#{@account}/groups/#{@group}/edit"} @@ -116,7 +115,7 @@ defmodule Web.Groups.Show do <.section> <:title>Actors - <:action :if={is_nil(@group.deleted_at)}> + <:action> <.edit_button :if={not Actors.group_synced?(@group) and not Actors.group_managed?(@group)} navigate={~p"/#{@account}/groups/#{@group}/edit_actors"} @@ -194,14 +193,10 @@ defmodule Web.Groups.Show do <:col :let={policy} label="status"> - <%= if is_nil(policy.deleted_at) do %> - <%= if is_nil(policy.disabled_at) do %> - Active - <% else %> - Disabled - <% end %> + <%= if is_nil(policy.disabled_at) do %> + Active <% else %> - Deleted + Disabled <% end %> <:empty> @@ -222,7 +217,7 @@ defmodule Web.Groups.Show do - <.danger_zone :if={is_nil(@group.deleted_at) and Actors.group_editable?(@group)}> + <.danger_zone :if={Actors.group_editable?(@group)}> <:action> <.button_with_confirmation id="delete_group" diff --git a/elixir/apps/web/lib/web/live/policies/edit.ex b/elixir/apps/web/lib/web/live/policies/edit.ex index 3bd26b960..afdf3d757 100644 --- a/elixir/apps/web/lib/web/live/policies/edit.ex +++ b/elixir/apps/web/lib/web/live/policies/edit.ex @@ -5,9 +5,8 @@ defmodule Web.Policies.Edit do def mount(%{"id" => id}, _session, socket) do with {:ok, policy} <- - Policies.fetch_policy_by_id_or_persistent_id(id, socket.assigns.subject, - preload: [:actor_group, :resource], - filter: [deleted?: false] + Policies.fetch_policy_by_id(id, socket.assigns.subject, + preload: [:actor_group, :resource] ) do providers = Auth.all_active_providers_for_account!(socket.assigns.account) diff --git a/elixir/apps/web/lib/web/live/policies/index.ex b/elixir/apps/web/lib/web/live/policies/index.ex index 5deae3bcf..b444a8045 100644 --- a/elixir/apps/web/lib/web/live/policies/index.ex +++ b/elixir/apps/web/lib/web/live/policies/index.ex @@ -92,14 +92,10 @@ defmodule Web.Policies.Index do <:col :let={policy} label="status"> - <%= if is_nil(policy.deleted_at) do %> - <%= if is_nil(policy.disabled_at) do %> - Active - <% else %> - Disabled - <% end %> + <%= if is_nil(policy.disabled_at) do %> + Active <% else %> - Deleted + Disabled <% end %> <:empty> diff --git a/elixir/apps/web/lib/web/live/policies/show.ex b/elixir/apps/web/lib/web/live/policies/show.ex index 1eb893b5f..38a641ce1 100644 --- a/elixir/apps/web/lib/web/live/policies/show.ex +++ b/elixir/apps/web/lib/web/live/policies/show.ex @@ -5,12 +5,10 @@ defmodule Web.Policies.Show do def mount(%{"id" => id}, _session, socket) do with {:ok, policy} <- - Policies.fetch_policy_by_id_or_persistent_id(id, socket.assigns.subject, + Policies.fetch_policy_by_id(id, socket.assigns.subject, preload: [ actor_group: [:provider], - resource: [], - replaced_by_policy: [:actor_group, :resource], - replaces_policy: [:actor_group, :resource] + resource: [] ] ) do providers = Auth.all_active_providers_for_account!(socket.assigns.account) @@ -73,23 +71,13 @@ defmodule Web.Policies.Show do <:title> <.policy_name policy={@policy} /> (disabled) - - (deleted) - - - (replaced) - - <:action :if={is_nil(@policy.deleted_at)}> + <:action> <.edit_button navigate={~p"/#{@account}/policies/#{@policy}/edit"}> Edit Policy - <:action :if={is_nil(@policy.deleted_at)}> + <:action> <.button_with_confirmation :if={is_nil(@policy.disabled_at)} id="disable" @@ -144,53 +132,12 @@ defmodule Web.Policies.Show do {@policy.id} - <.vertical_table_row :if={not is_nil(@policy.deleted_at)}> - <:label> - Persistent ID - - <:value> - {@policy.persistent_id} - - - <.vertical_table_row :if={ - not is_nil(@policy.deleted_at) and not is_nil(@policy.replaced_by_policy) - }> - <:label> - Replaced by Policy - - <:value> - <.link - navigate={~p"/#{@account}/policies/#{@policy.replaced_by_policy}"} - class={["text-accent-600"] ++ link_style()} - > - <.policy_name policy={@policy.replaced_by_policy} /> - - - - <.vertical_table_row :if={ - not is_nil(@policy.deleted_at) and not is_nil(@policy.replaces_policy) - }> - <:label> - Replaced Policy - - <:value> - <.link - navigate={~p"/#{@account}/policies/#{@policy.replaces_policy}"} - class={["text-accent-600"] ++ link_style()} - > - <.policy_name policy={@policy.replaces_policy} /> - - - <.vertical_table_row> <:label> Group <:value> <.group account={@account} group={@policy.actor_group} /> - - (deleted) - <.vertical_table_row> @@ -201,9 +148,6 @@ defmodule Web.Policies.Show do <.link navigate={~p"/#{@account}/resources/#{@policy.resource_id}"} class={link_style()}> {@policy.resource.name} - - (deleted) - <.vertical_table_row :if={@policy.conditions != []}> @@ -276,7 +220,7 @@ defmodule Web.Policies.Show do - <.danger_zone :if={is_nil(@policy.deleted_at)}> + <.danger_zone> <:action> <.button_with_confirmation id="delete_policy" @@ -309,14 +253,12 @@ defmodule Web.Policies.Show do ) when policy_id == id do {:ok, policy} = - Policies.fetch_policy_by_id_or_persistent_id( + Policies.fetch_policy_by_id( socket.assigns.policy.id, socket.assigns.subject, preload: [ actor_group: [:provider], - resource: [], - replaced_by_policy: [:actor_group, :resource], - replaces_policy: [:actor_group, :resource] + resource: [] ] ) @@ -336,9 +278,7 @@ defmodule Web.Policies.Show do policy = %{ policy | actor_group: socket.assigns.policy.actor_group, - resource: socket.assigns.policy.resource, - replaced_by_policy: socket.assigns.policy.replaced_by_policy, - replaces_policy: socket.assigns.policy.replaces_policy + resource: socket.assigns.policy.resource } {:noreply, assign(socket, policy: policy)} @@ -350,9 +290,7 @@ defmodule Web.Policies.Show do policy = %{ policy | actor_group: socket.assigns.policy.actor_group, - resource: socket.assigns.policy.resource, - replaced_by_policy: socket.assigns.policy.replaced_by_policy, - replaces_policy: socket.assigns.policy.replaces_policy + resource: socket.assigns.policy.resource } {:noreply, assign(socket, policy: policy)} diff --git a/elixir/apps/web/lib/web/live/relay_groups/edit.ex b/elixir/apps/web/lib/web/live/relay_groups/edit.ex index ab7b35ae1..da0cb044c 100644 --- a/elixir/apps/web/lib/web/live/relay_groups/edit.ex +++ b/elixir/apps/web/lib/web/live/relay_groups/edit.ex @@ -4,12 +4,7 @@ defmodule Web.RelayGroups.Edit do def mount(%{"id" => id}, _session, socket) do with true <- Accounts.self_hosted_relays_enabled?(socket.assigns.account), - {:ok, group} <- - Relays.fetch_group_by_id(id, socket.assigns.subject, - filter: [ - deleted?: false - ] - ) do + {:ok, group} <- Relays.fetch_group_by_id(id, socket.assigns.subject) do changeset = Relays.change_group(group) socket = diff --git a/elixir/apps/web/lib/web/live/relay_groups/new_token.ex b/elixir/apps/web/lib/web/live/relay_groups/new_token.ex index c3463f2d2..c586fbfd7 100644 --- a/elixir/apps/web/lib/web/live/relay_groups/new_token.ex +++ b/elixir/apps/web/lib/web/live/relay_groups/new_token.ex @@ -4,12 +4,7 @@ defmodule Web.RelayGroups.NewToken do def mount(%{"id" => id}, _session, socket) do with true <- Accounts.self_hosted_relays_enabled?(socket.assigns.account), - {:ok, group} <- - Relays.fetch_group_by_id(id, socket.assigns.subject, - filter: [ - deleted?: false - ] - ) do + {:ok, group} <- Relays.fetch_group_by_id(id, socket.assigns.subject) do {group, token, env} = if connected?(socket) do {:ok, token, encoded_token} = Relays.create_token(group, %{}, socket.assigns.subject) diff --git a/elixir/apps/web/lib/web/live/relay_groups/show.ex b/elixir/apps/web/lib/web/live/relay_groups/show.ex index 35e3be5f8..ae18ef0e7 100644 --- a/elixir/apps/web/lib/web/live/relay_groups/show.ex +++ b/elixir/apps/web/lib/web/live/relay_groups/show.ex @@ -62,9 +62,8 @@ defmodule Web.RelayGroups.Show do <.section> <:title> Relay Instance Group: {@group.name} - (deleted) - <:action :if={not is_nil(@group.account_id) and is_nil(@group.deleted_at)}> + <:action :if={not is_nil(@group.account_id)}> <.edit_button navigate={~p"/#{@account}/relay_groups/#{@group}/edit"}> Edit Instance Group @@ -89,12 +88,12 @@ defmodule Web.RelayGroups.Show do <.section> <:title>Relays - <:action :if={not is_nil(@group.account_id) and is_nil(@group.deleted_at)}> + <:action :if={not is_nil(@group.account_id)}> <.add_button navigate={~p"/#{@account}/relay_groups/#{@group}/new_token"}> Deploy - <:action :if={is_nil(@group.deleted_at)}> + <:action> <.button_with_confirmation id="delete_site" style="danger" @@ -149,7 +148,7 @@ defmodule Web.RelayGroups.Show do - <.danger_zone :if={not is_nil(@group.account_id) and is_nil(@group.deleted_at)}> + <.danger_zone :if={not is_nil(@group.account_id)}> <:action :if={@group.account_id}> <.button_with_confirmation id="delete_relay_group" diff --git a/elixir/apps/web/lib/web/live/relays/show.ex b/elixir/apps/web/lib/web/live/relays/show.ex index dfa872182..46c9ed27e 100644 --- a/elixir/apps/web/lib/web/live/relays/show.ex +++ b/elixir/apps/web/lib/web/live/relays/show.ex @@ -44,7 +44,6 @@ defmodule Web.Relays.Show do {@relay.ipv4} - (deleted) <:content>
@@ -112,7 +111,7 @@ defmodule Web.Relays.Show do - <.danger_zone :if={is_nil(@relay.deleted_at)}> + <.danger_zone> <:action :if={@relay.account_id}> <.button_with_confirmation id="delete_relay" diff --git a/elixir/apps/web/lib/web/live/resources/components.ex b/elixir/apps/web/lib/web/live/resources/components.ex index 58550dc63..851face7c 100644 --- a/elixir/apps/web/lib/web/live/resources/components.ex +++ b/elixir/apps/web/lib/web/live/resources/components.ex @@ -10,7 +10,7 @@ defmodule Web.Resources.Components do } def fetch_resource_option(id, subject) do - {:ok, resource} = Resources.fetch_resource_by_id_or_persistent_id(id, subject) + {:ok, resource} = Resources.fetch_resource_by_id(id, subject) {:ok, resource_option(resource)} end diff --git a/elixir/apps/web/lib/web/live/resources/edit.ex b/elixir/apps/web/lib/web/live/resources/edit.ex index 77d883f86..20eab80f4 100644 --- a/elixir/apps/web/lib/web/live/resources/edit.ex +++ b/elixir/apps/web/lib/web/live/resources/edit.ex @@ -8,7 +8,6 @@ defmodule Web.Resources.Edit do Resources.fetch_resource_by_id(id, socket.assigns.subject, preload: :gateway_groups, filter: [ - deleted?: false, type: ["cidr", "dns", "ip"] ] ) do diff --git a/elixir/apps/web/lib/web/live/resources/show.ex b/elixir/apps/web/lib/web/live/resources/show.ex index 0bc6c89da..8459f8292 100644 --- a/elixir/apps/web/lib/web/live/resources/show.ex +++ b/elixir/apps/web/lib/web/live/resources/show.ex @@ -92,19 +92,8 @@ defmodule Web.Resources.Show do <.section> <:title> Resource: {@resource.name} - - - (deleted) - - - (replaced) - - <:action :if={@resource.type != :internet && is_nil(@resource.deleted_at)}> + <:action :if={@resource.type != :internet}> <.edit_button navigate={~p"/#{@account}/resources/#{@resource.id}/edit?#{@params}"}> Edit Resource @@ -119,14 +108,6 @@ defmodule Web.Resources.Show do {@resource.id} - <.vertical_table_row :if={not is_nil(@resource.deleted_at)}> - <:label> - Persistent ID - - <:value> - {@resource.persistent_id} - - <.vertical_table_row> <:label> Name @@ -212,36 +193,6 @@ defmodule Web.Resources.Show do
- <.vertical_table_row :if={ - not is_nil(@resource.deleted_at) and not is_nil(@resource.replaced_by_resource) - }> - <:label> - Replaced by Resource - - <:value> - <.link - navigate={~p"/#{@account}/resources/#{@resource.replaced_by_resource}"} - class={["text-accent-600"] ++ link_style()} - > - {@resource.replaced_by_resource.name} - - - - <.vertical_table_row :if={ - not is_nil(@resource.deleted_at) and not is_nil(@resource.replaces_resource) - }> - <:label> - Replaced Resource - - <:value> - <.link - navigate={~p"/#{@account}/resources/#{@resource.replaces_resource}"} - class={["text-accent-600"] ++ link_style()} - > - {@resource.replaces_resource.name} - - - <.vertical_table_row> <:label> Created @@ -288,14 +239,10 @@ defmodule Web.Resources.Show do <.group account={@account} group={policy.actor_group} /> <:col :let={policy} label="status"> - <%= if is_nil(policy.deleted_at) do %> - <%= if is_nil(policy.disabled_at) do %> - Active - <% else %> - Disabled - <% end %> + <%= if is_nil(policy.disabled_at) do %> + Active <% else %> - Deleted + Disabled <% end %> <:empty> @@ -372,7 +319,7 @@ defmodule Web.Resources.Show do - <.danger_zone :if={is_nil(@resource.deleted_at) and @resource.type != :internet}> + <.danger_zone :if={@resource.type != :internet}> <:action> <.button_with_confirmation id="delete_resource" @@ -409,9 +356,7 @@ defmodule Web.Resources.Show do Resources.fetch_resource_by_id(socket.assigns.resource.id, socket.assigns.subject, preload: [ :gateway_groups, - :policies, - replaced_by_resource: [], - replaces_resource: [] + :policies ] ) @@ -451,23 +396,11 @@ defmodule Web.Resources.Show do end defp fetch_resource("internet", subject) do - Resources.fetch_internet_resource(subject, - preload: [ - :gateway_groups, - replaced_by_resource: [], - replaces_resource: [] - ] - ) + Resources.fetch_internet_resource(subject, preload: [:gateway_groups]) end defp fetch_resource(id, subject) do - Resources.fetch_resource_by_id_or_persistent_id(id, subject, - preload: [ - :gateway_groups, - replaced_by_resource: [], - replaces_resource: [] - ] - ) + Resources.fetch_resource_by_id(id, subject, preload: [:gateway_groups]) end defp format_ip_stack(:dual), do: "Dual-stack (IPv4 and IPv6)" diff --git a/elixir/apps/web/lib/web/live/settings/api_clients/edit.ex b/elixir/apps/web/lib/web/live/settings/api_clients/edit.ex index 4b0313255..e3cbfab3b 100644 --- a/elixir/apps/web/lib/web/live/settings/api_clients/edit.ex +++ b/elixir/apps/web/lib/web/live/settings/api_clients/edit.ex @@ -5,8 +5,7 @@ defmodule Web.Settings.ApiClients.Edit do def mount(%{"id" => id}, _session, socket) do if Domain.Accounts.rest_api_enabled?(socket.assigns.account) do - with {:ok, actor} <- Actors.fetch_actor_by_id(id, socket.assigns.subject, preload: []), - nil <- actor.deleted_at do + with {:ok, actor} <- Actors.fetch_actor_by_id(id, socket.assigns.subject, preload: []) do changeset = Actors.change_actor(actor) socket = diff --git a/elixir/apps/web/lib/web/live/settings/api_clients/show.ex b/elixir/apps/web/lib/web/live/settings/api_clients/show.ex index 11672d49c..abf4b54af 100644 --- a/elixir/apps/web/lib/web/live/settings/api_clients/show.ex +++ b/elixir/apps/web/lib/web/live/settings/api_clients/show.ex @@ -54,9 +54,8 @@ defmodule Web.Settings.ApiClients.Show do <.section> <:title> API Client: {@actor.name} - (deleted) - <:action :if={is_nil(@actor.deleted_at)}> + <:action> <.edit_button navigate={~p"/#{@account}/settings/api_clients/#{@actor}/edit"}> Edit API Client @@ -81,7 +80,7 @@ defmodule Web.Settings.ApiClients.Show do Disable API Client - <:action :if={is_nil(@actor.deleted_at) and Actors.actor_disabled?(@actor)}> + <:action :if={Actors.actor_disabled?(@actor)}> <.button_with_confirmation id="enable" style="warning" @@ -210,7 +209,7 @@ defmodule Web.Settings.ApiClients.Show do - <.danger_zone :if={is_nil(@actor.deleted_at)}> + <.danger_zone> <:action> <.button_with_confirmation id="delete_api_client" diff --git a/elixir/apps/web/lib/web/live/settings/identity_providers/components.ex b/elixir/apps/web/lib/web/live/settings/identity_providers/components.ex index 3271ffd49..35a25a7fd 100644 --- a/elixir/apps/web/lib/web/live/settings/identity_providers/components.ex +++ b/elixir/apps/web/lib/web/live/settings/identity_providers/components.ex @@ -1,17 +1,6 @@ defmodule Web.Settings.IdentityProviders.Components do use Web, :component_library - def status(%{provider: %{deleted_at: deleted_at}} = assigns) when not is_nil(deleted_at) do - ~H""" -
- <.ping_icon color="info" /> - - Deleted - -
- """ - end - def status( %{ provider: %{ diff --git a/elixir/apps/web/lib/web/live/settings/identity_providers/google_workspace/show.ex b/elixir/apps/web/lib/web/live/settings/identity_providers/google_workspace/show.ex index 8050b80f8..7c290029f 100644 --- a/elixir/apps/web/lib/web/live/settings/identity_providers/google_workspace/show.ex +++ b/elixir/apps/web/lib/web/live/settings/identity_providers/google_workspace/show.ex @@ -10,14 +10,11 @@ defmodule Web.Settings.IdentityProviders.GoogleWorkspace.Show do Auth.fetch_identities_count_grouped_by_provider_id(socket.assigns.subject), {:ok, groups_count_by_provider_id} <- Actors.fetch_groups_count_grouped_by_provider_id(socket.assigns.subject) do - safe_to_delete_actors_count = Actors.count_synced_actors_for_provider(provider) - {:ok, assign(socket, provider: provider, identities_count_by_provider_id: identities_count_by_provider_id, groups_count_by_provider_id: groups_count_by_provider_id, - safe_to_delete_actors_count: safe_to_delete_actors_count, page_title: "Identity Provider #{provider.name}" )} else @@ -41,16 +38,15 @@ defmodule Web.Settings.IdentityProviders.GoogleWorkspace.Show do <:title> Identity Provider: {@provider.name} (disabled) - (deleted) - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.edit_button navigate={ ~p"/#{@account}/settings/identity_providers/google_workspace/#{@provider.id}/edit" }> Edit - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button_with_confirmation :if={is_nil(@provider.disabled_at)} id="disable" @@ -95,7 +91,7 @@ defmodule Web.Settings.IdentityProviders.GoogleWorkspace.Show do <% end %> - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button style="primary" href={ @@ -125,29 +121,6 @@ defmodule Web.Settings.IdentityProviders.GoogleWorkspace.Show do <.flash_group flash={@flash} /> - <.flash :if={@safe_to_delete_actors_count > 0} kind={:warning}> - You have {@safe_to_delete_actors_count} Actor(s) that were synced from this provider and do not have any other identities. - <.button_with_confirmation - id="delete_stale_actors" - style="danger" - icon="hero-trash-solid" - on_confirm="delete_stale_actors" - class="mt-4" - > - <:dialog_title>Confirm deletion of stale Actors - <:dialog_content> - Are you sure you want to delete all Actors that were synced synced from this provider and do not have any other identities? - - <:dialog_confirm_button> - Delete Actors - - <:dialog_cancel_button> - Cancel - - Delete Actors - - -
<.vertical_table id="provider"> <.vertical_table_row> @@ -206,7 +179,7 @@ defmodule Web.Settings.IdentityProviders.GoogleWorkspace.Show do - <.danger_zone :if={is_nil(@provider.deleted_at)}> + <.danger_zone> <:action> <.button_with_confirmation id="delete_identity_provider" @@ -240,17 +213,6 @@ defmodule Web.Settings.IdentityProviders.GoogleWorkspace.Show do push_navigate(socket, to: ~p"/#{socket.assigns.account}/settings/identity_providers")} end - def handle_event("delete_stale_actors", _params, socket) do - :ok = - Actors.delete_stale_synced_actors_for_provider( - socket.assigns.provider, - socket.assigns.subject - ) - - {:noreply, - push_navigate(socket, to: view_provider(socket.assigns.account, socket.assigns.provider))} - end - def handle_event("enable", _params, socket) do attrs = %{disabled_at: nil} {:ok, provider} = Auth.update_provider(socket.assigns.provider, attrs, socket.assigns.subject) diff --git a/elixir/apps/web/lib/web/live/settings/identity_providers/jumpcloud/show.ex b/elixir/apps/web/lib/web/live/settings/identity_providers/jumpcloud/show.ex index f9a81b67a..7d480fcf0 100644 --- a/elixir/apps/web/lib/web/live/settings/identity_providers/jumpcloud/show.ex +++ b/elixir/apps/web/lib/web/live/settings/identity_providers/jumpcloud/show.ex @@ -10,7 +10,6 @@ defmodule Web.Settings.IdentityProviders.JumpCloud.Show do Auth.fetch_identities_count_grouped_by_provider_id(socket.assigns.subject), {:ok, groups_count_by_provider_id} <- Actors.fetch_groups_count_grouped_by_provider_id(socket.assigns.subject) do - safe_to_delete_actors_count = Actors.count_synced_actors_for_provider(provider) {:ok, maybe_workos_directory} = maybe_fetch_directory(provider) {:ok, @@ -19,7 +18,6 @@ defmodule Web.Settings.IdentityProviders.JumpCloud.Show do identities_count_by_provider_id: identities_count_by_provider_id, groups_count_by_provider_id: groups_count_by_provider_id, workos_directory: maybe_workos_directory, - safe_to_delete_actors_count: safe_to_delete_actors_count, page_title: "Identity Provider #{provider.name}" )} else @@ -43,16 +41,15 @@ defmodule Web.Settings.IdentityProviders.JumpCloud.Show do <:title> Identity Provider: {@provider.name} (disabled) - (deleted) - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.edit_button navigate={ ~p"/#{@account}/settings/identity_providers/jumpcloud/#{@provider.id}/edit" }> Edit - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button_with_confirmation :if={is_nil(@provider.disabled_at)} id="disable" @@ -97,7 +94,7 @@ defmodule Web.Settings.IdentityProviders.JumpCloud.Show do <% end %> - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button style="primary" href={~p"/#{@account.id}/settings/identity_providers/jumpcloud/#{@provider}/redirect"} @@ -125,29 +122,6 @@ defmodule Web.Settings.IdentityProviders.JumpCloud.Show do <.flash_group flash={@flash} /> - <.flash :if={@safe_to_delete_actors_count > 0} kind={:warning}> - You have {@safe_to_delete_actors_count} Actor(s) that were synced from this provider and do not have any other identities. - <.button_with_confirmation - id="delete_stale_actors" - style="danger" - icon="hero-trash-solid" - on_confirm="delete_stale_actors" - class="mt-4" - > - <:dialog_title>Confirm deletion of stale Actors - <:dialog_content> - Are you sure you want to delete all Actors that were synced synced from this provider and do not have any other identities? - - <:dialog_confirm_button> - Delete Actors - - <:dialog_cancel_button> - Cancel - - Delete Actors - - -
<.vertical_table id="provider"> <.vertical_table_row> @@ -217,7 +191,7 @@ defmodule Web.Settings.IdentityProviders.JumpCloud.Show do - <.danger_zone :if={is_nil(@provider.deleted_at)}> + <.danger_zone> <:action> <.button_with_confirmation id="delete_identity_provider" @@ -251,17 +225,6 @@ defmodule Web.Settings.IdentityProviders.JumpCloud.Show do push_navigate(socket, to: ~p"/#{socket.assigns.account}/settings/identity_providers")} end - def handle_event("delete_stale_actors", _params, socket) do - :ok = - Actors.delete_stale_synced_actors_for_provider( - socket.assigns.provider, - socket.assigns.subject - ) - - {:noreply, - push_navigate(socket, to: view_provider(socket.assigns.account, socket.assigns.provider))} - end - def handle_event("enable", _params, socket) do attrs = %{disabled_at: nil} {:ok, provider} = Auth.update_provider(socket.assigns.provider, attrs, socket.assigns.subject) @@ -320,7 +283,7 @@ defmodule Web.Settings.IdentityProviders.JumpCloud.Show do end defp provider_active?(provider) do - is_nil(provider.deleted_at) and is_nil(provider.disabled_at) + is_nil(provider.disabled_at) end defp maybe_fetch_directory(provider) do diff --git a/elixir/apps/web/lib/web/live/settings/identity_providers/microsoft_entra/show.ex b/elixir/apps/web/lib/web/live/settings/identity_providers/microsoft_entra/show.ex index e1a2d4fb7..a75ec3fc4 100644 --- a/elixir/apps/web/lib/web/live/settings/identity_providers/microsoft_entra/show.ex +++ b/elixir/apps/web/lib/web/live/settings/identity_providers/microsoft_entra/show.ex @@ -10,14 +10,11 @@ defmodule Web.Settings.IdentityProviders.MicrosoftEntra.Show do Auth.fetch_identities_count_grouped_by_provider_id(socket.assigns.subject), {:ok, groups_count_by_provider_id} <- Actors.fetch_groups_count_grouped_by_provider_id(socket.assigns.subject) do - safe_to_delete_actors_count = Actors.count_synced_actors_for_provider(provider) - {:ok, assign(socket, provider: provider, identities_count_by_provider_id: identities_count_by_provider_id, groups_count_by_provider_id: groups_count_by_provider_id, - safe_to_delete_actors_count: safe_to_delete_actors_count, page_title: "Identity Provider #{provider.name}" )} else @@ -41,16 +38,15 @@ defmodule Web.Settings.IdentityProviders.MicrosoftEntra.Show do <:title> Identity Provider: {@provider.name} (disabled) - (deleted) - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.edit_button navigate={ ~p"/#{@account}/settings/identity_providers/microsoft_entra/#{@provider.id}/edit" }> Edit - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button_with_confirmation :if={is_nil(@provider.disabled_at)} id="disable" @@ -95,7 +91,7 @@ defmodule Web.Settings.IdentityProviders.MicrosoftEntra.Show do <% end %> - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button style="primary" href={~p"/#{@account.id}/settings/identity_providers/microsoft_entra/#{@provider}/redirect"} @@ -123,29 +119,6 @@ defmodule Web.Settings.IdentityProviders.MicrosoftEntra.Show do <.flash_group flash={@flash} /> - <.flash :if={@safe_to_delete_actors_count > 0} kind={:warning}> - You have {@safe_to_delete_actors_count} Actor(s) that were synced from this provider and do not have any other identities. - <.button_with_confirmation - id="delete_stale_actors" - style="danger" - icon="hero-trash-solid" - on_confirm="delete_stale_actors" - class="mt-4" - > - <:dialog_title>Confirm deletion of stale Actors - <:dialog_content> - Are you sure you want to delete all Actors that were synced synced from this provider and do not have any other identities? - - <:dialog_confirm_button> - Delete Actors - - <:dialog_cancel_button> - Cancel - - Delete Actors - - -
<.vertical_table id="provider"> <.vertical_table_row> @@ -204,7 +177,7 @@ defmodule Web.Settings.IdentityProviders.MicrosoftEntra.Show do - <.danger_zone :if={is_nil(@provider.deleted_at)}> + <.danger_zone> <:action> <.button_with_confirmation id="delete_identity_provider" @@ -238,17 +211,6 @@ defmodule Web.Settings.IdentityProviders.MicrosoftEntra.Show do push_navigate(socket, to: ~p"/#{socket.assigns.account}/settings/identity_providers")} end - def handle_event("delete_stale_actors", _params, socket) do - :ok = - Actors.delete_stale_synced_actors_for_provider( - socket.assigns.provider, - socket.assigns.subject - ) - - {:noreply, - push_navigate(socket, to: view_provider(socket.assigns.account, socket.assigns.provider))} - end - def handle_event("enable", _params, socket) do attrs = %{disabled_at: nil} {:ok, provider} = Auth.update_provider(socket.assigns.provider, attrs, socket.assigns.subject) diff --git a/elixir/apps/web/lib/web/live/settings/identity_providers/mock/show.ex b/elixir/apps/web/lib/web/live/settings/identity_providers/mock/show.ex index b1cbac1c7..394bdbb0b 100644 --- a/elixir/apps/web/lib/web/live/settings/identity_providers/mock/show.ex +++ b/elixir/apps/web/lib/web/live/settings/identity_providers/mock/show.ex @@ -10,14 +10,11 @@ defmodule Web.Settings.IdentityProviders.Mock.Show do Auth.fetch_identities_count_grouped_by_provider_id(socket.assigns.subject), {:ok, groups_count_by_provider_id} <- Actors.fetch_groups_count_grouped_by_provider_id(socket.assigns.subject) do - safe_to_delete_actors_count = Actors.count_synced_actors_for_provider(provider) - {:ok, assign(socket, provider: provider, identities_count_by_provider_id: identities_count_by_provider_id, groups_count_by_provider_id: groups_count_by_provider_id, - safe_to_delete_actors_count: safe_to_delete_actors_count, page_title: "Identity Provider #{provider.name}" )} else @@ -41,16 +38,15 @@ defmodule Web.Settings.IdentityProviders.Mock.Show do <:title> Identity Provider: {@provider.name} (disabled) - (deleted) - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.edit_button navigate={ ~p"/#{@account}/settings/identity_providers/mock/#{@provider.id}/edit" }> Edit - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button_with_confirmation :if={is_nil(@provider.disabled_at)} id="disable" @@ -112,29 +108,6 @@ defmodule Web.Settings.IdentityProviders.Mock.Show do <.flash_group flash={@flash} /> - <.flash :if={@safe_to_delete_actors_count > 0} kind={:warning}> - You have {@safe_to_delete_actors_count} Actor(s) that were synced from this provider and do not have any other identities. - <.button_with_confirmation - id="delete_stale_actors" - style="danger" - icon="hero-trash-solid" - on_confirm="delete_stale_actors" - class="mt-4" - > - <:dialog_title>Confirm deletion of stale Actors - <:dialog_content> - Are you sure you want to delete all Actors that were synced synced from this provider and do not have any other identities? - - <:dialog_confirm_button> - Delete Actors - - <:dialog_cancel_button> - Cancel - - Delete Actors - - -
<.vertical_table id="provider"> <.vertical_table_row> @@ -209,7 +182,7 @@ defmodule Web.Settings.IdentityProviders.Mock.Show do - <.danger_zone :if={is_nil(@provider.deleted_at)}> + <.danger_zone> <:action> <.button_with_confirmation id="delete_identity_provider" @@ -243,17 +216,6 @@ defmodule Web.Settings.IdentityProviders.Mock.Show do push_navigate(socket, to: ~p"/#{socket.assigns.account}/settings/identity_providers")} end - def handle_event("delete_stale_actors", _params, socket) do - :ok = - Actors.delete_stale_synced_actors_for_provider( - socket.assigns.provider, - socket.assigns.subject - ) - - {:noreply, - push_navigate(socket, to: view_provider(socket.assigns.account, socket.assigns.provider))} - end - def handle_event("enable", _params, socket) do attrs = %{disabled_at: nil} {:ok, provider} = Auth.update_provider(socket.assigns.provider, attrs, socket.assigns.subject) diff --git a/elixir/apps/web/lib/web/live/settings/identity_providers/okta/show.ex b/elixir/apps/web/lib/web/live/settings/identity_providers/okta/show.ex index 3cc254b26..5b4862358 100644 --- a/elixir/apps/web/lib/web/live/settings/identity_providers/okta/show.ex +++ b/elixir/apps/web/lib/web/live/settings/identity_providers/okta/show.ex @@ -10,15 +10,12 @@ defmodule Web.Settings.IdentityProviders.Okta.Show do Auth.fetch_identities_count_grouped_by_provider_id(socket.assigns.subject), {:ok, groups_count_by_provider_id} <- Actors.fetch_groups_count_grouped_by_provider_id(socket.assigns.subject) do - safe_to_delete_actors_count = Actors.count_synced_actors_for_provider(provider) - {:ok, assign(socket, page_title: "Identity Provider #{provider.name}", provider: provider, identities_count_by_provider_id: identities_count_by_provider_id, - groups_count_by_provider_id: groups_count_by_provider_id, - safe_to_delete_actors_count: safe_to_delete_actors_count + groups_count_by_provider_id: groups_count_by_provider_id )} else _ -> raise Web.LiveErrors.NotFoundError @@ -41,16 +38,15 @@ defmodule Web.Settings.IdentityProviders.Okta.Show do <:title> Identity Provider: {@provider.name} (disabled) - (deleted) - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.edit_button navigate={ ~p"/#{@account}/settings/identity_providers/okta/#{@provider.id}/edit" }> Edit - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button_with_confirmation :if={is_nil(@provider.disabled_at)} id="disable" @@ -95,7 +91,7 @@ defmodule Web.Settings.IdentityProviders.Okta.Show do <% end %> - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button style="primary" href={~p"/#{@account.id}/settings/identity_providers/okta/#{@provider}/redirect"} @@ -123,29 +119,6 @@ defmodule Web.Settings.IdentityProviders.Okta.Show do <.flash_group flash={@flash} /> - <.flash :if={@safe_to_delete_actors_count > 0} kind={:warning}> - You have {@safe_to_delete_actors_count} Actor(s) that were synced from this provider and do not have any other identities. - <.button_with_confirmation - id="delete_stale_actors" - style="danger" - icon="hero-trash-solid" - on_confirm="delete_stale_actors" - class="mt-4" - > - <:dialog_title>Confirm deletion of stale Actors - <:dialog_content> - Are you sure you want to delete all Actors that were synced synced from this provider and do not have any other identities? - - <:dialog_confirm_button> - Delete Actors - - <:dialog_cancel_button> - Cancel - - Delete Actors - - -
<.vertical_table id="provider"> <.vertical_table_row> @@ -224,7 +197,7 @@ defmodule Web.Settings.IdentityProviders.Okta.Show do - <.danger_zone :if={is_nil(@provider.deleted_at)}> + <.danger_zone> <:action> <.button_with_confirmation id="delete_identity_provider" @@ -258,17 +231,6 @@ defmodule Web.Settings.IdentityProviders.Okta.Show do push_navigate(socket, to: ~p"/#{socket.assigns.account}/settings/identity_providers")} end - def handle_event("delete_stale_actors", _params, socket) do - :ok = - Actors.delete_stale_synced_actors_for_provider( - socket.assigns.provider, - socket.assigns.subject - ) - - {:noreply, - push_navigate(socket, to: view_provider(socket.assigns.account, socket.assigns.provider))} - end - def handle_event("enable", _params, socket) do attrs = %{disabled_at: nil} {:ok, provider} = Auth.update_provider(socket.assigns.provider, attrs, socket.assigns.subject) diff --git a/elixir/apps/web/lib/web/live/settings/identity_providers/openid_connect/show.ex b/elixir/apps/web/lib/web/live/settings/identity_providers/openid_connect/show.ex index 1deef0cd9..ec3f206b8 100644 --- a/elixir/apps/web/lib/web/live/settings/identity_providers/openid_connect/show.ex +++ b/elixir/apps/web/lib/web/live/settings/identity_providers/openid_connect/show.ex @@ -1,16 +1,13 @@ defmodule Web.Settings.IdentityProviders.OpenIDConnect.Show do use Web, :live_view import Web.Settings.IdentityProviders.Components - alias Domain.{Auth, Actors} + alias Domain.Auth def mount(%{"provider_id" => provider_id}, _session, socket) do with {:ok, provider} <- Auth.fetch_provider_by_id(provider_id, socket.assigns.subject) do - safe_to_delete_actors_count = Actors.count_synced_actors_for_provider(provider) - socket = assign(socket, provider: provider, - safe_to_delete_actors_count: safe_to_delete_actors_count, page_title: "Identity Provider #{provider.name}" ) @@ -35,16 +32,15 @@ defmodule Web.Settings.IdentityProviders.OpenIDConnect.Show do <:title> Identity Provider: {@provider.name} (disabled) - (deleted) - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.edit_button navigate={ ~p"/#{@account}/settings/identity_providers/openid_connect/#{@provider}/edit" }> Edit - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button_with_confirmation :if={is_nil(@provider.disabled_at)} id="disable" @@ -87,7 +83,7 @@ defmodule Web.Settings.IdentityProviders.OpenIDConnect.Show do Enable - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button style="primary" href={~p"/#{@account.id}/settings/identity_providers/openid_connect/#{@provider}/redirect"} @@ -102,29 +98,6 @@ defmodule Web.Settings.IdentityProviders.OpenIDConnect.Show do <.flash_group flash={@flash} /> - <.flash :if={@safe_to_delete_actors_count > 0} kind={:warning}> - You have {@safe_to_delete_actors_count} Actor(s) that were synced from this provider and do not have any other identities. - <.button_with_confirmation - id="delete_stale_actors" - style="danger" - icon="hero-trash-solid" - on_confirm="delete_stale_actors" - class="mt-4" - > - <:dialog_title>Confirm deletion of stale Actors - <:dialog_content> - Are you sure you want to delete all Actors that were synced synced from this provider and do not have any other identities? - - <:dialog_confirm_button> - Delete Actors - - <:dialog_cancel_button> - Cancel - - Delete Actors - - -
<.vertical_table id="provider"> <.vertical_table_row> @@ -179,7 +152,7 @@ defmodule Web.Settings.IdentityProviders.OpenIDConnect.Show do
- <.danger_zone :if={is_nil(@provider.deleted_at)}> + <.danger_zone> <:action> <.button_with_confirmation id="delete_identity_provider" @@ -213,17 +186,6 @@ defmodule Web.Settings.IdentityProviders.OpenIDConnect.Show do push_navigate(socket, to: ~p"/#{socket.assigns.account}/settings/identity_providers")} end - def handle_event("delete_stale_actors", _params, socket) do - :ok = - Actors.delete_stale_synced_actors_for_provider( - socket.assigns.provider, - socket.assigns.subject - ) - - {:noreply, - push_navigate(socket, to: view_provider(socket.assigns.account, socket.assigns.provider))} - end - def handle_event("enable", _params, socket) do attrs = %{disabled_at: nil} {:ok, provider} = Auth.update_provider(socket.assigns.provider, attrs, socket.assigns.subject) diff --git a/elixir/apps/web/lib/web/live/settings/identity_providers/system/show.ex b/elixir/apps/web/lib/web/live/settings/identity_providers/system/show.ex index 298b0678f..2f183c37d 100644 --- a/elixir/apps/web/lib/web/live/settings/identity_providers/system/show.ex +++ b/elixir/apps/web/lib/web/live/settings/identity_providers/system/show.ex @@ -21,9 +21,7 @@ defmodule Web.Settings.IdentityProviders.System.Show do Identity Providers Settings - <.breadcrumb path={ - ~p"/#{@account}/settings/identity_providers/google_workspace//DF43E951-7DFB-4921-8F7F-BF0F8D31FA89" - }> + <.breadcrumb path={~p"/#{@account}/settings/identity_providers/system/#{@provider}"}> {@provider.name} @@ -32,9 +30,8 @@ defmodule Web.Settings.IdentityProviders.System.Show do <:title> Identity Provider: {@provider.name} (disabled) - (deleted) - <:action :if={is_nil(@provider.deleted_at)}> + <:action> <.button_with_confirmation :if={is_nil(@provider.disabled_at)} id="disable" diff --git a/elixir/apps/web/lib/web/live/sites/edit.ex b/elixir/apps/web/lib/web/live/sites/edit.ex index 44208b806..de3fd3e56 100644 --- a/elixir/apps/web/lib/web/live/sites/edit.ex +++ b/elixir/apps/web/lib/web/live/sites/edit.ex @@ -3,8 +3,7 @@ defmodule Web.Sites.Edit do alias Domain.Gateways def mount(%{"id" => id}, _session, socket) do - with {:ok, group} <- - Gateways.fetch_group_by_id(id, socket.assigns.subject, filter: [deleted?: false]) do + with {:ok, group} <- Gateways.fetch_group_by_id(id, socket.assigns.subject) do changeset = Gateways.change_group(group) socket = diff --git a/elixir/apps/web/lib/web/live/sites/gateways/index.ex b/elixir/apps/web/lib/web/live/sites/gateways/index.ex index 7be37a04a..d445ef3fb 100644 --- a/elixir/apps/web/lib/web/live/sites/gateways/index.ex +++ b/elixir/apps/web/lib/web/live/sites/gateways/index.ex @@ -69,7 +69,7 @@ defmodule Web.Sites.Gateways.Index do Gateways deployed to the Internet Site will be used for full-route tunneling of traffic that doesn't match a more specific Resource. - <:help :if={is_nil(@group.deleted_at) and @group.managed_by == :account}> + <:help :if={@group.managed_by == :account}> Deploy gateways to terminate connections to your site's resources. All gateways deployed within a site must be able to reach all its resources. @@ -108,7 +108,7 @@ defmodule Web.Sites.Gateways.Index do Deploy a Gateway to the Internet Site. - + <.link class={[link_style()]} navigate={~p"/#{@account}/sites/#{@group}/new_token"}> Deploy a Gateway to connect Resources. diff --git a/elixir/apps/web/lib/web/live/sites/new_token.ex b/elixir/apps/web/lib/web/live/sites/new_token.ex index 80953cad9..b4e4913f4 100644 --- a/elixir/apps/web/lib/web/live/sites/new_token.ex +++ b/elixir/apps/web/lib/web/live/sites/new_token.ex @@ -4,11 +4,7 @@ defmodule Web.Sites.NewToken do def mount(%{"id" => id}, _session, socket) do with {:ok, group} <- - Gateways.fetch_group_by_id(id, socket.assigns.subject, - filter: [ - deleted?: false - ] - ) do + Gateways.fetch_group_by_id(id, socket.assigns.subject) do {group, token, env} = if connected?(socket) do {:ok, token, encoded_token} = Gateways.create_token(group, %{}, socket.assigns.subject) diff --git a/elixir/apps/web/lib/web/live/sites/show.ex b/elixir/apps/web/lib/web/live/sites/show.ex index 64bad2188..799598a64 100644 --- a/elixir/apps/web/lib/web/live/sites/show.ex +++ b/elixir/apps/web/lib/web/live/sites/show.ex @@ -25,11 +25,7 @@ defmodule Web.Sites.Show do defp mount_page(socket, %{managed_by: :system, name: "Internet"} = group) do with {:ok, resource} <- Resources.fetch_internet_resource(socket.assigns.subject, - preload: [ - :gateway_groups, - replaced_by_resource: [], - replaces_resource: [] - ] + preload: [:gateway_groups] ) do socket = socket @@ -177,10 +173,9 @@ defmodule Web.Sites.Show do <.section> <:title> Site: {@group.name} - (deleted) - <:action :if={is_nil(@group.deleted_at) and @group.managed_by == :account}> + <:action :if={@group.managed_by == :account}> <.edit_button navigate={~p"/#{@account}/sites/#{@group}/edit"}> Edit Site @@ -216,12 +211,12 @@ defmodule Web.Sites.Show do <:action> <.docs_action path="/deploy/gateways" /> - <:action :if={is_nil(@group.deleted_at)}> + <:action> <.add_button navigate={~p"/#{@account}/sites/#{@group}/new_token"}> Deploy Gateway - <:action :if={is_nil(@group.deleted_at)}> + <:action> <.button_with_confirmation id="revoke_all_tokens" style="danger" @@ -245,7 +240,7 @@ defmodule Web.Sites.Show do <:help :if={@group.managed_by == :system and @group.name == "Internet"}> Gateways deployed to the Internet Site are used to tunnel all traffic that doesn't match any specific Resource. - <:help :if={is_nil(@group.deleted_at) and @group.managed_by == :account}> + <:help :if={@group.managed_by == :account}> Deploy gateways to terminate connections to your site's resources. All gateways deployed within a site must be able to reach all its resources. @@ -298,7 +293,7 @@ defmodule Web.Sites.Show do Deploy a Gateway to the Internet Site. - + <.link class={[link_style()]} navigate={~p"/#{@account}/sites/#{@group}/new_token"} @@ -318,7 +313,7 @@ defmodule Web.Sites.Show do <:title> Resources - <:action :if={is_nil(@group.deleted_at)}> + <:action> <.add_button navigate={~p"/#{@account}/resources/new?site_id=#{@group}"}> Add Resource @@ -420,14 +415,10 @@ defmodule Web.Sites.Show do <.group account={@account} group={policy.actor_group} /> <:col :let={policy} label="status"> - <%= if is_nil(policy.deleted_at) do %> - <%= if is_nil(policy.disabled_at) do %> - Active - <% else %> - Disabled - <% end %> + <%= if is_nil(policy.disabled_at) do %> + Active <% else %> - Deleted + Disabled <% end %> <:empty> @@ -451,7 +442,7 @@ defmodule Web.Sites.Show do - <.danger_zone :if={is_nil(@group.deleted_at) and @group.managed_by == :account}> + <.danger_zone :if={@group.managed_by == :account}> <:action> <.button_with_confirmation id="delete_site" diff --git a/elixir/apps/web/test/web/live/settings/identity_providers/google_workspace/show_test.exs b/elixir/apps/web/test/web/live/settings/identity_providers/google_workspace/show_test.exs index 9782c15c6..9e571bedd 100644 --- a/elixir/apps/web/test/web/live/settings/identity_providers/google_workspace/show_test.exs +++ b/elixir/apps/web/test/web/live/settings/identity_providers/google_workspace/show_test.exs @@ -36,23 +36,6 @@ defmodule Web.Live.Settings.IdentityProviders.GoogleWorkspace.ShowTest do }}} end - test "renders deleted provider without action buttons", %{ - account: account, - provider: provider, - identity: identity, - conn: conn - } do - provider = Fixtures.Auth.delete_provider(provider) - - {:ok, _lv, html} = - conn - |> authorize_conn(identity) - |> live(~p"/#{account}/settings/identity_providers/google_workspace/#{provider}") - - assert html =~ "(deleted)" - assert active_buttons(html) == [] - end - test "renders breadcrumbs item", %{ account: account, provider: provider, diff --git a/elixir/apps/web/test/web/live/settings/identity_providers/jumpcloud/show_test.exs b/elixir/apps/web/test/web/live/settings/identity_providers/jumpcloud/show_test.exs index 6a704d1af..1202fea37 100644 --- a/elixir/apps/web/test/web/live/settings/identity_providers/jumpcloud/show_test.exs +++ b/elixir/apps/web/test/web/live/settings/identity_providers/jumpcloud/show_test.exs @@ -37,27 +37,6 @@ defmodule Web.Live.Settings.IdentityProviders.JumpCloud.ShowTest do }}} end - test "renders deleted provider without action buttons", %{ - account: account, - provider: provider, - identity: identity, - conn: conn - } do - provider = Fixtures.Auth.delete_provider(provider) - - bypass = Bypass.open() - WorkOSDirectory.override_base_url("http://localhost:#{bypass.port}") - WorkOSDirectory.mock_list_directories_endpoint(bypass) - - {:ok, _lv, html} = - conn - |> authorize_conn(identity) - |> live(~p"/#{account}/settings/identity_providers/jumpcloud/#{provider}") - - assert html =~ "(deleted)" - assert active_buttons(html) == [] - end - test "renders breadcrumbs item", %{ account: account, provider: provider, diff --git a/elixir/apps/web/test/web/live/settings/identity_providers/microsoft_entra/show_test.exs b/elixir/apps/web/test/web/live/settings/identity_providers/microsoft_entra/show_test.exs index d1293d601..6bf7abf4b 100644 --- a/elixir/apps/web/test/web/live/settings/identity_providers/microsoft_entra/show_test.exs +++ b/elixir/apps/web/test/web/live/settings/identity_providers/microsoft_entra/show_test.exs @@ -36,23 +36,6 @@ defmodule Web.Live.Settings.IdentityProviders.MicrosoftEntra.ShowTest do }}} end - test "renders deleted provider without action buttons", %{ - account: account, - provider: provider, - identity: identity, - conn: conn - } do - provider = Fixtures.Auth.delete_provider(provider) - - {:ok, _lv, html} = - conn - |> authorize_conn(identity) - |> live(~p"/#{account}/settings/identity_providers/microsoft_entra/#{provider}") - - assert html =~ "(deleted)" - assert active_buttons(html) == [] - end - test "renders breadcrumbs item", %{ account: account, provider: provider, diff --git a/elixir/apps/web/test/web/live/settings/identity_providers/okta/show_test.exs b/elixir/apps/web/test/web/live/settings/identity_providers/okta/show_test.exs index 5fb1884dc..71b764fbf 100644 --- a/elixir/apps/web/test/web/live/settings/identity_providers/okta/show_test.exs +++ b/elixir/apps/web/test/web/live/settings/identity_providers/okta/show_test.exs @@ -36,23 +36,6 @@ defmodule Web.Live.Settings.IdentityProviders.Okta.ShowTest do }}} end - test "renders deleted provider without action buttons", %{ - account: account, - provider: provider, - identity: identity, - conn: conn - } do - provider = Fixtures.Auth.delete_provider(provider) - - {:ok, _lv, html} = - conn - |> authorize_conn(identity) - |> live(~p"/#{account}/settings/identity_providers/okta/#{provider}") - - assert html =~ "(deleted)" - assert active_buttons(html) == [] - end - test "renders breadcrumbs item", %{ account: account, provider: provider, diff --git a/elixir/apps/web/test/web/live/settings/identity_providers/openid_connect/show_test.exs b/elixir/apps/web/test/web/live/settings/identity_providers/openid_connect/show_test.exs index c09cef8e1..d6b98f6a0 100644 --- a/elixir/apps/web/test/web/live/settings/identity_providers/openid_connect/show_test.exs +++ b/elixir/apps/web/test/web/live/settings/identity_providers/openid_connect/show_test.exs @@ -35,23 +35,6 @@ defmodule Web.Live.Settings.IdentityProviders.OpenIDConnect.ShowTest do }}} end - test "renders deleted provider without action buttons", %{ - account: account, - provider: provider, - identity: identity, - conn: conn - } do - provider = Fixtures.Auth.delete_provider(provider) - - {:ok, _lv, html} = - conn - |> authorize_conn(identity) - |> live(~p"/#{account}/settings/identity_providers/openid_connect/#{provider}") - - assert html =~ "(deleted)" - assert active_buttons(html) == [] - end - test "renders breadcrumbs item", %{ account: account, provider: provider, diff --git a/elixir/apps/web/test/web/live/settings/identity_providers/system/show_test.exs b/elixir/apps/web/test/web/live/settings/identity_providers/system/show_test.exs index 713cbe578..609ff20b4 100644 --- a/elixir/apps/web/test/web/live/settings/identity_providers/system/show_test.exs +++ b/elixir/apps/web/test/web/live/settings/identity_providers/system/show_test.exs @@ -33,23 +33,6 @@ defmodule Web.Live.Settings.IdentityProviders.System.ShowTest do }}} end - test "renders deleted provider without action buttons", %{ - account: account, - identity: identity, - conn: conn - } do - provider = Fixtures.Auth.create_userpass_provider(account: account) - provider = Fixtures.Auth.delete_provider(provider) - - {:ok, _lv, html} = - conn - |> authorize_conn(identity) - |> live(~p"/#{account}/settings/identity_providers/system/#{provider}") - - assert html =~ "(deleted)" - assert active_buttons(html) == [] - end - test "renders breadcrumbs item", %{ account: account, provider: provider, diff --git a/scripts/tests/lib.sh b/scripts/tests/lib.sh index 69acbe147..370b43036 100755 --- a/scripts/tests/lib.sh +++ b/scripts/tests/lib.sh @@ -41,10 +41,10 @@ function api_send_reject_access() { Application.ensure_all_started(:domain) account_id = \"c89bcc8c-9392-4dae-a40d-888aef6d28e0\" -[gateway_group] = Domain.Gateways.Group.Query.not_deleted() |> Domain.Gateways.Group.Query.by_account_id(account_id) |> Domain.Gateways.Group.Query.by_name(\"$site_name\") |> Domain.Repo.all() +[gateway_group] = Domain.Gateways.Group.Query.all() |> Domain.Gateways.Group.Query.by_account_id(account_id) |> Domain.Gateways.Group.Query.by_name(\"$site_name\") |> Domain.Repo.all() [gateway_id | _] = Domain.Gateways.Presence.Group.list(gateway_group.id) |> Map.keys() [client_id | _] = Domain.Clients.Presence.Account.list(account_id) |> Map.keys() -[resource] = Domain.Resources.Resource.Query.not_deleted() |> Domain.Resources.Resource.Query.by_account_id(account_id) |> Domain.Repo.all() |> Enum.filter(&(&1.name == \"$resource_name\")) +[resource] = Domain.Resources.Resource.Query.all() |> Domain.Resources.Resource.Query.by_account_id(account_id) |> Domain.Repo.all() |> Enum.filter(&(&1.name == \"$resource_name\")) Domain.PubSub.Account.broadcast(account_id, {{:reject_access, gateway_id}, client_id, resource.id}) "