Skip to main content

webgates_core/authz/
errors.rs

1//! Authorization-related error values.
2//!
3//! In `webgates-core`, most day-to-day authorization decisions are simple `bool`
4//! outcomes returned by [`crate::authz::authorization_service::AuthorizationService`].
5//! This module exists for exceptional authorization-domain problems, such as a
6//! permission collision that makes access decisions unsafe or ambiguous.
7//!
8//! Import `AuthzError` from the canonical public path
9//! `webgates_core::authz::errors::AuthzError`.
10//!
11//! # Example
12//!
13//! ```rust
14//! use webgates_core::authz::errors::AuthzError;
15//! use webgates_core::errors_core::{ErrorSeverity, UserFriendlyError};
16//!
17//! let err = AuthzError::collision(42, vec!["read:alpha".into(), "read:beta".into()]);
18//! assert!(err.support_code().starts_with("AUTHZ-PERM-COLLISION-"));
19//! assert_eq!(err.severity(), ErrorSeverity::Critical);
20//! ```
21
22use crate::errors_core::{ErrorSeverity, UserFriendlyError};
23use thiserror::Error;
24
25/// Authorization-domain errors.
26///
27/// Use these errors when the authorization system itself encounters a problem,
28/// rather than when a user simply lacks access. A typical example is a
29/// permission collision that should be treated as a configuration or integrity
30/// issue.
31#[derive(Debug, Error)]
32#[non_exhaustive]
33pub enum AuthzError {
34    /// Permission collision detected when multiple permissions hash to the same value.
35    #[error("Permission collision: {collision_count} permissions map to hash {hash_id}")]
36    PermissionCollision {
37        /// Number of permissions that collide.
38        collision_count: usize,
39        /// The 64-bit hash ID that has collisions.
40        hash_id: u64,
41        /// List of permission names that collide.
42        permissions: Vec<String>,
43    },
44}
45
46impl AuthzError {
47    /// Creates a permission collision error from the colliding permission names.
48    ///
49    /// The constructor derives `collision_count` from the provided list.
50    pub fn collision(hash_id: u64, permissions: Vec<String>) -> Self {
51        AuthzError::PermissionCollision {
52            collision_count: permissions.len(),
53            hash_id,
54            permissions,
55        }
56    }
57
58    /// Deterministic, category-specific support code for this error.
59    fn support_code_inner(&self) -> String {
60        match self {
61            AuthzError::PermissionCollision { hash_id, .. } => {
62                format!("AUTHZ-PERM-COLLISION-{}", hash_id)
63            }
64        }
65    }
66}
67
68impl UserFriendlyError for AuthzError {
69    fn user_message(&self) -> String {
70        match self {
71            AuthzError::PermissionCollision { .. } => {
72                "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()
73            }
74        }
75    }
76
77    fn developer_message(&self) -> String {
78        match self {
79            AuthzError::PermissionCollision {
80                collision_count,
81                hash_id,
82                permissions,
83            } => {
84                format!(
85                    "Permission collision detected: {} permissions [{}] map to hash ID {}. This indicates a critical hash collision in the permission system requiring immediate administrator attention.",
86                    collision_count,
87                    permissions.join(", "),
88                    hash_id
89                )
90            }
91        }
92    }
93
94    fn support_code(&self) -> String {
95        self.support_code_inner()
96    }
97
98    fn severity(&self) -> ErrorSeverity {
99        match self {
100            AuthzError::PermissionCollision { .. } => ErrorSeverity::Critical,
101        }
102    }
103
104    fn suggested_actions(&self) -> Vec<String> {
105        match self {
106            AuthzError::PermissionCollision { .. } => vec![
107                "Contact our support team immediately with the reference code below".to_string(),
108                "Do not attempt to retry this operation".to_string(),
109                "This is a critical system issue requiring immediate administrator attention"
110                    .to_string(),
111            ],
112        }
113    }
114
115    fn is_retryable(&self) -> bool {
116        match self {
117            AuthzError::PermissionCollision { .. } => false, // Critical system-level issue
118        }
119    }
120}