Malachi. Auth
(malachi v0.8.13)
View Source
Simple authentication system for Malachi. Manages users with username/password credentials.
Summary
Functions
Adds a new user. Permissions: :admin, :produce, :consume
Authenticates a user with username and password (legacy compatibility). Uses a dummy IP address. For production use, prefer authenticate/3 with actual client IP. Returns {:ok, session_token} or {:error, reason}
Authenticates a user with username and password. Returns {:ok, session_token} or {:error, reason}
Changes user password.
Returns a specification to start this module under a supervisor.
Generates a random password for username (an admin) and seeds it, but only when generation is enabled
(:generate_admin config, set when no admin password is configured) and no such user exists yet. The
password is logged once; the replicated store dedups, so on a multi-node boot exactly one node's seed
succeeds and announces its password (the others get :user_exists and discard theirs). A no-op when
generation is disabled or the admin already exists. Called at boot after seed_default_users/0.
Grants username a per-topic ACL: operation (:produce/:consume) on pattern (an exact topic, or a
*-suffixed prefix like "orders.*"). Returns :ok, or {:error, :invalid_acl} for a bad operation/pattern.
Whether the subject has permission (or is :admin). Accepts either a username, looked up in
the user table, where an unknown user has no permissions, or a permission list directly.
Lists username's ACL grants as %{operation, resource} maps (resource rendered as its pattern string).
Lists all users (without passwords).
Invalidates a session token (logout).
Parses an ACL operation string into :produce/:consume, or :error. Mapping explicitly (rather than
String.to_atom/1) keeps an untrusted client from exhausting the atom table. Shared by the ACL management
surfaces (wire ops, dashboard).
Parses a list of permission strings into the allowed permission atoms, or :error if any is unknown
(or the input is not a list). The allowed permissions are :admin, :produce, :consume. Mapping
explicitly (rather than String.to_atom/1) keeps an untrusted client from exhausting the atom table.
Removes a user.
Revokes a per-topic ACL grant (idempotent). Returns :ok or {:error, reason}.
Starts the auth server, which owns the in-memory user and session ETS tables.
Validates a session token without IP binding (legacy compatibility).
Validates a session token with IP binding. Returns {:ok, %{username: username, permissions: permissions}} or {:error, reason}
Verifies a username/password against the user store without creating a session: the session-less core
of authentication, used by Malachi.Auth.PasswordProvider. Returns {:ok, permissions} or
{:error, :invalid_password | :user_not_found}.
Functions
Adds a new user. Permissions: :admin, :produce, :consume
Authenticates a user with username and password (legacy compatibility). Uses a dummy IP address. For production use, prefer authenticate/3 with actual client IP. Returns {:ok, session_token} or {:error, reason}
Authenticates a user with username and password. Returns {:ok, session_token} or {:error, reason}
Parameters
username- Username to authenticatepassword- Password to verifyclient_ip- Client IP address (tuple) for session binding and audit logging
Changes user password.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec generate_admin_if_absent(String.t()) :: :ok
Generates a random password for username (an admin) and seeds it, but only when generation is enabled
(:generate_admin config, set when no admin password is configured) and no such user exists yet. The
password is logged once; the replicated store dedups, so on a multi-node boot exactly one node's seed
succeeds and announces its password (the others get :user_exists and discard theirs). A no-op when
generation is disabled or the admin already exists. Called at boot after seed_default_users/0.
Grants username a per-topic ACL: operation (:produce/:consume) on pattern (an exact topic, or a
*-suffixed prefix like "orders.*"). Returns :ok, or {:error, :invalid_acl} for a bad operation/pattern.
Whether the subject has permission (or is :admin). Accepts either a username, looked up in
the user table, where an unknown user has no permissions, or a permission list directly.
Lists username's ACL grants as %{operation, resource} maps (resource rendered as its pattern string).
Lists all users (without passwords).
Invalidates a session token (logout).
@spec parse_acl_operation(String.t()) :: {:ok, :produce | :consume} | :error
Parses an ACL operation string into :produce/:consume, or :error. Mapping explicitly (rather than
String.to_atom/1) keeps an untrusted client from exhausting the atom table. Shared by the ACL management
surfaces (wire ops, dashboard).
Parses a list of permission strings into the allowed permission atoms, or :error if any is unknown
(or the input is not a list). The allowed permissions are :admin, :produce, :consume. Mapping
explicitly (rather than String.to_atom/1) keeps an untrusted client from exhausting the atom table.
Removes a user.
Revokes a per-topic ACL grant (idempotent). Returns :ok or {:error, reason}.
Starts the auth server, which owns the in-memory user and session ETS tables.
Validates a session token without IP binding (legacy compatibility).
Validates a session token with IP binding. Returns {:ok, %{username: username, permissions: permissions}} or {:error, reason}
Parameters
token- Session token to validateclient_ip- Current client IP address for binding verification
@spec verify_credentials(String.t(), String.t()) :: {:ok, [atom()]} | {:error, :invalid_password | :user_not_found}
Verifies a username/password against the user store without creating a session: the session-less core
of authentication, used by Malachi.Auth.PasswordProvider. Returns {:ok, permissions} or
{:error, :invalid_password | :user_not_found}.
Runs a dummy hash (Argon2.no_user_verify/0) for an unknown user so the response time does not reveal
whether the username exists (timing-attack mitigation). The caller mints the session and maps both error
reasons to a single client-facing :invalid_credentials.