diff --git a/elixir/apps/domain/lib/domain/actors.ex b/elixir/apps/domain/lib/domain/actors.ex index d8cfe5273..cdaaf59fa 100644 --- a/elixir/apps/domain/lib/domain/actors.ex +++ b/elixir/apps/domain/lib/domain/actors.ex @@ -46,6 +46,15 @@ defmodule Domain.Actors do end end + 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.by_actor_id(actor.id) + |> Authorizer.for_subject(subject) + |> Repo.list(Group.Query, opts) + end + end + def all_groups!(%Auth.Subject{} = subject, opts \\ []) do {preload, _opts} = Keyword.pop(opts, :preload, []) diff --git a/elixir/apps/domain/lib/domain/actors/group/query.ex b/elixir/apps/domain/lib/domain/actors/group/query.ex index 2d32cdf52..f5b1e002d 100644 --- a/elixir/apps/domain/lib/domain/actors/group/query.ex +++ b/elixir/apps/domain/lib/domain/actors/group/query.ex @@ -42,6 +42,13 @@ defmodule Domain.Actors.Group.Query do where(queryable, [groups: groups], groups.account_id == ^account_id) end + def by_actor_id(queryable, actor_id) do + join(queryable, :left, [groups: groups], memberships in assoc(groups, :memberships), + as: :memberships + ) + |> where([memberships: memberships], memberships.actor_id == ^actor_id) + end + def by_provider_id(queryable, provider_id) do where(queryable, [groups: groups], groups.provider_id == ^provider_id) end diff --git a/elixir/apps/domain/test/domain/actors_test.exs b/elixir/apps/domain/test/domain/actors_test.exs index ca27d4092..f48e3f8cf 100644 --- a/elixir/apps/domain/test/domain/actors_test.exs +++ b/elixir/apps/domain/test/domain/actors_test.exs @@ -189,6 +189,83 @@ defmodule Domain.ActorsTest do end end + describe "list_groups_for/2" do + setup do + account = Fixtures.Accounts.create_account() + actor = Fixtures.Actors.create_actor(type: :account_admin_user, account: account) + identity = Fixtures.Auth.create_identity(account: account, actor: actor) + subject = Fixtures.Auth.create_subject(identity: identity) + + %{ + account: account, + actor: actor, + identity: identity, + subject: subject + } + end + + test "returns empty list when there are no groups", %{actor: actor, subject: subject} do + assert {:ok, [], _metadata} = list_groups_for(actor, subject) + end + + test "does not list groups from other accounts", %{actor: actor, subject: subject} do + Fixtures.Actors.create_group() + assert {:ok, [], _metadata} = list_groups_for(actor, subject) + end + + test "does not list groups from other actors in account", %{ + account: account, + actor: actor, + subject: subject + } do + actor2 = Fixtures.Actors.create_actor(account: account) + group = Fixtures.Actors.create_group(account: account) + Fixtures.Actors.create_membership(account: account, actor: actor2, group: group) + + assert {:ok, [], _metadata} = list_groups_for(actor, subject) + end + + 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, + subject: subject + } do + group1 = Fixtures.Actors.create_group(account: account) + Fixtures.Actors.create_membership(account: account, actor: actor, group: group1) + + group2 = Fixtures.Actors.create_group(account: account) + Fixtures.Actors.create_membership(account: account, actor: actor, group: group2) + + Fixtures.Actors.create_group(account: account) + Fixtures.Actors.create_group() + + assert {:ok, groups, _metadata} = list_groups_for(actor, subject) + assert length(groups) == 2 + end + + test "returns error when subject has no permission to manage groups", %{ + actor: actor, + subject: subject + } do + subject = Fixtures.Auth.remove_permissions(subject) + + assert list_groups_for(actor, subject) == + {:error, + {:unauthorized, + reason: :missing_permissions, + missing_permissions: [Actors.Authorizer.manage_actors_permission()]}} + end + end + describe "peek_group_actors/3" do setup do account = Fixtures.Accounts.create_account() diff --git a/elixir/apps/web/lib/web/live/actors/show.ex b/elixir/apps/web/lib/web/live/actors/show.ex index 946712222..d6f982b2c 100644 --- a/elixir/apps/web/lib/web/live/actors/show.ex +++ b/elixir/apps/web/lib/web/live/actors/show.ex @@ -42,6 +42,13 @@ defmodule Web.Actors.Show do limit: 10, callback: &handle_flows_update!/2 ) + |> assign_live_table("groups", + query_module: Actors.Group.Query, + sortable_fields: [], + hide_filters: [:provider_id], + limit: 15, + callback: &handle_groups_update!/2 + ) {:ok, socket} else @@ -67,6 +74,19 @@ defmodule Web.Actors.Show do end end + def handle_groups_update!(socket, list_opts) do + list_opts = Keyword.put(list_opts, :preload, [:provider]) + + with {:ok, groups, metadata} <- + Actors.list_groups_for(socket.assigns.actor, socket.assigns.subject, list_opts) do + {:ok, + assign(socket, + groups: groups, + groups_metadata: metadata + )} + end + end + def handle_tokens_update!(socket, list_opts) do list_opts = Keyword.put(list_opts, :preload, @@ -171,18 +191,6 @@ defmodule Web.Actors.Show do - <.vertical_table_row> - <:label>Groups - <:value> -