diff --git a/elixir/apps/domain/lib/domain/actors.ex b/elixir/apps/domain/lib/domain/actors.ex index 37d28e45d..4e2f2e37f 100644 --- a/elixir/apps/domain/lib/domain/actors.ex +++ b/elixir/apps/domain/lib/domain/actors.ex @@ -129,12 +129,18 @@ defmodule Domain.Actors do end end - def fetch_actor_by_id(id, %Auth.Subject{} = subject) do + def fetch_actor_by_id(id, %Auth.Subject{} = subject, opts \\ []) do + {preload, _opts} = Keyword.pop(opts, :preload, []) + with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()), true <- Validator.valid_uuid?(id) do Actor.Query.by_id(id) |> Authorizer.for_subject(subject) |> Repo.fetch() + |> case do + {:ok, actor} -> {:ok, Repo.preload(actor, preload)} + {:error, reason} -> {:error, reason} + end else false -> {:error, :not_found} other -> other @@ -156,14 +162,17 @@ defmodule Domain.Actors do end def list_actors(%Auth.Subject{} = subject, opts \\ []) do - with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do - {hydrate, _opts} = Keyword.pop(opts, :hydrate, []) + {preload, _opts} = Keyword.pop(opts, :preload, []) + {hydrate, _opts} = Keyword.pop(opts, :hydrate, []) - Actor.Query.all() - |> Authorizer.for_subject(subject) - # TODO: add filters - |> hydrate_fields(hydrate) - |> Repo.list() + with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do + {:ok, actors} = + Actor.Query.all() + |> Authorizer.for_subject(subject) + |> hydrate_fields(hydrate) + |> Repo.list() + + {:ok, Repo.preload(actors, preload)} end end diff --git a/elixir/apps/domain/lib/domain/devices.ex b/elixir/apps/domain/lib/domain/devices.ex index 2dd121ee0..1cba291a1 100644 --- a/elixir/apps/domain/lib/domain/devices.ex +++ b/elixir/apps/domain/lib/domain/devices.ex @@ -26,7 +26,9 @@ defmodule Domain.Devices do |> Repo.aggregate(:count) end - def fetch_device_by_id(id, %Auth.Subject{} = subject) do + def fetch_device_by_id(id, %Auth.Subject{} = subject, opts \\ []) do + {preload, _opts} = Keyword.pop(opts, :preload, []) + required_permissions = {:one_of, [ @@ -39,6 +41,10 @@ defmodule Domain.Devices do Device.Query.by_id(id) |> Authorizer.for_subject(subject) |> Repo.fetch() + |> case do + {:ok, device} -> {:ok, Repo.preload(device, preload)} + {:error, reason} -> {:error, reason} + end else false -> {:error, :not_found} other -> other @@ -53,7 +59,9 @@ defmodule Domain.Devices do |> Repo.preload(preload) end - def list_devices(%Auth.Subject{} = subject) do + def list_devices(%Auth.Subject{} = subject, opts \\ []) do + {preload, _opts} = Keyword.pop(opts, :preload, []) + required_permissions = {:one_of, [ @@ -62,9 +70,12 @@ defmodule Domain.Devices do ]} with :ok <- Auth.ensure_has_permissions(subject, required_permissions) do - Device.Query.all() - |> Authorizer.for_subject(subject) - |> Repo.list() + {:ok, devices} = + Device.Query.all() + |> Authorizer.for_subject(subject) + |> Repo.list() + + {:ok, Repo.preload(devices, preload)} end end diff --git a/elixir/apps/domain/priv/repo/seeds.exs b/elixir/apps/domain/priv/repo/seeds.exs index 8f3e2422c..076240589 100644 --- a/elixir/apps/domain/priv/repo/seeds.exs +++ b/elixir/apps/domain/priv/repo/seeds.exs @@ -146,6 +146,31 @@ end IO.puts("") +user_iphone = + Domain.Devices.upsert_device( + %{ + name: "FZ User iPhone", + external_id: Ecto.UUID.generate(), + public_key: :crypto.strong_rand_bytes(32) |> Base.encode64(), + last_seen_user_agent: "iOS/12.7 (iPhone) connlib/0.7.412" + }, + unprivileged_subject + ) + +admin_iphone = + Domain.Devices.upsert_device( + %{ + name: "FZ Admin iPhone", + external_id: Ecto.UUID.generate(), + public_key: :crypto.strong_rand_bytes(32) |> Base.encode64(), + last_seen_user_agent: "iOS/12.7 (iPhone) connlib/0.7.412" + }, + admin_subject + ) + +IO.puts("Devices created") +IO.puts("") + relay_group = account |> Relays.Group.Changeset.create_changeset( diff --git a/elixir/apps/domain/test/domain/actors_test.exs b/elixir/apps/domain/test/domain/actors_test.exs index 086c26cb2..79c667667 100644 --- a/elixir/apps/domain/test/domain/actors_test.exs +++ b/elixir/apps/domain/test/domain/actors_test.exs @@ -624,6 +624,17 @@ defmodule Domain.ActorsTest do {:unauthorized, [missing_permissions: [Actors.Authorizer.manage_actors_permission()]]}} end + + test "associations are preloaded when opts given" do + account = AccountsFixtures.create_account() + actor = ActorsFixtures.create_actor(type: :account_admin_user, account: account) + identity = AuthFixtures.create_identity(account: account, actor: actor) + subject = AuthFixtures.create_subject(identity) + + {:ok, actor} = fetch_actor_by_id(actor.id, subject, preload: :identities) + + assert Ecto.assoc_loaded?(actor.identities) == true + end end describe "fetch_actor_by_id/1" do @@ -704,6 +715,22 @@ defmodule Domain.ActorsTest do {:unauthorized, [missing_permissions: [Actors.Authorizer.manage_actors_permission()]]}} end + + test "associations are preloaded when opts given" do + account = AccountsFixtures.create_account() + + actor1 = ActorsFixtures.create_actor(type: :account_admin_user, account: account) + identity1 = AuthFixtures.create_identity(account: account, actor: actor1) + subject = AuthFixtures.create_subject(identity1) + + actor2 = ActorsFixtures.create_actor(type: :account_user, account: account) + AuthFixtures.create_identity(account: account, actor: actor2) + + {:ok, actors} = list_actors(subject, preload: :identities) + assert length(actors) == 2 + + assert Enum.all?(actors, fn a -> Ecto.assoc_loaded?(a.identities) end) == true + end end describe "create_actor/4" do diff --git a/elixir/apps/web/lib/web/components/layouts/app.html.heex b/elixir/apps/web/lib/web/components/layouts/app.html.heex index 1207b83bc..f42e4c899 100644 --- a/elixir/apps/web/lib/web/components/layouts/app.html.heex +++ b/elixir/apps/web/lib/web/components/layouts/app.html.heex @@ -101,13 +101,15 @@ Dashboard - <.sidebar_item_group id="organization"> - <:name>Organization - - <:item navigate={~p"/#{@account}/actors"}>Users - <:item navigate={~p"/#{@account}/groups"}>Groups - <:item navigate={~p"/#{@account}/devices"}>Devices - + <.sidebar_item navigate={~p"/#{@account}/actors"} icon="hero-user-circle-solid"> + Actors + + <.sidebar_item navigate={~p"/#{@account}/groups"} icon="hero-user-group-solid"> + Groups + + <.sidebar_item navigate={~p"/#{@account}/devices"} icon="hero-device-phone-mobile-solid"> + Devices + <.sidebar_item navigate={~p"/#{@account}/gateways"} icon="hero-arrow-left-on-rectangle-solid"> Gateways @@ -121,7 +123,7 @@ Policies - <.sidebar_item_group id="settings"> + <.sidebar_item_group id="settings" icon="hero-cog-solid"> <:name>Settings <:item navigate={~p"/#{@account}/settings/account"}>Account diff --git a/elixir/apps/web/lib/web/components/navigation_components.ex b/elixir/apps/web/lib/web/components/navigation_components.ex index bd3de26ea..094bfb1ae 100644 --- a/elixir/apps/web/lib/web/components/navigation_components.ex +++ b/elixir/apps/web/lib/web/components/navigation_components.ex @@ -55,7 +55,7 @@ defmodule Web.NavigationComponents do end attr :id, :string, required: true, doc: "ID of the nav group container" - # attr :icon, :string, required: true + attr :icon, :string, required: true # attr :navigate, :string, required: true slot :name, required: true @@ -77,7 +77,7 @@ defmodule Web.NavigationComponents do aria-controls={"dropdown-#{@id}"} data-collapse-toggle={"dropdown-#{@id}"} > - <.icon name="hero-user-group-solid" class={~w[ + <.icon name={@icon} class={~w[ w-6 h-6 text-gray-500 transition duration-75 group-hover:text-gray-900 diff --git a/elixir/apps/web/lib/web/components/table_components.ex b/elixir/apps/web/lib/web/components/table_components.ex index d88f5dcf6..1ccfd7048 100644 --- a/elixir/apps/web/lib/web/components/table_components.ex +++ b/elixir/apps/web/lib/web/components/table_components.ex @@ -223,9 +223,14 @@ defmodule Web.TableComponents do """ + attr :class, :string, default: nil + attr :rest, :global + + slot :inner_block + def vertical_table(assigns) do ~H""" - +
<%= render_slot(@inner_block) %> @@ -250,19 +255,26 @@ defmodule Web.TableComponents do """ + attr :label_class, :string, default: nil + attr :value_class, :string, default: nil + slot :label, doc: "the slot for rendering the label of a row" slot :value, doc: "the slot for rendering the value of a row" def vertical_table_row(assigns) do ~H""" - - diff --git a/elixir/apps/web/lib/web/live/actors/edit.ex b/elixir/apps/web/lib/web/live/actors/edit.ex new file mode 100644 index 000000000..a3c45346d --- /dev/null +++ b/elixir/apps/web/lib/web/live/actors/edit.ex @@ -0,0 +1,108 @@ +defmodule Web.Actors.Edit do + use Web, :live_view + + alias Domain.Actors + + def mount(%{"id" => id} = _params, _session, socket) do + {:ok, actor} = Actors.fetch_actor_by_id(id, socket.assigns.subject) + + {:ok, assign(socket, actor: actor)} + end + + def render(assigns) do + ~H""" + <.breadcrumbs home_path={~p"/#{@account}/dashboard"}> + <.breadcrumb path={~p"/#{@account}/actors"}>Actors + <.breadcrumb path={~p"/#{@account}/actors/#{@actor.id}"}> + <%= @actor.name %> + + <.breadcrumb path={~p"/#{@account}/actors/#{@actor.id}/edit"}> + Edit + + + <.header> + <:title> + Editing User: <%= @actor.name %> + + + +
+
+

Edit User Details

+
+
+
+ <.label for="first-name"> + Name + + <.input type="text" name="name" id="name" value={@actor.name} required="" /> +
+
+ <.label for="email"> + Email + + <.input + aria-describedby="email-explanation" + type="email" + name="email" + id="email" + value="TODO: Email here" + /> +

+ We'll send a confirmation email to both the current email address and the updated one to confirm the change. +

+
+
+ <.label for="confirm-email"> + Confirm email + + <.input type="email" name="confirm-email" id="confirm-email" value="TODO" /> +
+
+ <.label for="user-role"> + Role + + <.input + type="select" + id="user-role" + name="user-role" + options={[ + "End User": :account_user, + Admin: :account_admin_user, + "Service Account": :service_account + ]} + value={@actor.type} + /> +

+ Select Admin to make this user an administrator of your organization. +

+
+
+ <.label for="user-groups"> + Groups + + <.input + type="select" + multiple={true} + name="user-groups" + id="user-groups" + options={["TODO: Devops", "TODO: Engineering", "TODO: Accounting"]} + value="" + /> + +

+ Select one or more groups to allow this user access to resources. +

+
+
+
+ <.button type="submit"> + Save + +
+ +
+
+ """ + end +end diff --git a/elixir/apps/web/lib/web/live/actors/index.ex b/elixir/apps/web/lib/web/live/actors/index.ex new file mode 100644 index 000000000..596b5041b --- /dev/null +++ b/elixir/apps/web/lib/web/live/actors/index.ex @@ -0,0 +1,128 @@ +defmodule Web.Actors.Index do + use Web, :live_view + + alias Domain.Actors + + def mount(_params, _session, socket) do + {_, actors} = Actors.list_actors(socket.assigns.subject, preload: [identities: :provider]) + + {:ok, assign(socket, actors: actors)} + end + + defp actor_icon(type) do + case type do + :account_user -> "hero-user-circle-solid" + :account_admin_user -> "hero-user-circle-solid" + :service_account -> "hero-server-solid" + end + end + + def render(assigns) do + ~H""" + <.breadcrumbs home_path={~p"/#{@account}/dashboard"}> + <.breadcrumb path={~p"/#{@account}/actors"}>Actors + + <.header> + <:title> + All Actors + + <:actions> + <.add_button navigate={~p"/#{@account}/actors/new"}> + Add a new user + + + + +
+ <.resource_filter /> + <.table id="actors" rows={@actors} row_id={&"user-#{&1.id}"}> + <:col :let={actor} label="TYPE" sortable="false"> + <.icon name={actor_icon(actor.type)} class="w-5 h-5" /> + + <:col :let={actor} label="NAME" sortable="false"> + <.link + navigate={~p"/#{@account}/actors/#{actor.id}"} + class="font-medium text-blue-600 dark:text-blue-500 hover:underline" + > + <%= actor.name %> + + + <:col :let={actor} label="IDENTIFIERS" sortable="false"> + <%= for identity <- actor.identities do %> + <%= "#{identity.provider.name}: #{identity.provider_identifier}" %> +
+ <% end %> + + <:col :let={_actor} label="GROUPS" sortable="false"> + + <%= "TODO Admin, Engineering, 3 more..." %> + + <:col :let={_actor} label="LAST ACTIVE" sortable="false"> + + <%= "TODO Today at 2:30pm" %> + + <:action> + <.link + navigate={~p"/#{@account}/actors/#{@subject.actor.id}"} + class="block py-2 px-4 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white" + > + Show + + + <:action> + <.link + navigate={~p"/#{@account}/actors/#{@subject.actor.id}/edit"} + class="block py-2 px-4 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white" + > + Edit + + + <:action> + + Delete + + + + <.paginator page={3} total_pages={100} collection_base_path={~p"/#{@account}/actors"} /> +
+ """ + end + + defp resource_filter(assigns) do + ~H""" +
+
+
+ +
+
+ <.icon name="hero-magnifying-glass" class="w-5 h-5 text-gray-500 dark:text-gray-400" /> +
+ +
+ +
+ <.button_group> + <:first> + All + + <:middle> + Users + + <:last> + Service Accounts + + +
+ """ + end +end diff --git a/elixir/apps/web/lib/web/live/users/new.ex b/elixir/apps/web/lib/web/live/actors/new.ex similarity index 99% rename from elixir/apps/web/lib/web/live/users/new.ex rename to elixir/apps/web/lib/web/live/actors/new.ex index 875540fea..0b0d58de2 100644 --- a/elixir/apps/web/lib/web/live/users/new.ex +++ b/elixir/apps/web/lib/web/live/actors/new.ex @@ -1,4 +1,4 @@ -defmodule Web.Users.New do +defmodule Web.Actors.New do use Web, :live_view def render(assigns) do diff --git a/elixir/apps/web/lib/web/live/actors/show.ex b/elixir/apps/web/lib/web/live/actors/show.ex new file mode 100644 index 000000000..bfd1394f6 --- /dev/null +++ b/elixir/apps/web/lib/web/live/actors/show.ex @@ -0,0 +1,116 @@ +defmodule Web.Actors.Show do + use Web, :live_view + + alias Domain.Actors + + def mount(%{"id" => id} = _params, _session, socket) do + {:ok, actor} = + Actors.fetch_actor_by_id(id, socket.assigns.subject, preload: [identities: [:provider]]) + + {:ok, assign(socket, actor: actor)} + end + + defp account_type_to_string(type) do + case type do + :account_admin_user -> "Admin" + :account_user -> "User" + :service_account -> "Service Account" + end + end + + def render(assigns) do + ~H""" + <.breadcrumbs home_path={~p"/#{@account}/dashboard"}> + <.breadcrumb path={~p"/#{@account}/actors"}>Actors + <.breadcrumb path={~p"/#{@account}/actors/#{@actor.id}"}> + <%= @actor.name %> + + + <.header> + <:title> + Viewing User: <%= @actor.name %> + + <:actions> + <.edit_button navigate={~p"/#{@account}/actors/#{@actor.id}/edit"}> + Edit user + + + + +
+
User Info
+ <.vertical_table> + <.vertical_table_row label_class="w-1/5"> + <:label>Name + <:value><%= @actor.name %> + + <.vertical_table_row> + <:label>Source + <:value>TODO: Manually created by Jamil Bou Kheir on May 3rd, 2023. + + <.vertical_table_row> + <:label>Role + <:value> + <%= account_type_to_string(@actor.type) %> + + + <.vertical_table_row> + <:label>Groups + <:value>TODO: Groups Here + + <.vertical_table_row> + <:label>Last Active + <:value>TODO: Last Active Here + + +
+
+
+ Authentication Identities +
+ <.identity :for={identity <- @actor.identities} class="mb-4"> + <:provider><%= identity.provider.name %> + <:identity><%= identity.provider_identifier %> + <:last_auth><%= identity.last_seen_at %> + +
+ <.header> + <:title> + Danger zone + + <:actions> + <.delete_button> + Delete user + + + + """ + end + + attr(:rest, :global) + + slot(:provider) + slot(:identity) + slot(:last_auth) + + def identity(assigns) do + ~H""" +
+ <.vertical_table class="table-fixed"> + <.vertical_table_row label_class="w-1/5"> + <:label>Provider + <:value><%= render_slot(@provider) %> + + <.vertical_table_row> + <:label>Identity + <:value><%= render_slot(@identity) %> + + <.vertical_table_row> + <:label>Last Authentication + <:value><%= render_slot(@last_auth) %> + + +
+ """ + end +end diff --git a/elixir/apps/web/lib/web/live/devices/index.ex b/elixir/apps/web/lib/web/live/devices/index.ex index 66c43ae86..9f2d8f8d0 100644 --- a/elixir/apps/web/lib/web/live/devices/index.ex +++ b/elixir/apps/web/lib/web/live/devices/index.ex @@ -1,6 +1,14 @@ defmodule Web.Devices.Index do use Web, :live_view + alias Domain.Devices + + def mount(_params, _session, socket) do + {_, devices} = Devices.list_devices(socket.assigns.subject, preload: :actor) + + {:ok, assign(socket, devices: devices)} + end + def render(assigns) do ~H""" <.breadcrumbs home_path={~p"/#{@account}/dashboard"}> @@ -13,264 +21,83 @@ defmodule Web.Devices.Index do
-
-
-
- -
-
- <.icon name="hero-magnifying-glass" class="w-5 h-5 text-gray-500 dark:text-gray-400" /> -
- -
- -
- <.button_group> - <:first> - All - - <:middle> - Online - - <:last> - Archived - - -
-
-
+ <%= render_slot(@label) %> + <%= render_slot(@value) %>
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - Actions -
- <.link - navigate={~p"/#{@account}/devices/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"} - class="font-medium text-blue-600 dark:text-blue-500 hover:underline" - > - v1.01 Linux - - - <.link - navigate={~p"/#{@account}/actors/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"} - class="font-medium text-blue-600 dark:text-blue-500 hover:underline" - > - John Doe - - - - Online - - - - -
- <.link - navigate={~p"/#{@account}/devices/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"} - class="font-medium text-blue-600 dark:text-blue-500 hover:underline" - > - v1.01 iOS - - - <.link - navigate={~p"/#{@account}/actors/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"} - class="font-medium text-blue-600 dark:text-blue-500 hover:underline" - > - Steve Johnson - - - - Last seen 2 hours ago - - - - -
- <.link - navigate={~p"/#{@account}/devices/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"} - class="font-medium text-blue-600 dark:text-blue-500 hover:underline" - > - v1.01 macOS - - - <.link - navigate={~p"/#{@account}/actors/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"} - class="font-medium text-blue-600 dark:text-blue-500 hover:underline" - > - Steinberg, Gabriel - - - - Archived 6 months ago - - - - -
- + <.resource_filter /> + <.table id="devices" rows={@devices} row_id={&"device-#{&1.id}"}> + <:col :let={device} label="CLIENT" sortable="true"> + <.link + navigate={~p"/#{@account}/devices/#{device.id}"} + class="font-medium text-blue-600 dark:text-blue-500 hover:underline" + > + <%= device.name %> + + + <:col :let={device} label="USER" sortable="true"> + <.link + navigate={~p"/#{@account}/actors/#{device.actor.id}"} + class="font-medium text-blue-600 dark:text-blue-500 hover:underline" + > + <%= device.actor.name %> + + + <:col :let={_device} label="STATUS" sortable="true"> + <.badge type="success"> + TODO: Online + + + <:action :let={device}> + <.link + navigate={~p"/#{@account}/devices/#{device.id}"} + class="block py-2 px-4 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white" + > + Show + + + <:action> + + TODO: Archive + + + <.paginator page={3} total_pages={100} collection_base_path={~p"/#{@account}/devices"} /> """ end + + defp resource_filter(assigns) do + ~H""" +
+
+
+ +
+
+ <.icon name="hero-magnifying-glass" class="w-5 h-5 text-gray-500 dark:text-gray-400" /> +
+ +
+
+
+ <.button_group> + <:first> + All + + <:middle> + Online + + <:last> + Archived + + +
+ """ + end end diff --git a/elixir/apps/web/lib/web/live/devices/show.ex b/elixir/apps/web/lib/web/live/devices/show.ex index 4e8d9d263..14281e649 100644 --- a/elixir/apps/web/lib/web/live/devices/show.ex +++ b/elixir/apps/web/lib/web/live/devices/show.ex @@ -1,141 +1,79 @@ defmodule Web.Devices.Show do use Web, :live_view + alias Domain.Devices + + def mount(%{"id" => id} = _params, _session, socket) do + {:ok, device} = Devices.fetch_device_by_id(id, socket.assigns.subject, preload: :actor) + + {:ok, assign(socket, device: device)} + end + def render(assigns) do ~H""" <.breadcrumbs home_path={~p"/#{@account}/dashboard"}> <.breadcrumb path={~p"/#{@account}/devices"}>Devices - <.breadcrumb path={~p"/#{@account}/devices/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"}> - Jamil's Macbook Pro + <.breadcrumb path={~p"/#{@account}/devices/#{@device.id}"}> + <%= @device.name %> <.header> <:title> - Device details + Device Details
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ <.vertical_table_row> + <:label>Identifier + <:value><%= @device.id %> + + <.vertical_table_row> + <:label>Owner + <:value> + <.link + navigate={~p"/#{@account}/actors/#{@device.actor.id}"} + class="font-medium text-blue-600 dark:text-blue-500 hover:underline" > - Identifier - - BF0F8D31FA89 -
- User - - <.link - navigate={~p"/#{@account}/actors/55DDA8CB-69A7-48FC-9048-639021C205A2"} - class="text-blue-600 hover:underline" - > - Andrew Dryga - -
- First seen - - 3 days ago in Bangalore, India -
- Last seen - - 1 hour ago in San Francisco, CA -
- Remote IPv4 - - 69.100.123.11 -
- Remote IPv6 - - 2001:0db8:85a3:0000:0000:8a2e:0370:7334 -
- Transfer - - 4.43 GB up, 1.23 GB down -
- Client version - - v1.01 for macOS/arm64 -
- OS version - - macOS 13.4.1 -
- Machine type - - Macbook Pro -
+ <%= @device.actor.name %> + + + + <.vertical_table_row> + <:label>First Seen + <:value>TODO + + <.vertical_table_row> + <:label>Last Seen + <:value>TODO + + <.vertical_table_row> + <:label>Remote IPv4 + <:value><%= @device.ipv4 %> + + <.vertical_table_row> + <:label>Remote IPv6 + <:value><%= @device.ipv6 %> + + <.vertical_table_row> + <:label>Transfer + <:value>TODO + + <.vertical_table_row> + <:label>Client Version + <:value>TODO + + <.vertical_table_row> + <:label>OS Version + <:value>TODO + + <.vertical_table_row> + <:label>Machine Type + <:value>TODO + +
<.header> diff --git a/elixir/apps/web/lib/web/live/users/edit.ex b/elixir/apps/web/lib/web/live/users/edit.ex deleted file mode 100644 index 3d455aeaa..000000000 --- a/elixir/apps/web/lib/web/live/users/edit.ex +++ /dev/null @@ -1,128 +0,0 @@ -defmodule Web.Users.Edit do - use Web, :live_view - - def render(assigns) do - ~H""" - <.breadcrumbs home_path={~p"/#{@account}/dashboard"}> - <.breadcrumb path={~p"/#{@account}/actors"}>Users - <.breadcrumb path={~p"/#{@account}/actors/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"}> - Jamil Bou Kheir - - <.breadcrumb path={~p"/#{@account}/actors/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89/edit"}> - Edit - - - <.header> - <:title> - Editing user Bou Kheir, Jamil - - - -
-
-

Edit user details

-
-
-
- <.label for="first-name"> - First Name - - -
-
- <.label for="last-name"> - Last Name - - -
-
- <.label for="email"> - Email - - -

- We'll send a confirmation email to both the current email address and the updated one to confirm the change. -

-
-
- <.label for="confirm-email"> - Confirm email - - -
-
- <.label for="user-role"> - Role - - -

- Select Admin to make this user an administrator of your organization. -

-
-
- <.label for="user-groups"> - Groups - - -

- Select one or more groups to allow this user access to resources. -

-
-
-
- -
-
-
-
- """ - end -end diff --git a/elixir/apps/web/lib/web/live/users/index.ex b/elixir/apps/web/lib/web/live/users/index.ex deleted file mode 100644 index f239b56f2..000000000 --- a/elixir/apps/web/lib/web/live/users/index.ex +++ /dev/null @@ -1,278 +0,0 @@ -defmodule Web.Users.Index do - use Web, :live_view - - def render(assigns) do - ~H""" - <.breadcrumbs home_path={~p"/#{@account}/dashboard"}> - <.breadcrumb path={~p"/#{@account}/actors"}>Users - - <.header> - <:title> - All users - - <:actions> - <.add_button navigate={~p"/#{@account}/actors/new"}> - Add a new user - - - - -
-
-
-
- -
-
- <.icon name="hero-magnifying-glass" class="w-5 h-5 text-gray-500 dark:text-gray-400" /> -
- -
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - Actions -
- <.link - navigate={~p"/#{@account}/actors/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"} - class="font-medium text-blue-600 dark:text-blue-500 hover:underline" - > - Bou Kheir, Jamil - - - email:jamil@firezone.dev,okta:jamil@firezone.dev - Admin, Engineering, 3 more...Today at 2:30 pm - - -
- <.link - navigate={~p"/#{@account}/actors/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"} - class="font-medium text-blue-600 dark:text-blue-500 hover:underline" - > - Dryga, Andrew - - - email:a@firezone.dev,okta:andrew.dryga@firezone.dev - Admin, DevOps, Engineering, 3 more...Just now - - -
- <.link - navigate={~p"/#{@account}/actors/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"} - class="font-medium text-blue-600 dark:text-blue-500 hover:underline" - > - Steinberg, Gabriel - - - email:a@firezone.dev,okta:andrew.dryga@firezone.dev - DevOps, IT, SecurityA month ago - - -
-
- <.paginator page={3} total_pages={100} collection_base_path={~p"/#{@account}/actors"} /> -
- """ - end -end diff --git a/elixir/apps/web/lib/web/live/users/show.ex b/elixir/apps/web/lib/web/live/users/show.ex deleted file mode 100644 index 91f49adc9..000000000 --- a/elixir/apps/web/lib/web/live/users/show.ex +++ /dev/null @@ -1,126 +0,0 @@ -defmodule Web.Users.Show do - use Web, :live_view - - def render(assigns) do - ~H""" - <.breadcrumbs home_path={~p"/#{@account}/dashboard"}> - <.breadcrumb path={~p"/#{@account}/actors"}>Users - <.breadcrumb path={~p"/#{@account}/actors/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89"}> - Jamil Bou Kheir - - - <.header> - <:title> - Viewing User Bou Kheir, Jamil - - <:actions> - <.edit_button navigate={~p"/#{@account}/actors/DF43E951-7DFB-4921-8F7F-BF0F8D31FA89/edit"}> - Edit user - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- First name - - Steve -
- Last name - - Johnson -
- Source - - Manually created by - Jamil Bou Kheir - on May 3rd, 2023. -
- Email - - steve@tesla.com - Verified -
- Role - - Admin -
- Groups - - <.link - navigate={~p"/#{@account}/groups/55DDA8CB-69A7-48FC-9048-639021C205A2"} - class="text-blue-600 hover:underline" - > - Engineering - -
- Last active - - 1 hour ago -
-
- - <.header> - <:title> - Danger zone - - <:actions> - <.delete_button> - Delete user - - - - """ - end -end diff --git a/elixir/apps/web/lib/web/router.ex b/elixir/apps/web/lib/web/router.ex index 45430f6e1..48b2e43e6 100644 --- a/elixir/apps/web/lib/web/router.ex +++ b/elixir/apps/web/lib/web/router.ex @@ -100,7 +100,7 @@ defmodule Web.Router do ] do live "/dashboard", Dashboard - scope "/actors", Users do + scope "/actors", Actors do live "/", Index live "/new", New live "/:id/edit", Edit