opensaml 0.1.0

SAML 2.0 Service Provider library (bergshamra for XML crypto).
Documentation
//! Global parsing context (samlify `api.ts`): an optional schema validator.
//!
//! Unlike samlify — whose underlying parser does not harden input, so it fails
//! closed when no validator is set — our [`crate::xml::dom`] parser already
//! rejects DOCTYPEs (XXE/entity-expansion). [`is_valid_xml`] therefore always
//! runs that baseline hardening, and a registered schema validator is opt-in
//! defense-in-depth.

use crate::error::OpenSamlError;
use std::sync::RwLock;

/// A schema validator: returns `Err(reason)` to reject the XML.
pub type SchemaValidator = fn(&str) -> Result<(), String>;

static SCHEMA_VALIDATOR: RwLock<Option<SchemaValidator>> = RwLock::new(None);

/// Register a schema validator run by [`is_valid_xml`] (samlify `setSchemaValidator`).
pub fn set_schema_validator(validator: SchemaValidator) {
    if let Ok(mut guard) = SCHEMA_VALIDATOR.write() {
        *guard = Some(validator);
    }
}

/// Validate XML: baseline hardening (always) plus the registered validator (if any).
pub fn is_valid_xml(xml: &str) -> Result<(), OpenSamlError> {
    // Baseline: hardened parse rejects DOCTYPE / malformed structure.
    crate::xml::dom::parse(xml)?;
    if let Ok(guard) = SCHEMA_VALIDATOR.read() {
        if let Some(validate) = *guard {
            validate(xml).map_err(OpenSamlError::Invalid)?;
        }
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn baseline_rejects_doctype_without_validator() {
        assert!(is_valid_xml("<!DOCTYPE x><x/>").is_err());
        assert!(is_valid_xml("<x>ok</x>").is_ok());
    }
}