drogue_bazaar/actix/auth/authentication/
middleware.rs1use super::{AuthN, Credentials, UsernameAndToken};
2use crate::auth::AuthError;
3use actix_http::body::EitherBody;
4use actix_service::{Service, Transform};
5use actix_web::{
6 dev::{ServiceRequest, ServiceResponse},
7 web::Query,
8 Error, HttpMessage,
9};
10use actix_web_httpauth::extractors::{basic::BasicAuth, bearer::BearerAuth};
11use chrono::{DateTime, Utc};
12use futures_util::future::{ok, LocalBoxFuture, Ready};
13use serde::Deserialize;
14use std::rc::Rc;
15
16pub struct AuthMiddleware<S> {
17 service: Rc<S>,
18 authenticator: AuthN,
19}
20
21impl<S, B> Transform<S, ServiceRequest> for AuthN
26where
27 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
28 S::Future: 'static,
29 B: 'static,
30{
31 type Response = ServiceResponse<EitherBody<B>>;
32 type Error = S::Error;
33 type Transform = AuthMiddleware<S>;
34 type InitError = ();
35 type Future = Ready<Result<Self::Transform, Self::InitError>>;
36
37 fn new_transform(&self, service: S) -> Self::Future {
38 ok(AuthMiddleware {
39 service: Rc::new(service),
40 authenticator: self.clone(),
41 })
42 }
43}
44
45#[derive(Debug, Clone, PartialEq)]
46pub struct AuthenticatedUntil(pub DateTime<Utc>);
47
48#[derive(Deserialize, Debug)]
49struct Token {
50 token: String,
51}
52#[derive(Deserialize, Debug)]
53struct ApiKey {
54 username: String,
55 api_key: String,
56}
57
58impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
60where
61 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
62 S::Future: 'static,
63 B: 'static,
64{
65 type Response = ServiceResponse<EitherBody<B>>;
66 type Error = Error;
67 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
68
69 actix_service::forward_ready!(service);
70
71 fn call(&self, mut req: ServiceRequest) -> Self::Future {
72 let srv = Rc::clone(&self.service);
73 let auth = self.authenticator.clone();
74
75 Box::pin(async move {
76 let basic_auth = req.extract::<BasicAuth>().await;
77 let bearer_auth = req.extract::<BearerAuth>().await;
78
79 let query_str = req.query_string();
81 let token_query_param = Query::<Token>::from_query(query_str);
82 let api_key_query_param = Query::<ApiKey>::from_query(query_str);
83
84 log::debug!(
85 "Basic: {:?}, Bearer: {:?}, Query.token: {:?} Query.api_key: {:?}",
86 basic_auth,
87 bearer_auth,
88 token_query_param,
89 api_key_query_param,
90 );
91
92 let credentials = match (
95 basic_auth,
96 bearer_auth,
97 token_query_param,
98 api_key_query_param,
99 ) {
100 (Ok(basic), Err(_), Err(_), Err(_)) => {
102 Ok(Credentials::AccessToken(UsernameAndToken {
103 username: basic.user_id().to_string(),
104 access_token: basic.password().map(|k| k.to_string()),
105 }))
106 }
107 (Err(_), Ok(bearer), Err(_), Err(_)) => {
109 Ok(Credentials::OpenIDToken(bearer.token().to_string()))
110 }
111
112 (Err(_), Err(_), Ok(query), Err(_)) => Ok(Credentials::OpenIDToken(query.0.token)),
114 (Err(_), Err(_), Err(_), Ok(query)) => {
116 Ok(Credentials::AccessToken(UsernameAndToken {
117 username: query.0.username,
118 access_token: Some(query.0.api_key),
119 }))
120 }
121
122 (Err(_), Err(_), Err(_query), Err(_api_key)) => Ok(Credentials::Anonymous),
124 (_, _, _, _) => Err(AuthError::InvalidRequest(
127 "More than one way of authentication provided".to_string(),
128 )),
129 };
130
131 let auth_result = match credentials {
133 Ok(c) => auth.authenticate(c).await,
134 Err(err) => {
135 log::info!("Credentials error: {err}");
136 Err(err)
137 }
138 };
139
140 match auth_result {
141 Ok((user, time)) => {
142 log::debug!("Authenticated: {user:?}");
143 req.extensions_mut().insert(user);
145 if let Some(exp) = time {
146 req.extensions_mut().insert(AuthenticatedUntil(exp));
147 }
148 srv.call(req).await.map(|res| res.map_into_left_body())
150 }
151 Err(err) => {
152 log::debug!("Authentication error: {err}");
153 Ok(req.error_response(err).map_into_right_body())
154 }
155 }
156 })
157 }
158}