Skip to main content

AuditEvent

Enum AuditEvent 

Source
#[non_exhaustive]
pub enum AuditEvent {
Show 27 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, AiProposalApproved, AiProposalRejected, AiProposalApplied,
}
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
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

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-admin CLI binary. Subsumes every rustio-admin 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.

§

AiProposalApproved

A developer approved an AI assistant proposal via rustio-admin ai approve --as <email>. The approver is a real, active rustio_users row whose role meets the policy’s approver_role; user_id / object_id is that approver and model_name is "users". Emitted only from the CLI ai verbs (crates/rustio-admin-cli/). metadata carries proposal_id, capability, title, and ai_action = "approved". See DESIGN_AI_ASSISTANT.md §5.

§

AiProposalRejected

A developer rejected an AI assistant proposal via rustio-admin ai reject --as <email>. Same shape as Self::AiProposalApproved; metadata.reason carries the decline reason.

§

AiProposalApplied

A developer applied an approved (or Allowed) AI assistant proposal via rustio-admin ai apply --as <email>, writing its staged files. Same shape as Self::AiProposalApproved; metadata.files lists the paths written.

Implementations§

Source§

impl AuditEvent

Source

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

Source§

fn clone(&self) -> AuditEvent

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for AuditEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Hash for AuditEvent

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for AuditEvent

Source§

fn eq(&self, other: &AuditEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Copy for AuditEvent

Source§

impl Eq for AuditEvent

Source§

impl StructuralPartialEq for AuditEvent

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. 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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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