fix(portal): Prevent invalid characters when entering account slug at sign in (#3917)

This commit is contained in:
Andrew Dryga
2024-03-11 10:38:17 -06:00
committed by GitHub
parent b6b44fd6db
commit f52bcc3e37
7 changed files with 43 additions and 12 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]}

View File

@@ -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