Fix System missing system actor in prod and prevent deletion #379
2 changed files with 35 additions and 5 deletions
|
|
@ -172,6 +172,31 @@ defmodule Mv.Helpers.SystemActor do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns whether the given user is the system actor user (case-insensitive email match).
|
||||||
|
|
||||||
|
Use this instead of ad-hoc `to_string(user.email) == system_user_email()` so
|
||||||
|
comparisons are consistent and case-insensitive everywhere.
|
||||||
|
|
||||||
|
## Returns
|
||||||
|
|
||||||
|
- `boolean()` - true if user's email matches system user email (case-insensitive)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> Mv.Helpers.SystemActor.system_user?(user_with_system_email)
|
||||||
|
true
|
||||||
|
iex> Mv.Helpers.SystemActor.system_user?(other_user)
|
||||||
|
false
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec system_user?(Mv.Accounts.User.t() | map() | nil) :: boolean()
|
||||||
|
def system_user?(%{email: email}) when not is_nil(email) do
|
||||||
|
normalized_email(to_string(email)) == normalized_system_user_email()
|
||||||
|
end
|
||||||
|
|
||||||
|
def system_user?(_), do: false
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the email address of the system user.
|
Returns the email address of the system user.
|
||||||
|
|
||||||
|
|
@ -191,6 +216,11 @@ defmodule Mv.Helpers.SystemActor do
|
||||||
@spec system_user_email() :: String.t()
|
@spec system_user_email() :: String.t()
|
||||||
def system_user_email, do: system_user_email_config()
|
def system_user_email, do: system_user_email_config()
|
||||||
|
|
||||||
|
# Case-insensitive normalized form for comparisons
|
||||||
|
defp normalized_system_user_email, do: normalized_email(system_user_email_config())
|
||||||
|
|
||||||
|
defp normalized_email(email) when is_binary(email), do: String.downcase(email)
|
||||||
|
|
||||||
# Returns the system user email from environment variable or default
|
# Returns the system user email from environment variable or default
|
||||||
# This allows configuration via SYSTEM_ACTOR_EMAIL env var
|
# This allows configuration via SYSTEM_ACTOR_EMAIL env var
|
||||||
@spec system_user_email_config() :: String.t()
|
@spec system_user_email_config() :: String.t()
|
||||||
|
|
@ -368,7 +398,7 @@ defmodule Mv.Helpers.SystemActor do
|
||||||
upsert_identity: :unique_email,
|
upsert_identity: :unique_email,
|
||||||
authorize?: false
|
authorize?: false
|
||||||
)
|
)
|
||||||
|> Ash.Changeset.for_update(:update, %{})
|
|> Ash.Changeset.for_update(:update_internal, %{})
|
||||||
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|
||||||
|> Ash.update!(authorize?: false)
|
|> Ash.update!(authorize?: false)
|
||||||
|> Ash.load!(:role, domain: Mv.Accounts, authorize?: false)
|
|> Ash.load!(:role, domain: Mv.Accounts, authorize?: false)
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ defmodule Mv.Helpers.SystemActorTest do
|
||||||
|> Ash.read_one(domain: Mv.Accounts, authorize?: false) do
|
|> Ash.read_one(domain: Mv.Accounts, authorize?: false) do
|
||||||
{:ok, user} when not is_nil(user) ->
|
{:ok, user} when not is_nil(user) ->
|
||||||
user
|
user
|
||||||
|> Ash.Changeset.for_update(:update, %{})
|
|> Ash.Changeset.for_update(:update_internal, %{})
|
||||||
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|
||||||
|> Ash.update!(authorize?: false)
|
|> Ash.update!(authorize?: false)
|
||||||
|> Ash.load!(:role, domain: Mv.Accounts, authorize?: false)
|
|> Ash.load!(:role, domain: Mv.Accounts, authorize?: false)
|
||||||
|
|
@ -68,7 +68,7 @@ defmodule Mv.Helpers.SystemActorTest do
|
||||||
upsert_identity: :unique_email,
|
upsert_identity: :unique_email,
|
||||||
authorize?: false
|
authorize?: false
|
||||||
)
|
)
|
||||||
|> Ash.Changeset.for_update(:update, %{})
|
|> Ash.Changeset.for_update(:update_internal, %{})
|
||||||
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|
||||||
|> Ash.update!(authorize?: false)
|
|> Ash.update!(authorize?: false)
|
||||||
|> Ash.load!(:role, domain: Mv.Accounts, authorize?: false)
|
|> Ash.load!(:role, domain: Mv.Accounts, authorize?: false)
|
||||||
|
|
@ -373,9 +373,9 @@ defmodule Mv.Helpers.SystemActorTest do
|
||||||
|
|
||||||
system_actor = SystemActor.get_system_actor()
|
system_actor = SystemActor.get_system_actor()
|
||||||
|
|
||||||
# Assign wrong role to system user
|
# Assign wrong role to system user (use :update_internal so bootstrap-style update is allowed)
|
||||||
system_user
|
system_user
|
||||||
|> Ash.Changeset.for_update(:update, %{})
|
|> Ash.Changeset.for_update(:update_internal, %{})
|
||||||
|> Ash.Changeset.manage_relationship(:role, read_only_role, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:role, read_only_role, type: :append_and_remove)
|
||||||
|> Ash.update!(actor: system_actor)
|
|> Ash.update!(actor: system_actor)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue