You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

305 lines
8.5 KiB

defmodule DecentralisedBookIndex.Accounts.User do
use Ash.Resource,
otp_app: :decentralised_book_index,
domain: DecentralisedBookIndex.Accounts,
authorizers: [Ash.Policy.Authorizer],
extensions: [AshAuthentication],
data_layer: AshPostgres.DataLayer
authentication do
add_ons do
log_out_everywhere do
apply_on_password_change?(true)
end
confirmation :confirm_new_user do
monitor_fields [:email]
confirm_on_create? true
confirm_on_update? false
auto_confirm_actions [:sign_in_with_magic_link, :reset_password_with_token]
sender DecentralisedBookIndex.Accounts.User.Senders.SendNewUserConfirmationEmail
end
end
tokens do
enabled? true
token_resource DecentralisedBookIndex.Accounts.Token
signing_secret DecentralisedBookIndex.Secrets
store_all_tokens? true
require_token_presence_for_authentication? true
end
strategies do
password :password do
identity_field :email
resettable do
sender DecentralisedBookIndex.Accounts.User.Senders.SendPasswordResetEmail
# these configurations will be the default in a future release
password_reset_action_name :reset_password_with_token
request_password_reset_action_name :request_password_reset_token
end
end
end
end
postgres do
table "users"
repo DecentralisedBookIndex.Repo
end
code_interface do
define :list_moderators
define :search, action: :search, args: [:email]
define :get_by_id, action: :read, get_by: [:id]
define :get_by_email, args: [:email]
define :set_role, args: [:role]
define :make_moderator
define :make_user
end
actions do
defaults [:read]
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
update :change_password do
# Use this action to allow users to change their password by providing
# their current password and a new password.
require_atomic? false
accept []
argument :current_password, :string, sensitive?: true, allow_nil?: false
argument :password, :string, sensitive?: true, allow_nil?: false
argument :password_confirmation, :string, sensitive?: true, allow_nil?: false
validate confirm(:password, :password_confirmation)
validate {AshAuthentication.Strategy.Password.PasswordValidation,
strategy_name: :password, password_argument: :current_password}
change {AshAuthentication.Strategy.Password.HashPasswordChange, strategy_name: :password}
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
read :sign_in_with_token do
# In the generated sign in components, we validate the
# email and password directly in the LiveView
# and generate a short-lived token that can be used to sign in over
# a standard controller action, exchanging it for a standard token.
# This action performs that exchange. If you do not use the generated
# liveviews, you may remove this action, and set
# `sign_in_tokens_enabled? false` in the password strategy.
description "Attempt to sign in using a short-lived sign in token."
get? true
argument :token, :string do
description "The short-lived sign in token."
allow_nil? false
sensitive? true
end
# validates the provided sign in token and generates a token
prepare AshAuthentication.Strategy.Password.SignInWithTokenPreparation
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
action :request_password_reset_token do
description "Send password reset instructions to a user if they exist."
argument :email, :ci_string do
allow_nil? false
end
# creates a reset token and invokes the relevant senders
run {AshAuthentication.Strategy.Password.RequestPasswordReset, action: :get_by_email}
end
read :get_by_email do
description "Looks up a user by their email"
get? true
argument :email, :ci_string do
allow_nil? false
end
filter expr(email == ^arg(:email))
end
update :reset_password_with_token do
argument :reset_token, :string do
allow_nil? false
sensitive? true
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
# validates the provided reset token
validate AshAuthentication.Strategy.Password.ResetTokenValidation
# validates that the password matches the confirmation
validate AshAuthentication.Strategy.Password.PasswordConfirmationValidation
# Hashes the provided password
change AshAuthentication.Strategy.Password.HashPasswordChange
# Generates an authentication token for the user
change AshAuthentication.GenerateTokenChange
end
update :set_role do
description "Sets user role"
accept [:role]
end
update :make_moderator do
description "Sets user role to moderator"
change set_attribute(:role, :moderator)
end
update :make_user do
description "Sets user role to user"
change set_attribute(:role, :user)
end
read :list_moderators do
description "Looks up users with the moderator role"
filter expr(role == :moderator)
end
read :search do
argument :email, :ci_string do
constraints allow_empty?: true
default ""
end
filter expr(contains(email, ^arg(:email)))
pagination offset?: true, default_limit: 10
end
end
policies do
bypass AshAuthentication.Checks.AshAuthenticationInteraction do
authorize_if always()
end
bypass actor_attribute_equals(:role, :admin) do
authorize_if always()
end
policy always() do
forbid_if always()
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, DecentralisedBookIndex.Accounts.Role do
allow_nil? false
default :user
public? true
end
end
identities do
identity :unique_email, [:email]
end
end