kanidmd_core/https/views/
errors.rs

1use axum::http::StatusCode;
2use axum::response::{IntoResponse, Redirect, Response};
3use axum_htmx::{HxEvent, HxResponseTrigger, HxReswap, HxRetarget, SwapOption};
4use kanidmd_lib::idm::server::DomainInfoRead;
5use utoipa::ToSchema;
6use uuid::Uuid;
7
8use kanidm_proto::internal::OperationError;
9
10use crate::https::middleware::KOpId;
11use crate::https::views::{ErrorToastPartial, UnrecoverableErrorView};
12// #[derive(Template)]
13// #[template(path = "recoverable_error_partial.html")]
14// struct ErrorPartialView {
15//     error_message: String,
16//     operation_id: Uuid,
17//     recovery_path: String,
18//     recovery_boosted: bool,
19// }
20
21/// The web app's top level error type, this takes an `OperationError` and converts it into a HTTP response.
22#[derive(Debug, ToSchema)]
23pub(crate) enum HtmxError {
24    /// Something went wrong when doing things.
25    OperationError(Uuid, OperationError, DomainInfoRead),
26}
27
28impl HtmxError {
29    pub(crate) fn new(kopid: &KOpId, operr: OperationError, domain_info: DomainInfoRead) -> Self {
30        HtmxError::OperationError(kopid.eventid, operr, domain_info)
31    }
32}
33
34impl IntoResponse for HtmxError {
35    fn into_response(self) -> Response {
36        match self {
37            HtmxError::OperationError(kopid, inner, domain_info) => {
38                let body = serde_json::to_string(&inner).unwrap_or(inner.to_string());
39                match &inner {
40                    OperationError::NotAuthenticated
41                    | OperationError::SessionExpired
42                    | OperationError::InvalidSessionState => Redirect::to("/ui").into_response(),
43                    OperationError::SystemProtectedObject | OperationError::AccessDenied => {
44                        let trigger = HxResponseTrigger::after_swap([HxEvent::new(
45                            "permissionDenied".to_string(),
46                        )]);
47                        (
48                            trigger,
49                            HxRetarget("main".to_string()),
50                            HxReswap(SwapOption::BeforeEnd),
51                            (
52                                StatusCode::FORBIDDEN,
53                                ErrorToastPartial {
54                                    err_code: inner,
55                                    operation_id: kopid,
56                                },
57                            )
58                                .into_response(),
59                        )
60                            .into_response()
61                    }
62                    OperationError::NoMatchingEntries => {
63                        (StatusCode::NOT_FOUND, body).into_response()
64                    }
65                    OperationError::PasswordQuality(_)
66                    | OperationError::EmptyRequest
67                    | OperationError::SchemaViolation(_)
68                    | OperationError::CU0003WebauthnUserNotVerified => {
69                        (StatusCode::BAD_REQUEST, body).into_response()
70                    }
71                    _ => (
72                        StatusCode::INTERNAL_SERVER_ERROR,
73                        HxRetarget("body".to_string()),
74                        HxReswap(SwapOption::OuterHtml),
75                        UnrecoverableErrorView {
76                            err_code: inner,
77                            operation_id: kopid,
78                            domain_info,
79                        },
80                    )
81                        .into_response(),
82                }
83            }
84        }
85    }
86}