use super::AttemptAuditEventError;
use super::attempt::event_from_attempt;
use async_trait::async_trait;
use gatekeep::{AttemptAuditSink, AuditEntry, AuditSink, AuthorizationAttempt};
use sqlx::{MySql, MySqlPool, Transaction};
use dovecote::EnqueueOutcome;
use dovecote_sqlx_mysql::MySqlDovecote;
use super::{
DecisionAuditConfig, DecisionAuditConfigError, DecisionAuditEventError, event_from_entry,
};
#[derive(Clone)]
pub struct MySqlDovecoteAudit {
dovecote: MySqlDovecote,
config: DecisionAuditConfig,
}
impl MySqlDovecoteAudit {
pub fn new(
pool: MySqlPool,
source: impl Into<String>,
) -> Result<Self, DecisionAuditConfigError> {
let config = DecisionAuditConfig::new(source)?;
Ok(Self::from_config(pool, config))
}
#[must_use]
pub fn from_config(pool: MySqlPool, config: DecisionAuditConfig) -> Self {
Self {
dovecote: MySqlDovecote::new(pool),
config,
}
}
#[must_use]
pub const fn config(&self) -> &DecisionAuditConfig {
&self.config
}
pub async fn check_schema(&self) -> Result<(), dovecote_sqlx_mysql::SchemaError> {
self.dovecote.check_schema().await
}
pub async fn record_authorization_attempt(
&self,
entry: &AuthorizationAttempt,
) -> Result<EnqueueOutcome, MySqlDovecoteAuditError> {
let mut transaction = self.dovecote.pool().begin().await?;
let outcome = self
.record_attempt_in_transaction(&mut transaction, entry)
.await?;
transaction.commit().await?;
Ok(outcome)
}
pub async fn record_attempt_in_transaction(
&self,
transaction: &mut Transaction<'_, MySql>,
entry: &AuthorizationAttempt,
) -> Result<EnqueueOutcome, MySqlDovecoteAuditError> {
let (tenant, event) = event_from_attempt(&self.config, entry)?;
Ok(self
.dovecote
.for_tenant(tenant)
.enqueue(transaction, event)
.await?)
}
pub async fn record_decision_audit(
&self,
entry: &AuditEntry,
) -> Result<EnqueueOutcome, MySqlDovecoteAuditError> {
let mut transaction = self.dovecote.pool().begin().await?;
let outcome = self
.record_decision_audit_in_transaction(&mut transaction, entry)
.await?;
transaction.commit().await?;
Ok(outcome)
}
pub async fn record_decision_audit_in_transaction(
&self,
transaction: &mut Transaction<'_, MySql>,
entry: &AuditEntry,
) -> Result<EnqueueOutcome, MySqlDovecoteAuditError> {
let (tenant, event) = event_from_entry(&self.config, entry)?;
Ok(self
.dovecote
.for_tenant(tenant)
.enqueue(transaction, event)
.await?)
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum MySqlDovecoteAuditError {
#[error(transparent)]
Attempt(#[from] AttemptAuditEventError),
#[error(transparent)]
Event(#[from] DecisionAuditEventError),
#[error(transparent)]
Dovecote(#[from] dovecote_sqlx_mysql::EnqueueError),
#[error(transparent)]
Sqlx(#[from] sqlx::Error),
}
#[async_trait]
impl AuditSink for MySqlDovecoteAudit {
type Error = MySqlDovecoteAuditError;
async fn record(&self, entry: &AuditEntry) -> Result<(), Self::Error> {
self.record_decision_audit(entry).await.map(|_| ())
}
}
#[async_trait]
impl AttemptAuditSink for MySqlDovecoteAudit {
type Error = MySqlDovecoteAuditError;
async fn record_attempt(&self, entry: &AuthorizationAttempt) -> Result<(), Self::Error> {
self.record_authorization_attempt(entry).await.map(|_| ())
}
}