Skip to main content

SamlValidationContext

Struct SamlValidationContext 

Source
pub struct SamlValidationContext<'a> { /* private fields */ }
Expand description

Caller-owned validation context for typed inbound SAML messages.

Implementations§

Source§

impl<'a> SamlValidationContext<'a>

Source

pub fn new(now: SystemTime, replay: ReplayPolicy<'a>) -> Self

Build a validation context that allows five minutes of clock skew.

The same skew covers assertion conditions, bearer confirmation, SessionNotOnOrAfter, and LogoutRequest@NotOnOrAfter. Self::with_clock_skew replaces it.

Examples found in repository?
examples/sso.rs (line 34)
16fn main() -> Result<(), Box<dyn std::error::Error>> {
17    use saml_rs::{
18        AcsEndpoint, AuthnRequest, BrowserInput, CertificatePem, Credentials, EntityId, IdpConfig,
19        IdpDescriptor, IdpValidationPolicy, MetadataTrustPolicy, NameId, PrivateKeyPem,
20        RelayStateParam, ReplayPolicy, RespondSso, Saml, SamlValidationContext, SpConfig,
21        SpDescriptor, SpValidationPolicy, SsoEndpoint, SsoResponse, StartSso, Subject,
22    };
23    use std::time::SystemTime;
24
25    let privkey = include_str!("../tests/fixtures/key/sp_privkey.pem");
26    let cert = include_str!("../tests/fixtures/key/sp_signing_cert.cer");
27    let credentials = || Credentials {
28        signing_key: Some(PrivateKeyPem::new(privkey)),
29        signing_certificate: Some(CertificatePem::new(cert)),
30        ..Credentials::default()
31    };
32    // DisabledForCompatibility preserves the raw API, which has no replay cache.
33    let validation =
34        || SamlValidationContext::new(SystemTime::now(), ReplayPolicy::DisabledForCompatibility);
35
36    let sp = Saml::sp(
37        SpConfig::builder(EntityId::try_new("https://sp.example.com/metadata")?)
38            .acs_endpoint(AcsEndpoint::post("https://sp.example.com/acs")?)
39            .credentials(credentials())
40            .validation(SpValidationPolicy::strict())
41            .build()?,
42    )?;
43    let idp = Saml::idp(
44        IdpConfig::builder(EntityId::try_new("https://idp.example.com/metadata")?)
45            .sso_endpoint(SsoEndpoint::post("https://idp.example.com/sso")?)
46            .credentials(credentials())
47            .validation(IdpValidationPolicy::strict())
48            .build()?,
49    )?;
50
51    let sp_descriptor = SpDescriptor::from_metadata_xml_for(
52        EntityId::try_new("https://sp.example.com/metadata")?,
53        sp.metadata_xml(),
54        // UnsignedForCompatibility preserves an unsigned samlify-port metadata import.
55        MetadataTrustPolicy::UnsignedForCompatibility,
56    )?;
57    let idp_descriptor = IdpDescriptor::from_metadata_xml_for(
58        EntityId::try_new("https://idp.example.com/metadata")?,
59        idp.metadata_xml(),
60        // UnsignedForCompatibility preserves an unsigned samlify-port metadata import.
61        MetadataTrustPolicy::UnsignedForCompatibility,
62    )?;
63
64    let relay_state = RelayStateParam::try_from_option(Some("demo-state".to_string()))?;
65    let started = sp.start_sso(&idp_descriptor, StartSso::post().relay_state(relay_state))?;
66    println!(
67        "SP  -> AuthnRequest id = {}",
68        started.pending.request_id().as_str()
69    );
70
71    let request = idp.receive_sso(
72        &sp_descriptor,
73        BrowserInput::<AuthnRequest>::post(started.outbound.post_form()?.fields().to_vec()),
74        validation(),
75    )?;
76    println!(
77        "IdP <- request issuer  = {}",
78        request.message().issuer().as_str()
79    );
80
81    let response = idp.respond_sso(
82        &sp_descriptor,
83        &request,
84        Subject::new(NameId::new("alice@example.com", None), Vec::new()),
85        RespondSso::post(),
86    )?;
87
88    let session = sp.finish_sso(
89        &idp_descriptor,
90        &started.pending,
91        BrowserInput::<SsoResponse>::post(response.post_form()?.fields().to_vec()),
92        validation(),
93    );
94    let session = session?;
95    println!("SP  <- authenticated   = {}", session.name_id().value());
96    Ok(())
97}
More examples
Hide additional examples
examples/slo.rs (line 34)
15fn main() -> Result<(), Box<dyn std::error::Error>> {
16    use saml_rs::{
17        AcsEndpoint, AuthnRequest, BrowserInput, CertificatePem, Credentials, EntityId, IdpConfig,
18        IdpDescriptor, IdpValidationPolicy, LogoutRequest, LogoutResponse, MetadataTrustPolicy,
19        NameId, PrivateKeyPem, ReplayPolicy, RespondSlo, RespondSso, Saml, SamlValidationContext,
20        SloEndpoint, SpConfig, SpDescriptor, SpValidationPolicy, SsoEndpoint, SsoResponse,
21        StartSlo, StartSso, Subject,
22    };
23    use std::time::SystemTime;
24
25    let privkey = include_str!("../tests/fixtures/key/sp_privkey.pem");
26    let cert = include_str!("../tests/fixtures/key/sp_signing_cert.cer");
27    let credentials = || Credentials {
28        signing_key: Some(PrivateKeyPem::new(privkey)),
29        signing_certificate: Some(CertificatePem::new(cert)),
30        ..Credentials::default()
31    };
32    // DisabledForCompatibility preserves the raw API, which has no replay cache.
33    let validation =
34        || SamlValidationContext::new(SystemTime::now(), ReplayPolicy::DisabledForCompatibility);
35
36    let sp = Saml::sp(
37        SpConfig::builder(EntityId::try_new("https://sp.example.com/metadata")?)
38            .acs_endpoint(AcsEndpoint::post("https://sp.example.com/acs")?)
39            .slo_endpoint(SloEndpoint::post("https://sp.example.com/slo")?)
40            .credentials(credentials())
41            .validation(SpValidationPolicy::strict())
42            .build()?,
43    )?;
44    let idp = Saml::idp(
45        IdpConfig::builder(EntityId::try_new("https://idp.example.com/metadata")?)
46            .sso_endpoint(SsoEndpoint::post("https://idp.example.com/sso")?)
47            .slo_endpoint(SloEndpoint::post("https://idp.example.com/slo")?)
48            .credentials(credentials())
49            .validation(IdpValidationPolicy::strict())
50            .build()?,
51    )?;
52    let sp_descriptor = SpDescriptor::from_metadata_xml_for(
53        EntityId::try_new("https://sp.example.com/metadata")?,
54        sp.metadata_xml(),
55        // UnsignedForCompatibility preserves an unsigned samlify-port metadata import.
56        MetadataTrustPolicy::UnsignedForCompatibility,
57    )?;
58    let idp_descriptor = IdpDescriptor::from_metadata_xml_for(
59        EntityId::try_new("https://idp.example.com/metadata")?,
60        idp.metadata_xml(),
61        // UnsignedForCompatibility preserves an unsigned samlify-port metadata import.
62        MetadataTrustPolicy::UnsignedForCompatibility,
63    )?;
64
65    let sso = sp.start_sso(&idp_descriptor, StartSso::post())?;
66    let request = idp.receive_sso(
67        &sp_descriptor,
68        BrowserInput::<AuthnRequest>::post(sso.outbound.post_form()?.fields().to_vec()),
69        validation(),
70    )?;
71    let response = idp.respond_sso(
72        &sp_descriptor,
73        &request,
74        Subject::new(NameId::new("alice@example.com", None), Vec::new()),
75        RespondSso::post(),
76    )?;
77    let session = sp.finish_sso(
78        &idp_descriptor,
79        &sso.pending,
80        BrowserInput::<SsoResponse>::post(response.post_form()?.fields().to_vec()),
81        validation(),
82    )?;
83
84    let subject = session
85        .logout_subject()
86        .ok_or("session has no logout subject")?;
87    let logout = sp.start_slo(&idp_descriptor, subject, StartSlo::post())?;
88    println!("SP  -> LogoutRequest id = {}", logout.pending.id().as_str());
89
90    let logout_request = idp.receive_slo(
91        &sp_descriptor,
92        BrowserInput::<LogoutRequest>::post(logout.outbound.post_form()?.fields().to_vec()),
93        validation(),
94    )?;
95    let logout_response = idp.respond_slo(
96        &sp_descriptor,
97        &logout_request,
98        RespondSlo::post().relay_state(logout.pending.relay_state().clone()),
99    )?;
100    let completed = sp.finish_slo(
101        &idp_descriptor,
102        &logout.pending,
103        BrowserInput::<LogoutResponse>::post(logout_response.post_form()?.fields().to_vec()),
104        validation(),
105    )?;
106    println!(
107        "SP  <- LogoutResponse status = {}",
108        completed.status().unwrap_or("missing")
109    );
110    Ok(())
111}
Source

pub fn with_clock_skew(self, clock_skew: ClockSkew) -> Self

Replace the context clock skew, including ClockSkew::strict.

Source

pub fn with_authn_request_age( self, authn_request_age: AuthnRequestAgePolicy, ) -> Self

Select the maximum age of an inbound AuthnRequest IssueInstant.

AuthnRequestAgePolicy::Disabled skips the comparison. AuthnRequestAgePolicy::Bounded accepts an instant at or after now - max_age - past_tolerance and at or before now + future_tolerance. Both edges are inclusive. Past tolerance is the positive NotOnOrAfter skew. Future tolerance is the magnitude of a negative NotBefore skew. Zero skew and inverted skew do not shrink max_age.

Source

pub fn with_replay_retention(self, retention: Duration) -> Self

Set explicit replay retention for protocol messages that do not carry a SAML NotOnOrAfter value suitable for cache expiry.

Source

pub fn now(&self) -> SystemTime

Validation instant supplied by the caller.

Source

pub fn clock_skew(&self) -> ClockSkew

Clock skew applied to time-window checks.

Source

pub fn authn_request_age(&self) -> AuthnRequestAgePolicy

Maximum age policy for an inbound AuthnRequest IssueInstant.

Source

pub fn replay_retention(&self) -> Option<Duration>

Replay retention for protocol message IDs without SAML expiry.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V