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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use std::collections::HashMap;
use crate::http::Error;
use google_cloud_auth::token_source::TokenSource;
use reqwest::Client;
use std::sync::Arc;
pub struct ServiceAccountClient {
ts: Arc<dyn TokenSource>,
v1_endpoint: String,
}
impl ServiceAccountClient {
pub(crate) fn new(ts: Arc<dyn TokenSource>, endpoint: &str) -> Self {
Self {
ts,
v1_endpoint: format!("{}/v1", endpoint),
}
}
#[cfg(feature = "trace")]
#[tracing::instrument(skip_all)]
pub async fn sign_blob(&self, name: &str, data: &[u8]) -> Result<Vec<u8>, Error> {
self._sign_blob(name, data).await
}
#[cfg(not(feature = "trace"))]
pub async fn sign_blob(&self, name: &str, data: &[u8]) -> Result<Vec<u8>, Error> {
self._sign_blob(name, data).await
}
async fn _sign_blob(&self, name: &str, data: &[u8]) -> Result<Vec<u8>, Error> {
let url = format!("{}/{}:signBlob", self.v1_endpoint, name);
let json_request = format!(r#"{{"payload": "{}"}}"#, base64::encode(data));
let token = self.ts.token().await?;
let request = Client::default()
.post(url)
.body(json_request)
.header("X-Goog-Api-Client", "rust")
.header(reqwest::header::USER_AGENT, "google-cloud-storage")
.header(reqwest::header::AUTHORIZATION, token.value());
let response = request.send().await?;
let status = response.status();
if status.is_success() {
let body = response.json::<HashMap<String, String>>().await?;
match body.get("signedBlob") {
Some(v) => Ok(base64::decode(v)?),
None => Err(Error::Response(status.as_u16(), "no signedBlob found".to_string())),
}
} else {
Err(Error::Response(status.as_u16(), response.text().await?))
}
}
}
#[cfg(test)]
mod test {
use crate::http::service_account_client::ServiceAccountClient;
use google_cloud_auth::{create_token_source, Config};
use serial_test::serial;
use std::sync::Arc;
#[ctor::ctor]
fn init() {
let _ = tracing_subscriber::fmt::try_init();
}
async fn client() -> ServiceAccountClient {
let ts = create_token_source(Config {
audience: None,
scopes: Some(&["https://www.googleapis.com/auth/cloud-platform"]),
})
.await
.unwrap();
ServiceAccountClient::new(Arc::from(ts), "https://iamcredentials.googleapis.com")
}
#[tokio::test]
#[serial]
pub async fn sign_blob_test() {
let client = client().await;
let body = vec![
71, 79, 79, 71, 52, 45, 82, 83, 65, 45, 83, 72, 65, 50, 53, 54, 10, 50, 48, 50, 50, 48, 55, 48, 57, 84, 50,
51, 52, 56, 48, 56, 90, 10, 50, 48, 50, 50, 48, 55, 48, 57, 47, 97, 117, 116, 111, 47, 115, 116, 111, 114,
97, 103, 101, 47, 103, 111, 111, 103, 52, 95, 114, 101, 113, 117, 101, 115, 116, 10, 98, 101, 97, 48, 48,
49, 100, 98, 48, 50, 97, 56, 98, 55, 101, 101, 54, 50, 102, 50, 54, 53, 99, 101, 50, 52, 54, 53, 51, 49,
97, 98, 50, 54, 101, 102, 49, 97, 48, 97, 99, 100, 102, 102, 55, 99, 54, 55, 49, 100, 101, 56, 49, 100, 56,
56, 98, 50, 56, 101, 55, 48, 98, 101,
];
let data = client
.sign_blob(
"projects/-/serviceAccounts/rust-storage-test@atl-dev1.iam.gserviceaccount.com",
&body,
)
.await
.unwrap();
assert_eq!(256, data.len());
}
}