shaperail_runtime/auth/
extractor.rs1use std::future::{ready, Ready};
2use std::sync::Arc;
3
4use actix_web::dev::Payload;
5use actix_web::{web, FromRequest, HttpRequest};
6use shaperail_core::ShaperailError;
7
8use super::api_key::ApiKeyStore;
9use super::jwt::JwtConfig;
10
11#[derive(Debug, Clone)]
24pub struct AuthenticatedUser {
25 pub id: String,
27 pub role: String,
29}
30
31impl FromRequest for AuthenticatedUser {
32 type Error = ShaperailError;
33 type Future = Ready<Result<Self, Self::Error>>;
34
35 fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
36 ready(extract_auth(req))
37 }
38}
39
40fn extract_auth(req: &HttpRequest) -> Result<AuthenticatedUser, ShaperailError> {
46 if let Some(auth_header) = req.headers().get("Authorization") {
48 let header_str = auth_header
49 .to_str()
50 .map_err(|_| ShaperailError::Unauthorized)?;
51 if let Some(token) = header_str.strip_prefix("Bearer ") {
52 let jwt_config = req
53 .app_data::<web::Data<Arc<JwtConfig>>>()
54 .ok_or(ShaperailError::Internal("JWT not configured".to_string()))?;
55 let claims = jwt_config
56 .decode(token)
57 .map_err(|_| ShaperailError::Unauthorized)?;
58 if claims.token_type != "access" {
59 return Err(ShaperailError::Unauthorized);
60 }
61 return Ok(AuthenticatedUser {
62 id: claims.sub,
63 role: claims.role,
64 });
65 }
66 }
67
68 if let Some(api_key_header) = req.headers().get("X-API-Key") {
70 let key = api_key_header
71 .to_str()
72 .map_err(|_| ShaperailError::Unauthorized)?;
73 let store = req
74 .app_data::<web::Data<Arc<ApiKeyStore>>>()
75 .ok_or(ShaperailError::Unauthorized)?;
76 if let Some(user) = store.lookup(key) {
77 return Ok(user);
78 }
79 return Err(ShaperailError::Unauthorized);
80 }
81
82 Err(ShaperailError::Unauthorized)
83}
84
85pub fn try_extract_auth(req: &HttpRequest) -> Option<AuthenticatedUser> {
87 extract_auth(req).ok()
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn authenticated_user_debug() {
96 let user = AuthenticatedUser {
97 id: "u1".to_string(),
98 role: "admin".to_string(),
99 };
100 assert_eq!(user.id, "u1");
101 assert_eq!(user.role, "admin");
102 let cloned = user.clone();
104 assert_eq!(cloned.id, "u1");
105 }
106}