mirror of
https://github.com/openai/codex.git
synced 2026-11-08 20:40:33 +00:00
Add a credential file override for GCP provider authentication (#57188)
## What changed Support `model_providers.gcp.credential_file` as an absolute path to a Google Application Default Credentials (ADC) file. Pass it as `GOOGLE_APPLICATION_CREDENTIALS` in the provider auth command's environment, retaining the override for token refreshes without changing the parent process environment. Validate the path before merging GCP defaults and preserve the setting in thread configuration serialization. ## Testing Add coverage for invalid paths, configuration round trips, separate provider token caches, and credential overrides retained after a `401` refresh. Include a Windows regression test for credential paths containing command metacharacters. GitOrigin-RevId: d9a1d4b3775a9a4c4325dddaa153d92a2d1430a0
This commit is contained in:
@@ -986,6 +986,11 @@ pub fn validate_model_providers(
|
||||
"model_providers.{key}: provider name must not be empty"
|
||||
));
|
||||
}
|
||||
if key == GCP_PROVIDER_ID {
|
||||
provider
|
||||
.validate_gcp_credential_file()
|
||||
.map_err(|message| format!("model_providers.{key}: {message}"))?;
|
||||
}
|
||||
provider
|
||||
.validate()
|
||||
.map_err(|message| format!("model_providers.{key}: {message}"))?;
|
||||
@@ -1159,4 +1164,21 @@ command = "print-token"
|
||||
(Some("https://vertex.example.com/v1"), Some("print-token"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gcp_provider_rejects_relative_credential_file() {
|
||||
for credential_file in ["", "adc.json", "../adc.json"] {
|
||||
let error = toml::from_str::<ConfigToml>(&format!(
|
||||
r#"
|
||||
[model_providers.gcp]
|
||||
credential_file = "{credential_file}"
|
||||
"#,
|
||||
))
|
||||
.expect_err("GCP overrides must validate credential paths before merging defaults");
|
||||
|
||||
assert!(error.to_string().contains(
|
||||
"model_providers.gcp: provider credential_file must be an absolute path"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,6 +319,7 @@ mod tests {
|
||||
capabilities: None,
|
||||
project_id: None,
|
||||
location: None,
|
||||
credential_file: None,
|
||||
include_internal_metadata: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ message ModelProvider {
|
||||
optional string model_catalog_url = 19;
|
||||
optional string project_id = 20;
|
||||
optional string location = 21;
|
||||
optional string credential_file = 22;
|
||||
}
|
||||
|
||||
message StringMap {
|
||||
|
||||
@@ -83,6 +83,8 @@ pub struct ModelProvider {
|
||||
pub project_id: ::core::option::Option<::prost::alloc::string::String>,
|
||||
#[prost(string, optional, tag = "21")]
|
||||
pub location: ::core::option::Option<::prost::alloc::string::String>,
|
||||
#[prost(string, optional, tag = "22")]
|
||||
pub credential_file: ::core::option::Option<::prost::alloc::string::String>,
|
||||
}
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct StringMap {
|
||||
|
||||
@@ -198,6 +198,7 @@ fn model_provider_from_proto(
|
||||
capabilities: None,
|
||||
project_id: provider.project_id,
|
||||
location: provider.location,
|
||||
credential_file: provider.credential_file.map(Into::into),
|
||||
include_internal_metadata: false,
|
||||
};
|
||||
Ok((id, info))
|
||||
@@ -232,6 +233,7 @@ fn model_provider_to_proto(
|
||||
supports_websockets,
|
||||
supports_standalone_web_search,
|
||||
include_internal_metadata: _,
|
||||
credential_file,
|
||||
capabilities: _,
|
||||
} = provider;
|
||||
|
||||
@@ -240,6 +242,7 @@ fn model_provider_to_proto(
|
||||
name,
|
||||
base_url,
|
||||
model_catalog_url: model_catalog_url.map(RedactedString::into_inner),
|
||||
credential_file: credential_file.map(|path| path.to_string_lossy().into_owned()),
|
||||
env_key,
|
||||
env_key_instructions,
|
||||
experimental_bearer_token: experimental_bearer_token.map(RedactedString::into_inner),
|
||||
@@ -454,6 +457,7 @@ mod tests {
|
||||
let mut expected = expected_provider();
|
||||
expected.project_id = Some("vertex-project".to_string());
|
||||
expected.location = Some("us-central1".to_string());
|
||||
expected.credential_file = Some(workspace_dir().join("adc.json").into_path_buf());
|
||||
expected.auth = None;
|
||||
expected.experimental_bearer_token = Some("synthetic-provider-token".into());
|
||||
let proto = model_provider_to_proto("local", expected.clone());
|
||||
@@ -492,6 +496,7 @@ mod tests {
|
||||
model_catalog_url: Some(
|
||||
"http://127.0.0.1:8061/api/codex/models".to_string(),
|
||||
),
|
||||
credential_file: None,
|
||||
env_key: None,
|
||||
env_key_instructions: None,
|
||||
experimental_bearer_token: None,
|
||||
@@ -597,6 +602,7 @@ mod tests {
|
||||
capabilities: None,
|
||||
project_id: None,
|
||||
location: None,
|
||||
credential_file: None,
|
||||
include_internal_metadata: false,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user