mirror of
https://github.com/openai/codex.git
synced 2026-10-22 17:58:46 +00:00
## What changed - Read optional workload identity context from `OPENAI_WORKLOAD_IDENTITY_CONTEXT` and forward it unchanged as the `workload_identity_context` token exchange field. - Treat the context as sensitive by redacting it from session configuration debug output and removing it from model-reachable child environments. - Include the context in workload identity session fingerprints so sessions with different values cannot share an exchange. ## Testing - Cover request forwarding, debug redaction, session compatibility, and child-environment scrubbing. GitOrigin-RevId: fb50700478cf54d9d604944a4ed3e77acc928a0f
91 lines
3.1 KiB
Rust
91 lines
3.1 KiB
Rust
mod assertion;
|
|
mod exchange;
|
|
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
|
|
pub use exchange::WorkloadIdentityExchange;
|
|
pub use exchange::WorkloadIdentityToken;
|
|
use thiserror::Error;
|
|
|
|
/// The inputs needed to exchange a file-backed assertion for ChatGPT auth.
|
|
#[derive(Clone)]
|
|
pub struct WorkloadIdentityConfig {
|
|
pub(crate) assertion_file: PathBuf,
|
|
pub(crate) federation_rule_id: String,
|
|
pub(crate) workload_identity_context: Option<String>,
|
|
}
|
|
|
|
impl WorkloadIdentityConfig {
|
|
pub fn new(
|
|
federation_rule_id: String,
|
|
assertion_file: PathBuf,
|
|
workload_identity_context: Option<String>,
|
|
) -> Result<Self, WorkloadIdentityError> {
|
|
let federation_rule_id = federation_rule_id.trim();
|
|
if federation_rule_id.is_empty() {
|
|
return Err(WorkloadIdentityError::InvalidFederationRuleId);
|
|
}
|
|
if !assertion_file.is_absolute() {
|
|
return Err(WorkloadIdentityError::AssertionFileMustBeAbsolute);
|
|
}
|
|
Ok(Self {
|
|
assertion_file,
|
|
federation_rule_id: federation_rule_id.to_string(),
|
|
workload_identity_context,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Error)]
|
|
pub enum WorkloadIdentityError {
|
|
#[error("the workload identity federation rule ID must not be empty")]
|
|
InvalidFederationRuleId,
|
|
#[error("the workload identity assertion file path must be absolute")]
|
|
AssertionFileMustBeAbsolute,
|
|
#[error("the workload identity assertion is invalid")]
|
|
InvalidAssertion,
|
|
#[error("the workload identity assertion exceeds 16 KiB")]
|
|
AssertionTooLarge,
|
|
#[error("could not read workload identity assertion file {path}")]
|
|
AssertionFile {
|
|
path: PathBuf,
|
|
#[source]
|
|
source: Arc<std::io::Error>,
|
|
},
|
|
#[error("could not configure the workload identity HTTP client")]
|
|
HttpClientConfiguration,
|
|
#[error("the workload identity token URL must use HTTPS or loopback HTTP")]
|
|
InvalidTokenUrl,
|
|
#[error("the workload identity token exchange is unavailable")]
|
|
ExchangeUnavailable,
|
|
#[error("the workload identity token exchange was rejected with HTTP {0}")]
|
|
ExchangeRejected(u16),
|
|
#[error("the workload identity token exchange returned an invalid response")]
|
|
InvalidExchangeResponse,
|
|
}
|
|
|
|
impl WorkloadIdentityError {
|
|
/// Whether retrying the operation may succeed without changing configuration.
|
|
pub fn is_transient(&self) -> bool {
|
|
match self {
|
|
Self::AssertionFile { source, .. } => matches!(
|
|
source.kind(),
|
|
std::io::ErrorKind::Interrupted
|
|
| std::io::ErrorKind::NotFound
|
|
| std::io::ErrorKind::TimedOut
|
|
| std::io::ErrorKind::WouldBlock
|
|
),
|
|
Self::ExchangeUnavailable | Self::ExchangeRejected(408 | 429 | 500..=599) => true,
|
|
Self::InvalidFederationRuleId
|
|
| Self::AssertionFileMustBeAbsolute
|
|
| Self::InvalidAssertion
|
|
| Self::AssertionTooLarge
|
|
| Self::HttpClientConfiguration
|
|
| Self::InvalidTokenUrl
|
|
| Self::ExchangeRejected(_)
|
|
| Self::InvalidExchangeResponse => false,
|
|
}
|
|
}
|
|
}
|