#[non_exhaustive]pub enum AuditEvent {
Show 24 variants
UserCreated,
UserUpdated,
UserDeleted,
GroupCreated,
GroupUpdated,
GroupDeleted,
PasswordChangedSelf,
PasswordResetSelfRequest,
PasswordResetSelfConsume,
PasswordResetByOther,
ForcedPasswordChangeCompleted,
AccountLocked,
AccountUnlocked,
MfaEnabled,
MfaDisabled,
MfaResetByOther,
MfaCodeConsumed,
BackupCodesRegenerated,
SessionsRevokedSelf,
SessionsRevokedByOther,
SessionLogout,
LoginSucceeded,
LoginFailed,
EmergencyRecovery,
}Expand description
Typed representation of every audit action_type the framework
emits for authority + identity + recovery actions.
Public-API stability (0.5.0): the enum is pub from R1
onwards (doctrine 18). External consumers — SIEM tooling, custom
dashboards, integration tests — can match on these variants
instead of (or in addition to) the persisted strings. The
as_str() mapping is the single canonical boundary between the
typed surface and the rustio_admin_actions.action_type TEXT
column. Every existing variant’s string is locked-in by the
audit_event_existing_variants_have_stable_strings test below;
renaming a string is a breaking change requiring a major version
bump.
Coexistence with ActionType: the legacy
ActionType::{Create, Update, Delete} trio writes the strings
"create" / "update" / "delete", used for generic CRUD on
project-registered models. AuditEvent strings are richer
("user_created", "password_reset_self_consume", …) and used
for the framework’s own authority + identity + recovery surfaces.
The two vocabularies are disjoint by design;
action_type_and_audit_event_vocabularies_dont_collide asserts
the disjointness.
Future-extensibility: #[non_exhaustive] lets future
R-phases (R2 / R3 / R4) add variants without breaking external
matchers. Variants whose call-sites haven’t shipped yet are
listed here in anticipation — as_str() returns the canonical
string regardless of whether anything emits it. The roadmap
in DESIGN_RECOVERY.md §16 + ROADMAP.md covers when each
variant lights up.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
UserCreated
UserUpdated
UserDeleted
GroupCreated
GroupUpdated
GroupDeleted
PasswordChangedSelf
Authenticated user changed their own password via
/admin/password_change. R1 commit #11 wires emission.
PasswordResetSelfRequest
Anonymous user requested a password-reset email via
/admin/forgot-password. R1 commit #7 wires emission.
PasswordResetSelfConsume
Anonymous user consumed a reset token + set a new password
via /admin/reset-password/<token>. R1 commit #7 wires
emission.
PasswordResetByOther
An administrator reset another user’s password. R2 wires
emission via the dedicated /admin/users/<id>/reset-password
route.
ForcedPasswordChangeCompleted
A user with must_change_password = TRUE completed the
forced rotation via POST /admin/must-change-password,
clearing the flag and (per DESIGN_R2_ORGANISATIONAL.md
§3.4) revoking every other session for the same user.
metadata.triggered_by_audit_id links back to the
originating PasswordResetByOther row;
metadata.invalidated_session_count records how many
sessions were revoked. R2 commit #12 wires emission.
AccountLocked
AccountUnlocked
MfaEnabled
MfaDisabled
MfaResetByOther
MfaCodeConsumed
A user consumed a backup code as the second factor on
the login or re-auth flow. Emitted by
auth::mfa::consume_backup_code (R3 commit #8).
metadata.code_id identifies which code was used;
metadata.remaining_codes tracks the unused-code count
post-consume so the user can be nudged toward
regeneration before exhaustion;
metadata.via = "login" | "reauth" distinguishes the
caller context.
BackupCodesRegenerated
A user regenerated their backup-code batch. Emitted by
auth::mfa::regenerate_backup_codes (R3 commit #10).
The DELETE + INSERT runs in one transaction per D3 of
the design (atomic regeneration; the old batch is
unrecoverable from the moment the transaction commits).
metadata.previous_codes_invalidated records the
count of rows the DELETE removed (used + unused
combined); metadata.new_codes_count is locked at 8
per Appendix B.
SessionsRevokedSelf
SessionsRevokedByOther
SessionLogout
LoginSucceeded
A user successfully authenticated via POST /admin/login —
password verified, account active, not locked. Emitted once
per successful login (the MFA-verify step that may follow
rotates the existing session’s trust level; that’s not a
second login event). metadata.mfa_pending is true when
the user has MFA enrolled and the response redirects to
/admin/mfa/verify.
LoginFailed
A login attempt against a known user failed: wrong password,
account inactive, or account currently locked. metadata.reason
is one of "wrong_password" | "inactive" | "locked". The
uniform 401 the user sees collapses these three cases; the
audit row preserves the distinction so an operator can tell
“brute force on a real account” from “someone is poking the
locked account waiting it out.”
Unknown-email limitation. Attempts against an email that
doesn’t match any rustio_users row are not audited —
rustio_admin_actions.user_id has a NOT NULL REFERENCES rustio_users(id) constraint, so there’s no place to attach
the row. The trace log captures the attempt at info level
via auth::find_user_by_email; promoting that to audit
requires a migration loosening the FK and is intentionally
out of scope for this iteration.
EmergencyRecovery
Emergency-recovery operation initiated from the rustio
CLI binary. Subsumes every rustio user <op> emergency
subcommand — the specific operation lives in
metadata.cli_operation ("reset_password" | "unlock" | "disable_mfa" | "promote" | "emergency_access"). The
OS-level actor identity lives in metadata.os_actor
(<whoami>@<hostname>).
CLI-only invariant (D12). This variant must NOT be
emitted from any framework HTTP handler. The web surface
has its own audit variants (PasswordResetByOther,
MfaResetByOther, etc.); collapsing those into
EmergencyRecovery would lose the distinction between
“admin acted via the web” and “operator went around every
other tier via shell.” A grep-based unit test (see
audit::tests::emergency_recovery_is_cli_only) enforces
this by walking crates/rustio-admin/src/ and asserting
zero callsites; the variant is exercised only from
crates/rustio-admin-cli/src/.
See DESIGN_R4_EMERGENCY.md §5 for the full metadata
schema and §10 doctrine D12.
Implementations§
Source§impl AuditEvent
impl AuditEvent
Sourcepub const fn as_str(self) -> &'static str
pub const fn as_str(self) -> &'static str
Stable lowercase identifier persisted as
rustio_admin_actions.action_type.
Stability contract: every string returned here is
part of the public API from 0.5.0 onwards. Existing values
are locked-in by
audit_event_existing_variants_have_stable_strings and
changing one is a breaking change requiring a major bump.
New AuditEvent variants may be added in minor versions
(the enum is #[non_exhaustive]); each new variant ships
with its locked string from day one.
Trait Implementations§
Source§impl Clone for AuditEvent
impl Clone for AuditEvent
Source§fn clone(&self) -> AuditEvent
fn clone(&self) -> AuditEvent
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for AuditEvent
impl Debug for AuditEvent
Source§impl Hash for AuditEvent
impl Hash for AuditEvent
Source§impl PartialEq for AuditEvent
impl PartialEq for AuditEvent
Source§fn eq(&self, other: &AuditEvent) -> bool
fn eq(&self, other: &AuditEvent) -> bool
self and other values to be equal, and is used by ==.impl Copy for AuditEvent
impl Eq for AuditEvent
impl StructuralPartialEq for AuditEvent
Auto Trait Implementations§
impl Freeze for AuditEvent
impl RefUnwindSafe for AuditEvent
impl Send for AuditEvent
impl Sync for AuditEvent
impl Unpin for AuditEvent
impl UnsafeUnpin for AuditEvent
impl UnwindSafe for AuditEvent
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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