auth_framework/api/
mod.rs1pub mod admin;
7pub mod auth;
8pub mod email_verification;
9pub mod error_codes;
10pub mod health;
11pub mod metrics;
12pub mod mfa;
13pub mod middleware;
14pub mod oauth2;
15pub mod oauth_advanced; pub mod advanced_protocols;
16pub mod openapi;
17pub mod responses;
18pub mod saml;
19pub mod security;
20pub mod security_simple;
21pub mod server;
22pub mod users;
23pub mod validation;
24pub mod versioning;
25pub mod webauthn;
26
27#[cfg(feature = "enhanced-rbac")]
28#[cfg(feature = "role-system")]
29pub mod rbac_endpoints;
30
31pub use responses::{ApiError, ApiResponse, ApiResult};
32pub use security::SecurityManager;
33pub use server::ApiServer;
34
35use crate::AuthFramework;
36use crate::distributed::rate_limiting::{DistributedRateLimiter, RateLimitConfig};
37use crate::errors::AuthError;
38use std::sync::Arc;
39
40#[derive(Clone)]
42pub struct ApiState {
43 pub auth_framework: Arc<AuthFramework>,
44 pub rate_limiter: Arc<DistributedRateLimiter>,
45 #[cfg(feature = "enhanced-rbac")]
46 pub authorization_service: Arc<crate::authorization_enhanced::AuthorizationService>,
47}
48
49impl ApiState {
50 pub async fn new(auth_framework: Arc<AuthFramework>) -> crate::errors::Result<Self> {
59 let rate_limiter =
60 Arc::new(DistributedRateLimiter::new(RateLimitConfig::balanced()).await?);
61 Ok(Self {
62 auth_framework,
63 rate_limiter,
64 #[cfg(feature = "enhanced-rbac")]
65 authorization_service: Arc::new(
66 crate::authorization_enhanced::AuthorizationService::new().await?,
67 ),
68 })
69 }
70
71 #[cfg(feature = "enhanced-rbac")]
78 pub fn with_authorization_service(
79 auth_framework: Arc<AuthFramework>,
80 rate_limiter: Arc<DistributedRateLimiter>,
81 authorization_service: Arc<crate::authorization_enhanced::AuthorizationService>,
82 ) -> Self {
83 Self {
84 auth_framework,
85 rate_limiter,
86 authorization_service,
87 }
88 }
89}
90
91pub fn extract_bearer_token(headers: &axum::http::HeaderMap) -> Option<String> {
93 headers
94 .get("authorization")
95 .and_then(|header| header.to_str().ok())
96 .and_then(|auth_str| auth_str.strip_prefix("Bearer "))
97 .filter(|token| !token.is_empty())
98 .map(|token| token.to_string())
99}
100
101pub async fn validate_api_token(
103 auth_framework: &AuthFramework,
104 token: &str,
105) -> Result<crate::tokens::AuthToken, AuthError> {
106 let token_obj = auth_framework.token_manager().validate_jwt_token(token)?;
108
109 let revocation_key = format!("revoked_token:{}", token_obj.jti);
111 match auth_framework.storage().get_kv(&revocation_key).await {
112 Ok(Some(_)) => {
113 return Err(AuthError::Unauthorized(
114 "Token has been revoked".to_string(),
115 ));
116 }
117 Ok(None) => {} Err(e) => {
119 tracing::error!("Could not check token revocation list: {}", e);
120 return Err(AuthError::Unauthorized(
121 "Unable to verify token status".to_string(),
122 ));
123 }
124 }
125
126 let user_id_str = token_obj.sub.clone();
131 let roles = {
132 let user_key = format!("user:{}", user_id_str);
133 match auth_framework.storage().get_kv(&user_key).await {
134 Ok(Some(bytes)) => {
135 let json: serde_json::Value = match serde_json::from_slice(&bytes) {
136 Ok(v) => v,
137 Err(e) => {
138 tracing::warn!(user_id = %user_id_str, "Failed to parse user record JSON for role extraction: {}", e);
139 serde_json::Value::default()
140 }
141 };
142 if json["active"].as_bool() == Some(false) {
144 return Err(AuthError::Unauthorized("Account is disabled".to_string()));
145 }
146 json["roles"]
147 .as_array()
148 .map(|arr| {
149 arr.iter()
150 .filter_map(|v| v.as_str())
151 .map(|s| s.to_string())
152 .collect::<Vec<_>>()
153 })
154 .unwrap_or_else(|| token_obj.roles.clone().unwrap_or_default())
155 }
156 _ => token_obj.roles.clone().unwrap_or_default(),
157 }
158 };
159
160 Ok(crate::tokens::AuthToken {
161 token_id: token_obj.jti.clone(),
162 user_id: user_id_str.clone(),
163 access_token: token.to_string(),
164 token_type: Some("Bearer".to_string()),
165 subject: Some(user_id_str.clone()),
166 issuer: Some(token_obj.iss.clone()),
167 refresh_token: None,
168 issued_at: chrono::DateTime::from_timestamp(token_obj.iat, 0)
169 .unwrap_or_else(chrono::Utc::now),
170 expires_at: chrono::DateTime::from_timestamp(token_obj.exp, 0)
171 .unwrap_or_else(chrono::Utc::now),
172 scopes: token_obj
173 .scope
174 .split_whitespace()
175 .map(|s| s.to_string())
176 .collect(),
177 auth_method: "jwt".to_string(),
178 client_id: token_obj.client_id,
179 user_profile: None,
180 permissions: token_obj.permissions.unwrap_or_default().into(),
181 roles: roles.into(),
182 metadata: crate::tokens::TokenMetadata {
183 session_id: None, ..Default::default()
185 },
186 })
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use axum::http::HeaderMap;
193
194 #[test]
195 fn test_extract_bearer_token_valid() {
196 let mut headers = HeaderMap::new();
197 headers.insert("authorization", "Bearer mytoken123".parse().unwrap());
198 assert_eq!(
199 extract_bearer_token(&headers),
200 Some("mytoken123".to_string())
201 );
202 }
203
204 #[test]
205 fn test_extract_bearer_token_missing() {
206 let headers = HeaderMap::new();
207 assert_eq!(extract_bearer_token(&headers), None);
208 }
209
210 #[test]
211 fn test_extract_bearer_token_not_bearer() {
212 let mut headers = HeaderMap::new();
213 headers.insert("authorization", "Basic abc123".parse().unwrap());
214 assert_eq!(extract_bearer_token(&headers), None);
215 }
216
217 #[test]
218 fn test_extract_bearer_token_empty_value() {
219 let mut headers = HeaderMap::new();
220 headers.insert("authorization", "Bearer ".parse().unwrap());
221 assert_eq!(extract_bearer_token(&headers), None);
223 }
224
225 #[tokio::test]
226 async fn test_validate_api_token_invalid() {
227 let config = crate::AuthConfig::new().secret("a]Bc!d@e#f$g%h^i&j*k(l)m_n-o+p=q");
228 let fw = AuthFramework::new(config);
229 let result = validate_api_token(&fw, "not.a.valid.token").await;
230 assert!(result.is_err());
231 }
232
233 #[tokio::test]
234 async fn test_validate_api_token_revoked() {
235 let config = crate::AuthConfig::new().secret("a]Bc!d@e#f$g%h^i&j*k(l)m_n-o+p=q");
236 let mut fw = AuthFramework::new(config);
237 fw.initialize().await.unwrap();
238
239 let token = fw
241 .token_manager()
242 .create_jwt_token("user1", vec!["user".into()], None)
243 .unwrap();
244 let token_obj = fw.token_manager().validate_jwt_token(&token).unwrap();
245
246 let revocation_key = format!("revoked_token:{}", token_obj.jti);
248 fw.storage()
249 .store_kv(&revocation_key, b"revoked", None)
250 .await
251 .unwrap();
252
253 let result = validate_api_token(&fw, &token).await;
255 assert!(result.is_err());
256 let err_msg = format!("{}", result.unwrap_err());
257 assert!(err_msg.contains("revoked"));
258 }
259
260 #[tokio::test]
261 async fn test_validate_api_token_success() {
262 let config = crate::AuthConfig::new().secret("a]Bc!d@e#f$g%h^i&j*k(l)m_n-o+p=q");
263 let mut fw = AuthFramework::new(config);
264 fw.initialize().await.unwrap();
265
266 let token = fw
267 .token_manager()
268 .create_jwt_token("user_abc", vec!["user".into()], None)
269 .unwrap();
270
271 let auth_token = validate_api_token(&fw, &token).await.unwrap();
272 assert_eq!(auth_token.user_id, "user_abc");
273 assert_eq!(auth_token.auth_method, "jwt");
274 assert_eq!(auth_token.token_type.as_deref(), Some("Bearer"));
275 }
276
277 #[tokio::test]
278 async fn test_validate_api_token_with_roles_from_storage() {
279 let config = crate::AuthConfig::new().secret("a]Bc!d@e#f$g%h^i&j*k(l)m_n-o+p=q");
280 let mut fw = AuthFramework::new(config);
281 fw.initialize().await.unwrap();
282
283 let user_json = serde_json::json!({"user_id": "role_user", "roles": ["admin", "editor"]});
285 fw.storage()
286 .store_kv(
287 "user:role_user",
288 serde_json::to_vec(&user_json).unwrap().as_slice(),
289 None,
290 )
291 .await
292 .unwrap();
293
294 let token = fw
295 .token_manager()
296 .create_jwt_token("role_user", vec!["user".into()], None)
297 .unwrap();
298
299 let auth_token = validate_api_token(&fw, &token).await.unwrap();
300 assert!(auth_token.roles.contains(&"admin".to_string()));
301 assert!(auth_token.roles.contains(&"editor".to_string()));
302 }
303}