Current section
Files
Jump to
Current section
Files
lib/maat_feather/users/user.ex
defmodule MaatFeather.Users.User do
use Ecto.Schema
import Ecto.Changeset
alias MaatFeather.Auth.TOTPSecretKey
@primary_key false
@foreign_key_type :string
schema "users" do
field(:email, :string)
field(:password, :string, virtual: true)
field(:password_hash, :string)
field(:address, :string, primary_key: true)
field(:role, :string, default: "user")
field(:username, :string)
field(:has_mfa, :boolean, default: false)
field(:secret_key, TOTPSecretKey, autogenerate: true)
field(:private_key, :string, virtual: true)
field(:public_key, :string, virtual: true)
timestamps()
end
@doc false
def changeset(user, attrs) do
user
|> cast(attrs, [
:username,
:email,
:password,
:role,
:address,
:password_hash,
:secret_key,
:has_mfa
])
|> validate_required([:username, :email, :role])
|> validate_inclusion(:role, [:user, :admin])
|> validate_format(:email, ~r/\A[^@\s]+@[^@\s]+\z/, message: "is not a valid email")
|> validate_length(:password, min: 6, message: "must be at least 6 characters")
|> validate_length(:username, min: 3, message: "must be at least 3 characters")
|> unique_constraint(:address, message: "address is already taken")
|> unique_constraint(:email, message: "email is already taken")
|> unique_constraint(:username, message: "username is already taken")
|> unique_constraint(:secret_key, message: "secret key is already taken")
|> put_params()
end
defp put_params(%Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset) do
private_key = X509.PrivateKey.new_rsa(1024)
public_key = X509.PublicKey.derive(private_key)
private_key =
private_key
|> X509.PrivateKey.to_pem()
|> Base.encode16()
public_key =
public_key
|> X509.PublicKey.to_pem()
|> Base.encode16()
changeset
|> put_change(:private_key, private_key)
|> put_change(:public_key, public_key)
|> put_change(:address, public_key)
|> change(Argon2.add_hash(password))
end
defp put_params(changeset), do: changeset
end