Skip to main content

paas_server/auth/
core.rs

1use actix_web::{
2    dev::{Service, ServiceRequest, ServiceResponse, Transform},
3    error::ErrorUnauthorized,
4    Error, FromRequest, HttpMessage,
5};
6use actix_web_httpauth::extractors::bearer::BearerAuth;
7use futures::future::{ready, Ready};
8use std::future::Future;
9use std::pin::Pin;
10use std::rc::Rc;
11use std::task::{Context, Poll};
12
13// Trait for token validators
14pub trait TokenValidator: Clone + Send + Sync + 'static {
15    // Validate a token and return user info if valid
16    fn validate_token<'a>(
17        &'a self,
18        token: &'a str,
19    ) -> Pin<Box<dyn Future<Output = Result<AuthInfo, Error>> + Send + 'a>>;
20}
21
22// Auth info returned by validators
23#[derive(Clone, Debug, Eq, PartialEq)]
24pub struct AuthInfo {
25    pub name: String,
26    pub sub: String,
27    pub groups: Vec<String>,
28}
29
30impl std::fmt::Display for AuthInfo {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        write!(
33            f,
34            "user.name={} user.sub={} user.groups=[{}]",
35            self.name,
36            self.sub,
37            if self.groups.is_empty() {
38                String::from("(none)")
39            } else {
40                self.groups.join(", ")
41            }
42        )
43    }
44}
45
46// Generic authentication middleware
47#[derive(Clone)]
48pub struct Authentication<V> {
49    validator: V,
50}
51impl<V: TokenValidator> Authentication<V> {
52    pub fn new(validator: V) -> Self {
53        Self { validator }
54    }
55}
56
57impl<V: TokenValidator> Authentication<Box<V>> {
58    pub fn new_boxed(validator: Box<V>) -> Self {
59        Self { validator }
60    }
61}
62
63impl<S, B, V> Transform<S, ServiceRequest> for Authentication<V>
64where
65    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
66    S::Future: 'static,
67    B: 'static,
68    V: TokenValidator,
69{
70    type Response = ServiceResponse<B>;
71    type Error = Error;
72    type Transform = AuthenticationMiddleware<S, V>;
73    type InitError = ();
74    type Future = Ready<Result<Self::Transform, Self::InitError>>;
75
76    fn new_transform(&self, service: S) -> Self::Future {
77        ready(Ok(AuthenticationMiddleware {
78            service: Rc::new(service),
79            validator: self.validator.clone(),
80        }))
81    }
82}
83
84pub struct AuthenticationMiddleware<S, V: TokenValidator> {
85    service: Rc<S>,
86    validator: V,
87}
88
89impl<S, B, V> Service<ServiceRequest> for AuthenticationMiddleware<S, V>
90where
91    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
92    S::Future: 'static,
93    B: 'static,
94    V: TokenValidator,
95{
96    type Response = ServiceResponse<B>;
97    type Error = Error;
98    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
99
100    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
101        self.service.poll_ready(cx)
102    }
103
104    fn call(&self, req: ServiceRequest) -> Self::Future {
105        let validator = self.validator.clone();
106        let srv = self.service.clone();
107
108        Box::pin(async move {
109            // Extract bearer token from the request
110            let req_head = req.request();
111            let bearer_result = BearerAuth::extract(req_head).await;
112
113            match bearer_result {
114                Ok(bearer) => {
115                    let token = bearer.token();
116
117                    // Validate the token
118                    match validator.validate_token(token).await {
119                        Ok(auth_info) => {
120                            // Add auth info to request extensions
121                            req.extensions_mut().insert(auth_info);
122                        }
123                        Err(e) => {
124                            // Token validation failed
125                            return Err(e);
126                        }
127                    }
128                }
129                Err(_) => {
130                    // No bearer token provided
131                    return Err(ErrorUnauthorized("Missing or invalid Bearer token"));
132                }
133            }
134
135            // Continue with request processing
136            let res = srv.call(req).await?;
137            Ok(res)
138        })
139    }
140}