rustio_admin/auth/mod.rs
1//! Authentication & authorization.
2//!
3//! Three pieces:
4//! - `users.rs` — user records, password hashing, login
5//! - `sessions.rs` — DB-backed sessions with expiry cleanup
6//! - `permissions.rs` — granular permissions + groups
7//!
8//! A user belongs to zero or more groups. Permissions come from two
9//! sources: (a) direct assignments on the user, (b) inherited from
10//! the user's groups. The permission string is
11//! `<app>.<action>_<model>` — e.g. `posts.change_post`.
12
13pub mod emergency;
14pub mod guards;
15pub(crate) mod mfa;
16mod permissions;
17pub(crate) mod recovery;
18pub(crate) mod recovery_admin;
19mod role;
20// `sessions` is `pub(crate)` so the `__integration` test door at
21// `crate::__integration` can re-export individual `pub(crate)`
22// helpers (specifically `hash_token_for_storage`, used by the R4
23// emergency-access integration suite to verify token-hash format
24// parity with R1's consume path). Public re-exports still flow
25// through `pub use sessions::{...}` below — this visibility bump
26// does not change the external API surface.
27pub(crate) mod sessions;
28mod users;
29
30pub use mfa::MfaPolicy;
31pub(crate) use permissions::invalidate_user_cache;
32pub use permissions::{
33 add_user_to_group, check_permission, create_group, grant_to_group, grant_to_user,
34 init_permission_tables, permissions_for_user, register_model_permissions,
35 remove_user_from_group, Permission, PermissionError, Superuser,
36};
37pub use recovery::{
38 DefaultPasswordPolicy, DefaultRecoveryPolicy, LoginThrottle, PasswordPolicy,
39 PasswordPolicyError, RecoveryPolicy, SharedPasswordPolicy, SharedRecoveryPolicy,
40};
41// `issue_reset_token` / `consume_reset_token` and the `IssueOutcome` /
42// `ConsumeOutcome` / `MailerEmailStatus` types live in `recovery`
43// (`pub(crate) mod recovery`) so the admin handlers in commit #8+
44// reach them as `crate::auth::recovery::*`. They are intentionally
45// NOT re-exported here — the framework owns the handler shape, and
46// projects compose recovery via the trait surfaces re-exported above.
47// `purge_expired_reset_tokens` (R1 commit #12) is reached the same
48// way from `background::spawn_session_sweeper`.
49pub use role::{protected_roles, Role};
50pub use sessions::{
51 create_session, current_session_id, delete_session, identity_from_session, init_session_tables,
52 invalidate_sessions, list_active_for_user, logout_session, purge_expired_sessions,
53 session_token_from_cookie, InvalidationOutcome, Session, SessionInvalidationReason,
54 SessionTarget, SessionTrust, SESSION_COOKIE,
55};
56#[allow(deprecated)]
57pub use users::would_orphan_developers;
58pub use users::{
59 create_user, find_user_by_email, hash_password, init_user_tables, load_user_profile, login,
60 migrate_user_schema, set_password, update_user_role, verdict_for_orphan_role, verify_password,
61 would_orphan_protected, would_orphan_role, Identity, StoredUser, UserProfile,
62};
63
64use crate::error::Result;
65use crate::orm::Db;
66
67/// Initialise every auth-related table. Safe to call on every boot.
68pub async fn init_tables(db: &Db) -> Result<()> {
69 init_user_tables(db).await?;
70 migrate_user_schema(db).await?;
71 init_session_tables(db).await?;
72 sessions::migrate_session_schema(db).await?;
73 sessions::migrate_session_lifecycle(db).await?;
74 init_permission_tables(db).await?;
75 // R1 (0.5.0) — self password recovery schema. See
76 // DESIGN_RECOVERY.md §9 for the contract.
77 recovery::migrate_user_recovery_schema(db).await?;
78 recovery::init_recovery_tables(db).await?;
79 // R2 (0.6.0) — organisational recovery schema (lockout columns +
80 // partial index). See DESIGN_R2_ORGANISATIONAL.md §4 for the
81 // contract. Schema is additive and orthogonal to R1's recovery
82 // tables; the runtime that reads these columns lands in later
83 // R2 commits.
84 recovery_admin::migrate_user_lockout_schema(db).await?;
85 // R3 (0.7.0) — TOTP MFA schema (4 additive columns on
86 // rustio_users + new rustio_mfa_backup_codes table + partial
87 // index). See DESIGN_R3_MFA.md §7 for the contract. Schema
88 // is additive and orthogonal to R1 / R2; the encryption
89 // helpers, TOTP RFC 6238 implementation, enrolment /
90 // verification / disable runtime, and login-flow integration
91 // land in later R3 commits.
92 mfa::migrate_user_mfa_schema(db).await?;
93 // R2 (0.6.0) — surfaced by the testcontainers integration suite:
94 // `rustio_admin_actions` was previously created lazily on first
95 // dashboard hit (`handlers::ensure_audit_ready`), so any audit-
96 // emitting path that ran BEFORE someone visited `/admin` would
97 // fail. The R2 audit-heavy paths (auto-throttle, admin reset,
98 // admin lock/unlock/revoke, forced rotation) made this latent
99 // issue routinely reachable. Creating the table eagerly during
100 // boot closes the gap; `ensure_table` is already idempotent so
101 // this is safe to call alongside the lazy path that still
102 // exists in the dashboard handler.
103 crate::admin::audit::ensure_table(db).await?;
104 Ok(())
105}