reqsign_google/provide_credential/
static_provider.rs1use log::debug;
19
20use reqsign_core::{Context, ProvideCredential, Result, hash::base64_decode};
21
22use crate::credential::Credential;
23
24use super::parse::parse_credential_bytes;
25
26#[derive(Debug, Clone)]
28pub struct StaticCredentialProvider {
29 content: String,
30 scope: Option<String>,
31}
32
33impl StaticCredentialProvider {
34 pub fn new(content: impl Into<String>) -> Self {
36 Self {
37 content: content.into(),
38 scope: None,
39 }
40 }
41
42 pub fn from_base64(content: impl Into<String>) -> Result<Self> {
44 let content = content.into();
45 let decoded = base64_decode(&content).map_err(|e| {
46 reqsign_core::Error::unexpected("failed to decode base64").with_source(e)
47 })?;
48 let json_content = String::from_utf8(decoded).map_err(|e| {
49 reqsign_core::Error::unexpected("invalid UTF-8 in decoded content").with_source(e)
50 })?;
51 Ok(Self {
52 content: json_content,
53 scope: None,
54 })
55 }
56
57 pub fn with_scope(mut self, scope: impl Into<String>) -> Self {
59 self.scope = Some(scope.into());
60 self
61 }
62}
63impl ProvideCredential for StaticCredentialProvider {
64 type Credential = Credential;
65
66 async fn provide_credential(&self, ctx: &Context) -> Result<Option<Self::Credential>> {
67 debug!("loading credential from static content");
68
69 parse_credential_bytes(ctx, self.content.as_bytes(), self.scope.clone())
70 .await
71 .map_err(|err| {
72 debug!("failed to parse credential from content: {err:?}");
73 err
74 })
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use reqsign_core::Context;
82
83 #[tokio::test]
84 async fn test_static_service_account() {
85 let content = r#"{
86 "type": "service_account",
87 "private_key": "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----",
88 "client_email": "test@example.iam.gserviceaccount.com"
89 }"#;
90
91 let provider = StaticCredentialProvider::new(content);
92 let ctx = Context::new()
93 .with_file_read(reqsign_file_read_tokio::TokioFileRead)
94 .with_http_send(reqsign_http_send_reqwest::ReqwestHttpSend::default());
95
96 let result = provider.provide_credential(&ctx).await;
97 assert!(result.is_ok());
98
99 let cred = result.unwrap();
100 assert!(cred.is_some());
101
102 let cred = cred.unwrap();
103 assert!(cred.has_service_account());
104 }
105
106 #[tokio::test]
107 async fn test_static_service_account_from_base64() {
108 let content = r#"{
109 "type": "service_account",
110 "private_key": "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----",
111 "client_email": "test@example.iam.gserviceaccount.com"
112 }"#;
113
114 use reqsign_core::hash::base64_encode;
116 let encoded = base64_encode(content.as_bytes());
117
118 let provider =
119 StaticCredentialProvider::from_base64(encoded).expect("should decode base64");
120 let ctx = Context::new()
121 .with_file_read(reqsign_file_read_tokio::TokioFileRead)
122 .with_http_send(reqsign_http_send_reqwest::ReqwestHttpSend::default());
123
124 let result = provider.provide_credential(&ctx).await;
125 assert!(result.is_ok());
126
127 let cred = result.unwrap();
128 assert!(cred.is_some());
129
130 let cred = cred.unwrap();
131 assert!(cred.has_service_account());
132 }
133}