use time::OffsetDateTime;
use crate::http::IpTag;
use crate::ids::UserId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[non_exhaustive]
pub enum AuditKind {
SignIn,
SignInFailed,
Lockout,
SignOut,
SessionRevoked,
StepUp,
SetupLinkSent,
SetupCompleted,
SignInLinkSent,
InviteSent,
PasswordSet,
TotpEnrolled,
TotpRemoved,
PasskeyAdded,
PasskeyRemoved,
RecoveryCodesIssued,
RecoveryCodeUsed,
OwnerReset,
NewDevice,
}
impl AuditKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
AuditKind::SignIn => "sign_in",
AuditKind::SignInFailed => "sign_in_failed",
AuditKind::Lockout => "lockout",
AuditKind::SignOut => "sign_out",
AuditKind::SessionRevoked => "session_revoked",
AuditKind::StepUp => "step_up",
AuditKind::SetupLinkSent => "setup_link_sent",
AuditKind::SetupCompleted => "setup_completed",
AuditKind::SignInLinkSent => "sign_in_link_sent",
AuditKind::InviteSent => "invite_sent",
AuditKind::PasswordSet => "password_set",
AuditKind::TotpEnrolled => "totp_enrolled",
AuditKind::TotpRemoved => "totp_removed",
AuditKind::PasskeyAdded => "passkey_added",
AuditKind::PasskeyRemoved => "passkey_removed",
AuditKind::RecoveryCodesIssued => "recovery_codes_issued",
AuditKind::RecoveryCodeUsed => "recovery_code_used",
AuditKind::OwnerReset => "owner_reset",
AuditKind::NewDevice => "new_device",
}
}
}
impl std::fmt::Display for AuditKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub struct AuditEvent {
pub at: OffsetDateTime,
pub user_id: Option<UserId>,
pub kind: AuditKind,
pub detail: Option<String>,
pub ip_tag: Option<IpTag>,
}
impl AuditEvent {
#[must_use]
pub fn new(
kind: AuditKind,
user_id: Option<UserId>,
at: OffsetDateTime,
ip_tag: Option<IpTag>,
) -> Self {
Self {
at,
user_id,
kind,
detail: None,
ip_tag,
}
}
#[must_use]
pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
self.detail = Some(detail.into());
self
}
}