Skip to main content

saml_rs/config/
builders.rs

1use std::time::Duration;
2
3use crate::browser::{AcsEndpoint, SloEndpoint, SsoEndpoint};
4use crate::entity::EntitySetting;
5use crate::error::SamlError;
6
7use super::algorithms::{name_id_format_uris, transform_algorithm_uris, NameIdFormat};
8use super::credentials::Credentials;
9use super::descriptors::{validate_entity_id, EntityId, IdpMetadataConfig, SpMetadataConfig};
10use super::policies::{
11    assertion_signature_required, audience_restriction_required, audience_validation_enabled,
12    authn_request_signature_required, authn_request_signing_enabled,
13    encrypted_cbc_response_signature_required, logout_signature_required, name_id_creation_allowed,
14    response_signature_required, verify_present_authn_request_signature, AlgorithmPolicy,
15    AssertionEncryptionPolicy, AuthnRequestSigningPolicy, IdpValidationPolicy, SpValidationPolicy,
16    TemplatePolicy, XmlPolicy,
17};
18#[cfg(not(any(
19    feature = "crypto-rustcrypto",
20    feature = "crypto-aws-lc",
21    feature = "crypto-fips"
22)))]
23use super::policies::{
24    AssertionSignaturePolicy, AuthnRequestValidationPolicy, LogoutPolicy, LogoutSignaturePolicy,
25    ResponseSignaturePolicy,
26};
27
28/// Typed Service Provider configuration.
29///
30/// # Examples
31///
32/// ```
33/// use saml_rs::{AcsEndpoint, EntityId, SpConfig, SpMetadataConfig};
34///
35/// let acs = AcsEndpoint::post("https://sp.example.com/acs")?;
36/// let config = SpConfig::try_new(
37///     EntityId::try_new("https://sp.example.com/metadata")?,
38///     SpMetadataConfig::new(vec![acs]),
39/// )?;
40///
41/// assert_eq!(config.entity_id.as_str(), "https://sp.example.com/metadata");
42/// # Ok::<(), saml_rs::SamlError>(())
43/// ```
44#[derive(Debug, Clone)]
45pub struct SpConfig {
46    /// Local SP entity ID.
47    pub entity_id: EntityId,
48    /// Local SP metadata inputs.
49    pub metadata: SpMetadataConfig,
50    /// Local credentials.
51    pub credentials: Credentials,
52    /// Validation and outbound signing policy.
53    pub validation: SpValidationPolicy,
54    /// Algorithm policy.
55    pub algorithms: AlgorithmPolicy,
56    /// XML parser, redirect, clock, and encryption policy.
57    pub xml: XmlPolicy,
58    /// Template and prefix policy.
59    pub templates: TemplatePolicy,
60}
61
62impl SpConfig {
63    /// Create SP configuration with required identity and metadata inputs.
64    ///
65    /// This convenience constructor accepts already-validated typed inputs but
66    /// does not validate the final config. Use [`Self::try_new`] or
67    /// [`Self::builder`] for caller-provided setup.
68    pub fn new(entity_id: EntityId, metadata: SpMetadataConfig) -> Self {
69        Self {
70            entity_id,
71            metadata,
72            credentials: Credentials::default(),
73            validation: SpValidationPolicy::default(),
74            algorithms: AlgorithmPolicy::default(),
75            xml: XmlPolicy::default(),
76            templates: TemplatePolicy::default(),
77        }
78    }
79
80    /// Validate and create SP configuration with the legacy permissive preset.
81    ///
82    /// # Errors
83    ///
84    /// Returns [`SamlError`] when the entity ID is empty or required SP
85    /// metadata endpoints are missing.
86    pub fn try_new(entity_id: EntityId, metadata: SpMetadataConfig) -> Result<Self, SamlError> {
87        let config = Self::new(entity_id, metadata);
88        config.validate()?;
89        Ok(config)
90    }
91
92    /// Start a dependency-free SP config builder.
93    ///
94    /// The builder starts on the deprecated [`SpValidationPolicy::strict`]
95    /// bundle. [`Self::try_new`] uses [`SpValidationPolicy::compatibility`].
96    pub fn builder(entity_id: EntityId) -> SpConfigBuilder {
97        SpConfigBuilder::new(entity_id)
98    }
99
100    /// Validate required SP config fields.
101    ///
102    /// # Errors
103    ///
104    /// Returns [`SamlError`] when the entity ID is empty or required SP
105    /// metadata endpoints are missing.
106    pub fn validate(&self) -> Result<(), SamlError> {
107        validate_entity_id(&self.entity_id)?;
108        self.metadata.validate()?;
109        validate_sp_policy(self)
110    }
111}
112
113/// Dependency-free builder for [`SpConfig`].
114#[derive(Debug, Clone)]
115pub struct SpConfigBuilder {
116    entity_id: EntityId,
117    metadata: SpMetadataConfig,
118    credentials: Credentials,
119    validation: SpValidationPolicy,
120    algorithms: AlgorithmPolicy,
121    xml: XmlPolicy,
122    templates: TemplatePolicy,
123}
124
125impl SpConfigBuilder {
126    fn new(entity_id: EntityId) -> Self {
127        Self {
128            entity_id,
129            metadata: SpMetadataConfig::new(Vec::new()),
130            credentials: Credentials::default(),
131            validation: deprecated_strict_sp_validation(),
132            algorithms: AlgorithmPolicy::default(),
133            xml: XmlPolicy::default(),
134            templates: TemplatePolicy::default(),
135        }
136    }
137
138    /// Add an ACS endpoint.
139    pub fn acs_endpoint(mut self, endpoint: AcsEndpoint) -> Self {
140        self.metadata.assertion_consumer_service.push(endpoint);
141        self
142    }
143
144    /// Add an SLO endpoint.
145    pub fn slo_endpoint(mut self, endpoint: SloEndpoint) -> Self {
146        self.metadata.single_logout_service.push(endpoint);
147        self
148    }
149
150    /// Add an advertised NameID format.
151    pub fn name_id_format(mut self, format: NameIdFormat) -> Self {
152        self.metadata.name_id_format.push(format);
153        self
154    }
155
156    /// Set generated metadata element ordering.
157    pub fn elements_order(mut self, elements_order: Vec<String>) -> Self {
158        self.metadata.elements_order = Some(elements_order);
159        self
160    }
161
162    /// Set local credentials.
163    pub fn credentials(mut self, credentials: Credentials) -> Self {
164        self.credentials = credentials;
165        self
166    }
167
168    /// Set SP validation and outbound signing policy.
169    pub fn validation(mut self, validation: SpValidationPolicy) -> Self {
170        self.validation = validation;
171        self
172    }
173
174    /// Set algorithm policy.
175    pub fn algorithms(mut self, algorithms: AlgorithmPolicy) -> Self {
176        self.algorithms = algorithms;
177        self
178    }
179
180    /// Set XML policy.
181    pub fn xml(mut self, xml: XmlPolicy) -> Self {
182        self.xml = xml;
183        self
184    }
185
186    /// Set template policy.
187    pub fn templates(mut self, templates: TemplatePolicy) -> Self {
188        self.templates = templates;
189        self
190    }
191
192    /// Build and validate the SP config.
193    ///
194    /// # Errors
195    ///
196    /// Returns [`SamlError`] when required fields are missing, selected policy
197    /// needs unavailable credentials, or selected policy requires crypto in a
198    /// no-default-features build.
199    pub fn build(self) -> Result<SpConfig, SamlError> {
200        let config = SpConfig {
201            entity_id: self.entity_id,
202            metadata: self.metadata,
203            credentials: self.credentials,
204            validation: self.validation,
205            algorithms: self.algorithms,
206            xml: self.xml,
207            templates: self.templates,
208        };
209        config.validate()?;
210        Ok(config)
211    }
212}
213
214/// Typed Identity Provider configuration.
215///
216/// # Examples
217///
218/// The builder starts on the deprecated [`IdpValidationPolicy::strict`] bundle.
219/// Use [`IdpValidationPolicy::compatibility`] for the legacy permissive preset,
220/// including when compiling or testing without the default crypto feature.
221///
222/// ```
223/// use saml_rs::{EntityId, IdpConfig, IdpValidationPolicy, SsoEndpoint};
224/// use std::time::Duration;
225///
226/// let config = IdpConfig::builder(EntityId::try_new("https://idp.example.com/metadata")?)
227///     .sso_endpoint(SsoEndpoint::post("https://idp.example.com/sso")?)
228///     .issuance_lifetime(Duration::from_secs(10 * 60))
229///     .validation(IdpValidationPolicy::compatibility())
230///     .build()?;
231///
232/// assert_eq!(config.entity_id.as_str(), "https://idp.example.com/metadata");
233/// # Ok::<(), saml_rs::SamlError>(())
234/// ```
235#[derive(Debug, Clone)]
236pub struct IdpConfig {
237    /// Local IdP entity ID.
238    pub entity_id: EntityId,
239    /// Local IdP metadata inputs.
240    pub metadata: IdpMetadataConfig,
241    /// Local credentials.
242    pub credentials: Credentials,
243    /// Lifetime applied to assertions issued by this IdP and to
244    /// Session Authority LogoutRequests.
245    ///
246    /// The default is exactly five minutes. That value is saml-rs policy, not
247    /// an OASIS-mandated duration.
248    pub issuance_lifetime: Duration,
249    /// Validation policy.
250    pub validation: IdpValidationPolicy,
251    /// Algorithm policy.
252    pub algorithms: AlgorithmPolicy,
253    /// XML parser, redirect, clock, and encryption policy.
254    pub xml: XmlPolicy,
255    /// Template and prefix policy.
256    pub templates: TemplatePolicy,
257}
258
259impl IdpConfig {
260    /// Create IdP configuration with required identity and metadata inputs.
261    ///
262    /// This convenience constructor accepts already-validated typed inputs but
263    /// does not validate the final config. The issuance lifetime defaults to
264    /// exactly five minutes. Use [`Self::try_new`] or [`Self::builder`] for
265    /// caller-provided setup.
266    pub fn new(entity_id: EntityId, metadata: IdpMetadataConfig) -> Self {
267        Self {
268            entity_id,
269            metadata,
270            credentials: Credentials::default(),
271            issuance_lifetime: Duration::from_secs(300),
272            validation: IdpValidationPolicy::default(),
273            algorithms: AlgorithmPolicy::default(),
274            xml: XmlPolicy::default(),
275            templates: TemplatePolicy::default(),
276        }
277    }
278
279    /// Validate and create IdP configuration with the legacy permissive preset.
280    ///
281    /// # Errors
282    ///
283    /// Returns [`SamlError`] when the entity ID is empty, required IdP
284    /// metadata endpoints are missing, or the issuance lifetime is zero or
285    /// outside the range supported by the internal time representation.
286    pub fn try_new(entity_id: EntityId, metadata: IdpMetadataConfig) -> Result<Self, SamlError> {
287        let config = Self::new(entity_id, metadata);
288        config.validate()?;
289        Ok(config)
290    }
291
292    /// Start a dependency-free IdP config builder on the deprecated `strict()`
293    /// bundle and a five-minute issuance lifetime.
294    pub fn builder(entity_id: EntityId) -> IdpConfigBuilder {
295        IdpConfigBuilder::new(entity_id)
296    }
297
298    /// Validate required IdP config fields.
299    ///
300    /// # Errors
301    ///
302    /// Returns [`SamlError`] when the entity ID is empty, required IdP
303    /// metadata endpoints are missing, or the issuance lifetime is zero or
304    /// outside the range supported by the internal time representation.
305    pub fn validate(&self) -> Result<(), SamlError> {
306        validate_entity_id(&self.entity_id)?;
307        self.metadata.validate()?;
308        validate_idp_policy(self)
309    }
310}
311
312/// Dependency-free builder for [`IdpConfig`].
313#[derive(Debug, Clone)]
314pub struct IdpConfigBuilder {
315    entity_id: EntityId,
316    metadata: IdpMetadataConfig,
317    credentials: Credentials,
318    issuance_lifetime: Duration,
319    validation: IdpValidationPolicy,
320    algorithms: AlgorithmPolicy,
321    xml: XmlPolicy,
322    templates: TemplatePolicy,
323}
324
325impl IdpConfigBuilder {
326    fn new(entity_id: EntityId) -> Self {
327        Self {
328            entity_id,
329            metadata: IdpMetadataConfig::new(Vec::new()),
330            credentials: Credentials::default(),
331            issuance_lifetime: Duration::from_secs(300),
332            validation: deprecated_strict_idp_validation(),
333            algorithms: AlgorithmPolicy::default(),
334            xml: XmlPolicy::default(),
335            templates: TemplatePolicy::default(),
336        }
337    }
338
339    /// Add an SSO endpoint.
340    pub fn sso_endpoint(mut self, endpoint: SsoEndpoint) -> Self {
341        self.metadata.single_sign_on_service.push(endpoint);
342        self
343    }
344
345    /// Add an SLO endpoint.
346    pub fn slo_endpoint(mut self, endpoint: SloEndpoint) -> Self {
347        self.metadata.single_logout_service.push(endpoint);
348        self
349    }
350
351    /// Add an advertised NameID format.
352    pub fn name_id_format(mut self, format: NameIdFormat) -> Self {
353        self.metadata.name_id_format.push(format);
354        self
355    }
356
357    /// Set generated metadata element ordering.
358    pub fn elements_order(mut self, elements_order: Vec<String>) -> Self {
359        self.metadata.elements_order = Some(elements_order);
360        self
361    }
362
363    /// Set local credentials.
364    pub fn credentials(mut self, credentials: Credentials) -> Self {
365        self.credentials = credentials;
366        self
367    }
368
369    /// Set the lifetime for issued assertions and Session Authority
370    /// LogoutRequests.
371    ///
372    /// The value is validated by [`Self::build`].
373    pub fn issuance_lifetime(mut self, issuance_lifetime: Duration) -> Self {
374        self.issuance_lifetime = issuance_lifetime;
375        self
376    }
377
378    /// Set IdP validation policy.
379    pub fn validation(mut self, validation: IdpValidationPolicy) -> Self {
380        self.validation = validation;
381        self
382    }
383
384    /// Set algorithm policy.
385    pub fn algorithms(mut self, algorithms: AlgorithmPolicy) -> Self {
386        self.algorithms = algorithms;
387        self
388    }
389
390    /// Set XML policy.
391    pub fn xml(mut self, xml: XmlPolicy) -> Self {
392        self.xml = xml;
393        self
394    }
395
396    /// Set template policy.
397    pub fn templates(mut self, templates: TemplatePolicy) -> Self {
398        self.templates = templates;
399        self
400    }
401
402    /// Build and validate the IdP config.
403    ///
404    /// # Errors
405    ///
406    /// Returns [`SamlError`] when required fields are missing, the issuance
407    /// lifetime is zero or outside the internal supported range, or selected
408    /// policy requires crypto in a no-default-features build.
409    pub fn build(self) -> Result<IdpConfig, SamlError> {
410        let config = IdpConfig {
411            entity_id: self.entity_id,
412            metadata: self.metadata,
413            credentials: self.credentials,
414            issuance_lifetime: self.issuance_lifetime,
415            validation: self.validation,
416            algorithms: self.algorithms,
417            xml: self.xml,
418            templates: self.templates,
419        };
420        config.validate()?;
421        Ok(config)
422    }
423}
424
425fn validate_common_credentials(credentials: &Credentials) -> Result<(), SamlError> {
426    if credentials.signing_key_passphrase.is_some() && credentials.signing_key.is_none() {
427        return Err(SamlError::MissingKey("signing_key".into()));
428    }
429    if credentials.decryption_key_passphrase.is_some() && credentials.decryption_key.is_none() {
430        return Err(SamlError::MissingKey("decryption_key".into()));
431    }
432    Ok(())
433}
434
435fn validate_sp_policy(config: &SpConfig) -> Result<(), SamlError> {
436    validate_sp_crypto_support(config)?;
437    validate_common_credentials(&config.credentials)?;
438    if matches!(
439        config.validation.authn_requests,
440        AuthnRequestSigningPolicy::Sign
441    ) {
442        validate_signing_credentials(&config.credentials)?;
443    }
444    if matches!(
445        config.xml.encryption.assertions,
446        AssertionEncryptionPolicy::EncryptAssertions
447    ) && config.credentials.decryption_key.is_none()
448    {
449        return Err(SamlError::MissingKey("decryption_key".into()));
450    }
451    Ok(())
452}
453
454fn validate_idp_policy(config: &IdpConfig) -> Result<(), SamlError> {
455    validated_idp_issuance_lifetime(config.issuance_lifetime)?;
456    validate_idp_crypto_support(config)?;
457    validate_common_credentials(&config.credentials)
458}
459
460pub(crate) fn validated_idp_issuance_lifetime(
461    issuance_lifetime: Duration,
462) -> Result<time::Duration, SamlError> {
463    if issuance_lifetime.is_zero() {
464        return Err(SamlError::Invalid(
465            "IdP issuance lifetime must be greater than zero".into(),
466        ));
467    }
468    time::Duration::try_from(issuance_lifetime).map_err(|_| {
469        SamlError::Invalid(
470            "IdP issuance lifetime must fit in time::Duration (at most i64::MAX seconds plus 999,999,999 nanoseconds)".into(),
471        )
472    })
473}
474
475fn validate_signing_credentials(credentials: &Credentials) -> Result<(), SamlError> {
476    if credentials.signing_key.is_none() {
477        return Err(SamlError::MissingKey("signing_key".into()));
478    }
479    if credentials.signing_certificate.is_none() {
480        return Err(SamlError::MissingKey("signing_certificate".into()));
481    }
482    Ok(())
483}
484
485#[cfg(any(
486    feature = "crypto-rustcrypto",
487    feature = "crypto-aws-lc",
488    feature = "crypto-fips"
489))]
490fn validate_sp_crypto_support(_config: &SpConfig) -> Result<(), SamlError> {
491    Ok(())
492}
493
494#[cfg(not(any(
495    feature = "crypto-rustcrypto",
496    feature = "crypto-aws-lc",
497    feature = "crypto-fips"
498)))]
499fn validate_sp_crypto_support(config: &SpConfig) -> Result<(), SamlError> {
500    if sp_config_requires_crypto(config) {
501        return Err(SamlError::Unsupported(
502            "selected SP config policy requires a crypto provider feature".into(),
503        ));
504    }
505    Ok(())
506}
507
508#[cfg(any(
509    feature = "crypto-rustcrypto",
510    feature = "crypto-aws-lc",
511    feature = "crypto-fips"
512))]
513fn validate_idp_crypto_support(_config: &IdpConfig) -> Result<(), SamlError> {
514    Ok(())
515}
516
517#[cfg(not(any(
518    feature = "crypto-rustcrypto",
519    feature = "crypto-aws-lc",
520    feature = "crypto-fips"
521)))]
522fn validate_idp_crypto_support(config: &IdpConfig) -> Result<(), SamlError> {
523    if idp_config_requires_crypto(config) {
524        return Err(SamlError::Unsupported(
525            "selected IdP config policy requires a crypto provider feature".into(),
526        ));
527    }
528    Ok(())
529}
530
531#[cfg(not(any(
532    feature = "crypto-rustcrypto",
533    feature = "crypto-aws-lc",
534    feature = "crypto-fips"
535)))]
536fn sp_config_requires_crypto(config: &SpConfig) -> bool {
537    matches!(
538        config.validation.assertions,
539        AssertionSignaturePolicy::RequireSigned
540    ) || matches!(
541        config.validation.responses,
542        ResponseSignaturePolicy::RequireSigned | ResponseSignaturePolicy::RequireForEncryptedCbc
543    ) || matches!(
544        config.validation.authn_requests,
545        AuthnRequestSigningPolicy::Sign
546    ) || logout_policy_requires_crypto(config.validation.logout)
547        || matches!(
548            config.xml.encryption.assertions,
549            AssertionEncryptionPolicy::EncryptAssertions
550        )
551}
552
553#[cfg(not(any(
554    feature = "crypto-rustcrypto",
555    feature = "crypto-aws-lc",
556    feature = "crypto-fips"
557)))]
558fn idp_config_requires_crypto(config: &IdpConfig) -> bool {
559    matches!(
560        config.validation.authn_requests,
561        AuthnRequestValidationPolicy::RequireSigned
562    ) || logout_policy_requires_crypto(config.validation.logout)
563        || matches!(
564            config.xml.encryption.assertions,
565            AssertionEncryptionPolicy::EncryptAssertions
566        )
567}
568
569#[cfg(not(any(
570    feature = "crypto-rustcrypto",
571    feature = "crypto-aws-lc",
572    feature = "crypto-fips"
573)))]
574fn logout_policy_requires_crypto(policy: LogoutPolicy) -> bool {
575    logout_signature_policy_requires_crypto(policy.requests)
576        || logout_signature_policy_requires_crypto(policy.responses)
577}
578
579#[cfg(not(any(
580    feature = "crypto-rustcrypto",
581    feature = "crypto-aws-lc",
582    feature = "crypto-fips"
583)))]
584fn logout_signature_policy_requires_crypto(policy: LogoutSignaturePolicy) -> bool {
585    matches!(policy, LogoutSignaturePolicy::RequireSigned)
586}
587
588fn apply_common_settings(
589    entity_id: &EntityId,
590    name_id_format: &[NameIdFormat],
591    credentials: &Credentials,
592    algorithms: &AlgorithmPolicy,
593    xml: &XmlPolicy,
594    templates: &TemplatePolicy,
595    setting: &mut EntitySetting,
596) {
597    setting.entity_id = Some(entity_id.as_str().to_string());
598    setting.request_signature_algorithm = algorithms.signature.as_uri().to_string();
599    setting.data_encryption_algorithm = algorithms.data_encryption.as_uri().to_string();
600    setting.key_encryption_algorithm = algorithms.key_encryption.as_uri().to_string();
601    setting.message_signing_order = algorithms.message_signing_order;
602    setting.is_assertion_encrypted = matches!(
603        xml.encryption.assertions,
604        AssertionEncryptionPolicy::EncryptAssertions
605    );
606    setting.allow_insecure_software_rsa_key_transport_decryption = xml
607        .encryption
608        .allows_insecure_software_rsa_key_transport_decryption();
609    setting.relay_state = templates.relay_state.clone();
610    setting.name_id_format = name_id_format_uris(name_id_format);
611    setting.private_key = credentials
612        .signing_key
613        .as_ref()
614        .map(|key| key.as_str().to_string());
615    setting.private_key_pass = credentials
616        .signing_key_passphrase
617        .as_ref()
618        .map(|passphrase| passphrase.as_str().to_string());
619    setting.signing_cert = credentials
620        .signing_certificate
621        .as_ref()
622        .map(|certificate| certificate.as_str().to_string());
623    setting.encrypt_cert = credentials
624        .encryption_certificate
625        .as_ref()
626        .map(|certificate| certificate.as_str().to_string());
627    setting.enc_private_key = credentials
628        .decryption_key
629        .as_ref()
630        .map(|key| key.as_str().to_string());
631    setting.enc_private_key_pass = credentials
632        .decryption_key_passphrase
633        .as_ref()
634        .map(|passphrase| passphrase.as_str().to_string());
635    setting.clock_drifts = xml.clock_drifts;
636    setting.redirect_inflate_max_bytes = xml.redirect_inflate_max_bytes;
637    setting.xml_limits = xml.limits;
638    setting.tag_prefix_protocol = templates.tag_prefix_protocol.clone();
639    setting.tag_prefix_assertion = templates.tag_prefix_assertion.clone();
640    setting.tag_prefix_encrypted_assertion = templates.tag_prefix_encrypted_assertion.clone();
641    setting.login_response_template = templates.login_response_template.clone();
642    setting.login_request_template = templates.login_request_template.clone();
643    setting.logout_request_template = templates.logout_request_template.clone();
644    setting.logout_response_template = templates.logout_response_template.clone();
645    setting.signature_config = templates.signature_config.clone();
646    setting.transformation_algorithms =
647        transform_algorithm_uris(&algorithms.signed_reference_transforms);
648}
649
650impl TryFrom<&SpConfig> for EntitySetting {
651    type Error = SamlError;
652
653    fn try_from(config: &SpConfig) -> Result<Self, Self::Error> {
654        config.validate()?;
655        let mut setting = Self::default();
656        apply_common_settings(
657            &config.entity_id,
658            &config.metadata.name_id_format,
659            &config.credentials,
660            &config.algorithms,
661            &config.xml,
662            &config.templates,
663            &mut setting,
664        );
665        setting.allow_create = name_id_creation_allowed(config.validation.name_id_creation);
666        setting.authn_requests_signed =
667            authn_request_signing_enabled(config.validation.authn_requests);
668        setting.want_assertions_signed = assertion_signature_required(config.validation.assertions);
669        setting.validate_audience = audience_validation_enabled(config.validation.audience);
670        setting.require_audience_restriction =
671            audience_restriction_required(config.validation.audience);
672        setting.want_message_signed = response_signature_required(config.validation.responses);
673        setting.strict_xml_signature_profile = matches!(
674            config.validation.xml_signatures,
675            super::XmlSignatureProfile::StrictRsaSha2
676        );
677        setting.want_encrypted_cbc_response_signed =
678            encrypted_cbc_response_signature_required(config.validation.responses);
679        setting.want_logout_request_signed =
680            logout_signature_required(config.validation.logout.requests)?;
681        setting.want_logout_response_signed =
682            logout_signature_required(config.validation.logout.responses)?;
683        Ok(setting)
684    }
685}
686
687impl TryFrom<&IdpConfig> for EntitySetting {
688    type Error = SamlError;
689
690    fn try_from(config: &IdpConfig) -> Result<Self, Self::Error> {
691        config.validate()?;
692        let mut setting = Self::default();
693        apply_common_settings(
694            &config.entity_id,
695            &config.metadata.name_id_format,
696            &config.credentials,
697            &config.algorithms,
698            &config.xml,
699            &config.templates,
700            &mut setting,
701        );
702        setting.want_authn_requests_signed =
703            authn_request_signature_required(config.validation.authn_requests);
704        setting.verify_authn_request_signature_if_present =
705            verify_present_authn_request_signature(config.validation.authn_requests);
706        setting.want_logout_request_signed =
707            logout_signature_required(config.validation.logout.requests)?;
708        setting.want_logout_response_signed =
709            logout_signature_required(config.validation.logout.responses)?;
710        Ok(setting)
711    }
712}
713
714#[expect(
715    deprecated,
716    reason = "config builders stay on the deprecated strict preset in this release"
717)]
718fn deprecated_strict_sp_validation() -> SpValidationPolicy {
719    SpValidationPolicy::strict()
720}
721
722#[expect(
723    deprecated,
724    reason = "config builders stay on the deprecated strict preset in this release"
725)]
726fn deprecated_strict_idp_validation() -> IdpValidationPolicy {
727    IdpValidationPolicy::strict()
728}