-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy patherrors.rs
More file actions
105 lines (93 loc) · 3.41 KB
/
errors.rs
File metadata and controls
105 lines (93 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
/// AuthZen error codes as defined in the specification
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AuthZenErrorCode {
InvalidRequest,
Unauthorized,
Forbidden,
InternalError,
}
impl AuthZenErrorCode {
/// Get the corresponding HTTP status code
pub fn status_code(&self) -> StatusCode {
match self {
Self::InvalidRequest => StatusCode::BAD_REQUEST,
Self::Unauthorized => StatusCode::UNAUTHORIZED,
Self::Forbidden => StatusCode::FORBIDDEN,
Self::InternalError => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
/// AuthZen error response - compliant with spec section 12.1.11
/// The spec requires error responses to be plain strings, not structured JSON
#[derive(Debug, Clone)]
pub struct AuthZenError {
pub code: AuthZenErrorCode,
pub message: String,
}
/// AuthZen error details for OpenAPI documentation only
/// This is NOT used in actual responses, only for OpenAPI schema generation
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct AuthZenErrorDetails {
pub code: String,
pub message: String,
}
impl AuthZenError {
/// Create a new AuthZen error
pub fn new(code: AuthZenErrorCode, message: impl Into<String>) -> Self {
Self {
code,
message: message.into(),
}
}
/// Create an invalid_request error (400)
pub fn invalid_request(message: impl Into<String>) -> Self {
Self::new(AuthZenErrorCode::InvalidRequest, message)
}
/// Create an unauthorized error (401)
pub fn unauthorized(message: impl Into<String>) -> Self {
Self::new(AuthZenErrorCode::Unauthorized, message)
}
/// Create a forbidden error (403)
pub fn forbidden(message: impl Into<String>) -> Self {
Self::new(AuthZenErrorCode::Forbidden, message)
}
/// Create an internal_error (500)
pub fn internal_error(message: impl Into<String>) -> Self {
Self::new(AuthZenErrorCode::InternalError, message)
}
}
impl IntoResponse for AuthZenError {
fn into_response(self) -> Response {
// AuthZen spec section 12.1.11 requires error responses to be plain strings
(self.code.status_code(), self.message).into_response()
}
}
/// Convert internal errors to AuthZen format
impl From<crate::errors::ApiError> for AuthZenError {
fn from(err: crate::errors::ApiError) -> Self {
match err.status_code {
StatusCode::UNAUTHORIZED => AuthZenError::unauthorized("Authentication required"),
StatusCode::FORBIDDEN => AuthZenError::forbidden("Access denied"),
StatusCode::BAD_REQUEST => AuthZenError::invalid_request(err.detail),
_ => {
log::error!("Internal error converted to AuthZen format: {err:?}");
AuthZenError::internal_error("Internal server error")
}
}
}
}
/// Convert OPA forwarding errors to AuthZen format
impl From<crate::opa_client::ForwardingError> for AuthZenError {
fn from(err: crate::opa_client::ForwardingError) -> Self {
log::error!("OPA forwarding error: {err:?}");
// Use generic message to avoid leaking internal implementation details
AuthZenError::internal_error("Internal server error")
}
}