axum_gate/authz/
errors.rs

1//! Authorization-category native errors.
2//!
3//! This module defines category-native errors for authorization (authz) concerns,
4//! focused on permission hash collisions. Use `AuthzError` directly in handlers,
5//! services, and middleware.
6//!
7//! # Overview
8//! - `AuthzError`: category-native error enum for authorization
9//! - Convenience constructors: `collision`
10//!
11//! # Examples
12//!
13//! Detect a permission hash collision:
14//! ```rust
15//! use axum_gate::authz::AuthzError;
16//! use axum_gate::errors::UserFriendlyError;
17//!
18//! let err = AuthzError::collision(42, vec!["read:alpha".into(), "read:beta".into()]);
19//! assert!(err.support_code().starts_with("AUTHZ-PERM-COLLISION-"));
20//! assert_eq!(err.severity(), axum_gate::errors::ErrorSeverity::Critical);
21//! ```
22
23use crate::errors::{ErrorSeverity, UserFriendlyError};
24use thiserror::Error;
25
26/// Authorization-category native errors.
27///
28/// Use these errors in authorization flows to model permission hash collisions.
29#[derive(Debug, Error)]
30#[non_exhaustive]
31pub enum AuthzError {
32    /// Permission collision detected when multiple permissions hash to the same value.
33    #[error("Permission collision: {collision_count} permissions map to hash {hash_id}")]
34    PermissionCollision {
35        /// Number of permissions that collide.
36        collision_count: usize,
37        /// The 64-bit hash ID that has collisions.
38        hash_id: u64,
39        /// List of permission names that collide.
40        permissions: Vec<String>,
41    },
42}
43
44impl AuthzError {
45    /// Create a permission collision error with collision details.
46    ///
47    /// This constructor calculates the `collision_count` from the provided list.
48    pub fn collision(hash_id: u64, permissions: Vec<String>) -> Self {
49        AuthzError::PermissionCollision {
50            collision_count: permissions.len(),
51            hash_id,
52            permissions,
53        }
54    }
55
56    /// Deterministic, category-specific support code for this error.
57    fn support_code_inner(&self) -> String {
58        match self {
59            AuthzError::PermissionCollision { hash_id, .. } => {
60                format!("AUTHZ-PERM-COLLISION-{}", hash_id)
61            }
62        }
63    }
64}
65
66impl UserFriendlyError for AuthzError {
67    fn user_message(&self) -> String {
68        match self {
69            AuthzError::PermissionCollision { .. } => {
70                "There's a technical issue with your account permissions. Our support team has been notified and will resolve this shortly. Please contact support if you need immediate assistance.".to_string()
71            }
72        }
73    }
74
75    fn developer_message(&self) -> String {
76        match self {
77            AuthzError::PermissionCollision {
78                collision_count,
79                hash_id,
80                permissions,
81            } => {
82                format!(
83                    "Permission collision detected: {} permissions [{}] map to hash ID {}. This indicates a critical hash collision in the permission system requiring immediate administrator attention.",
84                    collision_count,
85                    permissions.join(", "),
86                    hash_id
87                )
88            }
89        }
90    }
91
92    fn support_code(&self) -> String {
93        self.support_code_inner()
94    }
95
96    fn severity(&self) -> ErrorSeverity {
97        match self {
98            AuthzError::PermissionCollision { .. } => ErrorSeverity::Critical,
99        }
100    }
101
102    fn suggested_actions(&self) -> Vec<String> {
103        match self {
104            AuthzError::PermissionCollision { .. } => vec![
105                "Contact our support team immediately with the reference code below".to_string(),
106                "Do not attempt to retry this operation".to_string(),
107                "This is a critical system issue requiring immediate administrator attention"
108                    .to_string(),
109            ],
110        }
111    }
112
113    fn is_retryable(&self) -> bool {
114        match self {
115            AuthzError::PermissionCollision { .. } => false, // Critical system-level issue
116        }
117    }
118}