#![allow(
clippy::disallowed_types,
reason = "dev/verification tooling over JSON artifacts (the catalogue, results, wire \
exchanges) — not the application (#1694); the carriers here are cfg(test)-only, so \
#[expect] would be unfulfilled in the non-test build"
)]
use std::io::Read;
use std::sync::{Arc, RwLock};
use std::time::Duration;
use base64::Engine;
use crate::ixit::{AuthMode, BearerMint, Instance, Ixit};
use crate::perf::Principal;
pub(crate) const CLIENT_TIMEOUT: Duration = Duration::from_mins(1);
const MINT_REFRESH_MARGIN_MS: i64 = 30_000;
#[derive(Debug)]
struct MintedToken {
header: String,
expires_at_ms: i64,
}
#[derive(Debug)]
struct MintedGrant {
mint: BearerMint,
subject: Option<String>,
roles: Option<Vec<String>>,
scopes: Vec<String>,
current: RwLock<Arc<MintedToken>>,
}
#[derive(Debug)]
enum Credential {
Fixed(Option<String>),
Minted(Box<MintedGrant>),
}
#[derive(Clone)]
pub struct PerfClient {
client: reqwest::blocking::Client,
base_url: String,
credential: Arc<Credential>,
extra_headers: Vec<(String, String)>,
}
impl std::fmt::Debug for PerfClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PerfClient")
.field("base_url", &self.base_url)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub(crate) struct WireReply {
pub(crate) status: reqwest::StatusCode,
pub(crate) etag: Option<String>,
pub(crate) location: Option<String>,
}
pub(crate) type RequestBody = Option<(&'static str, Vec<u8>)>;
impl PerfClient {
pub fn from_instance(instance: &Instance, ixit: &Ixit) -> Result<Self, String> {
let credential = match &instance.auth {
AuthMode::None => Credential::Fixed(None),
AuthMode::Basic {
user_env,
password_env,
} => {
let user = std::env::var(user_env)
.map_err(|error| format!("credential env {user_env}: {error}"))?;
let pass = std::env::var(password_env)
.map_err(|error| format!("credential env {password_env}: {error}"))?;
let token = base64::engine::general_purpose::STANDARD
.encode(format!("{user}:{pass}").as_bytes());
Credential::Fixed(Some(format!("Basic {token}")))
}
AuthMode::Bearer { token_env } => {
let token = std::env::var(token_env)
.map_err(|error| format!("credential env {token_env}: {error}"))?;
Credential::Fixed(Some(format!("Bearer {token}")))
}
AuthMode::BearerMint {
subject,
roles,
default_scopes,
} => {
let lane = ixit.smart.as_ref().ok_or_else(|| {
"instance declares auth mode `bearer_mint` but the ixit declares no `smart` \
lane to mint against"
.to_owned()
})?;
let grant = MintedGrant {
mint: lane.mint.clone(),
subject: subject.clone(),
roles: roles.clone(),
scopes: default_scopes.clone(),
current: RwLock::new(Arc::new(MintedToken {
header: String::new(),
expires_at_ms: i64::MIN,
})),
};
let token = grant.freshly_minted()?;
grant
.current
.write()
.map_err(|error| format!("minted-token lock poisoned: {error}"))?
.clone_from(&token);
Credential::Minted(Box::new(grant))
}
};
let client = reqwest::blocking::Client::builder()
.timeout(CLIENT_TIMEOUT)
.pool_max_idle_per_host(256)
.build()
.map_err(|e| format!("http client: {e}"))?;
Ok(Self {
client,
base_url: instance.base_url.trim_end_matches('/').to_owned(),
credential: Arc::new(credential),
extra_headers: instance.headers.clone().unwrap_or_default(),
})
}
fn authorization(&self) -> Result<Option<String>, String> {
match self.credential.as_ref() {
Credential::Fixed(header) => Ok(header.clone()),
Credential::Minted(grant) => grant.presentable().map(Some),
}
}
pub(crate) fn request(
&self,
method: reqwest::Method,
path: &str,
body: RequestBody,
prefer_minimal: bool,
if_match: Option<&str>,
) -> Result<WireReply, String> {
self.request_negotiated(method, path, body, prefer_minimal, if_match, None, &[])
}
#[expect(
clippy::too_many_arguments,
reason = "the single request-construction seam"
)]
pub(crate) fn request_negotiated(
&self,
method: reqwest::Method,
path: &str,
body: RequestBody,
prefer_minimal: bool,
if_match: Option<&str>,
accept: Option<&str>,
extra: &[(&str, String)],
) -> Result<WireReply, String> {
let accept = accept.unwrap_or(match &body {
Some((content_type, _)) if content_type.contains("xml") => "application/xml",
_ => "application/json",
});
let mut request = self
.client
.request(method, format!("{}{path}", self.base_url))
.header("Accept", accept);
if let Some(auth) = self.authorization()? {
request = request.header("Authorization", auth);
}
for (name, value) in extra {
request = request.header(*name, value);
}
for (name, value) in &self.extra_headers {
request = request.header(name, value);
}
if prefer_minimal {
request = request.header("Prefer", "return=minimal");
}
if let Some(preceding) = if_match {
request = request.header("If-Match", format!("\"{preceding}\""));
}
if let Some((content_type, bytes)) = body {
request = request.header("Content-Type", content_type).body(bytes);
}
let response = request.send().map_err(|e| format!("transport: {e}"))?;
let header = |name: &str| {
response
.headers()
.get(name)
.and_then(|v| v.to_str().ok())
.map(str::to_owned)
};
let reply = WireReply {
status: response.status(),
etag: header("etag"),
location: header("location"),
};
let mut sink = Vec::new();
let mut reader = response;
let _drained = reader.read_to_end(&mut sink);
Ok(reply)
}
}
impl MintedGrant {
fn freshly_minted(&self) -> Result<Arc<MintedToken>, String> {
let token = crate::exec::driver::mint_access_token(
&self.mint,
self.subject.as_deref(),
self.roles.as_deref(),
&self.scopes,
)?;
let ttl_ms = i64::try_from(self.mint.ttl_seconds.saturating_mul(1_000)).unwrap_or(i64::MAX);
Ok(Arc::new(MintedToken {
header: format!("Bearer {token}"),
expires_at_ms: crate::exec::driver::now_ms().saturating_add(ttl_ms),
}))
}
fn presentable(&self) -> Result<String, String> {
let now = crate::exec::driver::now_ms();
{
let current = self
.current
.read()
.map_err(|error| format!("minted-token lock poisoned: {error}"))?;
if now.saturating_add(MINT_REFRESH_MARGIN_MS) < current.expires_at_ms {
return Ok(current.header.clone());
}
}
let mut current = self
.current
.write()
.map_err(|error| format!("minted-token lock poisoned: {error}"))?;
if now.saturating_add(MINT_REFRESH_MARGIN_MS) < current.expires_at_ms {
return Ok(current.header.clone());
}
let fresh = self.freshly_minted()?;
let header = fresh.header.clone();
*current = fresh;
Ok(header)
}
}
const UNAUTHENTICATED_INSTANCE: &str = "unauthenticated";
const READONLY_INSTANCE: &str = "readonly";
const ADMIN_INSTANCE: &str = "admin";
#[derive(Debug, Clone)]
pub struct PerfPrincipals {
primary: PerfClient,
unauthenticated: Option<PerfClient>,
readonly: Option<PerfClient>,
admin: Option<PerfClient>,
smart_platform: Option<PerfClient>,
}
impl PerfPrincipals {
pub fn from_ixit(ixit: &Ixit) -> Result<Self, String> {
let primary = PerfClient::from_instance(ixit.default_instance()?, ixit)?;
let optional = |name: &crate::ids::InstanceName| -> Result<Option<PerfClient>, String> {
match ixit.instance(name) {
None => Ok(None),
Some(instance) => PerfClient::from_instance(instance, ixit)
.map(Some)
.map_err(|e| format!("ixit instance {name}: {e}")),
}
};
let named = |token: &str| -> Result<Option<PerfClient>, String> {
match crate::ids::InstanceName::parse(token) {
Ok(name) => optional(&name),
Err(e) => Err(e.to_string()),
}
};
let smart_platform = match ixit.smart.as_ref() {
None => None,
Some(lane) => optional(&lane.platform_instance)?,
};
Ok(Self {
primary,
unauthenticated: named(UNAUTHENTICATED_INSTANCE)?,
readonly: named(READONLY_INSTANCE)?,
admin: named(ADMIN_INSTANCE)?,
smart_platform,
})
}
#[must_use]
pub fn single(primary: PerfClient) -> Self {
Self {
primary,
unauthenticated: None,
readonly: None,
admin: None,
smart_platform: None,
}
}
#[must_use]
pub fn with_unauthenticated(mut self, client: PerfClient) -> Self {
self.unauthenticated = Some(client);
self
}
#[must_use]
pub fn with_readonly(mut self, client: PerfClient) -> Self {
self.readonly = Some(client);
self
}
#[must_use]
pub fn with_admin(mut self, client: PerfClient) -> Self {
self.admin = Some(client);
self
}
#[must_use]
pub fn with_smart_platform(mut self, client: PerfClient) -> Self {
self.smart_platform = Some(client);
self
}
#[must_use]
pub fn primary(&self) -> &PerfClient {
&self.primary
}
#[must_use]
pub fn client(&self, principal: Principal) -> Option<&PerfClient> {
match principal {
Principal::Primary => Some(&self.primary),
Principal::Unauthenticated => self.unauthenticated.as_ref(),
Principal::ReadOnly => self.readonly.as_ref(),
Principal::SmartPlatform => self.smart_platform.as_ref(),
Principal::Admin => self.admin.as_ref(),
}
}
#[must_use]
pub fn declares(&self, principal: Principal) -> bool {
self.client(principal).is_some()
}
}
pub(crate) fn strip_weak_quotes(etag: &str) -> String {
etag.trim_start_matches("W/").trim_matches('"').to_owned()
}
pub(crate) fn location_last_segment(location: &str) -> Option<String> {
location
.rsplit('/')
.next()
.filter(|s| !s.is_empty())
.map(str::to_owned)
}
pub(crate) fn object_uid_of(version_uid: &str) -> String {
version_uid
.split("::")
.next()
.unwrap_or(version_uid)
.to_owned()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_bearer_mint_principal_presents_one_cached_standing_grant() {
let key = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("party/smart/cnf-smart-test.key.pem");
assert!(key.is_file(), "committed test issuer key is missing");
let ixit: Ixit = serde_json::from_value(serde_json::json!({
"instances": { "sut": { "base_url": "http://stub", "auth": {
"mode": "bearer_mint", "subject": "cnf-user", "roles": ["USER"],
"default_scopes": ["user/aql-*.r"] } } },
"smart": { "platform_instance": "sut", "mint": {
"issuer": "https://as.cnf.test", "subject": "cnf-smart-app",
"roles": ["USER"], "key_file": key, "kid": "cnf-smart-test",
"ttl_seconds": 3600 } }
}))
.unwrap();
let client = PerfClient::from_instance(ixit.default_instance().unwrap(), &ixit).unwrap();
let first = client.authorization().unwrap().unwrap();
let second = client.authorization().unwrap().unwrap();
assert!(first.starts_with("Bearer "));
assert_eq!(first.split('.').count(), 3, "not a JWS compact token");
assert_eq!(first, second, "the standing grant was re-minted per call");
}
#[test]
fn a_bearer_mint_principal_without_a_lane_is_refused() {
let ixit: Ixit = serde_json::from_value(serde_json::json!({
"instances": { "sut": { "base_url": "http://stub",
"auth": { "mode": "bearer_mint" } } }
}))
.unwrap();
let error = PerfClient::from_instance(ixit.default_instance().unwrap(), &ixit).unwrap_err();
assert!(error.contains("no `smart` lane"), "{error}");
}
#[test]
fn principals_resolve_from_the_ixit_declarations_only() {
let ixit: Ixit = serde_json::from_value(serde_json::json!({
"instances": {
"sut": { "base_url": "http://stub", "auth": { "mode": "none" } },
"unauthenticated": { "base_url": "http://stub", "auth": { "mode": "none" } }
}
}))
.unwrap();
let principals = PerfPrincipals::from_ixit(&ixit).unwrap();
assert!(principals.declares(Principal::Primary));
assert!(principals.declares(Principal::Unauthenticated));
assert!(!principals.declares(Principal::ReadOnly));
assert!(!principals.declares(Principal::SmartPlatform));
assert!(principals.client(Principal::ReadOnly).is_none());
}
#[test]
fn header_captures_match_the_bindings() {
assert_eq!(
strip_weak_quotes("W/\"abc::sys::1\""),
"abc::sys::1".to_owned()
);
assert_eq!(strip_weak_quotes("\"abc\""), "abc".to_owned());
assert_eq!(
location_last_segment("http://sut/ehr/42").as_deref(),
Some("42")
);
assert_eq!(location_last_segment(""), None);
assert_eq!(object_uid_of("abc::sys::3"), "abc");
assert_eq!(object_uid_of("bare"), "bare");
}
}