1use crate::token_cache::CachedTokenProvider;
5use crate::{error::Error, jwt};
6
7pub mod end_user;
8pub mod metadata_server;
9pub mod service_account;
10
11use end_user as eu;
12use metadata_server as ms;
13use service_account as sa;
14
15pub use crate::id_token::{
16 AccessTokenResponse, IdToken, IdTokenOrRequest, IdTokenProvider, IdTokenRequest,
17 IdTokenResponse,
18};
19pub use crate::token::{Token, TokenOrRequest, TokenProvider};
20pub use {
21 end_user::{EndUserCredentials, EndUserCredentialsInfo},
22 metadata_server::MetadataServerProvider,
23 service_account::{ServiceAccountInfo, ServiceAccountProvider},
24};
25
26#[derive(serde::Deserialize, Debug)]
29struct TokenResponse {
30 access_token: String,
32 token_type: String,
34 expires_in: i64,
36}
37
38pub type TokenProviderWrapper = CachedTokenProvider<TokenProviderWrapperInner>;
39impl TokenProviderWrapper {
40 pub fn get_default_provider() -> Result<Option<Self>, Error> {
58 TokenProviderWrapperInner::get_default_provider()
59 .map(|provider| provider.map(CachedTokenProvider::wrap))
60 }
61
62 pub fn kind(&self) -> &'static str {
64 self.inner().kind()
65 }
66
67 pub fn is_service_account_provider(&self) -> bool {
68 self.inner().is_service_account_provider()
69 }
70 pub fn is_metadata_server_provider(&self) -> bool {
71 self.inner().is_metadata_server_provider()
72 }
73 pub fn is_end_user_credentials_provider(&self) -> bool {
74 self.inner().is_end_user_credentials_provider()
75 }
76}
77
78#[derive(Debug)]
81pub enum TokenProviderWrapperInner {
82 EndUser(eu::EndUserCredentialsInner),
83 Metadata(ms::MetadataServerProviderInner),
84 ServiceAccount(sa::ServiceAccountProviderInner),
85}
86
87impl TokenProviderWrapperInner {
88 pub fn get_default_provider() -> Result<Option<Self>, Error> {
92 use std::{fs::read_to_string, path::PathBuf};
93
94 if let Some(cred_path) = std::env::var_os("GOOGLE_APPLICATION_CREDENTIALS") {
97 let key_data = match read_to_string(&cred_path) {
98 Ok(kd) => kd,
99 Err(e) => {
100 return Err(Error::InvalidCredentials {
101 file: cred_path.into(),
102 error: Box::new(Error::Io(e)),
103 });
104 }
105 };
106
107 let sa_info = match sa::ServiceAccountInfo::deserialize(key_data) {
108 Ok(si) => si,
109 Err(e) => {
110 return Err(Error::InvalidCredentials {
111 file: cred_path.into(),
112 error: Box::new(e),
113 });
114 }
115 };
116
117 return Ok(Some(TokenProviderWrapperInner::ServiceAccount(
118 sa::ServiceAccountProviderInner::new(sa_info).map_err(|e| {
119 Error::InvalidCredentials {
120 file: cred_path.into(),
121 error: Box::new(e),
122 }
123 })?,
124 )));
125 }
126
127 fn gcloud_config_file() -> Option<PathBuf> {
132 let cred_file = "application_default_credentials.json";
133
134 if let Some(override_dir) = std::env::var_os("CLOUDSDK_CONFIG") {
136 let mut pb = PathBuf::from(override_dir);
137 pb.push(cred_file);
138 return Some(pb);
139 }
140
141 if cfg!(windows) {
145 std::env::var_os("APPDATA").map(PathBuf::from)
146 } else {
147 std::env::var_os("HOME").map(|pb| {
148 let mut pb = PathBuf::from(pb);
149 pb.push(".config");
150 pb
151 })
152 }
153 .map(|mut bd| {
154 bd.push("gcloud");
155 bd.push(cred_file);
156 bd
157 })
158 }
159
160 if let Some(gcloud_file) = gcloud_config_file() {
161 match read_to_string(&gcloud_file) {
162 Ok(json_data) => {
163 let end_user_credentials = eu::EndUserCredentialsInfo::deserialize(json_data)
164 .map_err(|e| Error::InvalidCredentials {
165 file: gcloud_file,
166 error: Box::new(e),
167 })?;
168
169 return Ok(Some(TokenProviderWrapperInner::EndUser(
170 eu::EndUserCredentialsInner::new(end_user_credentials),
171 )));
172 }
173 Err(nf) if nf.kind() == std::io::ErrorKind::NotFound => {}
175 Err(err) => {
176 return Err(Error::InvalidCredentials {
177 file: gcloud_file,
178 error: Box::new(Error::Io(err)),
179 });
180 }
181 }
182 }
183
184 if let Ok(full_name) = read_to_string("/sys/class/dmi/id/product_name") {
187 let trimmed = full_name.trim();
189 match trimmed {
190 "Google" | "Google Compute Engine" => {
193 return Ok(Some(TokenProviderWrapperInner::Metadata(
194 ms::MetadataServerProviderInner::new(None),
195 )));
196 }
197 _ => {}
198 }
199 }
200
201 Ok(None)
203 }
204
205 pub fn kind(&self) -> &'static str {
207 match self {
208 Self::EndUser(_) => "End User",
209 Self::Metadata(_) => "Metadata Server",
210 Self::ServiceAccount(_) => "Service Account",
211 }
212 }
213
214 pub fn is_service_account_provider(&self) -> bool {
215 matches!(self, TokenProviderWrapperInner::ServiceAccount(_))
216 }
217 pub fn is_metadata_server_provider(&self) -> bool {
218 matches!(self, TokenProviderWrapperInner::Metadata(_))
219 }
220 pub fn is_end_user_credentials_provider(&self) -> bool {
221 matches!(self, TokenProviderWrapperInner::EndUser(_))
222 }
223}
224
225impl TokenProvider for TokenProviderWrapperInner {
226 fn get_token_with_subject<'a, S, I, T>(
227 &self,
228 subject: Option<T>,
229 scopes: I,
230 ) -> Result<TokenOrRequest, Error>
231 where
232 S: AsRef<str> + 'a,
233 I: IntoIterator<Item = &'a S> + Clone,
234 T: Into<String>,
235 {
236 match self {
237 Self::EndUser(token_provider) => token_provider.get_token_with_subject(subject, scopes),
238 Self::Metadata(token_provider) => {
239 token_provider.get_token_with_subject(subject, scopes)
240 }
241 Self::ServiceAccount(token_provider) => {
242 token_provider.get_token_with_subject(subject, scopes)
243 }
244 }
245 }
246
247 fn parse_token_response<S>(
248 &self,
249 hash: u64,
250 response: http::Response<S>,
251 ) -> Result<Token, Error>
252 where
253 S: AsRef<[u8]>,
254 {
255 match self {
256 Self::EndUser(token_provider) => token_provider.parse_token_response(hash, response),
257 Self::Metadata(token_provider) => token_provider.parse_token_response(hash, response),
258 Self::ServiceAccount(token_provider) => {
259 token_provider.parse_token_response(hash, response)
260 }
261 }
262 }
263}
264
265impl IdTokenProvider for TokenProviderWrapperInner {
266 fn get_id_token(&self, audience: &str) -> Result<IdTokenOrRequest, Error> {
267 match self {
268 Self::EndUser(token_provider) => token_provider.get_id_token(audience),
269 Self::Metadata(token_provider) => token_provider.get_id_token(audience),
270 Self::ServiceAccount(token_provider) => token_provider.get_id_token(audience),
271 }
272 }
273
274 fn get_id_token_with_access_token<S>(
275 &self,
276 audience: &str,
277 response: AccessTokenResponse<S>,
278 ) -> Result<IdTokenRequest, Error>
279 where
280 S: AsRef<[u8]>,
281 {
282 match self {
283 Self::EndUser(token_provider) => {
284 token_provider.get_id_token_with_access_token(audience, response)
285 }
286 Self::Metadata(token_provider) => {
287 token_provider.get_id_token_with_access_token(audience, response)
288 }
289 Self::ServiceAccount(token_provider) => {
290 token_provider.get_id_token_with_access_token(audience, response)
291 }
292 }
293 }
294
295 fn parse_id_token_response<S>(
296 &self,
297 hash: u64,
298 response: http::Response<S>,
299 ) -> Result<IdToken, Error>
300 where
301 S: AsRef<[u8]>,
302 {
303 match self {
304 Self::EndUser(token_provider) => token_provider.parse_id_token_response(hash, response),
305 Self::Metadata(token_provider) => {
306 token_provider.parse_id_token_response(hash, response)
307 }
308 Self::ServiceAccount(token_provider) => {
309 token_provider.parse_id_token_response(hash, response)
310 }
311 }
312 }
313}
314
315impl From<TokenResponse> for Token {
316 fn from(tr: TokenResponse) -> Self {
317 Self {
318 access_token: tr.access_token,
319 token_type: tr.token_type,
320 refresh_token: String::new(),
321 expires_in: Some(tr.expires_in),
322 expires_in_timestamp: std::time::SystemTime::now()
323 .checked_add(std::time::Duration::from_secs(tr.expires_in as u64)),
324 }
325 }
326}