use std::sync::Arc;
use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use rustls::pki_types::CertificateDer;
use sha2::{Digest, Sha256};
#[derive(Clone, Debug)]
pub struct PeerClientCert(pub Arc<Vec<CertificateDer<'static>>>);
impl PeerClientCert {
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub fn leaf_sha256_hex(&self) -> Option<String> {
let leaf = self.0.first()?;
Some(hex::encode(Sha256::digest(leaf.as_ref())))
}
}
impl<S> FromRequestParts<S> for PeerClientCert
where
S: Send + Sync,
{
type Rejection = ();
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
parts
.extensions
.get::<Self>()
.cloned()
.ok_or(())
}
}
#[must_use]
pub fn from_rustls_server(
conn: &rustls::ServerConnection,
) -> PeerClientCert {
let certs = conn
.peer_certificates()
.map(|chain| {
chain
.iter()
.map(|c| c.clone().into_owned())
.collect::<Vec<_>>()
})
.unwrap_or_default();
PeerClientCert(Arc::new(certs))
}
#[must_use]
pub fn from_quinn_connection(connection: &quinn::Connection) -> PeerClientCert {
let Some(identity) = connection.peer_identity() else {
return PeerClientCert(Arc::new(Vec::new()));
};
let Some(certs) = identity.downcast_ref::<Vec<CertificateDer<'_>>>() else {
return PeerClientCert(Arc::new(Vec::new()));
};
PeerClientCert(Arc::new(certs.clone()))
}