use axum::{
Json,
http::StatusCode,
response::{IntoResponse, Response},
};
use serde::Serialize;
use thiserror::Error;
use crate::DenialResponse;
#[derive(Debug, Error)]
pub enum GatekeepAxumError<Resolve, Audit> {
#[error(transparent)]
Context(#[from] gatekeep::ContextError),
#[error("failed to hash policy")]
PolicyHash(#[source] postcard::Error),
#[error(transparent)]
Resolve(#[from] gatekeep::ResolveError<Resolve>),
#[error(transparent)]
FactResolutionEvidence(#[from] gatekeep::FactResolutionEvidenceError),
#[error(transparent)]
AuditEntry(#[from] gatekeep::AuditEntryError),
#[error(transparent)]
Trace(#[from] gatekeep::TraceError),
#[error(transparent)]
Occurrence(#[from] gatekeep::DecisionAuditOccurrenceError),
#[error("audit sink failed")]
Audit {
occurrence: gatekeep::DecisionAuditOccurrence,
#[source]
source: Audit,
},
}
impl<Resolve, Audit> GatekeepAxumError<Resolve, Audit> {
#[must_use]
pub const fn audit_occurrence(&self) -> Option<&gatekeep::DecisionAuditOccurrence> {
match self {
Self::Audit { occurrence, .. } => Some(occurrence),
Self::Context(_)
| Self::PolicyHash(_)
| Self::Resolve(_)
| Self::FactResolutionEvidence(_)
| Self::AuditEntry(_)
| Self::Trace(_)
| Self::Occurrence(_) => None,
}
}
}
#[derive(Debug)]
pub enum GatekeepRejection<Resolve, Audit> {
Denied(DenialResponse),
Error(GatekeepAxumError<Resolve, Audit>),
}
impl<Resolve, Audit> GatekeepRejection<Resolve, Audit> {
pub(crate) const fn from_error(error: GatekeepAxumError<Resolve, Audit>) -> Self {
Self::Error(error)
}
}
impl<Resolve, Audit> From<DenialResponse> for GatekeepRejection<Resolve, Audit> {
fn from(response: DenialResponse) -> Self {
Self::Denied(response)
}
}
impl<Resolve, Audit> From<GatekeepAxumError<Resolve, Audit>> for GatekeepRejection<Resolve, Audit> {
fn from(error: GatekeepAxumError<Resolve, Audit>) -> Self {
Self::Error(error)
}
}
impl<Resolve, Audit> IntoResponse for GatekeepRejection<Resolve, Audit> {
fn into_response(self) -> Response {
match self {
Self::Denied(denial) => denial.into_response(),
Self::Error(_error) => (
StatusCode::INTERNAL_SERVER_ERROR,
Json(ErrorBody {
error: "authorization_error",
message: "authorization failed",
}),
)
.into_response(),
}
}
}
#[derive(Serialize)]
struct ErrorBody {
error: &'static str,
message: &'static str,
}