rs_firebase_admin_sdk/credentials/
mod.rs1pub mod emulator;
4
5use error_stack::{Report, ResultExt};
6use google_cloud_auth::credentials::{CacheableResource, Credentials};
7use headers::HeaderMapExt;
8use headers::{Header, HeaderName, HeaderValue};
9use http::{Extensions, HeaderMap};
10use std::env::var;
11
12#[derive(thiserror::Error, Debug, Clone)]
13#[error("Failed to extract GCP credentials")]
14pub struct GCPCredentialsError;
15
16static X_GOOG_USER_PROJECT: HeaderName = HeaderName::from_static("x-goog-user-project");
17
18pub struct GoogleUserProject(String);
19
20impl Header for GoogleUserProject {
21 fn name() -> &'static HeaderName {
22 &X_GOOG_USER_PROJECT
23 }
24
25 fn decode<'i, I>(values: &mut I) -> Result<Self, headers::Error>
26 where
27 I: Iterator<Item = &'i HeaderValue>,
28 {
29 let value = values
30 .next()
31 .ok_or_else(headers::Error::invalid)?
32 .as_bytes();
33
34 match std::str::from_utf8(value) {
35 Ok(v) => Ok(Self(v.into())),
36 Err(_) => Err(headers::Error::invalid()),
37 }
38 }
39
40 fn encode<E>(&self, values: &mut E)
41 where
42 E: Extend<HeaderValue>,
43 {
44 let value = HeaderValue::from_str(&self.0).unwrap_or_else(|_| HeaderValue::from_static(""));
45
46 values.extend(std::iter::once(value));
47 }
48}
49
50pub(crate) async fn get_project_id(
51 creds: &Credentials,
52) -> Result<String, Report<GCPCredentialsError>> {
53 if let Ok(project_id) = var("GOOGLE_CLOUD_PROJECT") {
54 return Ok(project_id);
55 }
56
57 let headers = get_headers(creds).await?;
58
59 let user_project: GoogleUserProject = headers
60 .typed_get()
61 .ok_or(Report::new(GCPCredentialsError))?;
62
63 Ok(user_project.0)
64}
65
66pub(crate) async fn get_headers(
67 creds: &Credentials,
68) -> Result<HeaderMap, Report<GCPCredentialsError>> {
69 let headers = creds
70 .headers(Extensions::new())
71 .await
72 .change_context(GCPCredentialsError)?;
73
74 let headers = match headers {
75 CacheableResource::New {
76 entity_tag: _,
77 data,
78 } => data,
79 _ => unreachable!(),
80 };
81
82 Ok(headers)
83}