pub mod google;
use anyhow::anyhow;
use systemprompt_models::services::ProviderEntry;
use systemprompt_security::credential::{
CredentialError, CredentialScope, ProviderCredential, fill_endpoint,
};
use super::DispatchError;
#[derive(Debug, Clone)]
pub(super) struct Credential {
pub(super) value: String,
pub(super) is_bearer: bool,
pub(super) scope: CredentialScope,
}
pub fn fill_project(endpoint: &str, project: Option<&str>) -> Result<String, CredentialError> {
let scope = CredentialScope {
project: project.map(str::to_owned),
..CredentialScope::empty()
};
fill_endpoint(endpoint, &scope)
}
pub(super) async fn resolve(provider: &ProviderEntry) -> Result<Credential, DispatchError> {
let secrets = systemprompt_config::SecretsBootstrap::get()
.map_err(|e| DispatchError::PreAudit(anyhow!("Secrets not available: {e}")))?;
let secret_name = provider.api_key_secret.as_str();
let secret = secrets.get(secret_name).ok_or_else(|| {
DispatchError::PreAudit(anyhow!(
"Gateway API key secret '{secret_name}' not configured"
))
})?;
let credential = ProviderCredential::parse(secret).map_err(|e| {
DispatchError::PreAudit(anyhow!(
"secret '{secret_name}' declares a Google service account but is malformed: {e}"
))
})?;
let header = credential.bearer(secret_name).await.map_err(|e| {
DispatchError::PreAudit(anyhow!(
"could not mint a Google access token from secret '{secret_name}': {e}"
))
})?;
let is_bearer = header.is_bearer();
Ok(Credential {
value: header.value,
is_bearer,
scope: credential.scope(),
})
}