aura_core/effects/authorization.rs
1//! Authorization Effects
2//!
3//! Provides capability-based authorization primitives for access control across
4//! the Aura system. These effects enable verification of permissions, delegation
5//! of authority, and enforcement of security policies.
6//!
7//! # Effect Classification
8//!
9//! - **Category**: Application Effect
10//! - **Implementation**: `aura-authorization` / `aura-protocol` (Layer 2/4)
11//! - **Usage**: Biscuit token evaluation and capability-based authorization
12//!
13//! This is an application effect implemented in domain crates by composing
14//! infrastructure effects with authorization-specific logic.
15
16use crate::types::identifiers::AuthorityId;
17use crate::types::scope::{AuthorizationOp, ResourceScope};
18use crate::{AuraError, Cap};
19use async_trait::async_trait;
20
21/// Authorization operations for capability-based access control
22///
23/// This trait provides pure authorization primitives that can be composed
24/// into complex permission systems. All operations are stateless and work
25/// with explicit capability tokens and access policies.
26#[async_trait]
27pub trait AuthorizationEffects {
28 /// Verify that capabilities grant permission for a specific operation
29 ///
30 /// # Arguments
31 /// * `capabilities` - The capability lattice to check
32 /// * `operation` - The operation requiring authorization
33 /// * `resource` - The resource being accessed
34 ///
35 /// # Returns
36 /// * `Ok(true)` if authorization is granted
37 /// * `Ok(false)` if authorization is denied
38 /// * `Err(_)` if verification failed due to system error
39 async fn verify_capability(
40 &self,
41 capabilities: &Cap,
42 operation: AuthorizationOp,
43 scope: &ResourceScope,
44 ) -> Result<bool, AuthorizationError>;
45
46 /// Delegate a subset of capabilities to another entity
47 ///
48 /// Creates a new capability set that contains only the permissions
49 /// that are both requested and present in the source capabilities.
50 /// This implements the principle of least privilege using meet-semilattice operations.
51 ///
52 /// # Arguments
53 /// * `source_capabilities` - The capabilities to delegate from
54 /// * `requested_capabilities` - The capabilities being requested
55 /// * `target_authority` - The authority receiving the delegation
56 ///
57 /// # Returns
58 /// The intersection of source and requested capabilities (source ⊓ requested)
59 async fn delegate_capabilities(
60 &self,
61 source_capabilities: &Cap,
62 requested_capabilities: &Cap,
63 target_authority: &AuthorityId,
64 ) -> Result<Cap, AuthorizationError>;
65}
66
67/// Errors that can occur during authorization operations
68#[derive(Debug, thiserror::Error)]
69pub enum AuthorizationError {
70 /// The requested operation is not permitted
71 #[error("Access denied: {operation} on {resource}")]
72 AccessDenied { operation: String, resource: String },
73
74 /// The capability set is invalid or malformed
75 #[error("Invalid capability set: {reason}")]
76 InvalidCapabilities { reason: String },
77
78 /// The access token is invalid, expired, or revoked
79 #[error("Invalid access token: {reason}")]
80 InvalidToken { reason: String },
81
82 /// Cryptographic verification failed
83 #[error("Signature verification failed")]
84 SignatureError,
85
86 /// System error during authorization
87 #[error("Authorization system error: {0}")]
88 SystemError(#[from] AuraError),
89}
90
91/// Authorization decision result
92#[derive(Debug, Clone)]
93pub struct AuthorizationDecision {
94 /// Whether the operation is authorized
95 pub authorized: bool,
96 /// Optional reason for the decision
97 pub reason: Option<String>,
98}
99
100/// Biscuit token-based authorization effects
101///
102/// This trait enables Biscuit authorization checks without creating domain dependencies.
103/// The journal domain can use this trait while the implementation lives in aura-authorization.
104#[async_trait]
105pub trait BiscuitAuthorizationEffects {
106 /// Authorize an operation against a Biscuit token and resource scope
107 ///
108 /// # Arguments
109 /// * `token_data` - Raw Biscuit token bytes
110 /// * `operation` - The operation being attempted
111 /// * `scope` - The resource scope for the operation
112 ///
113 /// # Returns
114 /// Authorization decision with detailed reasoning
115 async fn authorize_biscuit(
116 &self,
117 token_data: &[u8],
118 operation: AuthorizationOp,
119 scope: &ResourceScope,
120 ) -> Result<AuthorizationDecision, AuthorizationError>;
121
122 /// Verify a fact authorization using Biscuit tokens
123 ///
124 /// # Arguments
125 /// * `token_data` - Raw Biscuit token bytes
126 /// * `fact_type` - Type of fact being authorized
127 /// * `scope` - Resource scope for the fact
128 ///
129 /// # Returns
130 /// Whether the fact is authorized
131 async fn authorize_fact(
132 &self,
133 token_data: &[u8],
134 fact_type: &str,
135 scope: &ResourceScope,
136 ) -> Result<bool, AuthorizationError>;
137}
138
139/// Blanket implementation for Arc<T> where T: AuthorizationEffects
140#[async_trait]
141impl<T> AuthorizationEffects for std::sync::Arc<T>
142where
143 T: AuthorizationEffects + ?Sized + Send + Sync,
144{
145 async fn verify_capability(
146 &self,
147 capabilities: &Cap,
148 operation: AuthorizationOp,
149 scope: &ResourceScope,
150 ) -> Result<bool, AuthorizationError> {
151 (**self)
152 .verify_capability(capabilities, operation, scope)
153 .await
154 }
155
156 async fn delegate_capabilities(
157 &self,
158 source_capabilities: &Cap,
159 requested_capabilities: &Cap,
160 target_authority: &AuthorityId,
161 ) -> Result<Cap, AuthorizationError> {
162 (**self)
163 .delegate_capabilities(
164 source_capabilities,
165 requested_capabilities,
166 target_authority,
167 )
168 .await
169 }
170}
171
172/// Blanket implementation for Arc<T> where T: BiscuitAuthorizationEffects
173#[async_trait]
174impl<T> BiscuitAuthorizationEffects for std::sync::Arc<T>
175where
176 T: BiscuitAuthorizationEffects + ?Sized + Send + Sync,
177{
178 async fn authorize_biscuit(
179 &self,
180 token_data: &[u8],
181 operation: AuthorizationOp,
182 scope: &ResourceScope,
183 ) -> Result<AuthorizationDecision, AuthorizationError> {
184 (**self)
185 .authorize_biscuit(token_data, operation, scope)
186 .await
187 }
188
189 async fn authorize_fact(
190 &self,
191 token_data: &[u8],
192 fact_type: &str,
193 scope: &ResourceScope,
194 ) -> Result<bool, AuthorizationError> {
195 (**self).authorize_fact(token_data, fact_type, scope).await
196 }
197}