feat: implement attribute-level default for role_id assignment
Replace action-level changes with attribute default function to ensure all users get the 'Mitglied' role regardless of creation path.
This commit is contained in:
parent
885fe613cb
commit
5164836d32
1 changed files with 46 additions and 14 deletions
|
|
@ -68,12 +68,9 @@ defmodule Mv.Accounts.User do
|
|||
hash_provider AshAuthentication.BcryptProvider
|
||||
confirmation_required? false
|
||||
|
||||
# NOTE: The auto-generated :register_with_password action does NOT assign a default role.
|
||||
# This is intentional because:
|
||||
# - In production, users are created via OIDC (:register_with_rauthy), which DOES assign roles
|
||||
# - Manual user creation via :create_user DOES assign roles
|
||||
# - Tests that need a role can use :create_user or manually assign via fixtures
|
||||
# - The migration ensures existing users without roles get the "Mitglied" role
|
||||
resettable do
|
||||
sender Mv.Accounts.User.Senders.SendPasswordResetEmail
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -122,8 +119,7 @@ defmodule Mv.Accounts.User do
|
|||
argument :member, :map, allow_nil?: true
|
||||
upsert? true
|
||||
|
||||
# Assign default "Mitglied" role to new users
|
||||
change Mv.Accounts.User.Changes.AssignDefaultRole
|
||||
# Note: Default role is automatically assigned via attribute default (see attributes block)
|
||||
|
||||
# Manage the member relationship during user creation
|
||||
change manage_relationship(:member, :member,
|
||||
|
|
@ -273,9 +269,8 @@ defmodule Mv.Accounts.User do
|
|||
# - The LinkOidcAccountLive will auto-link passwordless users without password prompt
|
||||
validate Mv.Accounts.User.Validations.OidcEmailCollision
|
||||
|
||||
# Assign default "Mitglied" role to new OIDC users
|
||||
# Note: upsert_fields [:email] ensures this doesn't overwrite existing users' roles
|
||||
change Mv.Accounts.User.Changes.AssignDefaultRole
|
||||
# Note: Default role is automatically assigned via attribute default (see attributes block)
|
||||
# upsert_fields [:email] ensures existing users' roles are preserved during upserts
|
||||
|
||||
# Sync user email to member when linking (User → Member)
|
||||
change Mv.EmailSync.Changes.SyncUserEmailToMember
|
||||
|
|
@ -395,6 +390,15 @@ defmodule Mv.Accounts.User do
|
|||
|
||||
attribute :hashed_password, :string, sensitive?: true, allow_nil?: true
|
||||
attribute :oidc_id, :string, allow_nil?: true
|
||||
|
||||
# Role assignment: Explicitly defined to enforce default value
|
||||
# This ensures every user has a role, regardless of creation path
|
||||
# (register_with_password, create_user, seeds, etc.)
|
||||
attribute :role_id, :uuid do
|
||||
allow_nil? false
|
||||
default &__MODULE__.default_role_id/0
|
||||
public? false
|
||||
end
|
||||
end
|
||||
|
||||
relationships do
|
||||
|
|
@ -404,10 +408,13 @@ defmodule Mv.Accounts.User do
|
|||
belongs_to :member, Mv.Membership.Member
|
||||
|
||||
# 1:1 relationship - User belongs to a Role
|
||||
# This automatically creates a `role_id` attribute in the User table
|
||||
# The relationship is optional (allow_nil? true by default)
|
||||
# We define role_id ourselves (above in attributes) to control default value
|
||||
# Foreign key constraint: on_delete: :restrict (prevents deleting roles assigned to users)
|
||||
belongs_to :role, Mv.Authorization.Role
|
||||
belongs_to :role, Mv.Authorization.Role do
|
||||
define_attribute? false
|
||||
source_attribute :role_id
|
||||
allow_nil? false
|
||||
end
|
||||
end
|
||||
|
||||
identities do
|
||||
|
|
@ -427,4 +434,29 @@ defmodule Mv.Accounts.User do
|
|||
# forbid_if(always())
|
||||
# end
|
||||
# end
|
||||
|
||||
@doc """
|
||||
Returns the default role ID for new users.
|
||||
|
||||
This function is called automatically when creating a user without an explicit role_id.
|
||||
It fetches the "Mitglied" role from the database without authorization checks
|
||||
(safe during user creation bootstrap phase).
|
||||
|
||||
## Returns
|
||||
|
||||
- UUID of the "Mitglied" role if it exists
|
||||
- `nil` if the role doesn't exist (will cause validation error due to `allow_nil? false`)
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Mv.Accounts.User.default_role_id()
|
||||
"019bf2e2-873a-7712-a7ce-a5a1f90c5f4f"
|
||||
"""
|
||||
@spec default_role_id() :: Ecto.UUID.t() | nil
|
||||
def default_role_id do
|
||||
case Mv.Authorization.Role.get_mitglied_role() do
|
||||
{:ok, %Mv.Authorization.Role{id: role_id}} -> role_id
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue