Skip to main content

rustauth_sso/options/
audit.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4
5use serde::{Deserialize, Serialize};
6
7type AuditEventFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11/// Severity level for SSO audit events.
12pub enum SsoAuditSeverity {
13    /// Informational event.
14    Info,
15    /// Suspicious or recoverable condition.
16    Warn,
17    /// Failed security-sensitive operation.
18    Error,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22#[serde(rename_all = "camelCase")]
23/// SSO audit event kind emitted by provider, domain, SAML, and SLO flows.
24pub enum SsoAuditEventKind {
25    /// A provider was registered.
26    ProviderRegistered,
27    /// A provider was updated.
28    ProviderUpdated,
29    /// A provider was deleted.
30    ProviderDeleted,
31    /// A domain verification token was requested.
32    DomainVerificationRequested,
33    /// Domain verification succeeded.
34    DomainVerificationSucceeded,
35    /// Domain verification failed.
36    DomainVerificationFailed,
37    /// A previously verified provider domain was revoked by an update.
38    DomainVerificationRevoked,
39    /// A replayed SAML assertion was rejected.
40    SamlReplayRejected,
41    /// SAML signature validation failed.
42    SamlSignatureFailed,
43    /// A SAML SLO flow deleted a local session.
44    SamlSloSessionDeleted,
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
48#[serde(rename_all = "camelCase")]
49/// Audit event emitted by the SSO plugin.
50pub struct SsoAuditEvent {
51    /// Event kind.
52    pub kind: SsoAuditEventKind,
53    /// Event severity.
54    pub severity: SsoAuditSeverity,
55    /// Provider id related to the event, when available.
56    pub provider_id: Option<String>,
57    /// User id related to the event, when available.
58    pub user_id: Option<String>,
59    /// Organization id related to the event, when available.
60    pub organization_id: Option<String>,
61    /// Human-readable reason or stable error code.
62    pub reason: Option<String>,
63}
64
65impl SsoAuditEvent {
66    /// Create an audit event with no optional context.
67    pub fn new(kind: SsoAuditEventKind, severity: SsoAuditSeverity) -> Self {
68        Self {
69            kind,
70            severity,
71            provider_id: None,
72            user_id: None,
73            organization_id: None,
74            reason: None,
75        }
76    }
77
78    #[must_use]
79    /// Attach a provider id to the event.
80    pub fn provider_id(mut self, provider_id: impl Into<String>) -> Self {
81        self.provider_id = Some(provider_id.into());
82        self
83    }
84
85    #[must_use]
86    /// Attach a user id to the event.
87    pub fn user_id(mut self, user_id: impl Into<String>) -> Self {
88        self.user_id = Some(user_id.into());
89        self
90    }
91
92    #[must_use]
93    /// Attach an organization id to the event.
94    pub fn organization_id(mut self, organization_id: impl Into<String>) -> Self {
95        self.organization_id = Some(organization_id.into());
96        self
97    }
98
99    #[must_use]
100    /// Attach a reason or stable error code to the event.
101    pub fn reason(mut self, reason: impl Into<String>) -> Self {
102        self.reason = Some(reason.into());
103        self
104    }
105}
106
107#[derive(Clone)]
108/// Async sink for SSO audit events.
109pub struct SsoAuditEventResolver {
110    resolver: Arc<dyn Fn(SsoAuditEvent) -> AuditEventFuture + Send + Sync>,
111}
112
113impl SsoAuditEventResolver {
114    /// Create an audit event sink from an async function.
115    pub fn new<F, Fut>(resolver: F) -> Self
116    where
117        F: Fn(SsoAuditEvent) -> Fut + Send + Sync + 'static,
118        Fut: Future<Output = ()> + Send + 'static,
119    {
120        Self {
121            resolver: Arc::new(move |event| Box::pin(resolver(event))),
122        }
123    }
124
125    /// Emit an audit event.
126    pub async fn resolve(&self, event: SsoAuditEvent) {
127        (self.resolver)(event).await;
128    }
129}
130
131impl std::fmt::Debug for SsoAuditEventResolver {
132    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        formatter.write_str("SsoAuditEventResolver(..)")
134    }
135}
136
137impl PartialEq for SsoAuditEventResolver {
138    fn eq(&self, _other: &Self) -> bool {
139        true
140    }
141}
142
143impl Eq for SsoAuditEventResolver {}