gcloud_sdk/
reqwest_auth_middleware.rs1use std::sync::Arc;
2
3use async_trait::async_trait;
4use reqwest_middleware::{Middleware, Next};
5
6use crate::middleware::replace_headers;
7use crate::GoogleAuthHeaders;
8
9#[derive(Clone)]
13pub struct GoogleAuthReqwestMiddleware {
14 auth_headers: Arc<GoogleAuthHeaders>,
15}
16
17impl GoogleAuthReqwestMiddleware {
18 pub fn new(auth_headers: impl Into<Arc<GoogleAuthHeaders>>) -> Self {
19 Self {
20 auth_headers: auth_headers.into(),
21 }
22 }
23}
24
25#[async_trait]
26impl Middleware for GoogleAuthReqwestMiddleware {
27 async fn handle(
28 &self,
29 mut req: reqwest::Request,
30 extensions: &mut hyper::http::Extensions,
31 next: Next<'_>,
32 ) -> reqwest_middleware::Result<reqwest::Response> {
33 let auth_headers = self
34 .auth_headers
35 .headers()
36 .await
37 .map_err(reqwest_middleware::Error::middleware)?;
38 replace_headers(req.headers_mut(), &auth_headers);
39 next.run(req, extensions).await
40 }
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46 use crate::test_support::StubCredentials;
47 use google_cloud_auth::credentials::Credentials;
48 use reqwest::header::{HeaderMap, HeaderValue};
49 use std::sync::Mutex;
50
51 #[derive(Clone, Default)]
54 struct CaptureHeaders {
55 received: Arc<Mutex<Vec<HeaderMap>>>,
56 }
57
58 #[async_trait]
59 impl Middleware for CaptureHeaders {
60 async fn handle(
61 &self,
62 req: reqwest::Request,
63 _extensions: &mut hyper::http::Extensions,
64 _next: Next<'_>,
65 ) -> reqwest_middleware::Result<reqwest::Response> {
66 self.received.lock().unwrap().push(req.headers().clone());
67 Ok(reqwest::Response::from(hyper::Response::new(
68 Vec::<u8>::new(),
69 )))
70 }
71 }
72
73 #[tokio::test]
74 async fn every_credentials_header_is_forwarded_and_authorization_is_sensitive() {
75 let mut credentials_headers = HeaderMap::new();
76 credentials_headers.insert(
77 reqwest::header::AUTHORIZATION,
78 HeaderValue::from_static("Bearer billed-token"),
79 );
80 credentials_headers.insert(
81 "x-goog-user-project",
82 HeaderValue::from_static("billing-project"),
83 );
84 let auth_headers =
85 GoogleAuthHeaders::from(Credentials::from(StubCredentials::new(credentials_headers)));
86 let capture = CaptureHeaders::default();
87 let client = reqwest_middleware::ClientBuilder::new(reqwest::Client::new())
88 .with(GoogleAuthReqwestMiddleware::new(auth_headers))
89 .with(capture.clone())
90 .build();
91
92 client.get("http://orders.invalid/").send().await.unwrap();
93
94 let received = capture.received.lock().unwrap();
95 let authorization = &received[0][reqwest::header::AUTHORIZATION];
96 assert_eq!(authorization, "Bearer billed-token");
97 assert!(authorization.is_sensitive());
98 assert_eq!(received[0]["x-goog-user-project"], "billing-project");
99 }
100}