mirror of
https://codeberg.org/JasterV/spazio-solazzo.git
synced 2026-04-26 18:20:03 +00:00
190 lines
4.4 KiB
Elixir
190 lines
4.4 KiB
Elixir
defmodule SpazioSolazzo.Accounts.User do
|
|
@moduledoc """
|
|
Represents a user in the system with magic link authentication.
|
|
"""
|
|
|
|
use Ash.Resource,
|
|
otp_app: :spazio_solazzo,
|
|
domain: SpazioSolazzo.Accounts,
|
|
data_layer: AshPostgres.DataLayer,
|
|
authorizers: [Ash.Policy.Authorizer],
|
|
extensions: [AshAuthentication]
|
|
|
|
authentication do
|
|
add_ons do
|
|
log_out_everywhere do
|
|
apply_on_password_change? true
|
|
end
|
|
end
|
|
|
|
tokens do
|
|
enabled? true
|
|
token_resource SpazioSolazzo.Accounts.Token
|
|
signing_secret SpazioSolazzo.Secrets
|
|
store_all_tokens? true
|
|
require_token_presence_for_authentication? true
|
|
end
|
|
|
|
strategies do
|
|
magic_link do
|
|
identity_field :email
|
|
registration_enabled? true
|
|
require_interaction? true
|
|
sender SpazioSolazzo.Accounts.User.Senders.SendMagicLinkEmail
|
|
end
|
|
|
|
remember_me :remember_me
|
|
end
|
|
end
|
|
|
|
postgres do
|
|
table "users"
|
|
repo SpazioSolazzo.Repo
|
|
end
|
|
|
|
actions do
|
|
defaults [:read]
|
|
|
|
read :get_by_email do
|
|
description "Looks up a user by their email"
|
|
argument :email, :ci_string, allow_nil?: false
|
|
get? true
|
|
filter expr(email == ^arg(:email))
|
|
end
|
|
|
|
create :sign_in_with_magic_link do
|
|
description "Sign in or register a user with magic link."
|
|
|
|
argument :token, :string do
|
|
description "The token from the magic link that was sent to the user"
|
|
allow_nil? false
|
|
end
|
|
|
|
argument :remember_me, :boolean do
|
|
description "Whether to generate a remember me token"
|
|
allow_nil? true
|
|
end
|
|
|
|
argument :name, :string do
|
|
description "User's full name (required for new users)"
|
|
allow_nil? true
|
|
end
|
|
|
|
argument :phone_number, :string do
|
|
description "User's phone number (required for new users)"
|
|
allow_nil? true
|
|
end
|
|
|
|
upsert? true
|
|
upsert_identity :unique_email
|
|
upsert_fields [:email, :name, :phone_number]
|
|
|
|
# Uses the information from the token to create or sign in the user
|
|
change AshAuthentication.Strategy.MagicLink.SignInChange
|
|
|
|
# Conditionally validate name and phone_number for new users
|
|
change SpazioSolazzo.Accounts.User.Changes.ParseRegistrationFields
|
|
|
|
change {AshAuthentication.Strategy.RememberMe.MaybeGenerateTokenChange,
|
|
strategy_name: :remember_me}
|
|
|
|
metadata :token, :string do
|
|
allow_nil? false
|
|
end
|
|
end
|
|
|
|
action :request_magic_link do
|
|
argument :email, :ci_string, allow_nil?: false
|
|
run AshAuthentication.Strategy.MagicLink.Request
|
|
end
|
|
|
|
update :update_profile do
|
|
description "Update user profile (name and phone number)"
|
|
accept [:name, :phone_number]
|
|
require_atomic? false
|
|
end
|
|
|
|
update :make_admin do
|
|
accept []
|
|
change set_attribute(:role, :admin)
|
|
end
|
|
|
|
destroy :terminate_account do
|
|
description "Delete user account with optional booking data removal"
|
|
require_atomic? false
|
|
|
|
argument :delete_history, :boolean do
|
|
description "Whether to permanently delete all booking history"
|
|
default false
|
|
end
|
|
|
|
change SpazioSolazzo.Accounts.User.Changes.HandleBookingsOnAccountDeletion
|
|
end
|
|
end
|
|
|
|
policies do
|
|
bypass AshAuthentication.Checks.AshAuthenticationInteraction do
|
|
authorize_if always()
|
|
end
|
|
|
|
policy action(:make_admin) do
|
|
authorize_if never()
|
|
end
|
|
|
|
policy action_type(:read) do
|
|
authorize_if always()
|
|
end
|
|
|
|
policy action_type(:update) do
|
|
authorize_if expr(id == ^actor(:id))
|
|
end
|
|
|
|
policy action_type(:destroy) do
|
|
authorize_if expr(id == ^actor(:id))
|
|
end
|
|
end
|
|
|
|
# Make sure it is loaded in all read actions
|
|
preparations do
|
|
prepare build(load: [:is_admin])
|
|
end
|
|
|
|
# Make sure it is loaded in all write actions
|
|
changes do
|
|
change load(:is_admin)
|
|
end
|
|
|
|
attributes do
|
|
uuid_primary_key :id
|
|
|
|
attribute :email, :ci_string do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :name, :string do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :phone_number, :string do
|
|
allow_nil? true
|
|
public? true
|
|
end
|
|
|
|
attribute :role, :atom do
|
|
allow_nil? false
|
|
public? false
|
|
constraints one_of: [:customer, :admin]
|
|
default :customer
|
|
end
|
|
end
|
|
|
|
calculations do
|
|
calculate :is_admin, :boolean, expr(role == :admin)
|
|
end
|
|
|
|
identities do
|
|
identity :unique_email, [:email]
|
|
end
|
|
end
|