1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use time::format_description::well_known::Rfc3339;

use crate::error::Error;
use crate::token::Token;
use crate::token_source::{default_http_client, TokenSource};

#[derive(Debug)]
pub struct ImpersonateTokenSource {
    target: Box<dyn TokenSource>,
    scopes: Vec<String>,
    delegates: Vec<String>,
    url: String,
    lifetime: Option<i32>,
    client: reqwest::Client,
}

impl ImpersonateTokenSource {
    #[allow(dead_code)]
    pub(crate) fn new(
        url: String,
        delegates: Vec<String>,
        scopes: Vec<String>,
        lifetime: Option<i32>,
        target: Box<dyn TokenSource>,
    ) -> Self {
        ImpersonateTokenSource {
            target,
            scopes,
            delegates,
            url,
            lifetime,
            client: default_http_client(),
        }
    }
}

#[async_trait]
impl TokenSource for ImpersonateTokenSource {
    async fn token(&self) -> Result<Token, Error> {
        let body = ImpersonateTokenRequest {
            lifetime: format!("{}s", self.lifetime.unwrap_or(3600)),
            scope: self.scopes.clone(),
            delegates: self.delegates.clone(),
        };

        let auth_token = self.target.token().await?;
        let response = self
            .client
            .post(&self.url)
            .json(&body)
            .header(
                "Authorization",
                format!("{} {}", auth_token.token_type, auth_token.access_token),
            )
            .send()
            .await?;
        let response = if !response.status().is_success() {
            let status = response.status().as_u16();
            return Err(Error::UnexpectedImpersonateTokenResponse(status, response.text().await?));
        } else {
            response.json::<ImpersonateTokenResponse>().await?
        };

        let expiry = time::OffsetDateTime::parse(&response.expire_time, &Rfc3339)?;
        Ok(Token {
            access_token: response.access_token,
            token_type: "Bearer".to_string(),
            expiry: Some(expiry),
        })
    }
}

#[derive(Serialize)]
struct ImpersonateTokenRequest {
    pub delegates: Vec<String>,
    pub lifetime: String,
    pub scope: Vec<String>,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct ImpersonateTokenResponse {
    pub access_token: String,
    pub expire_time: String,
}