gcloud_auth/token_source/
impersonate_token_source.rs

1use async_trait::async_trait;
2use serde::{Deserialize, Serialize};
3use time::format_description::well_known::Rfc3339;
4
5use crate::error::Error;
6use crate::token::Token;
7use crate::token_source::{default_http_client, TokenSource};
8
9#[derive(Debug)]
10pub struct ImpersonateTokenSource {
11    target: Box<dyn TokenSource>,
12    scopes: Vec<String>,
13    delegates: Vec<String>,
14    url: String,
15    lifetime: Option<i32>,
16    client: reqwest::Client,
17}
18
19impl ImpersonateTokenSource {
20    #[allow(dead_code)]
21    pub(crate) fn new(
22        url: String,
23        delegates: Vec<String>,
24        scopes: Vec<String>,
25        lifetime: Option<i32>,
26        target: Box<dyn TokenSource>,
27    ) -> Self {
28        ImpersonateTokenSource {
29            target,
30            scopes,
31            delegates,
32            url,
33            lifetime,
34            client: default_http_client(),
35        }
36    }
37}
38
39#[async_trait]
40impl TokenSource for ImpersonateTokenSource {
41    async fn token(&self) -> Result<Token, Error> {
42        let body = ImpersonateTokenRequest {
43            lifetime: format!("{}s", self.lifetime.unwrap_or(3600)),
44            scope: self.scopes.clone(),
45            delegates: self.delegates.clone(),
46        };
47
48        let auth_token = self.target.token().await?;
49        let response = self
50            .client
51            .post(&self.url)
52            .json(&body)
53            .header(
54                "Authorization",
55                format!("{} {}", auth_token.token_type, auth_token.access_token),
56            )
57            .send()
58            .await?;
59        let response = if !response.status().is_success() {
60            let status = response.status().as_u16();
61            return Err(Error::UnexpectedImpersonateTokenResponse(status, response.text().await?));
62        } else {
63            response.json::<ImpersonateTokenResponse>().await?
64        };
65
66        let expiry = time::OffsetDateTime::parse(&response.expire_time, &Rfc3339)?;
67        Ok(Token {
68            access_token: response.access_token,
69            token_type: "Bearer".to_string(),
70            expiry: Some(expiry),
71        })
72    }
73}
74
75#[derive(Serialize)]
76struct ImpersonateTokenRequest {
77    pub delegates: Vec<String>,
78    pub lifetime: String,
79    pub scope: Vec<String>,
80}
81
82#[derive(Deserialize)]
83#[serde(rename_all = "camelCase")]
84struct ImpersonateTokenResponse {
85    pub access_token: String,
86    pub expire_time: String,
87}