defmodule MyApp.Accounts.User do @moduledoc """ The Ash Resource representing user accounts. """ use Ash.Resource, domain: MyApp.Accounts, data_layer: AshPostgres.DataLayer, authorizers: [Ash.Policy.Authorizer], extensions: [AshAuthentication] alias MyApp.{Market, Accounts} postgres do table "users" repo MyApp.Repo end authentication do strategies do password :password do identity_field :email hashed_password_field :hashed_password require_confirmed_with :confirmed_at resettable do sender MyApp.Accounts.User.Senders.SendPasswordResetEmail end end add_ons do confirmation :confirm_new_user do monitor_fields [:email] require_interaction? true confirmed_at_field :confirmed_at confirm_on_create? true confirm_on_update? false sender MyApp.Accounts.User.Senders.SendNewUserConfirmationEmail end end end tokens do enabled? true require_token_presence_for_authentication? true store_all_tokens? true token_resource MyApp.Accounts.Token signing_secret MyApp.Accounts.Secrets end add_ons do log_out_everywhere do apply_on_password_change? true end end end actions do defaults [:read] update :set_display_name do accept [:display_name] require_attributes [:display_name] end read :get_by_subject do description "Get a user by the subject claim in a JWT" argument :subject, :string, allow_nil?: false get? true prepare AshAuthentication.Preparations.FilterBySubject end read :sign_in_with_password do description "Attempt to sign in using a email and password." get? true argument :email, :ci_string do description "The email to use for retrieving the user." allow_nil? false end argument :password, :string do description "The password to check for the matching user." allow_nil? false sensitive? true end # Validates the provided email and password and generates a token. prepare AshAuthentication.Strategy.Password.SignInPreparation metadata :token, :string do description "A JWT that can be used to authenticate the user." allow_nil? false end end create :register_with_password do description "Register a new user with a email and password." argument :email, :ci_string do allow_nil? false end argument :password, :string do description "The proposed password for the user, in plain text." allow_nil? false constraints min_length: 8 sensitive? true end argument :password_confirmation, :string do description "The proposed password for the user (again), in plain text." allow_nil? false sensitive? true end # Sets the email from the argument change set_attribute(:email, arg(:email)) # Hashes the provided password change AshAuthentication.Strategy.Password.HashPasswordChange # Generates an authentication token for the user change AshAuthentication.GenerateTokenChange # Validates that the password matches the confirmation validate AshAuthentication.Strategy.Password.PasswordConfirmationValidation metadata :token, :string do description "A JWT that can be used to authenticate the user." allow_nil? false end end end policies do bypass AshAuthentication.Checks.AshAuthenticationInteraction do description "AshAuthentication has full access" authorize_if always() end bypass actor_attribute_equals(:role, :admin) do description "Admin users have full access" authorize_if always() end policy action_type(:read) do description "Can read users via classified ownership" authorize_if accessing_from(Market.Classified, :owner) authorize_if expr(id == ^actor(:id)) end policy action(:set_display_name) do description "Users can set their display name once" authorize_if expr(id == ^actor(:id)) end end field_policies do field_policy_bypass :* do description "AshAuthentication has full access" authorize_if AshAuthentication.Checks.AshAuthenticationInteraction authorize_if actor_attribute_equals(:role, :admin) end field_policy_bypass [:email, :display_name] do description "Internal emailers have access" authorize_if actor_attribute_equals(:role, :email) end private_fields :hide field_policy [:display_name] do description "Display name is always accessible" authorize_if always() end field_policy [:email] do description "User email is only accessible by that user" authorize_if expr(id == ^actor(:id)) end end attributes do uuid_primary_key :id attribute :email, :ci_string do allow_nil? false public? true end attribute :hashed_password, :string do allow_nil? false sensitive? true end attribute :role, Accounts.Role do allow_nil? false public? true default :user end attribute :display_name, :string, public?: true end identities do identity :unique_email, [:email] end end