use super::{AuditContext, AuthEvent, AuthEventStatus, AuthEventType};
use crate::authn::factor::FactorKind;
use crate::authn::ids::{DeviceId, TenantId, UserId};
use crate::session::id::SessionId;
use chrono::{DateTime, Utc};
pub struct AuthEventBuilder {
user_id: Option<UserId>,
tenant_id: Option<TenantId>,
event_type: AuthEventType,
event_status: AuthEventStatus,
session_id: Option<SessionId>,
factor_kind: Option<FactorKind>,
ip_address: Option<String>,
user_agent: Option<String>,
request_id: Option<String>,
geo_country: Option<String>,
error: Option<String>,
actor_id: Option<UserId>,
device_id: Option<DeviceId>,
factors_completed: Vec<FactorKind>,
}
impl AuthEventBuilder {
pub fn new(
user_id: Option<UserId>,
tenant_id: Option<TenantId>,
event_type: AuthEventType,
event_status: AuthEventStatus,
) -> Self {
Self {
user_id,
tenant_id,
event_type,
event_status,
session_id: None,
factor_kind: None,
ip_address: None,
user_agent: None,
request_id: None,
geo_country: None,
error: None,
actor_id: None,
device_id: None,
factors_completed: Vec::new(),
}
}
pub fn attributed(
user_id: UserId,
tenant_id: TenantId,
event_type: AuthEventType,
event_status: AuthEventStatus,
) -> Self {
Self::new(Some(user_id), Some(tenant_id), event_type, event_status)
}
pub fn unattributed(event_type: AuthEventType, event_status: AuthEventStatus) -> Self {
Self::new(None, None, event_type, event_status)
}
pub fn failure(event_type: AuthEventType) -> Self {
Self::new(None, None, event_type, AuthEventStatus::Failure)
}
pub fn success(event_type: AuthEventType) -> Self {
Self::new(None, None, event_type, AuthEventStatus::Success)
}
pub fn attributed_to(mut self, user_id: &UserId, tenant_id: &TenantId) -> Self {
self.user_id = Some(*user_id);
self.tenant_id = Some(*tenant_id);
self
}
pub fn maybe_attributed_to(
mut self,
user_id: Option<&UserId>,
tenant_id: Option<&TenantId>,
) -> Self {
self.user_id = user_id.cloned();
self.tenant_id = tenant_id.cloned();
self
}
pub fn with_session(mut self, id: SessionId) -> Self {
self.session_id = Some(id);
self
}
pub fn with_factor(mut self, kind: FactorKind) -> Self {
self.factor_kind = Some(kind);
self
}
pub fn with_ip(mut self, ip: impl Into<String>) -> Self {
self.ip_address = Some(ip.into());
self
}
pub fn with_user_agent(mut self, ua: impl Into<String>) -> Self {
self.user_agent = Some(ua.into());
self
}
pub fn with_request_id(mut self, id: impl Into<String>) -> Self {
self.request_id = Some(id.into());
self
}
pub fn with_geo_country(mut self, country: impl Into<String>) -> Self {
self.geo_country = Some(country.into());
self
}
pub fn with_error(mut self, err: impl Into<String>) -> Self {
self.error = Some(err.into());
self
}
pub fn with_actor(mut self, actor: UserId) -> Self {
self.actor_id = Some(actor);
self
}
pub fn with_device(mut self, device: DeviceId) -> Self {
self.device_id = Some(device);
self
}
pub fn with_factors_completed(mut self, kind: FactorKind) -> Self {
self.factors_completed.push(kind);
self
}
pub fn with_audit_context(mut self, ctx: &AuditContext) -> Self {
if let Some(ip) = &ctx.ip_address {
self.ip_address = Some(ip.to_string());
}
if let Some(ua) = &ctx.user_agent {
self.user_agent = Some(ua.clone());
}
if let Some(rid) = &ctx.request_id {
self.request_id = Some(rid.clone());
}
if let Some(geo) = &ctx.geo_country {
self.geo_country = Some(geo.clone());
}
if let Some(sid) = &ctx.session_id {
if self.session_id.is_none()
&& let Ok(parsed) = sid.parse::<uuid::Uuid>()
{
self.session_id = Some(SessionId::from_bytes(*parsed.as_bytes()));
}
}
self
}
pub fn build(self) -> AuthEvent {
self.build_at(Utc::now())
}
pub fn build_at(self, event_time: DateTime<Utc>) -> AuthEvent {
AuthEvent {
user_id: self.user_id,
tenant_id: self.tenant_id,
session_id: self.session_id,
event_type: self.event_type,
event_status: self.event_status,
event_time: event_time.timestamp_micros(),
factor_kind: self.factor_kind,
ip_address: self.ip_address,
user_agent: self.user_agent,
request_id: self.request_id,
geo_country: self.geo_country,
error: self.error,
actor_id: self.actor_id,
device_id: self.device_id,
factors_completed: self.factors_completed,
}
}
}