mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
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
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. " <>
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<from a0 in Domain.Accounts.Account,\
|
||||
as: :accounts,\
|
||||
where: is_nil(a0.deleted_at)>\
|
||||
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<from a0 in Domain.Accounts.Account,\
|
||||
as: :accounts,\
|
||||
where: not is_nil(a0.deleted_at)>\
|
||||
where: not is_nil(a0.disabled_at)>\
|
||||
"""
|
||||
end
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,10 +29,7 @@ defmodule Web.Actors.Components do
|
||||
<span :if={Actors.actor_disabled?(@actor)} class="text-red-800">
|
||||
Disabled
|
||||
</span>
|
||||
<span :if={Actors.actor_deleted?(@actor)} class="text-red-800">
|
||||
Deleted
|
||||
</span>
|
||||
<span :if={not Actors.actor_disabled?(@actor) and not Actors.actor_deleted?(@actor)}>
|
||||
<span :if={not Actors.actor_disabled?(@actor)}>
|
||||
Active
|
||||
</span>
|
||||
"""
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -159,15 +159,14 @@ defmodule Web.Actors.Show do
|
||||
<:title>
|
||||
{actor_type(@actor.type)}: <span class="font-medium">{@actor.name}</span>
|
||||
<span :if={@actor.id == @subject.actor.id} class="text-sm text-neutral-400">(you)</span>
|
||||
<span :if={Actors.actor_deleted?(@actor)} class="text-red-600">(deleted)</span>
|
||||
<span :if={Actors.actor_disabled?(@actor)} class="text-red-600">(disabled)</span>
|
||||
</:title>
|
||||
<:action :if={is_nil(@actor.deleted_at)}>
|
||||
<:action>
|
||||
<.edit_button navigate={~p"/#{@account}/actors/#{@actor}/edit"}>
|
||||
Edit {actor_type(@actor.type)}
|
||||
</.edit_button>
|
||||
</:action>
|
||||
<: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)}
|
||||
</.button_with_confirmation>
|
||||
</:action>
|
||||
<: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.
|
||||
</:help>
|
||||
|
||||
<: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
|
||||
<div class="flex justify-center text-center text-neutral-500 p-4">
|
||||
<div class="w-auto pb-4">
|
||||
No authentication identities to display.
|
||||
<span :if={is_nil(@actor.deleted_at) and @actor.type == :service_account}>
|
||||
<span :if={@actor.type == :service_account}>
|
||||
<.link
|
||||
class={[link_style()]}
|
||||
navigate={~p"/#{@account}/actors/service_accounts/#{@actor}/new_identity"}
|
||||
@@ -325,7 +324,7 @@ defmodule Web.Actors.Show do
|
||||
</.link>
|
||||
to authenticate this service account.
|
||||
</span>
|
||||
<span :if={is_nil(@actor.deleted_at) and @actor.type != :service_account}>
|
||||
<span :if={@actor.type != :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.
|
||||
</:help>
|
||||
|
||||
<: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
|
||||
</.add_button>
|
||||
</:action>
|
||||
|
||||
<: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
|
||||
</:content>
|
||||
</.section>
|
||||
|
||||
<.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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -73,10 +73,9 @@ defmodule Web.Clients.Show do
|
||||
<.section>
|
||||
<:title>
|
||||
Client Details
|
||||
<span :if={not is_nil(@client.deleted_at)} class="text-red-600">(deleted)</span>
|
||||
</:title>
|
||||
|
||||
<:action :if={is_nil(@client.deleted_at)}>
|
||||
<:action>
|
||||
<.edit_button navigate={~p"/#{@account}/clients/#{@client}/edit"}>
|
||||
Edit Client
|
||||
</.edit_button>
|
||||
@@ -182,7 +181,7 @@ defmodule Web.Clients.Show do
|
||||
Information about the device that the Client is running on.
|
||||
</:help>
|
||||
|
||||
<: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
|
||||
</.button_with_confirmation>
|
||||
</:action>
|
||||
<: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
|
||||
</:content>
|
||||
</.section>
|
||||
|
||||
<.danger_zone :if={is_nil(@client.deleted_at)}>
|
||||
<.danger_zone>
|
||||
<:action>
|
||||
<.button_with_confirmation
|
||||
id="delete_client"
|
||||
|
||||
@@ -39,7 +39,6 @@ defmodule Web.Gateways.Show do
|
||||
<.section>
|
||||
<:title>
|
||||
Gateway: <code>{@gateway.name}</code>
|
||||
<span :if={not is_nil(@gateway.deleted_at)} class="text-red-600">(deleted)</span>
|
||||
</:title>
|
||||
<:content>
|
||||
<.vertical_table id="gateway">
|
||||
@@ -106,7 +105,7 @@ defmodule Web.Gateways.Show do
|
||||
</:content>
|
||||
</.section>
|
||||
|
||||
<.danger_zone :if={is_nil(@gateway.deleted_at)}>
|
||||
<.danger_zone>
|
||||
<:action>
|
||||
<.button_with_confirmation
|
||||
id="delete_gateway"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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} />
|
||||
|
||||
<span :if={Actors.group_soft_deleted?(group)} class="text-xs text-neutral-100">
|
||||
(deleted)
|
||||
</span>
|
||||
</:col>
|
||||
<:col :let={group} label="actors">
|
||||
<.peek peek={Map.fetch!(@group_actors, group.id)}>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user