gatekeep_sqlx/audit/
postgres.rs1use super::AttemptAuditEventError;
2use super::attempt::event_from_attempt;
3use async_trait::async_trait;
4use gatekeep::{AttemptAuditSink, AuditEntry, AuditSink, AuthorizationAttempt};
5use sqlx::{PgPool, Postgres, Transaction};
6
7use dovecote::EnqueueOutcome;
8use dovecote_sqlx_postgres::PostgresDovecote;
9
10use super::{
11 DecisionAuditConfig, DecisionAuditConfigError, DecisionAuditEventError, event_from_entry,
12};
13
14#[derive(Clone)]
16pub struct PgDovecoteAudit {
17 dovecote: PostgresDovecote,
18 config: DecisionAuditConfig,
19}
20
21impl PgDovecoteAudit {
22 pub fn new(pool: PgPool, source: impl Into<String>) -> Result<Self, DecisionAuditConfigError> {
28 let config = DecisionAuditConfig::new(source)?;
29 Ok(Self::from_config(pool, config))
30 }
31
32 #[must_use]
34 pub fn from_config(pool: PgPool, config: DecisionAuditConfig) -> Self {
35 Self {
36 dovecote: PostgresDovecote::new(pool),
37 config,
38 }
39 }
40
41 #[must_use]
43 pub const fn config(&self) -> &DecisionAuditConfig {
44 &self.config
45 }
46
47 pub async fn check_schema(&self) -> Result<(), dovecote_sqlx_postgres::SchemaError> {
54 self.dovecote.check_schema().await
55 }
56
57 pub async fn record_authorization_attempt(
63 &self,
64 entry: &AuthorizationAttempt,
65 ) -> Result<EnqueueOutcome, PgDovecoteAuditError> {
66 let mut transaction = self.dovecote.pool().begin().await?;
67 let outcome = self
68 .record_attempt_in_transaction(&mut transaction, entry)
69 .await?;
70 transaction.commit().await?;
71 Ok(outcome)
72 }
73
74 pub async fn record_attempt_in_transaction(
80 &self,
81 transaction: &mut Transaction<'_, Postgres>,
82 entry: &AuthorizationAttempt,
83 ) -> Result<EnqueueOutcome, PgDovecoteAuditError> {
84 let (tenant, event) = event_from_attempt(&self.config, entry)?;
85 Ok(self
86 .dovecote
87 .for_tenant(tenant)
88 .enqueue(transaction, event)
89 .await?)
90 }
91
92 pub async fn record_decision_audit(
102 &self,
103 entry: &AuditEntry,
104 ) -> Result<EnqueueOutcome, PgDovecoteAuditError> {
105 let mut transaction = self.dovecote.pool().begin().await?;
106 let outcome = self
107 .record_decision_audit_in_transaction(&mut transaction, entry)
108 .await?;
109 transaction.commit().await?;
110 Ok(outcome)
111 }
112
113 pub async fn record_decision_audit_in_transaction(
124 &self,
125 transaction: &mut Transaction<'_, Postgres>,
126 entry: &AuditEntry,
127 ) -> Result<EnqueueOutcome, PgDovecoteAuditError> {
128 let (tenant, event) = event_from_entry(&self.config, entry)?;
129 Ok(self
130 .dovecote
131 .for_tenant(tenant)
132 .enqueue(transaction, event)
133 .await?)
134 }
135}
136
137#[derive(Debug, thiserror::Error)]
139#[non_exhaustive]
140pub enum PgDovecoteAuditError {
141 #[error(transparent)]
143 Attempt(#[from] AttemptAuditEventError),
144 #[error(transparent)]
146 Event(#[from] DecisionAuditEventError),
147 #[error(transparent)]
149 Dovecote(#[from] dovecote_sqlx_postgres::EnqueueError),
150 #[error(transparent)]
152 Sqlx(#[from] sqlx::Error),
153}
154
155#[async_trait]
156impl AuditSink for PgDovecoteAudit {
157 type Error = PgDovecoteAuditError;
158
159 async fn record(&self, entry: &AuditEntry) -> Result<(), Self::Error> {
160 self.record_decision_audit(entry).await.map(|_| ())
161 }
162}
163
164#[async_trait]
165impl AttemptAuditSink for PgDovecoteAudit {
166 type Error = PgDovecoteAuditError;
167 async fn record_attempt(&self, entry: &AuthorizationAttempt) -> Result<(), Self::Error> {
168 self.record_authorization_attempt(entry).await.map(|_| ())
169 }
170}