pub mod http_client {
use std::sync::LazyLock;
use manta_backend_dispatcher::error::Error;
use serde_json::{Value, json};
const VAULT_API_PREFIX: &str = "/v1";
const VAULT_SECRET_PATH_PREFIX: &str = "manta/data";
const VAULT_ROLE: &str = "manta";
static VAULT_HTTP_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
reqwest::Client::builder()
.build()
.expect("default reqwest::ClientBuilder build cannot fail")
});
pub async fn auth_oidc_jwt(
vault_base_url: &str,
shasta_token: &str,
site_name: &str,
) -> Result<String, Error> {
let role = VAULT_ROLE;
let api_url = format!(
"{vault_base_url}{VAULT_API_PREFIX}/auth/jwt-manta-{site_name}/login"
);
tracing::debug!("Accessing/login to {}", api_url);
let request_payload = json!({ "jwt": shasta_token, "role": role });
let resp = VAULT_HTTP_CLIENT
.post(api_url)
.header("X-Vault-Request", "true")
.json(&request_payload)
.send()
.await?
.error_for_status()?;
let resp_value = resp.json::<Value>().await?;
let client_token = resp_value["auth"]
.get("client_token")
.and_then(Value::as_str)
.ok_or_else(|| {
Error::MissingField(
"Vault auth response missing 'client_token' field".to_string(),
)
})?;
Ok(client_token.to_string())
}
pub async fn get_secret(
vault_auth_token: &str,
vault_base_url: &str,
secret_path: &str,
) -> Result<Value, Error> {
let api_url = vault_base_url.to_owned() + secret_path;
tracing::debug!("Vault url to fetch VCS secrets is '{}'", api_url);
let resp = VAULT_HTTP_CLIENT
.get(api_url)
.header("X-Vault-Token", vault_auth_token)
.send()
.await?
.error_for_status()?;
let secret_value: Value = resp.json().await?;
Ok(secret_value["data"].clone())
}
pub async fn get_shasta_vcs_token(
shasta_token: &str,
vault_base_url: &str,
site_name: &str,
) -> Result<String, Error> {
let vault_token =
auth_oidc_jwt(vault_base_url, shasta_token, site_name).await?;
let vault_secret_path = format!("{VAULT_SECRET_PATH_PREFIX}/{site_name}");
let vault_secret = get_secret(
&vault_token,
vault_base_url,
&format!("{VAULT_API_PREFIX}/{vault_secret_path}/vcs"),
)
.await?;
let vcs_token = vault_secret["data"]
.get("token")
.and_then(Value::as_str)
.ok_or_else(|| {
Error::MissingField(
"Vault secret response missing 'token' field".to_string(),
)
})?;
Ok(vcs_token.to_string())
}
}