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, KanidmHxEventName, UnrecoverableErrorView};
12
13/// The web app's top level error type, this takes an `OperationError` and converts it into a HTTP response.
14#[derive(Debug, ToSchema)]
15pub(crate) enum HtmxError {
16    /// Something went wrong when doing things.
17    OperationError(Uuid, OperationError, DomainInfoRead),
18}
19
20impl HtmxError {
21    pub(crate) fn new(kopid: &KOpId, operr: OperationError, domain_info: DomainInfoRead) -> Self {
22        HtmxError::OperationError(kopid.eventid, operr, domain_info)
23    }
24}
25
26impl IntoResponse for HtmxError {
27    fn into_response(self) -> Response {
28        match self {
29            HtmxError::OperationError(kopid, inner, domain_info) => {
30                let body = serde_json::to_string(&inner).unwrap_or(inner.to_string());
31                match &inner {
32                    OperationError::NotAuthenticated
33                    | OperationError::SessionExpired
34                    | OperationError::InvalidSessionState => Redirect::to("/ui").into_response(),
35                    OperationError::SystemProtectedObject | OperationError::AccessDenied => {
36                        let trigger = HxResponseTrigger::after_swap([HxEvent::from(
37                            KanidmHxEventName::PermissionDenied,
38                        )]);
39                        (
40                            trigger,
41                            HxRetarget("main".to_string()),
42                            HxReswap(SwapOption::BeforeEnd),
43                            (
44                                StatusCode::FORBIDDEN,
45                                ErrorToastPartial {
46                                    err_code: inner,
47                                    operation_id: kopid,
48                                },
49                            )
50                                .into_response(),
51                        )
52                            .into_response()
53                    }
54                    OperationError::NoMatchingEntries => {
55                        (StatusCode::NOT_FOUND, body).into_response()
56                    }
57                    OperationError::PasswordQuality(_)
58                    | OperationError::EmptyRequest
59                    | OperationError::SchemaViolation(_)
60                    | OperationError::CU0003WebauthnUserNotVerified => {
61                        (StatusCode::BAD_REQUEST, body).into_response()
62                    }
63                    _ => (
64                        StatusCode::INTERNAL_SERVER_ERROR,
65                        HxRetarget("body".to_string()),
66                        HxReswap(SwapOption::OuterHtml),
67                        UnrecoverableErrorView {
68                            err_code: inner,
69                            operation_id: kopid,
70                            domain_info,
71                        },
72                    )
73                        .into_response(),
74                }
75            }
76        }
77    }
78}