use axum::{
Json,
extract::State,
http::StatusCode,
response::{IntoResponse, Response},
};
use crate::crypto::{SealedEnvelope, sealed_seal};
use crate::error::{AppError, AppResult};
use crate::secrets::broker::CrossRegionResolveRequest;
use crate::secrets::server_region;
use crate::services::credential::CredentialService;
#[derive(Clone)]
pub struct CrossRegionDeps {
pub credentials: CredentialService,
}
pub async fn resolve(
State(deps): State<CrossRegionDeps>,
Json(body): Json<CrossRegionResolveRequest>,
) -> AppResult<Json<SealedEnvelope>> {
let span = tracing::info_span!(
"credential.cross_region_resolve",
alias = %body.alias,
worker_id = %body.worker_id,
execution_id = body.execution_id,
expected_entry_region = %body.expected_entry_region,
requesting_region = %body.requesting_region,
);
let _g = span.enter();
let my_region = server_region();
if body.expected_entry_region != my_region {
crate::metrics::record_cross_region_broker_call(
&body.expected_entry_region,
"wrong_region",
);
return Err(AppError::Forbidden(format!(
"cross-region broker: this server is in region '{}', not '{}' as the \
requesting peer expected",
my_region, body.expected_entry_region
)));
}
let pubkey_bytes = decode_pubkey(&body.worker_public_key_b64).map_err(|e| {
crate::metrics::record_cross_region_broker_call(my_region, "bad_pubkey");
AppError::BadRequest(format!("cross-region: invalid worker_public_key_b64: {e}"))
})?;
let recipient = x25519_dalek::PublicKey::from(pubkey_bytes);
let credential = deps
.credentials
.get(&body.alias, true, body.execution_id)
.await
.map_err(|e| {
crate::metrics::record_cross_region_broker_call(my_region, "resolve_error");
e
})?;
let plaintext = serde_json::to_vec(&credential).map_err(|e| {
crate::metrics::record_cross_region_broker_call(my_region, "serialize_error");
AppError::Internal(format!("cross-region: serialise credential: {e}"))
})?;
let envelope = sealed_seal(&recipient, &plaintext).map_err(|e| {
crate::metrics::record_cross_region_broker_call(my_region, "seal_error");
e
})?;
crate::metrics::record_cross_region_broker_call(my_region, "ok");
Ok(Json(envelope))
}
fn decode_pubkey(s: &str) -> Result<[u8; 32], String> {
use base64::Engine as _;
let raw = base64::engine::general_purpose::STANDARD
.decode(s.trim())
.map_err(|e| format!("base64 decode: {e}"))?;
if raw.len() != 32 {
return Err(format!("expected 32 bytes, got {}", raw.len()));
}
let mut out = [0u8; 32];
out.copy_from_slice(&raw);
Ok(out)
}
impl IntoResponse for CrossRegionDeps {
fn into_response(self) -> Response {
(
StatusCode::INTERNAL_SERVER_ERROR,
"CrossRegionDeps is state-only",
)
.into_response()
}
}
#[cfg(test)]
mod tests {
use super::*;
use x25519_dalek::{PublicKey, StaticSecret};
#[test]
fn decode_pubkey_round_trips_x25519() {
let sk = StaticSecret::random_from_rng(rand_core::OsRng);
let pk = PublicKey::from(&sk);
let b64 = {
use base64::Engine as _;
base64::engine::general_purpose::STANDARD.encode(pk.as_bytes())
};
let decoded = decode_pubkey(&b64).expect("decode ok");
assert_eq!(decoded, *pk.as_bytes());
}
#[test]
fn decode_pubkey_rejects_wrong_length() {
let raw = vec![0u8; 31];
let b64 = {
use base64::Engine as _;
base64::engine::general_purpose::STANDARD.encode(&raw)
};
let err = decode_pubkey(&b64).expect_err("must reject");
assert!(err.contains("expected 32 bytes"), "wrong error: {err}");
}
#[test]
fn decode_pubkey_rejects_non_base64() {
let err = decode_pubkey("not-base64-!!@#").expect_err("must reject");
assert!(err.contains("base64 decode"), "wrong error: {err}");
}
#[test]
fn cross_region_resolve_request_round_trips_json() {
let req = CrossRegionResolveRequest {
alias: "eu-token".to_string(),
worker_public_key_b64: "AAAA".to_string(),
worker_id: "noetl-worker-rust-abc".to_string(),
execution_id: Some(12345),
parent_execution_id: None,
expected_entry_region: "eu-central-1".to_string(),
requesting_region: "us-east-1".to_string(),
};
let json = serde_json::to_string(&req).unwrap();
let back: CrossRegionResolveRequest = serde_json::from_str(&json).unwrap();
assert_eq!(back.alias, req.alias);
assert_eq!(back.worker_id, req.worker_id);
assert_eq!(back.expected_entry_region, req.expected_entry_region);
}
}