ferro_rs/authorization/
error.rs1use crate::http::{HttpResponse, Response};
4use std::fmt;
5
6#[derive(Debug, Clone)]
8pub struct AuthorizationError {
9 pub ability: String,
11 pub message: Option<String>,
13 pub status: u16,
15}
16
17impl AuthorizationError {
18 pub fn new(ability: impl Into<String>) -> Self {
20 Self {
21 ability: ability.into(),
22 message: None,
23 status: 403,
24 }
25 }
26
27 pub fn with_message(ability: impl Into<String>, message: impl Into<String>) -> Self {
29 Self {
30 ability: ability.into(),
31 message: Some(message.into()),
32 status: 403,
33 }
34 }
35
36 pub fn not_found(ability: impl Into<String>) -> Self {
38 Self {
39 ability: ability.into(),
40 message: None,
41 status: 404,
42 }
43 }
44
45 pub fn with_status(mut self, status: u16) -> Self {
47 self.status = status;
48 self
49 }
50
51 pub fn message_or_default(&self) -> String {
53 self.message
54 .clone()
55 .unwrap_or_else(|| "This action is unauthorized.".to_string())
56 }
57}
58
59impl fmt::Display for AuthorizationError {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match &self.message {
62 Some(msg) => write!(f, "Authorization failed for '{}': {}", self.ability, msg),
63 None => write!(f, "Authorization failed for '{}'", self.ability),
64 }
65 }
66}
67
68impl std::error::Error for AuthorizationError {}
69
70impl From<AuthorizationError> for HttpResponse {
71 fn from(err: AuthorizationError) -> HttpResponse {
72 let message = err.message_or_default();
73 let body = serde_json::json!({
74 "message": message
75 });
76 HttpResponse::json(body).status(err.status)
77 }
78}
79
80impl From<AuthorizationError> for Response {
81 fn from(err: AuthorizationError) -> Response {
82 Err(HttpResponse::from(err))
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn test_new_error() {
92 let error = AuthorizationError::new("update");
93 assert_eq!(error.ability, "update");
94 assert_eq!(error.status, 403);
95 assert!(error.message.is_none());
96 }
97
98 #[test]
99 fn test_error_with_message() {
100 let error = AuthorizationError::with_message("delete", "You do not own this resource");
101 assert_eq!(error.ability, "delete");
102 assert_eq!(
103 error.message,
104 Some("You do not own this resource".to_string())
105 );
106 }
107
108 #[test]
109 fn test_not_found_error() {
110 let error = AuthorizationError::not_found("view");
111 assert_eq!(error.status, 404);
112 }
113
114 #[test]
115 fn test_display() {
116 let error = AuthorizationError::with_message("update", "Forbidden");
117 assert_eq!(
118 error.to_string(),
119 "Authorization failed for 'update': Forbidden"
120 );
121 }
122
123 #[test]
124 fn test_message_or_default() {
125 let error = AuthorizationError::new("test");
126 assert_eq!(error.message_or_default(), "This action is unauthorized.");
127
128 let error = AuthorizationError::with_message("test", "Custom message");
129 assert_eq!(error.message_or_default(), "Custom message");
130 }
131}