gcloud_auth/token_source/
impersonate_token_source.rs1use 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}