diff --git a/elixir/apps/api/test/api/gateway/channel_test.exs b/elixir/apps/api/test/api/gateway/channel_test.exs index f007cb14c..8af227d84 100644 --- a/elixir/apps/api/test/api/gateway/channel_test.exs +++ b/elixir/apps/api/test/api/gateway/channel_test.exs @@ -661,7 +661,7 @@ defmodule API.Gateway.ChannelTest do }, otel_ctx} ) - assert_push "request_connection", %{} + assert_push "request_connection", %{}, 200 {:updated, resource} = Domain.Resources.update_or_replace_resource( @@ -670,7 +670,7 @@ defmodule API.Gateway.ChannelTest do subject ) - assert_push "resource_updated", payload + assert_push "resource_updated", payload, 200 assert payload == %{ address: resource.address, diff --git a/elixir/apps/domain/lib/domain/actors/actor/query.ex b/elixir/apps/domain/lib/domain/actors/actor/query.ex index 44bd2a40e..5bc14f6ec 100644 --- a/elixir/apps/domain/lib/domain/actors/actor/query.ex +++ b/elixir/apps/domain/lib/domain/actors/actor/query.ex @@ -194,7 +194,7 @@ defmodule Domain.Actors.Actor.Query do title: "Status", type: :string, values: [ - {"Enabled", "enabled"}, + {"Active", "active"}, {"Disabled", "disabled"} ], fun: &filter_by_status/2 @@ -234,7 +234,7 @@ defmodule Domain.Actors.Actor.Query do } ] - def filter_by_status(queryable, "enabled") do + def filter_by_status(queryable, "active") do {queryable, dynamic([actors: actors], is_nil(actors.disabled_at))} end diff --git a/elixir/apps/domain/lib/domain/flows/flow/query.ex b/elixir/apps/domain/lib/domain/flows/flow/query.ex index db9c86c13..dcf7e96a1 100644 --- a/elixir/apps/domain/lib/domain/flows/flow/query.ex +++ b/elixir/apps/domain/lib/domain/flows/flow/query.ex @@ -100,4 +100,27 @@ defmodule Domain.Flows.Flow.Query do {:flows, :desc, :inserted_at}, {:flows, :asc, :id} ] + + @impl Domain.Repo.Query + def filters, + do: [ + %Domain.Repo.Filter{ + name: :expiration, + title: "Expired", + type: :string, + values: [ + {"Expired", "expired"}, + {"Not Expired", "not_expired"} + ], + fun: &filter_by_expired/2 + } + ] + + def filter_by_expired(queryable, "expired") do + {queryable, dynamic([flows: flows], flows.expires_at < fragment("NOW()"))} + end + + def filter_by_expired(queryable, "not_expired") do + {queryable, dynamic([flows: flows], flows.expires_at >= fragment("NOW()"))} + end end diff --git a/elixir/apps/domain/lib/domain/repo/changeset.ex b/elixir/apps/domain/lib/domain/repo/changeset.ex index 0f352de9f..06d18af44 100644 --- a/elixir/apps/domain/lib/domain/repo/changeset.ex +++ b/elixir/apps/domain/lib/domain/repo/changeset.ex @@ -39,6 +39,9 @@ defmodule Domain.Repo.Changeset do Keyword.has_key?(changeset.errors, field) end + def empty?(%Ecto.Changeset{} = changeset), do: Enum.empty?(changeset.changes) + def empty?(%{}), do: true + def empty?(%Ecto.Changeset{} = changeset, field) do case fetch_field(changeset, field) do :error -> true @@ -505,7 +508,7 @@ defmodule Domain.Repo.Changeset do data = Map.get(changeset.data, field) changes = get_change(changeset, field) - if required? and is_nil(changes) and empty?(data) do + if required? and is_nil(changes) and empty_value?(data) do add_error(changeset, field, "can't be blank", validation: :required) else %Changeset{} = nested_changeset = on_cast.(data || %{}, changes || %{}) @@ -546,7 +549,7 @@ defmodule Domain.Repo.Changeset do Keyword.has_key?(changeset.errors, field) end - defp empty?(term), do: is_nil(term) or term == %{} + defp empty_value?(term), do: is_nil(term) or term == %{} defp dump(changeset, field, original_type) do map = diff --git a/elixir/apps/web/lib/web/components/core_components.ex b/elixir/apps/web/lib/web/components/core_components.ex index 58e4a0967..475f2f893 100644 --- a/elixir/apps/web/lib/web/components/core_components.ex +++ b/elixir/apps/web/lib/web/components/core_components.ex @@ -472,6 +472,12 @@ defmodule Web.CoreComponents do """ end + def icon(%{name: "firezone"} = assigns) do + ~H""" + + """ + end + def icon(%{name: "spinner"} = assigns) do ~H""" DateTime.utc_now() end) + assigns = + assign_new(assigns, :relative_to, fn -> DateTime.utc_now() end) ~H""" <.popover :if={not is_nil(@datetime)}> <:target> - + <%= Cldr.DateTime.Relative.to_string!(@datetime, Web.CLDR, relative_to: @relative_to) |> String.capitalize() %> @@ -1021,8 +1032,8 @@ defmodule Web.CoreComponents do text-xs rounded-l py-0.5 px-1.5 - text-neutral-900 - bg-neutral-50 + text-neutral-800 + bg-neutral-100 border-neutral-100 border ]} @@ -1035,7 +1046,7 @@ defmodule Web.CoreComponents do rounded-r mr-2 py-0.5 pl-1.5 pr-2.5 text-neutral-900 - bg-neutral-100 + bg-neutral-50 ]}> <%= get_identity_email(@identity) %> @@ -1068,14 +1079,25 @@ defmodule Web.CoreComponents do class={~w[ rounded-l py-0.5 px-1.5 - text-neutral-900 - bg-neutral-50 + text-neutral-800 + bg-neutral-100 border-neutral-100 border ]} > <.provider_icon adapter={@group.provider.adapter} class="h-3.5 w-3.5" /> +
+ <.icon name="firezone" class="h-3.5 w-3.5" /> +
<.link title={"View Group \"#{@group.name}\""} navigate={~p"/#{@account}/groups/#{@group}"} @@ -1083,10 +1105,10 @@ defmodule Web.CoreComponents do text-xs truncate min-w-0 - #{if(Actors.group_synced?(@group), do: "rounded-r pl-1.5 pr-2.5", else: "rounded px-1.5")} + rounded-r pl-1.5 pr-2.5 py-0.5 - text-neutral-800 - bg-neutral-100 + text-neutral-900 + bg-neutral-50 ]} > <%= @group.name %> diff --git a/elixir/apps/web/lib/web/components/form_components.ex b/elixir/apps/web/lib/web/components/form_components.ex index 38bd65ed1..78c1ccea8 100644 --- a/elixir/apps/web/lib/web/components/form_components.ex +++ b/elixir/apps/web/lib/web/components/form_components.ex @@ -387,9 +387,11 @@ defmodule Web.FormComponents do attr :id, :string, required: true, doc: "The id of the dialog" attr :class, :string, default: "", doc: "Custom classes to be added to the button" attr :style, :string, default: "danger", doc: "The style of the button" + attr :confirm_style, :string, default: "danger", doc: "The style of the confirm button" attr :icon, :string, default: nil, doc: "The icon of the button" attr :size, :string, default: "md", doc: "The size of the button" attr :on_confirm, :string, required: true, doc: "The phx event to broadcast on confirm" + attr :disabled, :boolean, default: false, doc: "Whether the button is disabled" attr :on_confirm_id, :string, default: nil, @@ -418,7 +420,7 @@ defmodule Web.FormComponents do <%= render_slot(@dialog_title) %> @@ -104,7 +114,7 @@ defmodule Web.Settings.DNS do value="new" phx-click={JS.dispatch("change")} > - New DNS Server + New Resolver <.error :for={error <- dns_config_errors(@form.source.changes)} @@ -117,7 +127,7 @@ defmodule Web.Settings.DNS do

Note: It is highly recommended to to specify both - IPv4 and IPv6 addresses when adding custom resolvers. Otherwise, Clients without IPv4 + IPv4 and IPv6 addresses when adding upstream resolvers. Otherwise, Clients without IPv4 or IPv6 connectivity may not be able to resolve DNS queries.

<.submit_button> diff --git a/elixir/apps/web/lib/web/live/settings/identity_providers/index.ex b/elixir/apps/web/lib/web/live/settings/identity_providers/index.ex index 09423a226..d02e96518 100644 --- a/elixir/apps/web/lib/web/live/settings/identity_providers/index.ex +++ b/elixir/apps/web/lib/web/live/settings/identity_providers/index.ex @@ -55,16 +55,16 @@ defmodule Web.Settings.IdentityProviders.Index do <:title> Identity Providers + <:action> + <.docs_action path="/authenticate" /> + <:action> <.add_button navigate={~p"/#{@account}/settings/identity_providers/new"}> Add Identity Provider <:help> - <.website_link path="/kb/authenticate"> - Read more - - about how authentication works in Firezone. + Identity providers authenticate and sync your users and groups with an external source. <:content> <.flash_group flash={@flash} /> diff --git a/elixir/apps/web/lib/web/live/sign_in/email.ex b/elixir/apps/web/lib/web/live/sign_in/email.ex index e5a0c0b3b..7d62d5709 100644 --- a/elixir/apps/web/lib/web/live/sign_in/email.ex +++ b/elixir/apps/web/lib/web/live/sign_in/email.ex @@ -65,9 +65,8 @@ defmodule Web.SignIn.Email do

- If <%= @provider_identifier %> is registered, a sign in token has - been sent to that email. Please copy and paste this into the form below to proceed - with your login. + If <%= @provider_identifier %> + is registered, a sign-in token has been sent.

- Your account has been created! +

Your account has been created!

Please check your email for sign in instructions.

@@ -141,7 +141,7 @@ defmodule Web.SignUp do - - -
+ Account Name: @@ -149,7 +149,7 @@ defmodule Web.SignUp do
+ Account Slug: @@ -157,7 +157,7 @@ defmodule Web.SignUp do
+ Sign In URL: @@ -204,8 +204,8 @@ defmodule Web.SignUp do <.input field={@form[:email]} type="text" - label="Email" - placeholder="Enter your work email here" + label="Work Email" + placeholder="E.g. foo@example.com" required autofocus phx-debounce="300" @@ -215,8 +215,8 @@ defmodule Web.SignUp do <.input field={account[:name]} type="text" - label="Account Name" - placeholder="Enter an account name" + label="Company Name" + placeholder="E.g. Example Corp" required phx-debounce="300" /> @@ -228,7 +228,7 @@ defmodule Web.SignUp do field={actor[:name]} type="text" label="Your Name" - placeholder="Enter your name here" + placeholder="E.g. John Smith" required phx-debounce="300" /> @@ -293,8 +293,6 @@ defmodule Web.SignUp do changeset = attrs - |> maybe_put_default_account_name(account_name_changed?) - |> maybe_put_default_actor_name(actor_name_changed?) |> Registration.changeset() |> Map.put(:action, :validate) @@ -313,8 +311,6 @@ defmodule Web.SignUp do changeset = attrs - |> maybe_put_default_account_name() - |> maybe_put_default_actor_name() |> put_in(["actor", "type"], :account_admin_user) |> Registration.changeset() |> Map.put(:action, :insert) @@ -382,33 +378,6 @@ defmodule Web.SignUp do end end - defp maybe_put_default_account_name(attrs, account_name_changed? \\ true) - - defp maybe_put_default_account_name(attrs, true) do - attrs - end - - defp maybe_put_default_account_name(attrs, false) do - case String.split(attrs["email"], "@", parts: 2) do - [default_name | _] when byte_size(default_name) > 0 -> - put_in(attrs, ["account", "name"], "#{default_name}'s account") - - _ -> - attrs - end - end - - defp maybe_put_default_actor_name(attrs, actor_name_changed? \\ true) - - defp maybe_put_default_actor_name(attrs, true) do - attrs - end - - defp maybe_put_default_actor_name(attrs, false) do - [default_name | _] = String.split(attrs["email"], "@", parts: 2) - put_in(attrs, ["actor", "name"], default_name) - end - defp register_account(socket, registration) do Ecto.Multi.new() |> Ecto.Multi.run( diff --git a/elixir/apps/web/lib/web/live/sites/index.ex b/elixir/apps/web/lib/web/live/sites/index.ex index 66308e97f..625951213 100644 --- a/elixir/apps/web/lib/web/live/sites/index.ex +++ b/elixir/apps/web/lib/web/live/sites/index.ex @@ -49,6 +49,11 @@ defmodule Web.Sites.Index do <:title> Sites + + <:action> + <.docs_action path="/deploy/sites" /> + + <:action> <.add_button navigate={~p"/#{@account}/sites/new"}> Add Site diff --git a/elixir/apps/web/lib/web/live/sites/new.ex b/elixir/apps/web/lib/web/live/sites/new.ex index 96ecf1325..80841b686 100644 --- a/elixir/apps/web/lib/web/live/sites/new.ex +++ b/elixir/apps/web/lib/web/live/sites/new.ex @@ -20,16 +20,15 @@ defmodule Web.Sites.New do <:content> <.flash kind={:error} flash={@flash} />
-

- Site details -

<.form for={@form} phx-change={:change} phx-submit={:submit}>
- <.input label="Name" field={@form[:name]} placeholder="Name of this Site" required /> -

- Enter a name for this Site. -

+ <.input + label="Name" + field={@form[:name]} + placeholder="Enter a name for this Site" + required + />
<.submit_button> diff --git a/elixir/apps/web/lib/web/live/sites/show.ex b/elixir/apps/web/lib/web/live/sites/show.ex index 15274db60..9dd809b35 100644 --- a/elixir/apps/web/lib/web/live/sites/show.ex +++ b/elixir/apps/web/lib/web/live/sites/show.ex @@ -129,6 +129,9 @@ defmodule Web.Sites.Show do see all <.icon name="hero-arrow-right" class="w-2 h-2" /> + <:action> + <.docs_action path="/deploy/gateways" /> + <:action :if={is_nil(@group.deleted_at)}> <.add_button navigate={~p"/#{@account}/sites/#{@group}/new_token"}> Deploy Gateway @@ -245,9 +248,14 @@ defmodule Web.Sites.Show do <:col :let={resource} label="Authorized groups"> <.peek peek={Map.fetch!(@resource_actor_groups_peek, resource.id)}> <:empty> - None - +
+ <.icon + name="hero-exclamation-triangle-solid" + class="inline-block w-3.5 h-3.5 text-red-500" + /> None. +
<.link - class={["px-1", link_style()]} + class={[link_style(), "mr-1"]} navigate={~p"/#{@account}/policies/new?resource_id=#{resource}&site_id=#{@group}"} > Create a Policy diff --git a/elixir/apps/web/lib/web/live_table.ex b/elixir/apps/web/lib/web/live_table.ex index 389c97964..e04fbea0a 100644 --- a/elixir/apps/web/lib/web/live_table.ex +++ b/elixir/apps/web/lib/web/live_table.ex @@ -186,7 +186,7 @@ defmodule Web.LiveTable do defp filter(%{filter: %{type: {:string, :websearch}}} = assigns) do ~H""" -
+
<.icon name="hero-magnifying-glass" class="w-5 h-5 text-neutral-500" /> @@ -200,7 +200,7 @@ defmodule Web.LiveTable do placeholder={"Search by " <> @filter.title} class={[ "bg-neutral-50 border border-neutral-300 text-neutral-900 text-sm rounded", - "block w-full pl-10 p-2", + "block w-full md:w-72 pl-10 p-2", "disabled:bg-neutral-50 disabled:text-neutral-500 disabled:border-neutral-300 disabled:shadow-none", "focus:outline-none focus:border-1 focus:ring-0", @form[@filter.name].errors != [] && "border-rose-400" @@ -219,7 +219,7 @@ defmodule Web.LiveTable do defp filter(%{filter: %{type: {:string, :email}}} = assigns) do ~H""" -
+
<.icon name="hero-magnifying-glass" class="w-5 h-5 text-neutral-500" /> @@ -233,7 +233,7 @@ defmodule Web.LiveTable do placeholder={"Search by " <> @filter.title} class={[ "bg-neutral-50 border border-neutral-300 text-neutral-900 text-sm rounded", - "block w-full pl-10 p-2", + "block w-full md:w-72 pl-10 p-2", "disabled:bg-neutral-50 disabled:text-neutral-500 disabled:border-neutral-300 disabled:shadow-none", "focus:outline-none focus:border-1 focus:ring-0", @form[@filter.name].errors != [] && "border-rose-400" diff --git a/elixir/apps/web/test/web/live/actors/service_accounts/new_identity_test.exs b/elixir/apps/web/test/web/live/actors/service_accounts/new_identity_test.exs index 2cabc6f16..98e2de450 100644 --- a/elixir/apps/web/test/web/live/actors/service_accounts/new_identity_test.exs +++ b/elixir/apps/web/test/web/live/actors/service_accounts/new_identity_test.exs @@ -53,7 +53,7 @@ defmodule Web.Live.Actors.ServiceAccounts.NewIdentityTest do breadcrumbs = String.trim(Floki.text(item)) assert breadcrumbs =~ "Actors" assert breadcrumbs =~ actor.name - assert breadcrumbs =~ "Add Token" + assert breadcrumbs =~ "Create Token" end test "renders form", %{ diff --git a/elixir/apps/web/test/web/live/clients/show_test.exs b/elixir/apps/web/test/web/live/clients/show_test.exs index 8df52a54f..5c570ebdd 100644 --- a/elixir/apps/web/test/web/live/clients/show_test.exs +++ b/elixir/apps/web/test/web/live/clients/show_test.exs @@ -92,8 +92,7 @@ defmodule Web.Live.Clients.ShowTest do assert table["status"] =~ "Offline" assert table["created"] assert table["last started"] - assert table["verification"] =~ "Not Verified" - assert table["client version"] =~ client.last_seen_version + assert table["version"] =~ client.last_seen_version assert table["user agent"] =~ client.last_seen_user_agent table = @@ -104,6 +103,7 @@ defmodule Web.Live.Clients.ShowTest do assert table["file id"] == client.external_id + assert table["verification"] =~ "Not Verified" assert table["device serial"] =~ to_string(client.device_serial) assert table["device uuid"] =~ to_string(client.device_uuid) assert table["app installation id"] =~ to_string(client.firebase_installation_id) @@ -322,7 +322,7 @@ defmodule Web.Live.Clients.ShowTest do table = lv - |> element("#client") + |> element("#posture") |> render() |> vertical_table_to_map() diff --git a/elixir/apps/web/test/web/live/groups/edit_actors_test.exs b/elixir/apps/web/test/web/live/groups/edit_actors_test.exs index 40f2029a1..421378b24 100644 --- a/elixir/apps/web/test/web/live/groups/edit_actors_test.exs +++ b/elixir/apps/web/test/web/live/groups/edit_actors_test.exs @@ -212,7 +212,7 @@ defmodule Web.Live.Groups.EditActorsTest do |> render_click() lv - |> element("button[type=submit]", "Save") + |> element("button[type=submit]", "Confirm") |> render_click() assert_redirected(lv, ~p"/#{account}/groups/#{group}") diff --git a/elixir/apps/web/test/web/live/sites/show_test.exs b/elixir/apps/web/test/web/live/sites/show_test.exs index 8c2feeb55..f250d4068 100644 --- a/elixir/apps/web/test/web/live/sites/show_test.exs +++ b/elixir/apps/web/test/web/live/sites/show_test.exs @@ -237,7 +237,7 @@ defmodule Web.Live.Sites.ShowTest do Enum.each(resource_rows, fn row -> assert row["name"] =~ resource.name assert row["address"] =~ resource.address - assert row["authorized groups"] == "None - Create a Policy to grant access." + assert row["authorized groups"] == "None. Create a Policy to grant access." end) end