143 lines
3.3 KiB
Elixir
143 lines
3.3 KiB
Elixir
defmodule Mv.Membership.JoinRequest do
|
|
@moduledoc """
|
|
Ash resource for public join requests (onboarding flow).
|
|
|
|
Created only after email confirmation (double opt-in). Per concept §2.3.2:
|
|
- email (dedicated field), payload, schema_version, status, submitted_at, source
|
|
- approved_at, rejected_at, reviewed_by_user_id for audit (Step 2)
|
|
- confirmation_token_hash for idempotency (unique constraint)
|
|
"""
|
|
use Ash.Resource,
|
|
domain: Mv.Membership,
|
|
data_layer: AshPostgres.DataLayer,
|
|
authorizers: [Ash.Policy.Authorizer]
|
|
|
|
alias Ash.Policy.Check.Builtins, as: AshBuiltins
|
|
|
|
postgres do
|
|
table "join_requests"
|
|
repo Mv.Repo
|
|
end
|
|
|
|
actions do
|
|
defaults [:destroy]
|
|
|
|
# Admin: list and get by id (used with HasPermission)
|
|
read :admin_read do
|
|
description "List and get JoinRequests; requires permission (e.g. admin / normal_user)"
|
|
primary? true
|
|
end
|
|
|
|
create :create do
|
|
primary? true
|
|
|
|
accept [
|
|
:email,
|
|
:confirmation_token_hash,
|
|
:status,
|
|
:submitted_at,
|
|
:source,
|
|
:schema_version,
|
|
:payload,
|
|
:approved_at,
|
|
:rejected_at,
|
|
:reviewed_by_user_id
|
|
]
|
|
end
|
|
|
|
create :confirm do
|
|
description "Public action: create JoinRequest after confirmation link click (actor: nil)"
|
|
accept [:email, :confirmation_token_hash, :payload]
|
|
|
|
change Mv.Membership.JoinRequest.Changes.SetConfirmServerMetadata
|
|
end
|
|
|
|
update :update do
|
|
accept [:status, :approved_at, :rejected_at, :reviewed_by_user_id]
|
|
require_atomic? false
|
|
end
|
|
end
|
|
|
|
policies do
|
|
policy action(:confirm) do
|
|
description "Allow public confirmation (actor nil) for join flow"
|
|
authorize_if AshBuiltins.actor_absent()
|
|
end
|
|
|
|
policy action(:admin_read) do
|
|
description "List/get JoinRequests only with permission (admin, later normal_user)"
|
|
authorize_if Mv.Authorization.Checks.HasPermission
|
|
end
|
|
|
|
policy action(:create) do
|
|
description "Generic create only for authorized users; public uses :confirm"
|
|
authorize_if Mv.Authorization.Checks.HasPermission
|
|
end
|
|
|
|
policy action_type([:update, :destroy]) do
|
|
authorize_if Mv.Authorization.Checks.HasPermission
|
|
end
|
|
end
|
|
|
|
attributes do
|
|
uuid_v7_primary_key :id
|
|
|
|
attribute :email, :string do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :confirmation_token_hash, :string do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :status, :string do
|
|
allow_nil? false
|
|
public? true
|
|
default "submitted"
|
|
end
|
|
|
|
attribute :submitted_at, :utc_datetime_usec do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :source, :string do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :schema_version, :integer do
|
|
allow_nil? false
|
|
public? true
|
|
end
|
|
|
|
attribute :payload, :map do
|
|
allow_nil? true
|
|
public? true
|
|
default %{}
|
|
end
|
|
|
|
attribute :approved_at, :utc_datetime_usec do
|
|
allow_nil? true
|
|
public? true
|
|
end
|
|
|
|
attribute :rejected_at, :utc_datetime_usec do
|
|
allow_nil? true
|
|
public? true
|
|
end
|
|
|
|
attribute :reviewed_by_user_id, :uuid do
|
|
allow_nil? true
|
|
public? true
|
|
end
|
|
|
|
timestamps()
|
|
end
|
|
|
|
identities do
|
|
identity :unique_confirmation_token_hash, [:confirmation_token_hash]
|
|
end
|
|
end
|