Macaw.Auth.Native.UserRecord (macaw_auth v0.4.1)

Copy Markdown View Source

A row of macaw_auth_users, the native backend's users, in the host's repo (mix macaw_auth.gen.migration writes the table's migration).

  • username is stored normalized (normalize_username/1: trimmed, lower case), with a unique index, so Alice and alice are one user on every database without a case-insensitive collation.

  • hashed_password is nil for a directory user: the directory checks their password, and the Password source does not know them.

  • source names where the identity came from: "local" (the Password source) or an LDAP source's name, which owns the row and re-derives its roles at each sign-in.

  • roles is {:array, :string}: a native array on Postgres, and on SQLite a JSON text column, which ecto_sqlite3 encodes and decodes for an array field itself. One migration serves both; no query needs to look inside the list.

  • disabled_at set means refused, whatever the password.

  • extra holds what a source knows beyond these (an LDAP DN, its id attribute, email) and the host's own fields.

  • totp_secret_sealed, totp_enabled_at, totp_last_step: the second factor (Macaw.Auth.Native.Totp), added by their own migration. The secret is sealed (Macaw.Auth.Native.Seal), never stored as it is; an enrollment is pending while totp_enabled_at is nil; the last accepted step is what makes a code single-use. None of them is cast by changeset/2: only Totp writes them, each by a conditional update.

Summary

Functions

A changeset for a new or changed user; the username is normalized.

A username trimmed and lower-cased: {:ok, name}, or {:error, :invalid_username} unless it is 1–64 characters of [a-z0-9._@+-], starting with a letter or digit. Nothing else is rewritten, so two distinct inputs never become one user.

Types

t()

@type t() :: %Macaw.Auth.Native.UserRecord{
  __meta__: term(),
  disabled_at: term(),
  display_name: term(),
  extra: term(),
  hashed_password: term(),
  id: term(),
  inserted_at: term(),
  must_change_password: term(),
  roles: term(),
  source: term(),
  totp_enabled_at: term(),
  totp_last_step: term(),
  totp_secret_sealed: term(),
  updated_at: term(),
  username: term()
}

Functions

changeset(record, attrs)

@spec changeset(t(), map()) :: Ecto.Changeset.t()

A changeset for a new or changed user; the username is normalized.

normalize_username(input)

@spec normalize_username(term()) :: {:ok, String.t()} | {:error, :invalid_username}

A username trimmed and lower-cased: {:ok, name}, or {:error, :invalid_username} unless it is 1–64 characters of [a-z0-9._@+-], starting with a letter or digit. Nothing else is rewritten, so two distinct inputs never become one user.