use super::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct SoapEnvelope {
pub action: String,
pub body: Arc<[u8]>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct As4SendOutput {
pub message_id: String,
pub action: String,
pub http_content_type: String,
pub soap_envelope: SoapEnvelope,
pub ref_to_message_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ParsedWsAddressingHeaders {
pub message_id: Option<String>,
pub action: Option<String>,
pub to: Option<String>,
pub reply_to: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(test, derive(Default))]
pub(crate) struct ParsedAs4UserMessage {
pub message_id: String,
pub action: String,
pub from_party_ids: Vec<String>,
pub to_party_ids: Vec<String>,
pub from_party_type: Option<String>,
pub to_party_type: Option<String>,
pub mpc: Option<String>,
pub conversation_id: Option<String>,
pub has_ws_security_header: bool,
pub service: Option<String>,
pub ref_to_message_id: Option<String>,
pub original_sender: Option<String>,
pub final_recipient: Option<String>,
pub tracking_identifier: Option<String>,
pub timestamp: Option<String>,
pub wsa_headers: Option<ParsedWsAddressingHeaders>,
}
impl ParsedAs4UserMessage {
#[inline]
pub(crate) fn sender_party_id(&self) -> &str {
&self.from_party_ids[0]
}
#[inline]
pub(crate) fn to_party_id(&self) -> &str {
&self.to_party_ids[0]
}
pub(crate) fn check_timestamp_freshness(
&self,
window: super::TimestampWindow,
now: std::time::SystemTime,
) -> crate::core::Result<()> {
use crate::core::{AsxError, ErrorCode, ErrorContext};
let ts_str = match &self.timestamp {
Some(s) => s,
None => return Ok(()),
};
let ts_secs = crate::time_utils::parse_rfc3339_to_unix_secs(ts_str).ok_or_else(|| {
AsxError::new(
ErrorCode::ParseFailed,
format!("eb:Timestamp value '{ts_str}' is not a valid RFC 3339 timestamp"),
ErrorContext::new("as4_timestamp_freshness"),
)
})?;
let now_secs = crate::time_utils::unix_secs(now);
let (ahead, behind) = (ts_secs - now_secs, now_secs - ts_secs);
let refused = |detail: String| {
AsxError::new(
ErrorCode::SecurityVerificationFailed,
format!("eb:Timestamp {detail}; message rejected to prevent replay"),
ErrorContext::new("as4_timestamp_freshness")
.with_message_id(self.message_id.clone()),
)
};
if ahead > window.future.as_secs() as i64 {
return Err(refused(format!(
"is {ahead} s in the future (allowed {} s)",
window.future.as_secs()
)));
}
if behind > window.past.as_secs() as i64 {
return Err(refused(format!(
"is {behind} s old, beyond the duplicate-detection retention ({} s)",
window.past.as_secs()
)));
}
Ok(())
}
}