Skip to main content

potree_auth/common/utils/http/
api_error.rs

1use axum::response::IntoResponse;
2use http::StatusCode;
3
4use crate::authorization::domain::action::Action;
5use crate::authorization::domain::resource::ResourceIdentifier;
6use crate::authorization::domain::resource::ResourceType;
7use crate::user::domain::User;
8
9/// Errors that can be experienced by an API `axum` route handler.
10///
11/// All API route handlers should return this error. All domain specific errors
12/// should be marshalled into an [`ApiError`] to ensure consistent HTTP
13/// responses.
14#[derive(Debug, Clone, thiserror::Error)]
15pub enum ApiError {
16    #[error("unable to extract router state")]
17    StateExtraction,
18
19    #[error("unable to find resource: {resource_name}")]
20    ResourceNotFound { resource_name: String },
21
22    #[error("{} is not authorized to {} the {:?}: {:?}", user.name, action, resource_type, resource_identifier)]
23    NotAuthorized {
24        user: Box<User>,
25        action: Action,
26        resource_identifier: Option<ResourceIdentifier>,
27        resource_type: ResourceType,
28    },
29
30    #[error("user is not authenticated")]
31    NotAuthenticated,
32
33    #[error("the server is not configured correctly: {message}")]
34    ServerConfiguration { message: String },
35
36    #[error("there is an issue with the the server infrastructure: {message}")]
37    Infrastucture { message: String },
38}
39
40impl IntoResponse for ApiError {
41    fn into_response(self) -> axum::response::Response {
42        // Log the error.
43        tracing::error!("{self}");
44
45        // Generate a response to send to the client.
46        match self {
47            ApiError::StateExtraction
48            | ApiError::ServerConfiguration { .. }
49            | ApiError::Infrastucture { .. } => (StatusCode::INTERNAL_SERVER_ERROR).into_response(),
50            ApiError::ResourceNotFound { .. } => {
51                (StatusCode::NOT_FOUND, self.to_string()).into_response()
52            }
53            ApiError::NotAuthorized { .. } => {
54                (StatusCode::FORBIDDEN, self.to_string()).into_response()
55            }
56            ApiError::NotAuthenticated => (StatusCode::UNAUTHORIZED).into_response(),
57        }
58    }
59}