use crate::dal::unified::DAL;
use crate::error::ValidationError;
#[cfg(feature = "postgres")]
use chrono::{DateTime, Utc};
#[cfg(feature = "postgres")]
use diesel::prelude::*;
#[cfg(feature = "postgres")]
#[derive(Insertable)]
#[diesel(table_name = crate::database::schema::postgres::oidc_login_flows)]
struct NewLoginFlow {
state: String,
nonce: String,
pkce_verifier: String,
expires_at: chrono::NaiveDateTime,
}
pub struct OidcLoginFlowDAL<'a> {
dal: &'a DAL,
}
impl<'a> OidcLoginFlowDAL<'a> {
pub fn new(dal: &'a DAL) -> Self {
Self { dal }
}
#[cfg(feature = "postgres")]
pub async fn put(
&self,
state: String,
nonce: String,
pkce_verifier: String,
expires_at: DateTime<Utc>,
) -> Result<(), ValidationError> {
let row = NewLoginFlow {
state,
nonce,
pkce_verifier,
expires_at: expires_at.naive_utc(),
};
let conn = self
.dal
.database
.get_postgres_connection()
.await
.map_err(|e| ValidationError::ConnectionPool(e.to_string()))?;
conn.interact(move |conn| {
diesel::insert_into(crate::database::schema::postgres::oidc_login_flows::table)
.values(&row)
.execute(conn)
})
.await
.map_err(|e| ValidationError::ConnectionPool(e.to_string()))??;
Ok(())
}
#[cfg(feature = "postgres")]
pub async fn take(&self, state: &str) -> Result<Option<(String, String)>, ValidationError> {
use crate::database::schema::postgres::oidc_login_flows as t;
let state = state.to_string();
let now = Utc::now().naive_utc();
let conn = self
.dal
.database
.get_postgres_connection()
.await
.map_err(|e| ValidationError::ConnectionPool(e.to_string()))?;
let row: Option<(String, String)> = conn
.interact(move |conn| {
diesel::delete(t::table.filter(t::state.eq(state).and(t::expires_at.gt(now))))
.returning((t::nonce, t::pkce_verifier))
.get_result(conn)
.optional()
})
.await
.map_err(|e| ValidationError::ConnectionPool(e.to_string()))??;
Ok(row)
}
#[cfg(feature = "postgres")]
pub async fn sweep_expired(&self) -> Result<usize, ValidationError> {
use crate::database::schema::postgres::oidc_login_flows as t;
let now = Utc::now().naive_utc();
let conn = self
.dal
.database
.get_postgres_connection()
.await
.map_err(|e| ValidationError::ConnectionPool(e.to_string()))?;
let n: usize = conn
.interact(move |conn| {
diesel::delete(t::table.filter(t::expires_at.lt(now))).execute(conn)
})
.await
.map_err(|e| ValidationError::ConnectionPool(e.to_string()))??;
Ok(n)
}
}