Skip to main content

gatekeep_axum/
error.rs

1use axum::{
2    Json,
3    http::StatusCode,
4    response::{IntoResponse, Response},
5};
6use serde::Serialize;
7use thiserror::Error;
8
9use crate::DenialResponse;
10
11/// Error produced while resolving, evaluating, tracing, or auditing a decision.
12#[derive(Debug, Error)]
13pub enum GatekeepAxumError<Resolve, Audit> {
14    /// Policy hashing failed before the decision could be anchored.
15    #[error("failed to hash policy")]
16    PolicyHash(#[source] postcard::Error),
17    /// Fact resolution failed before evaluation.
18    #[error(transparent)]
19    Resolve(#[from] gatekeep::ResolveError<Resolve>),
20    /// Trace serialization failed after evaluation.
21    #[error(transparent)]
22    Trace(#[from] gatekeep::TraceError),
23    /// Audit recording failed.
24    #[error("audit sink failed")]
25    Audit(#[source] Audit),
26}
27
28/// Axum rejection returned by [`crate::Gatekeeper::authorize`].
29#[derive(Debug)]
30pub enum GatekeepRejection<Resolve, Audit> {
31    /// The policy denied the request.
32    Denied(DenialResponse),
33    /// The authorization boundary failed before a response could be trusted.
34    Error(GatekeepAxumError<Resolve, Audit>),
35}
36
37impl<Resolve, Audit> GatekeepRejection<Resolve, Audit> {
38    pub(crate) const fn from_error(error: GatekeepAxumError<Resolve, Audit>) -> Self {
39        Self::Error(error)
40    }
41}
42
43impl<Resolve, Audit> From<DenialResponse> for GatekeepRejection<Resolve, Audit> {
44    fn from(response: DenialResponse) -> Self {
45        Self::Denied(response)
46    }
47}
48
49impl<Resolve, Audit> From<GatekeepAxumError<Resolve, Audit>> for GatekeepRejection<Resolve, Audit> {
50    fn from(error: GatekeepAxumError<Resolve, Audit>) -> Self {
51        Self::Error(error)
52    }
53}
54
55impl<Resolve, Audit> IntoResponse for GatekeepRejection<Resolve, Audit> {
56    fn into_response(self) -> Response {
57        match self {
58            Self::Denied(denial) => denial.into_response(),
59            Self::Error(_error) => (
60                StatusCode::INTERNAL_SERVER_ERROR,
61                Json(ErrorBody {
62                    error: "authorization_error",
63                    message: "authorization failed",
64                }),
65            )
66                .into_response(),
67        }
68    }
69}
70
71#[derive(Serialize)]
72struct ErrorBody {
73    error: &'static str,
74    message: &'static str,
75}