kanidmd_core/https/
errors.rs
1use axum::http::header::ACCESS_CONTROL_ALLOW_ORIGIN;
5use axum::http::{HeaderValue, StatusCode};
6use axum::response::{IntoResponse, Response};
7
8use hyper::header::WWW_AUTHENTICATE;
9use kanidm_proto::oauth2::ErrorResponse;
10use kanidmd_lib::idm::oauth2::Oauth2Error;
11use utoipa::ToSchema;
12
13use kanidm_proto::internal::OperationError;
14
15#[derive(Debug, ToSchema)]
17pub enum WebError {
18 OperationError(OperationError),
20 InternalServerError(String),
21 #[schema(value_type=Object)]
22 OAuth2(Oauth2Error),
23}
24
25impl From<OperationError> for WebError {
26 fn from(inner: OperationError) -> Self {
27 WebError::OperationError(inner)
28 }
29}
30
31impl From<Oauth2Error> for WebError {
32 fn from(inner: Oauth2Error) -> Self {
33 WebError::OAuth2(inner)
34 }
35}
36
37impl WebError {
38 pub(crate) fn response_with_access_control_origin_header(self) -> Response {
39 let mut res = self.into_response();
40 res.headers_mut().insert(
41 ACCESS_CONTROL_ALLOW_ORIGIN,
42 #[allow(clippy::expect_used)]
43 HeaderValue::from_str("*").expect("Header generation failed, this is weird."),
44 );
45 res
46 }
47}
48
49impl IntoResponse for WebError {
50 fn into_response(self) -> Response {
51 match self {
52 WebError::OAuth2(error) => {
53 if let Oauth2Error::AuthenticationRequired = error {
54 (
55 StatusCode::UNAUTHORIZED,
56 [
57 (WWW_AUTHENTICATE, "Bearer"),
58 (ACCESS_CONTROL_ALLOW_ORIGIN, "*"),
59 ],
60 )
61 .into_response()
62 } else {
63 let err = ErrorResponse {
64 error: error.to_string(),
65 ..Default::default()
66 };
67
68 let body = match serde_json::to_string(&err) {
69 Ok(val) => val,
70 Err(e) => {
71 warn!("Failed to serialize error response: original_error=\"{:?}\" serialization_error=\"{:?}\"", err, e);
72 format!("{:?}", err)
73 }
74 };
75
76 (
77 StatusCode::BAD_REQUEST,
78 [(ACCESS_CONTROL_ALLOW_ORIGIN, "*")],
79 body,
80 )
81 .into_response()
82 }
83 }
84 WebError::InternalServerError(inner) => {
85 (StatusCode::INTERNAL_SERVER_ERROR, inner).into_response()
86 }
87 WebError::OperationError(inner) => {
88 let (code, headers) = match &inner {
89 OperationError::NotAuthenticated | OperationError::SessionExpired => {
90 (
92 StatusCode::UNAUTHORIZED,
93 Some([("WWW-Authenticate", "Bearer"); 1]),
94 )
95 }
96 OperationError::SystemProtectedObject | OperationError::AccessDenied => {
97 (StatusCode::FORBIDDEN, None)
98 }
99 OperationError::NoMatchingEntries => (StatusCode::NOT_FOUND, None),
100 OperationError::PasswordQuality(_)
101 | OperationError::EmptyRequest
102 | OperationError::InvalidAttribute(_)
103 | OperationError::InvalidAttributeName(_)
104 | OperationError::SchemaViolation(_)
105 | OperationError::CU0003WebauthnUserNotVerified
106 | OperationError::VL0001ValueSshPublicKeyString => {
107 (StatusCode::BAD_REQUEST, None)
108 }
109 _ => (StatusCode::INTERNAL_SERVER_ERROR, None),
110 };
111 let body = serde_json::to_string(&inner).unwrap_or(inner.to_string());
112
113 match headers {
114 Some(headers) => (code, headers, body).into_response(),
115 None => (code, body).into_response(),
116 }
117 }
118 }
119 }
120}