1use crate::types::{
2 messages::GenericDataStruct,
3 problem_report::{ProblemReport, ProblemReportScope, ProblemReportSorter},
4};
5use axum::{
6 Json,
7 http::StatusCode,
8 response::{IntoResponse, Response},
9};
10use rand::{RngExt, distr::Alphanumeric};
11use serde::{Deserialize, Serialize};
12use std::fmt;
13use thiserror::Error;
14use tracing::{Level, event};
15
16type SessId = String;
18
19type ErrorCode = u16;
21
22#[derive(Debug, Default, Clone, Serialize)]
28pub struct ErrorContext {
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub request_id: Option<String>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub session_id: Option<String>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub did_hash: Option<String>,
35}
36
37pub struct AppError {
39 error: MediatorError,
40 context: ErrorContext,
41}
42
43impl AppError {
44 pub fn with_context(mut self, ctx: ErrorContext) -> Self {
49 self.context = ctx;
50 self
51 }
52}
53
54impl<E> From<E> for AppError
55where
56 E: Into<MediatorError>,
57{
58 fn from(err: E) -> Self {
59 Self {
60 error: err.into(),
61 context: ErrorContext::default(),
62 }
63 }
64}
65
66#[derive(Clone, Error, Debug)]
69pub enum ProcessorError {
70 #[error("CommonError: {0}")]
72 CommonError(String),
73 #[error("ForwardingError: {0}")]
75 ForwardingError(String),
76 #[error("MessageExpiryCleanupError: {0}")]
78 MessageExpiryCleanupError(String),
79}
80
81#[derive(Error, Debug)]
83#[non_exhaustive]
84pub enum MediatorError {
85 #[error("Internal error handling failure: {2}")]
86 ErrorHandlingError(ErrorCode, SessId, String),
87 #[error("{2}")]
88 InternalError(ErrorCode, SessId, String),
89 #[error("Couldn't parse ({2}). Reason: {3}")]
90 ParseError(ErrorCode, SessId, String, String),
91 #[error("Permission Error: {2}")]
92 PermissionError(ErrorCode, SessId, String),
93 #[error("Request is invalid: {2}")]
94 RequestDataError(ErrorCode, SessId, String),
95 #[error("Service Limit exceeded: {2}")]
96 ServiceLimitError(ErrorCode, SessId, String),
97 #[error("Unauthorized: {2}")]
98 Unauthorized(ErrorCode, SessId, String),
99 #[error("DID error: did({2}) Reason: {3}")]
100 DIDError(ErrorCode, SessId, String, String),
101 #[error("Configuration Error: {2}")]
102 ConfigError(ErrorCode, SessId, String),
103 #[error("Database Error: {2}")]
104 DatabaseError(ErrorCode, SessId, String),
105 #[error("Failed to unpack message: {2}")]
106 MessageUnpackError(ErrorCode, SessId, String),
107 #[error("MessageExpired: expiry({2}) now({3})")]
108 MessageExpired(ErrorCode, SessId, String, String),
109 #[error("Failed to pack message: {2}")]
110 MessagePackError(ErrorCode, SessId, String),
111 #[error("Feature not implemented: {2}")]
112 NotImplemented(ErrorCode, SessId, String),
113 #[error("Authorization Session ({1}) error: {2}")]
114 SessionError(ErrorCode, SessId, String),
115 #[error("Anonymous message error: {2}")]
116 AnonymousMessageError(ErrorCode, SessId, String),
117 #[error("Forwarding/Routing message error: {2}")]
118 ForwardMessageError(ErrorCode, SessId, String),
119 #[error("Authentication error: {1}")]
120 AuthenticationError(ErrorCode, String),
121 #[error("ACL denied: {1}")]
122 ACLDenied(ErrorCode, String),
123 #[error("Processor ({1}) error: {2}")]
124 ProcessorError(ErrorCode, ProcessorError, String),
125
126 #[error("Mediator Error: code({3}): {4}")]
134 MediatorError(
135 ErrorCode,
136 SessId,
137 Option<String>,
138 Box<ProblemReport>,
139 u16,
140 String,
141 ),
142}
143
144impl MediatorError {
145 #[allow(clippy::too_many_arguments)]
151 pub fn problem(
152 code: u16,
153 session_id: impl Into<String>,
154 msg_id: Option<String>,
155 sorter: ProblemReportSorter,
156 scope: ProblemReportScope,
157 descriptor: &str,
158 comment: &str,
159 args: Vec<String>,
160 http_status: StatusCode,
161 ) -> Self {
162 Self::MediatorError(
163 code,
164 session_id.into(),
165 msg_id,
166 Box::new(ProblemReport::new(
167 sorter,
168 scope,
169 descriptor.into(),
170 comment.into(),
171 args,
172 None,
173 )),
174 http_status.as_u16(),
175 comment.to_string(),
176 )
177 }
178
179 #[allow(clippy::too_many_arguments)]
184 pub fn problem_with_log(
185 code: u16,
186 session_id: impl Into<String>,
187 msg_id: Option<String>,
188 sorter: ProblemReportSorter,
189 scope: ProblemReportScope,
190 descriptor: &str,
191 comment: &str,
192 args: Vec<String>,
193 http_status: StatusCode,
194 log_msg: impl Into<String>,
195 ) -> Self {
196 Self::MediatorError(
197 code,
198 session_id.into(),
199 msg_id,
200 Box::new(ProblemReport::new(
201 sorter,
202 scope,
203 descriptor.into(),
204 comment.into(),
205 args,
206 None,
207 )),
208 http_status.as_u16(),
209 log_msg.into(),
210 )
211 }
212}
213
214impl From<MediatorError> for ProcessorError {
215 fn from(error: MediatorError) -> Self {
216 ProcessorError::CommonError(error.to_string())
217 }
218}
219
220impl From<ProcessorError> for MediatorError {
221 fn from(error: ProcessorError) -> Self {
222 MediatorError::ProcessorError(0, error.clone(), error.to_string())
223 }
224}
225
226impl IntoResponse for AppError {
227 fn into_response(self) -> Response {
228 let ctx = &self.context;
229
230 let ctx_log: String = {
233 let mut parts = Vec::new();
234 if let Some(ref rid) = ctx.request_id {
235 parts.push(format!("request_id={rid}"));
236 }
237 if let Some(ref sid) = ctx.session_id {
238 parts.push(format!("session_id={sid}"));
239 }
240 if let Some(ref dh) = ctx.did_hash {
241 parts.push(format!("did_hash={dh}"));
242 }
243 if parts.is_empty() {
244 String::new()
245 } else {
246 format!(" [{}]", parts.join(" "))
247 }
248 };
249
250 let request_id = ctx.request_id.clone();
251
252 let response = match self.error {
253 MediatorError::ErrorHandlingError(error_code, session_id, msg) => {
254 event!(
255 Level::WARN,
256 "{}: ErrorHandlingError({}): {}{}",
257 session_id,
258 error_code,
259 msg,
260 ctx_log
261 );
262 ErrorResponse {
263 http_code: StatusCode::INTERNAL_SERVER_ERROR.as_u16(),
264 session_id,
265 request_id,
266 error_code,
267 error_code_str: "ErrorHandlingError".to_string(),
268 message: "Internal server error".to_string(),
269 }
270 }
271 MediatorError::InternalError(error_code, session_id, msg) => {
272 event!(
273 Level::WARN,
274 "{}: InternalError({}): {}{}",
275 session_id,
276 error_code,
277 msg,
278 ctx_log
279 );
280 ErrorResponse {
281 http_code: StatusCode::INTERNAL_SERVER_ERROR.as_u16(),
282 session_id,
283 request_id,
284 error_code,
285 error_code_str: "InternalError".to_string(),
286 message: "Internal server error".to_string(),
287 }
288 }
289 MediatorError::ParseError(error_code, session_id, what, msg) => {
290 event!(
291 Level::WARN,
292 "{}: ParseError({}): couldn't parse ({}). Reason: {}{}",
293 session_id,
294 error_code,
295 what,
296 msg,
297 ctx_log
298 );
299 ErrorResponse {
300 http_code: StatusCode::BAD_REQUEST.as_u16(),
301 session_id,
302 request_id,
303 error_code,
304 error_code_str: "BadRequest: ParseError".to_string(),
305 message: format!("Couldn't parse ({what})"),
306 }
307 }
308 MediatorError::PermissionError(error_code, session_id, msg) => {
309 let response = ErrorResponse {
310 http_code: StatusCode::FORBIDDEN.as_u16(),
311 session_id: session_id.to_string(),
312 request_id,
313 error_code,
314 error_code_str: "Forbidden: PermissionError".to_string(),
315 message: msg.to_string(),
316 };
317 event!(Level::WARN, "{}{}", response.to_string(), ctx_log);
318 response
319 }
320 MediatorError::RequestDataError(error_code, session_id, msg) => {
321 let response = ErrorResponse {
322 http_code: StatusCode::BAD_REQUEST.as_u16(),
323 session_id: session_id.to_string(),
324 request_id,
325 error_code,
326 error_code_str: "BadRequest: RequestDataError".to_string(),
327 message: format!("Bad Request: ({msg})"),
328 };
329 event!(Level::WARN, "{}{}", response.to_string(), ctx_log);
330 response
331 }
332 MediatorError::ServiceLimitError(error_code, session_id, msg) => {
333 let response = ErrorResponse {
334 http_code: StatusCode::BAD_REQUEST.as_u16(),
335 session_id: session_id.to_string(),
336 request_id,
337 error_code,
338 error_code_str: "BadRequest: ServiceLimitError".to_string(),
339 message: msg.to_string(),
340 };
341 event!(Level::WARN, "{}{}", response.to_string(), ctx_log);
342 response
343 }
344 MediatorError::Unauthorized(error_code, session_id, msg) => {
345 let response = ErrorResponse {
346 http_code: StatusCode::UNAUTHORIZED.as_u16(),
347 session_id: session_id.to_string(),
348 request_id,
349 error_code,
350 error_code_str: "Unauthorized".to_string(),
351 message: "Unauthorized access".to_string(),
352 };
353 event!(
354 Level::WARN,
355 "{}: Unauthorized({}): {}{}",
356 session_id,
357 error_code,
358 msg,
359 ctx_log
360 );
361 response
362 }
363 MediatorError::DIDError(error_code, session_id, did, msg) => {
364 event!(
365 Level::WARN,
366 "{}: DIDError({}): did({}) Error: {}{}",
367 session_id,
368 error_code,
369 did,
370 msg,
371 ctx_log
372 );
373 ErrorResponse {
374 http_code: StatusCode::BAD_REQUEST.as_u16(),
375 session_id,
376 request_id,
377 error_code,
378 error_code_str: "DIDError".to_string(),
379 message: format!("did({did}) Error: invalid or unresolvable DID"),
380 }
381 }
382 MediatorError::ConfigError(error_code, session_id, message) => {
383 event!(
384 Level::WARN,
385 "{}: ConfigError({}): {}{}",
386 session_id,
387 error_code,
388 message,
389 ctx_log
390 );
391 ErrorResponse {
392 http_code: StatusCode::SERVICE_UNAVAILABLE.as_u16(),
393 session_id,
394 request_id,
395 error_code,
396 error_code_str: "ConfigError".to_string(),
397 message: "Service configuration error".to_string(),
398 }
399 }
400 MediatorError::DatabaseError(error_code, session_id, message) => {
401 event!(
402 Level::WARN,
403 "{}: DatabaseError({}): {}{}",
404 session_id,
405 error_code,
406 message,
407 ctx_log
408 );
409 ErrorResponse {
410 http_code: StatusCode::SERVICE_UNAVAILABLE.as_u16(),
411 session_id,
412 request_id,
413 error_code,
414 error_code_str: "DatabaseError".to_string(),
415 message: "Service temporarily unavailable".to_string(),
416 }
417 }
418 MediatorError::MessageUnpackError(error_code, session_id, message) => {
419 event!(
420 Level::WARN,
421 "{}: MessageUnpackError({}): {}{}",
422 session_id,
423 error_code,
424 message,
425 ctx_log
426 );
427 ErrorResponse {
428 http_code: StatusCode::BAD_REQUEST.as_u16(),
429 session_id,
430 request_id,
431 error_code,
432 error_code_str: "MessageUnpackError".to_string(),
433 message: "Failed to unpack message".to_string(),
434 }
435 }
436 MediatorError::MessageExpired(error_code, session_id, expired, now) => {
437 let response = ErrorResponse {
438 http_code: StatusCode::UNPROCESSABLE_ENTITY.as_u16(),
439 session_id: session_id.to_string(),
440 request_id,
441 error_code,
442 error_code_str: "MessageExpired".to_string(),
443 message: format!("Message expired: expiry({expired}) now({now})"),
444 };
445 event!(Level::WARN, "{}{}", response.to_string(), ctx_log);
446 response
447 }
448 MediatorError::MessagePackError(error_code, session_id, message) => {
449 event!(
450 Level::WARN,
451 "{}: MessagePackError({}): {}{}",
452 session_id,
453 error_code,
454 message,
455 ctx_log
456 );
457 ErrorResponse {
458 http_code: StatusCode::BAD_REQUEST.as_u16(),
459 session_id,
460 request_id,
461 error_code,
462 error_code_str: "MessagePackError".to_string(),
463 message: "Failed to pack message".to_string(),
464 }
465 }
466 MediatorError::NotImplemented(error_code, session_id, message) => {
467 let response = ErrorResponse {
468 http_code: StatusCode::NOT_IMPLEMENTED.as_u16(),
469 session_id: session_id.to_string(),
470 request_id,
471 error_code,
472 error_code_str: "NotImplemented".to_string(),
473 message,
474 };
475 event!(Level::WARN, "{}{}", response.to_string(), ctx_log);
476 response
477 }
478 MediatorError::SessionError(error_code, session_id, message) => {
479 let response = ErrorResponse {
480 http_code: StatusCode::NOT_ACCEPTABLE.as_u16(),
481 session_id: session_id.to_string(),
482 request_id,
483 error_code,
484 error_code_str: "SessionError".to_string(),
485 message,
486 };
487 event!(Level::WARN, "{}{}", response.to_string(), ctx_log);
488 response
489 }
490 MediatorError::AnonymousMessageError(error_code, session_id, message) => {
491 let response = ErrorResponse {
492 http_code: StatusCode::NOT_ACCEPTABLE.as_u16(),
493 session_id: session_id.to_string(),
494 request_id,
495 error_code,
496 error_code_str: "AnonymousMessageError".to_string(),
497 message,
498 };
499 event!(Level::WARN, "{}{}", response.to_string(), ctx_log);
500 response
501 }
502 MediatorError::ForwardMessageError(error_code, session_id, message) => {
503 let response = ErrorResponse {
504 http_code: StatusCode::NOT_ACCEPTABLE.as_u16(),
505 session_id: session_id.to_string(),
506 request_id,
507 error_code,
508 error_code_str: "ForwardMessageError".to_string(),
509 message,
510 };
511 event!(Level::WARN, "{}{}", response.to_string(), ctx_log);
512 response
513 }
514 MediatorError::AuthenticationError(error_code, message) => {
515 event!(
516 Level::WARN,
517 "AuthenticationError({}): {}{}",
518 error_code,
519 message,
520 ctx_log
521 );
522 ErrorResponse {
523 http_code: StatusCode::UNAUTHORIZED.as_u16(),
524 session_id: "NO-SESSION".to_string(),
525 request_id,
526 error_code,
527 error_code_str: "AuthenticationError".to_string(),
528 message: "Authentication failed".to_string(),
529 }
530 }
531 MediatorError::ACLDenied(error_code, message) => {
532 let response = ErrorResponse {
533 http_code: StatusCode::UNAUTHORIZED.as_u16(),
534 session_id: "NO-SESSION".to_string(),
535 request_id,
536 error_code,
537 error_code_str: "ACLDenied".to_string(),
538 message,
539 };
540 event!(Level::WARN, "{}{}", response.to_string(), ctx_log);
541 response
542 }
543 MediatorError::ProcessorError(error_code, processor, message) => {
544 event!(
545 Level::WARN,
546 "ProcessorError({}): Processor ({}): {}{}",
547 error_code,
548 processor,
549 message,
550 ctx_log
551 );
552 ErrorResponse {
553 http_code: StatusCode::INTERNAL_SERVER_ERROR.as_u16(),
554 session_id: "NO-SESSION".to_string(),
555 request_id,
556 error_code,
557 error_code_str: "ProcessorError".to_string(),
558 message: "Internal server error".to_string(),
559 }
560 }
561 MediatorError::MediatorError(
562 error_code,
563 session_id,
564 _,
565 problem_report,
566 http_code,
567 log_text,
568 ) => {
569 event!(Level::WARN, "{}{}", log_text, ctx_log);
570 ErrorResponse {
571 http_code,
572 session_id,
573 request_id,
574 error_code,
575 error_code_str: "DIDCommProblemReport".to_string(),
576 message: serde_json::to_string(&problem_report)
577 .unwrap_or_else(|_| "Failed to serialize Problem Report".to_string()),
578 }
579 }
580 };
581 (
582 StatusCode::from_u16(response.http_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR),
583 Json(response),
584 )
585 .into_response()
586 }
587}
588
589#[derive(Serialize, Debug)]
591#[serde(rename_all = "camelCase")]
592pub struct ErrorResponse {
593 pub session_id: String,
594 #[serde(skip_serializing_if = "Option::is_none")]
595 pub request_id: Option<String>,
596 pub http_code: u16,
597 pub error_code: u16,
598 pub error_code_str: String,
599 pub message: String,
600}
601
602impl fmt::Display for ErrorResponse {
603 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
604 write!(
605 f,
606 "{}: httpcode({}) errorCode({}), errorCodeStr({}) message({})",
607 self.session_id, self.http_code, self.error_code, self.error_code_str, self.message,
608 )?;
609 if let Some(ref rid) = self.request_id {
610 write!(f, " request_id({rid})")?;
611 }
612 Ok(())
613 }
614}
615#[derive(Serialize, Deserialize, Debug)]
617#[serde(rename_all = "camelCase")]
618pub struct SuccessResponse<T: GenericDataStruct> {
619 pub session_id: String,
620 pub http_code: u16,
621 pub error_code: i32,
622 pub error_code_str: String,
623 pub message: String,
624 #[serde(bound(deserialize = ""))]
625 pub data: Option<T>,
626}
627
628impl<T: GenericDataStruct> fmt::Display for SuccessResponse<T> {
629 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
630 write!(
631 f,
632 "{}: httpcode({}) errorCode({}), errorCodeStr({}) message({})",
633 self.session_id, self.http_code, self.error_code, self.error_code_str, self.message,
634 )
635 }
636}
637
638impl<T: GenericDataStruct> SuccessResponse<T> {
639 pub fn response(
640 session_id: &str,
641 http_code: StatusCode,
642 msg: &str,
643 data: Option<T>,
644 ) -> Json<SuccessResponse<T>> {
645 let response = SuccessResponse {
646 session_id: session_id.to_string(),
647 http_code: http_code.as_u16(),
648 error_code: 0,
649 error_code_str: "Ok".to_string(),
650 message: msg.to_string(),
651 data,
652 };
653 event!(Level::INFO, "{response}");
654 Json(response)
655 }
656}
657
658pub fn create_session_id() -> String {
660 rand::rng()
661 .sample_iter(&Alphanumeric)
662 .take(12)
663 .map(char::from)
664 .collect()
665}
666
667#[cfg(test)]
668mod tests {
669 use super::*;
670 use crate::types::problem_report::{ProblemReportScope, ProblemReportSorter};
671 use axum::http::StatusCode;
672
673 #[test]
674 fn test_problem_creates_mediator_error() {
675 let err = MediatorError::problem(
676 44,
677 "test-session",
678 None,
679 ProblemReportSorter::Error,
680 ProblemReportScope::Protocol,
681 "authorization.send",
682 "DID isn't allowed to send",
683 vec![],
684 StatusCode::FORBIDDEN,
685 );
686 match err {
687 MediatorError::MediatorError(code, session, msg_id, report, http, log) => {
688 assert_eq!(code, 44);
689 assert_eq!(session, "test-session");
690 assert!(msg_id.is_none());
691 assert_eq!(report.code, "e.p.authorization.send");
692 assert_eq!(report.comment, "DID isn't allowed to send");
693 assert_eq!(http, 403);
694 assert_eq!(log, "DID isn't allowed to send");
695 }
696 _ => panic!("Expected MediatorError::MediatorError variant"),
697 }
698 }
699
700 #[test]
701 fn test_problem_with_log_separate_messages() {
702 let err = MediatorError::problem_with_log(
703 19,
704 "sess-123",
705 Some("msg-456".to_string()),
706 ProblemReportSorter::Warning,
707 ProblemReportScope::Message,
708 "message.serialize",
709 "Couldn't serialize: {1}",
710 vec!["parse error".to_string()],
711 StatusCode::BAD_REQUEST,
712 "Couldn't serialize message",
713 );
714 match err {
715 MediatorError::MediatorError(code, session, msg_id, report, http, log) => {
716 assert_eq!(code, 19);
717 assert_eq!(session, "sess-123");
718 assert_eq!(msg_id, Some("msg-456".to_string()));
719 assert_eq!(report.code, "w.m.message.serialize");
720 assert_eq!(report.comment, "Couldn't serialize: {1}");
721 assert_eq!(report.args, vec!["parse error"]);
722 assert_eq!(http, 400);
723 assert_eq!(log, "Couldn't serialize message");
724 }
725 _ => panic!("Expected MediatorError::MediatorError variant"),
726 }
727 }
728
729 #[test]
730 fn test_problem_with_args() {
731 let err = MediatorError::problem(
732 63,
733 "s1",
734 Some("m1".to_string()),
735 ProblemReportSorter::Warning,
736 ProblemReportScope::Message,
737 "protocol.forwarding.attachments.too_many",
738 "Too many attachments ({1}). Limit ({2})",
739 vec!["5".to_string(), "1".to_string()],
740 StatusCode::BAD_REQUEST,
741 );
742 match err {
743 MediatorError::MediatorError(_, _, _, report, _, _) => {
744 assert_eq!(report.args.len(), 2);
745 assert_eq!(report.args[0], "5");
746 assert_eq!(report.args[1], "1");
747 }
748 _ => panic!("Expected MediatorError::MediatorError variant"),
749 }
750 }
751
752 #[test]
753 fn test_create_session_id_length() {
754 let id = create_session_id();
755 assert_eq!(id.len(), 12);
756 assert!(id.chars().all(|c| c.is_ascii_alphanumeric()));
757 }
758
759 #[test]
760 fn test_create_session_id_uniqueness() {
761 let id1 = create_session_id();
762 let id2 = create_session_id();
763 assert_ne!(id1, id2, "Two session IDs should not be identical");
764 }
765}