google_cloud_default/
lib.rs

1//! # google-cloud-default
2//!
3//! Google Cloud Platform default configuration for google-cloud-rust.
4//!
5//! ## Quick Start
6//!
7//! * [pubsub](https://github.com/yoshidan/google-cloud-rust/tree/main/pubsub)
8//! * [spanner](https://github.com/yoshidan/google-cloud-rust/tree/main/spanner)
9//! * [storage](https://github.com/yoshidan/google-cloud-rust/tree/main/storage)
10//! * [bigquery](https://github.com/yoshidan/google-cloud-rust/tree/main/bigquery)
11//!
12use async_trait::async_trait;
13
14use google_cloud_auth::error::Error;
15
16#[async_trait]
17pub trait WithAuthExt {
18    async fn with_auth(mut self) -> Result<Self, Error>
19    where
20        Self: Sized;
21
22    async fn with_credentials(
23        self,
24        credentials: google_cloud_auth::credentials::CredentialsFile,
25    ) -> Result<Self, Error>
26    where
27        Self: Sized;
28}
29
30#[cfg(feature = "pubsub")]
31#[async_trait]
32impl WithAuthExt for google_cloud_pubsub::client::ClientConfig {
33    async fn with_auth(mut self) -> Result<Self, Error> {
34        if let google_cloud_gax::conn::Environment::GoogleCloud(_) = self.environment {
35            let ts = google_cloud_auth::token::DefaultTokenSourceProvider::new(google_cloud_auth::project::Config {
36                audience: Some(google_cloud_pubsub::apiv1::conn_pool::AUDIENCE),
37                scopes: Some(&google_cloud_pubsub::apiv1::conn_pool::SCOPES),
38                sub: None,
39            })
40            .await?;
41            self.project_id = ts.project_id.clone();
42            self.environment = google_cloud_gax::conn::Environment::GoogleCloud(Box::new(ts))
43        }
44        Ok(self)
45    }
46
47    async fn with_credentials(
48        mut self,
49        credentials: google_cloud_auth::credentials::CredentialsFile,
50    ) -> Result<Self, Error> {
51        if let google_cloud_gax::conn::Environment::GoogleCloud(_) = self.environment {
52            let ts = google_cloud_auth::token::DefaultTokenSourceProvider::new_with_credentials(
53                google_cloud_auth::project::Config {
54                    audience: Some(google_cloud_pubsub::apiv1::conn_pool::AUDIENCE),
55                    scopes: Some(&google_cloud_pubsub::apiv1::conn_pool::SCOPES),
56                    sub: None,
57                },
58                Box::new(credentials),
59            )
60            .await?;
61            self.project_id = ts.project_id.clone();
62            self.environment = google_cloud_gax::conn::Environment::GoogleCloud(Box::new(ts))
63        }
64
65        Ok(self)
66    }
67}
68
69#[cfg(feature = "spanner")]
70#[async_trait]
71impl WithAuthExt for google_cloud_spanner::client::ClientConfig {
72    async fn with_auth(mut self) -> Result<Self, Error> {
73        if let google_cloud_gax::conn::Environment::GoogleCloud(_) = self.environment {
74            let ts = google_cloud_auth::token::DefaultTokenSourceProvider::new(google_cloud_auth::project::Config {
75                audience: Some(google_cloud_spanner::apiv1::conn_pool::AUDIENCE),
76                scopes: Some(&google_cloud_spanner::apiv1::conn_pool::SCOPES),
77                sub: None,
78            })
79            .await?;
80            self.environment = google_cloud_gax::conn::Environment::GoogleCloud(Box::new(ts))
81        }
82        Ok(self)
83    }
84
85    async fn with_credentials(
86        mut self,
87        credentials: google_cloud_auth::credentials::CredentialsFile,
88    ) -> Result<Self, Error> {
89        if let google_cloud_gax::conn::Environment::GoogleCloud(_) = self.environment {
90            let ts = google_cloud_auth::token::DefaultTokenSourceProvider::new_with_credentials(
91                google_cloud_auth::project::Config {
92                    audience: Some(google_cloud_spanner::apiv1::conn_pool::AUDIENCE),
93                    scopes: Some(&google_cloud_spanner::apiv1::conn_pool::SCOPES),
94                    sub: None,
95                },
96                Box::new(credentials),
97            )
98            .await?;
99            self.environment = google_cloud_gax::conn::Environment::GoogleCloud(Box::new(ts))
100        }
101        Ok(self)
102    }
103}
104
105#[cfg(feature = "storage")]
106#[async_trait]
107impl WithAuthExt for google_cloud_storage::client::ClientConfig {
108    async fn with_auth(mut self) -> Result<Self, Error> {
109        let ts = google_cloud_auth::token::DefaultTokenSourceProvider::new(google_cloud_auth::project::Config {
110            audience: None,
111            scopes: Some(&google_cloud_storage::http::storage_client::SCOPES),
112            sub: None,
113        })
114        .await?;
115
116        match &ts.source_credentials {
117            // Credential file is used.
118            Some(cred) => {
119                self.project_id = cred.project_id.clone();
120                if let Some(pk) = &cred.private_key {
121                    self.default_sign_by =
122                        Some(google_cloud_storage::sign::SignBy::PrivateKey(pk.clone().into_bytes()));
123                }
124                self.default_google_access_id = cred.client_email.clone();
125            }
126            // On Google Cloud
127            None => {
128                self.project_id = Some(google_cloud_metadata::project_id().await);
129                self.default_sign_by = Some(google_cloud_storage::sign::SignBy::SignBytes);
130                self.default_google_access_id = google_cloud_metadata::email("default").await.ok();
131            }
132        }
133
134        self.token_source_provider = Box::new(ts);
135        Ok(self)
136    }
137
138    async fn with_credentials(
139        mut self,
140        credentials: google_cloud_auth::credentials::CredentialsFile,
141    ) -> Result<Self, Error> {
142        let ts = google_cloud_auth::token::DefaultTokenSourceProvider::new_with_credentials(
143            google_cloud_auth::project::Config {
144                audience: None,
145                scopes: Some(&google_cloud_storage::http::storage_client::SCOPES),
146                sub: None,
147            },
148            Box::new(credentials),
149        )
150        .await?;
151
152        match &ts.source_credentials {
153            // Credential file is used.
154            Some(cred) => {
155                self.project_id = cred.project_id.clone();
156                if let Some(pk) = &cred.private_key {
157                    self.default_sign_by =
158                        Some(google_cloud_storage::sign::SignBy::PrivateKey(pk.clone().into_bytes()));
159                }
160                self.default_google_access_id = cred.client_email.clone();
161            }
162            // On Google Cloud
163            None => {
164                self.project_id = Some(google_cloud_metadata::project_id().await);
165                self.default_sign_by = Some(google_cloud_storage::sign::SignBy::SignBytes);
166                self.default_google_access_id = google_cloud_metadata::email("default").await.ok();
167            }
168        }
169
170        self.token_source_provider = Box::new(ts);
171        Ok(self)
172    }
173}
174
175#[cfg(feature = "bigquery")]
176pub mod bigquery {
177    use async_trait::async_trait;
178    use google_cloud_auth::credentials::CredentialsFile;
179    use google_cloud_auth::error::Error;
180    use google_cloud_auth::project::Config;
181    use google_cloud_auth::token::DefaultTokenSourceProvider;
182    use google_cloud_bigquery::client::ClientConfig;
183
184    #[async_trait]
185    pub trait CreateAuthExt {
186        async fn new_with_auth() -> Result<(Self, Option<String>), Error>
187        where
188            Self: Sized;
189
190        async fn new_with_credentials(credentials: CredentialsFile) -> Result<(Self, Option<String>), Error>
191        where
192            Self: Sized;
193    }
194
195    #[async_trait]
196    impl CreateAuthExt for ClientConfig {
197        async fn new_with_auth() -> Result<(Self, Option<String>), Error> {
198            let ts_http = DefaultTokenSourceProvider::new(bigquery_http_auth_config()).await?;
199            let ts_grpc = DefaultTokenSourceProvider::new(bigquery_grpc_auth_config()).await?;
200            let project_id = ts_grpc.project_id.clone();
201            let config = Self::new(Box::new(ts_http), Box::new(ts_grpc));
202            Ok((config, project_id))
203        }
204        async fn new_with_credentials(credentials: CredentialsFile) -> Result<(Self, Option<String>), Error>
205        where
206            Self: Sized,
207        {
208            let ts_http = DefaultTokenSourceProvider::new_with_credentials(
209                bigquery_http_auth_config(),
210                Box::new(credentials.clone()),
211            )
212            .await?;
213            let ts_grpc =
214                DefaultTokenSourceProvider::new_with_credentials(bigquery_grpc_auth_config(), Box::new(credentials))
215                    .await?;
216            let project_id = ts_grpc.project_id.clone();
217            let config = Self::new(Box::new(ts_http), Box::new(ts_grpc));
218            Ok((config, project_id))
219        }
220    }
221
222    #[cfg(feature = "bigquery")]
223    fn bigquery_http_auth_config() -> Config<'static> {
224        Config {
225            audience: None,
226            scopes: Some(&google_cloud_bigquery::http::bigquery_client::SCOPES),
227            sub: None,
228        }
229    }
230
231    #[cfg(feature = "bigquery")]
232    fn bigquery_grpc_auth_config() -> Config<'static> {
233        Config {
234            audience: Some(google_cloud_bigquery::grpc::apiv1::conn_pool::AUDIENCE),
235            scopes: Some(&google_cloud_bigquery::grpc::apiv1::conn_pool::SCOPES),
236            sub: None,
237        }
238    }
239}
240
241#[cfg(test)]
242mod test {
243    use google_cloud_gax::conn::Environment;
244
245    use crate::WithAuthExt;
246
247    #[tokio::test]
248    async fn test_spanner() {
249        let config = google_cloud_spanner::client::ClientConfig::default()
250            .with_auth()
251            .await
252            .unwrap();
253        if let Environment::Emulator(_) = config.environment {
254            unreachable!()
255        }
256    }
257
258    #[tokio::test]
259    async fn test_pubsub() {
260        let config = google_cloud_pubsub::client::ClientConfig::default()
261            .with_auth()
262            .await
263            .unwrap();
264        if let Environment::Emulator(_) = config.environment {
265            unreachable!()
266        }
267    }
268
269    #[tokio::test]
270    async fn test_storage() {
271        let config = google_cloud_storage::client::ClientConfig::default()
272            .with_auth()
273            .await
274            .unwrap();
275        assert!(config.default_google_access_id.is_some());
276        assert!(config.default_sign_by.is_some());
277    }
278
279    #[tokio::test]
280    async fn test_bigquery() {
281        use crate::bigquery::CreateAuthExt;
282        let (_config, project_id) = google_cloud_bigquery::client::ClientConfig::new_with_auth()
283            .await
284            .unwrap();
285        assert!(project_id.is_some())
286    }
287}