fix(client): For ADC, send x-goog-user-project header (#215)
* fix(client): send x-goog-user-project header from ADC quota project When using Application Default Credentials with a quota_project_id set, API requests failed with 403 because the quota project header was never sent. Read quota_project_id from ADC and set it as a default header. * Update src/auth.rs Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@googleworkspace/cli": patch
|
||||
---
|
||||
|
||||
Send x-goog-user-project header when using ADC with a quota_project_id
|
||||
+30
@@ -24,6 +24,20 @@ use anyhow::Context;
|
||||
|
||||
use crate::credential_store;
|
||||
|
||||
/// Returns the `quota_project_id` from Application Default Credentials, if present.
|
||||
/// This is used to set the `x-goog-user-project` header on API requests.
|
||||
pub fn get_quota_project() -> Option<String> {
|
||||
let path = std::env::var("GOOGLE_APPLICATION_CREDENTIALS")
|
||||
.ok()
|
||||
.map(PathBuf::from)
|
||||
.or_else(adc_well_known_path)?;
|
||||
let content = std::fs::read_to_string(path).ok()?;
|
||||
let json: serde_json::Value = serde_json::from_str(&content).ok()?;
|
||||
json.get("quota_project_id")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string())
|
||||
}
|
||||
|
||||
/// Returns the well-known Application Default Credentials path:
|
||||
/// `~/.config/gcloud/application_default_credentials.json`.
|
||||
///
|
||||
@@ -710,4 +724,20 @@ mod tests {
|
||||
.to_string()
|
||||
.contains("No credentials found"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial_test::serial]
|
||||
fn test_get_quota_project_reads_adc() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let adc_dir = tmp.path().join(".config").join("gcloud");
|
||||
std::fs::create_dir_all(&adc_dir).unwrap();
|
||||
std::fs::write(
|
||||
adc_dir.join("application_default_credentials.json"),
|
||||
r#"{"quota_project_id": "my-project-123"}"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let _home_guard = EnvVarGuard::set("HOME", tmp.path());
|
||||
assert_eq!(get_quota_project(), Some("my-project-123".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,13 @@ pub fn build_client() -> Result<reqwest::Client, crate::error::GwsError> {
|
||||
headers.insert("x-goog-api-client", header_value);
|
||||
}
|
||||
|
||||
// Set quota project from ADC for billing/quota attribution
|
||||
if let Some(quota_project) = crate::auth::get_quota_project() {
|
||||
if let Ok(header_value) = HeaderValue::from_str("a_project) {
|
||||
headers.insert("x-goog-user-project", header_value);
|
||||
}
|
||||
}
|
||||
|
||||
reqwest::Client::builder()
|
||||
.default_headers(headers)
|
||||
.build()
|
||||
|
||||
Reference in New Issue
Block a user