Skip to main content

Db

Struct Db 

Source
pub struct Db { /* private fields */ }
Expand description

Handle to the allowthem database.

Wraps a SqlitePool and guarantees that migrations have been applied. All query code receives a &Db and calls db.pool() to access the pool.

Implementations§

Source§

impl Db

Source

pub async fn create_api_token( &self, user_id: UserId, name: &str, expires_at: Option<DateTime<Utc>>, ) -> Result<(String, ApiTokenInfo), AuthError>

Generate and store a new API token for the user.

Returns the raw token string (shown once, never stored) and ApiTokenInfo metadata. The caller must present the raw token to the user — it cannot be retrieved again.

Source

pub async fn validate_api_token( &self, raw_token: &str, ) -> Result<Option<UserId>, AuthError>

Validate a raw bearer token.

Hashes the token and queries by hash. Tokens with a past expires_at are excluded. Returns Some(UserId) if valid, None otherwise.

Source

pub async fn list_api_tokens( &self, user_id: UserId, ) -> Result<Vec<ApiTokenInfo>, AuthError>

List all API tokens for a user (metadata only, no hashes).

Source

pub async fn delete_api_token(&self, id: ApiTokenId) -> Result<bool, AuthError>

Delete a single API token by ID.

Returns true if a token was found and deleted.

Source

pub async fn delete_user_api_tokens( &self, user_id: UserId, ) -> Result<u64, AuthError>

Delete all API tokens for a user.

Returns the number of tokens deleted.

Source§

impl Db

Source

pub async fn log_audit( &self, event_type: AuditEvent, user_id: Option<&UserId>, target_id: Option<&str>, ip_address: Option<&str>, user_agent: Option<&str>, detail: Option<&str>, ) -> Result<(), AuthError>

Record an audit event.

user_id may be None for events where no authenticated user is involved (e.g. a failed login attempt against an unknown email).

Source

pub async fn get_audit_log( &self, user_id: Option<&UserId>, limit: u32, offset: u32, ) -> Result<Vec<AuditEntry>, AuthError>

Retrieve audit log entries, optionally filtered by user.

Results are ordered by created_at descending (newest first).

Source

pub async fn get_audit_log_by_event( &self, event_type: AuditEvent, limit: u32, offset: u32, ) -> Result<Vec<AuditEntry>, AuthError>

Retrieve audit log entries filtered by event type.

Results are ordered by created_at descending (newest first).

Source§

impl Db

Source

pub async fn new(pool: SqlitePool) -> Result<Self, AuthError>

Create a Db from an integrator-provided pool and run migrations.

This is the embedded-mode constructor. The caller is responsible for configuring PRAGMA foreign_keys = ON on their pool’s SqliteConnectOptions — this constructor cannot set per-connection pragmas on a pool it did not create.

Migrations are idempotent: safe to call on a pool that has already been migrated. SQLx tracks applied migrations in _sqlx_migrations and CREATE TABLE IF NOT EXISTS in the SQL is a no-op on existing tables.

ignore_missing is set so that migrations from the integrating application already recorded in _sqlx_migrations do not cause an error — those are the integrator’s own migrations, not allowthem’s.

Source

pub async fn connect(url: &str) -> Result<Self, AuthError>

Create a pool from a URL, apply pragmas, run migrations, and return a Db.

Configures the pool with:

  • PRAGMA foreign_keys = ON — FK constraint enforcement
  • PRAGMA journal_mode = WAL — concurrent reads (silently ignored for :memory:)
  • PRAGMA busy_timeout = 5000 — wait under contention instead of immediate SQLITE_BUSY
Source

pub fn pool(&self) -> &SqlitePool

Return a reference to the underlying connection pool.

Source§

impl Db

Source

pub async fn create_oauth_state( &self, provider: &str, redirect_uri: &str, pkce_verifier: &str, post_login_redirect: Option<&str>, linking_user_id: Option<UserId>, ) -> Result<String, AuthError>

Create an OAuth state record. Returns the raw state value (for the authorize URL).

linking_user_id is Some when initiating the account-linking flow (the user is already authenticated and wants to add a provider). It is None for the standard login/register flow.

Source

pub async fn validate_oauth_state( &self, raw_state: &str, ) -> Result<Option<OAuthStateInfo>, AuthError>

Validate and consume an OAuth state. Returns the stored info or None if invalid/expired. Atomically deletes to prevent reuse.

Source

pub async fn create_oauth_user( &self, email: Email, provider: &str, provider_user_id: &str, ) -> Result<User, AuthError>

Create a user via OAuth – no password.

Creates the user (password_hash = NULL) and the oauth_accounts row in a single transaction. Returns the created User.

Link an OAuth identity to an existing user.

Source

pub async fn find_user_by_oauth( &self, provider: &str, provider_user_id: &str, ) -> Result<Option<User>, AuthError>

Find an allowthem user by provider + provider_user_id.

Source

pub async fn get_user_oauth_accounts( &self, user_id: UserId, ) -> Result<Vec<OAuthAccountInfo>, AuthError>

List all OAuth accounts linked to a user.

Remove an OAuth account link for a user + provider.

Returns true if a row was deleted, false if no link existed.

Source§

impl Db

Source

pub async fn create_password_reset( &self, email: &Email, ) -> Result<Option<String>, AuthError>

Create a password reset token for the user with the given email.

Looks up the user by email. If found, inserts a new reset token record (hashed) and returns the raw token for inclusion in the reset URL. Returns None if no user exists for that email (caller should not reveal this to prevent email enumeration).

Source

pub async fn validate_reset_token( &self, raw_token: &str, ) -> Result<Option<(ResetTokenId, UserId)>, AuthError>

Validate a raw reset token.

Hashes the token and looks it up in the database. Returns the associated UserId and token record ID if the token exists, has not expired, and has not been used. Returns None if the token is invalid or expired.

Source

pub async fn execute_reset( &self, raw_token: &str, new_password: &str, ) -> Result<bool, AuthError>

Execute a password reset: update the password and mark the token used.

Runs atomically in a transaction:

  1. Validate the token (not expired, not used).
  2. Mark the token as used (used_at = now).
  3. Hash the new password.
  4. Update the user’s password_hash and updated_at.

Returns Ok(true) on success, Ok(false) if the token was invalid.

Source

pub async fn send_password_reset( &self, email: &Email, base_url: &str, sender: &dyn EmailSender, ) -> Result<(), AuthError>

Send a password reset email for the given address.

Calls create_password_reset to generate a token. If the email exists, constructs a reset URL using base_url and delivers it via sender. If the email does not exist, returns Ok(()) silently (no enumeration).

Source§

impl Db

Source

pub async fn create_permission( &self, name: &PermissionName, description: Option<&str>, ) -> Result<Permission, AuthError>

Create a permission with a unique name and optional description.

Source

pub async fn get_permission( &self, id: &PermissionId, ) -> Result<Option<Permission>, AuthError>

Get a permission by ID. Returns None if not found.

Source

pub async fn get_permission_by_name( &self, name: &PermissionName, ) -> Result<Option<Permission>, AuthError>

Get a permission by name. Returns None if not found.

Source

pub async fn list_permissions(&self) -> Result<Vec<Permission>, AuthError>

List all permissions, ordered by creation time.

Source

pub async fn delete_permission( &self, id: &PermissionId, ) -> Result<bool, AuthError>

Delete a permission by ID. Returns true if a row was deleted, false if not found.

Cascades to allowthem_role_permissions and allowthem_user_permissions.

Source

pub async fn assign_permission_to_role( &self, role_id: &RoleId, permission_id: &PermissionId, ) -> Result<(), AuthError>

Assign a permission to a role. Idempotent — silently succeeds if already assigned.

Source

pub async fn assign_permission_to_user( &self, user_id: &UserId, permission_id: &PermissionId, ) -> Result<(), AuthError>

Assign a permission directly to a user. Idempotent — silently succeeds if already assigned.

Source

pub async fn unassign_permission_from_role( &self, role_id: &RoleId, permission_id: &PermissionId, ) -> Result<bool, AuthError>

Unassign a permission from a role. Returns true if removed, false if not found.

Source

pub async fn unassign_permission_from_user( &self, user_id: &UserId, permission_id: &PermissionId, ) -> Result<bool, AuthError>

Unassign a permission from a user. Returns true if removed, false if not found.

Source

pub async fn has_permission( &self, user_id: &UserId, perm_name: &PermissionName, ) -> Result<bool, AuthError>

Check whether a user has a permission by name via either path: direct user assignment or any of the user’s roles.

Source

pub async fn get_user_permissions( &self, user_id: &UserId, ) -> Result<Vec<Permission>, AuthError>

Return all permissions for a user — both directly assigned and via roles — deduplicated and ordered by name.

Source§

impl Db

Source

pub async fn create_role( &self, name: &RoleName, description: Option<&str>, ) -> Result<Role, AuthError>

Create a role with a unique name and optional description.

Source

pub async fn get_role(&self, id: &RoleId) -> Result<Option<Role>, AuthError>

Get a role by ID. Returns None if not found.

Source

pub async fn get_role_by_name( &self, name: &RoleName, ) -> Result<Option<Role>, AuthError>

Get a role by name. Returns None if not found.

Source

pub async fn list_roles(&self) -> Result<Vec<Role>, AuthError>

List all roles, ordered by creation time.

Source

pub async fn delete_role(&self, id: &RoleId) -> Result<bool, AuthError>

Delete a role by ID. Returns true if a row was deleted, false if not found.

Cascades to allowthem_user_roles and allowthem_role_permissions.

Source

pub async fn assign_role( &self, user_id: &UserId, role_id: &RoleId, ) -> Result<(), AuthError>

Assign a role to a user. Silently succeeds if already assigned (idempotent).

Source

pub async fn unassign_role( &self, user_id: &UserId, role_id: &RoleId, ) -> Result<bool, AuthError>

Unassign a role from a user. Returns true if removed, false if the assignment did not exist.

Source

pub async fn has_role( &self, user_id: &UserId, role_name: &RoleName, ) -> Result<bool, AuthError>

Check whether a user has a specific role by name.

Source

pub async fn get_user_roles( &self, user_id: &UserId, ) -> Result<Vec<Role>, AuthError>

Return all roles assigned to a user, ordered by creation time.

Source§

impl Db

Source

pub async fn create_session( &self, user_id: UserId, token_hash: TokenHash, ip_address: Option<&str>, user_agent: Option<&str>, expires_at: DateTime<Utc>, ) -> Result<Session, AuthError>

Insert a new session record and return it.

The caller is responsible for hashing the token before calling this function via hash_token(). The raw token must never be passed here.

Source

pub async fn lookup_session( &self, token: &SessionToken, ) -> Result<Option<Session>, AuthError>

Look up a session by raw token.

Hashes the token internally and queries by hash. Expired sessions (where expires_at is in the past) are excluded. Returns None when no matching active session is found.

Source

pub async fn validate_session( &self, token: &SessionToken, ttl: Duration, ) -> Result<Option<Session>, AuthError>

Validate a session token and optionally extend it.

Fetches the active session by token hash. If the session is past the halfway point of its TTL (now > expires_at - ttl/2), it is renewed by setting expires_at = now + ttl. Returns the session with the updated expiry, or None if no active session was found.

Source

pub async fn delete_session( &self, token: &SessionToken, ) -> Result<bool, AuthError>

Delete a single session by raw token.

Returns true if a session was found and deleted, false if no matching session existed.

Source

pub async fn delete_user_sessions( &self, user_id: &UserId, ) -> Result<u64, AuthError>

Delete all sessions for a user.

Returns the number of sessions that were deleted.

Source§

impl Db

Source

pub async fn create_mfa_secret( &self, user_id: UserId, mfa_key: &[u8; 32], ) -> Result<String, AuthError>

Generate a new TOTP secret for a user and store it (encrypted, not yet enabled).

Returns the plaintext base32-encoded secret for display to the user during the setup flow. The caller must present this secret (or a QR code derived from it) and require the user to confirm with a valid code before calling enable_mfa.

Fails with MfaAlreadyEnabled if the user already has an enabled MFA secret. If a non-enabled secret exists (abandoned setup attempt), it is replaced.

Source

pub async fn enable_mfa( &self, user_id: UserId, code: &str, mfa_key: &[u8; 32], ) -> Result<Vec<String>, AuthError>

Enable MFA for a user after verifying a TOTP code.

Decrypts the stored secret, validates the provided code against it, and if valid, sets enabled = 1 and inserts 10 hashed recovery codes. Returns the plaintext recovery codes (this is the only time they are visible).

Runs in a transaction to ensure MFA is never enabled without recovery codes.

Source

pub async fn verify_totp( &self, user_id: UserId, code: &str, mfa_key: &[u8; 32], ) -> Result<bool, AuthError>

Validate a TOTP code against a user’s enabled MFA secret.

Returns Ok(true) if the code is valid, Ok(false) if invalid. Returns Err(MfaNotEnabled) if the user has no enabled MFA.

Source

pub async fn has_mfa_enabled(&self, user_id: UserId) -> Result<bool, AuthError>

Check whether a user has MFA enabled.

Source

pub async fn disable_mfa(&self, user_id: UserId) -> Result<(), AuthError>

Disable MFA for a user. Deletes the secret and all recovery codes.

Uses a transaction to ensure both deletes are atomic.

Source

pub async fn verify_recovery_code( &self, user_id: UserId, code: &str, ) -> Result<bool, AuthError>

Verify a recovery code. If valid, marks it as used (one-time use).

Uses atomic UPDATE ... RETURNING to prevent race conditions. Returns Ok(true) if the code was valid and consumed, Ok(false) if no matching unused code was found.

Source

pub async fn remaining_recovery_codes( &self, user_id: UserId, ) -> Result<i64, AuthError>

Count remaining unused recovery codes for a user.

Source

pub async fn regenerate_recovery_codes( &self, user_id: UserId, ) -> Result<Vec<String>, AuthError>

Replace all recovery codes with a fresh set of 10.

Deletes all existing codes (used and unused) and inserts 10 new ones. Returns the plaintext codes. Runs in a transaction.

Source

pub async fn create_mfa_challenge( &self, user_id: UserId, ) -> Result<String, AuthError>

Create a short-lived MFA challenge token after password verification.

The integrator calls this when a user with MFA enabled passes password verification. Returns the raw token string to send to the client. The client presents this token along with a TOTP code to complete login. Challenge tokens expire after 5 minutes.

Source

pub async fn validate_mfa_challenge( &self, raw_token: &str, ) -> Result<Option<UserId>, AuthError>

Validate an MFA challenge token without consuming it.

Returns Some(user_id) if the token is valid and not expired, None otherwise. Does not consume the token so the user can retry if they mistype the TOTP code.

Source

pub async fn consume_mfa_challenge( &self, raw_token: &str, ) -> Result<(), AuthError>

Consume an MFA challenge token after successful TOTP verification.

Uses DELETE ... RETURNING for atomicity.

Source§

impl Db

Source

pub async fn create_user( &self, email: Email, password: &str, username: Option<Username>, ) -> Result<User, AuthError>

Create a user with email, plaintext password, and optional username.

Hashes the password with Argon2id (via password::hash_password). Returns the created User (without password_hash in the returned struct).

Source

pub async fn get_user(&self, id: UserId) -> Result<User, AuthError>

Look up a user by ID. Returns User with password_hash = None.

Source

pub async fn get_user_by_email(&self, email: &Email) -> Result<User, AuthError>

Look up a user by email. Returns User with password_hash = None.

Source

pub async fn get_user_by_username( &self, username: &Username, ) -> Result<User, AuthError>

Look up a user by username. Returns User with password_hash = None.

Source

pub async fn find_for_login(&self, identifier: &str) -> Result<User, AuthError>

Look up a user by email OR username for login.

Returns User WITH password_hash populated. The caller is responsible for calling verify_password() to check the password.

Source

pub async fn update_user_email( &self, id: UserId, email: Email, ) -> Result<(), AuthError>

Update a user’s email. Also updates updated_at.

Source

pub async fn update_user_username( &self, id: UserId, username: Option<Username>, ) -> Result<(), AuthError>

Update a user’s username (set or clear). Also updates updated_at.

Source

pub async fn update_user_active( &self, id: UserId, is_active: bool, ) -> Result<(), AuthError>

Update a user’s is_active flag. Also updates updated_at.

Source

pub async fn delete_user(&self, id: UserId) -> Result<(), AuthError>

Delete a user by ID. Cascades to sessions, user_roles, user_permissions.

Source

pub async fn list_users(&self) -> Result<Vec<User>, AuthError>

List all users ordered by created_at ASC. Returns User with password_hash = None.

Source

pub async fn update_user_password( &self, id: UserId, new_password: &str, ) -> Result<(), AuthError>

Update a user’s password. Hashes new_password with Argon2id and stores it.

Returns AuthError::NotFound if no user with id exists.

Auto Trait Implementations§

§

impl Freeze for Db

§

impl !RefUnwindSafe for Db

§

impl Send for Db

§

impl Sync for Db

§

impl Unpin for Db

§

impl UnsafeUnpin for Db

§

impl !UnwindSafe for Db

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more