diff --git a/elixir/apps/domain/lib/domain/accounts/account/changeset.ex b/elixir/apps/domain/lib/domain/accounts/account/changeset.ex index f079731e0..53fbfa44e 100644 --- a/elixir/apps/domain/lib/domain/accounts/account/changeset.ex +++ b/elixir/apps/domain/lib/domain/accounts/account/changeset.ex @@ -12,6 +12,8 @@ defmodule Domain.Accounts.Account.Changeset do admin user system internal ] + @slug_regex ~r/^[a-zA-Z0-9_]+$/ + def create(attrs) do %Account{} |> cast(attrs, [:name, :slug]) @@ -60,7 +62,7 @@ defmodule Domain.Accounts.Account.Changeset do changeset |> validate_length(:slug, min: 3, max: 100) |> update_change(:slug, &String.downcase/1) - |> validate_format(:slug, ~r/^[a-zA-Z0-9_]+$/, + |> validate_format(:slug, @slug_regex, message: "can only contain letters, numbers, and underscores" ) |> validate_exclusion(:slug, @blacklisted_slugs) @@ -88,4 +90,17 @@ defmodule Domain.Accounts.Account.Changeset do stripe |> cast(attrs, [:customer_id, :subscription_id, :product_name]) end + + def validate_account_id_or_slug(account_id_or_slug) do + cond do + valid_uuid?(account_id_or_slug) -> + {:ok, String.downcase(account_id_or_slug)} + + String.match?(account_id_or_slug, @slug_regex) -> + {:ok, String.downcase(account_id_or_slug)} + + true -> + {:error, "Account ID or Slug contains invalid characters"} + end + end end diff --git a/elixir/apps/domain/lib/domain/actors.ex b/elixir/apps/domain/lib/domain/actors.ex index 19e46539d..706fdfc49 100644 --- a/elixir/apps/domain/lib/domain/actors.ex +++ b/elixir/apps/domain/lib/domain/actors.ex @@ -522,7 +522,7 @@ defmodule Domain.Actors do end end - def delete_actor(%Actor{last_synced_at: nil} = actor, %Auth.Subject{} = subject) do + def delete_actor(%Actor{} = actor, %Auth.Subject{} = subject) do with :ok <- Auth.ensure_has_permissions(subject, Authorizer.manage_actors_permission()) do Actor.Query.by_id(actor.id) |> Authorizer.for_subject(subject) diff --git a/elixir/apps/domain/lib/domain/auth/identity.ex b/elixir/apps/domain/lib/domain/auth/identity.ex index 5f3fc5286..9f0b01d3f 100644 --- a/elixir/apps/domain/lib/domain/auth/identity.ex +++ b/elixir/apps/domain/lib/domain/auth/identity.ex @@ -6,8 +6,8 @@ defmodule Domain.Auth.Identity do belongs_to :provider, Domain.Auth.Provider field :provider_identifier, :string - field :provider_state, :map - field :provider_virtual_state, :map, virtual: true + field :provider_state, :map, redact: true + field :provider_virtual_state, :map, virtual: true, redact: true field :last_seen_user_agent, :string field :last_seen_remote_ip, Domain.Types.IP diff --git a/elixir/apps/domain/lib/domain/auth/provider.ex b/elixir/apps/domain/lib/domain/auth/provider.ex index 698d95bb8..4d1e75cc3 100644 --- a/elixir/apps/domain/lib/domain/auth/provider.ex +++ b/elixir/apps/domain/lib/domain/auth/provider.ex @@ -8,8 +8,8 @@ defmodule Domain.Auth.Provider do values: ~w[email openid_connect google_workspace microsoft_entra okta userpass]a field :provisioner, Ecto.Enum, values: ~w[manual just_in_time custom]a - field :adapter_config, :map - field :adapter_state, :map + field :adapter_config, :map, redact: true + field :adapter_state, :map, redact: true belongs_to :account, Domain.Accounts.Account diff --git a/elixir/apps/web/lib/web/controllers/home_controller.ex b/elixir/apps/web/lib/web/controllers/home_controller.ex index fbadbb9a1..762e3fcca 100644 --- a/elixir/apps/web/lib/web/controllers/home_controller.ex +++ b/elixir/apps/web/lib/web/controllers/home_controller.ex @@ -30,8 +30,16 @@ defmodule Web.HomeController do end def redirect_to_sign_in(conn, %{"account_id_or_slug" => account_id_or_slug} = params) do - account_id_or_slug = String.downcase(account_id_or_slug) params = Web.Auth.take_sign_in_params(params) - redirect(conn, to: ~p"/#{account_id_or_slug}?#{params}") + + case Domain.Accounts.Account.Changeset.validate_account_id_or_slug(account_id_or_slug) do + {:ok, account_id_or_slug} -> + redirect(conn, to: ~p"/#{account_id_or_slug}?#{params}") + + {:error, reason} -> + conn + |> put_flash(:error, reason) + |> redirect(to: ~p"/?#{params}") + end end end diff --git a/elixir/apps/web/lib/web/controllers/home_html.ex b/elixir/apps/web/lib/web/controllers/home_html.ex index 518ca7bba..cb1552415 100644 --- a/elixir/apps/web/lib/web/controllers/home_html.ex +++ b/elixir/apps/web/lib/web/controllers/home_html.ex @@ -31,6 +31,8 @@ defmodule Web.HomeHTML do <.separator :if={@accounts != []} /> + <.flash kind={:error} flash={@flash} /> + <.form :let={f} for={%{}} action={~p"/?#{@params}"} class="space-y-4 lg:space-y-6"> <.input field={f[:account_id_or_slug]} diff --git a/elixir/apps/web/test/web/controllers/home_controller_test.exs b/elixir/apps/web/test/web/controllers/home_controller_test.exs index f9ddaa43e..a2cbd2591 100644 --- a/elixir/apps/web/test/web/controllers/home_controller_test.exs +++ b/elixir/apps/web/test/web/controllers/home_controller_test.exs @@ -50,13 +50,19 @@ defmodule Web.HomeControllerTest do describe "redirect_to_sign_in/2" do test "redirects to the sign in page", %{conn: conn} do id = Ecto.UUID.generate() - conn = post(conn, ~p"/", %{"account_id_or_slug" => id}) - assert redirected_to(conn) == ~p"/#{id}" + conn = post(conn, ~p"/", %{"account_id_or_slug" => id, "as" => "client"}) + assert redirected_to(conn) == ~p"/#{id}?as=client" end test "downcases account slug on redirect", %{conn: conn} do - conn = post(conn, ~p"/", %{"account_id_or_slug" => "FOO"}) - assert redirected_to(conn) == ~p"/foo" + conn = post(conn, ~p"/", %{"account_id_or_slug" => "FOO", "as" => "client"}) + assert redirected_to(conn) == ~p"/foo?as=client" + end + + test "puts an error flash when slug is invalid", %{conn: conn} do + conn = post(conn, ~p"/", %{"account_id_or_slug" => "?1", "as" => "client"}) + assert redirected_to(conn) == ~p"/?as=client" + assert conn.assigns.flash["error"] == "Account ID or Slug contains invalid characters" end end end