Add authorize?: false to SystemActor bootstrap operations
- Role lookup and creation (find_admin_role, create_admin_role) - System user creation and role assignment - Role loading during initialization
This commit is contained in:
parent
e72b7ab2e8
commit
686f69c9e9
1 changed files with 28 additions and 11 deletions
|
|
@ -271,11 +271,12 @@ defmodule Mv.Helpers.SystemActor do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Finds admin role in existing roles
|
# Finds admin role in existing roles
|
||||||
|
# SECURITY: Uses authorize?: false for bootstrap role lookup.
|
||||||
@spec find_admin_role() :: {:ok, Mv.Authorization.Role.t()} | {:error, :not_found}
|
@spec find_admin_role() :: {:ok, Mv.Authorization.Role.t()} | {:error, :not_found}
|
||||||
defp find_admin_role do
|
defp find_admin_role do
|
||||||
alias Mv.Authorization
|
alias Mv.Authorization
|
||||||
|
|
||||||
case Authorization.list_roles() do
|
case Authorization.list_roles(authorize?: false) do
|
||||||
{:ok, roles} ->
|
{:ok, roles} ->
|
||||||
case Enum.find(roles, &(&1.permission_set_name == "admin")) do
|
case Enum.find(roles, &(&1.permission_set_name == "admin")) do
|
||||||
nil -> {:error, :not_found}
|
nil -> {:error, :not_found}
|
||||||
|
|
@ -305,16 +306,20 @@ defmodule Mv.Helpers.SystemActor do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Attempts to create admin role
|
# Attempts to create admin role
|
||||||
|
# SECURITY: Uses authorize?: false for bootstrap role creation.
|
||||||
@spec create_admin_role() ::
|
@spec create_admin_role() ::
|
||||||
{:ok, Mv.Authorization.Role.t()} | {:error, :already_exists | term()}
|
{:ok, Mv.Authorization.Role.t()} | {:error, :already_exists | term()}
|
||||||
defp create_admin_role do
|
defp create_admin_role do
|
||||||
alias Mv.Authorization
|
alias Mv.Authorization
|
||||||
|
|
||||||
case Authorization.create_role(%{
|
case Authorization.create_role(
|
||||||
name: "Admin",
|
%{
|
||||||
description: "Administrator with full access",
|
name: "Admin",
|
||||||
permission_set_name: "admin"
|
description: "Administrator with full access",
|
||||||
}) do
|
permission_set_name: "admin"
|
||||||
|
},
|
||||||
|
authorize?: false
|
||||||
|
) do
|
||||||
{:ok, role} ->
|
{:ok, role} ->
|
||||||
{:ok, role}
|
{:ok, role}
|
||||||
|
|
||||||
|
|
@ -327,11 +332,12 @@ defmodule Mv.Helpers.SystemActor do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Finds existing admin role after creation attempt failed due to race condition
|
# Finds existing admin role after creation attempt failed due to race condition
|
||||||
|
# SECURITY: Uses authorize?: false for bootstrap role lookup.
|
||||||
@spec find_existing_admin_role() :: Mv.Authorization.Role.t() | no_return()
|
@spec find_existing_admin_role() :: Mv.Authorization.Role.t() | no_return()
|
||||||
defp find_existing_admin_role do
|
defp find_existing_admin_role do
|
||||||
alias Mv.Authorization
|
alias Mv.Authorization
|
||||||
|
|
||||||
case Authorization.list_roles() do
|
case Authorization.list_roles(authorize?: false) do
|
||||||
{:ok, roles} ->
|
{:ok, roles} ->
|
||||||
Enum.find(roles, &(&1.permission_set_name == "admin")) ||
|
Enum.find(roles, &(&1.permission_set_name == "admin")) ||
|
||||||
raise "Admin role should exist but was not found"
|
raise "Admin role should exist but was not found"
|
||||||
|
|
@ -350,14 +356,22 @@ defmodule Mv.Helpers.SystemActor do
|
||||||
defp create_system_user_with_role(admin_role) do
|
defp create_system_user_with_role(admin_role) do
|
||||||
alias Mv.Accounts
|
alias Mv.Accounts
|
||||||
|
|
||||||
|
# SECURITY: Uses authorize?: false for bootstrap user creation.
|
||||||
|
# This is necessary because we're creating the system actor itself,
|
||||||
|
# which would otherwise be needed for authorization (chicken-and-egg).
|
||||||
|
# This is safe because:
|
||||||
|
# 1. Only creates system user with known email
|
||||||
|
# 2. Only called during system actor initialization (bootstrap)
|
||||||
|
# 3. Once created, all subsequent operations use proper authorization
|
||||||
Accounts.create_user!(%{email: system_user_email_config()},
|
Accounts.create_user!(%{email: system_user_email_config()},
|
||||||
upsert?: true,
|
upsert?: true,
|
||||||
upsert_identity: :unique_email
|
upsert_identity: :unique_email,
|
||||||
|
authorize?: false
|
||||||
)
|
)
|
||||||
|> Ash.Changeset.for_update(:update, %{})
|
|> Ash.Changeset.for_update(:update, %{})
|
||||||
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|
||||||
|> Ash.update!()
|
|> Ash.update!(authorize?: false)
|
||||||
|> Ash.load!(:role, domain: Mv.Accounts)
|
|> Ash.load!(:role, domain: Mv.Accounts, authorize?: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Finds a user by email address
|
# Finds a user by email address
|
||||||
|
|
@ -376,9 +390,12 @@ defmodule Mv.Helpers.SystemActor do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Loads a user with their role preloaded (required for authorization)
|
# Loads a user with their role preloaded (required for authorization)
|
||||||
|
# SECURITY: Uses authorize?: false for bootstrap role loading.
|
||||||
|
# This is necessary because loading the role is part of system actor initialization,
|
||||||
|
# which would otherwise require an actor (chicken-and-egg).
|
||||||
@spec load_user_with_role(Mv.Accounts.User.t()) :: Mv.Accounts.User.t() | no_return()
|
@spec load_user_with_role(Mv.Accounts.User.t()) :: Mv.Accounts.User.t() | no_return()
|
||||||
defp load_user_with_role(user) do
|
defp load_user_with_role(user) do
|
||||||
case Ash.load(user, :role, domain: Mv.Accounts) do
|
case Ash.load(user, :role, domain: Mv.Accounts, authorize?: false) do
|
||||||
{:ok, user_with_role} ->
|
{:ok, user_with_role} ->
|
||||||
validate_admin_role(user_with_role)
|
validate_admin_role(user_with_role)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue