mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
fix(portal): Prevent invalid characters when entering account slug at sign in (#3917)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user