1use core::str::FromStr;
2
3use crate::browser::{AcsEndpoint, SloEndpoint, SsoEndpoint};
4use crate::error::SamlError;
5use crate::metadata::{IdpMetadata, SpMetadata};
6
7use super::algorithms::NameIdFormat;
8use super::metadata_trust::{
9 ensure_expected_entity_id, ensure_metadata_trust, metadata_entity_id, AppliedMetadataTrust,
10 MetadataTrustPolicy,
11};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct EntityId(String);
16
17impl EntityId {
18 pub fn new(value: impl Into<String>) -> Self {
23 Self(value.into())
24 }
25
26 pub fn try_new(value: impl Into<String>) -> Result<Self, SamlError> {
32 let value = value.into();
33 validate_entity_id_text(&value)?;
34 Ok(Self(value))
35 }
36
37 pub fn as_str(&self) -> &str {
39 &self.0
40 }
41}
42
43impl FromStr for EntityId {
44 type Err = SamlError;
45
46 fn from_str(value: &str) -> Result<Self, Self::Err> {
47 Self::try_new(value)
48 }
49}
50
51impl TryFrom<&str> for EntityId {
52 type Error = SamlError;
53
54 fn try_from(value: &str) -> Result<Self, Self::Error> {
55 Self::try_new(value)
56 }
57}
58
59impl TryFrom<String> for EntityId {
60 type Error = SamlError;
61
62 fn try_from(value: String) -> Result<Self, Self::Error> {
63 Self::try_new(value)
64 }
65}
66
67pub(super) fn validate_entity_id_text(value: &str) -> Result<(), SamlError> {
68 if value.trim().is_empty() {
69 return Err(SamlError::Invalid("entity ID must not be empty".into()));
70 }
71 Ok(())
72}
73
74pub(super) fn validate_entity_id(entity_id: &EntityId) -> Result<(), SamlError> {
75 validate_entity_id_text(entity_id.as_str())
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct SpMetadataConfig {
81 pub name_id_format: Vec<NameIdFormat>,
83 pub single_logout_service: Vec<SloEndpoint>,
85 pub assertion_consumer_service: Vec<AcsEndpoint>,
87 pub elements_order: Option<Vec<String>>,
89}
90
91impl SpMetadataConfig {
92 pub fn new(assertion_consumer_service: Vec<AcsEndpoint>) -> Self {
97 Self {
98 name_id_format: Vec::new(),
99 single_logout_service: Vec::new(),
100 assertion_consumer_service,
101 elements_order: None,
102 }
103 }
104
105 pub fn try_new(assertion_consumer_service: Vec<AcsEndpoint>) -> Result<Self, SamlError> {
111 let config = Self::new(assertion_consumer_service);
112 config.validate()?;
113 Ok(config)
114 }
115
116 pub fn validate(&self) -> Result<(), SamlError> {
122 if self.assertion_consumer_service.is_empty() {
123 return Err(SamlError::MissingMetadata(
124 "AssertionConsumerService".into(),
125 ));
126 }
127 Ok(())
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Eq)]
133pub struct IdpMetadataConfig {
134 pub name_id_format: Vec<NameIdFormat>,
136 pub single_sign_on_service: Vec<SsoEndpoint>,
138 pub single_logout_service: Vec<SloEndpoint>,
140 pub elements_order: Option<Vec<String>>,
142}
143
144impl IdpMetadataConfig {
145 pub fn new(single_sign_on_service: Vec<SsoEndpoint>) -> Self {
150 Self {
151 name_id_format: Vec::new(),
152 single_sign_on_service,
153 single_logout_service: Vec::new(),
154 elements_order: None,
155 }
156 }
157
158 pub fn try_new(single_sign_on_service: Vec<SsoEndpoint>) -> Result<Self, SamlError> {
164 let config = Self::new(single_sign_on_service);
165 config.validate()?;
166 Ok(config)
167 }
168
169 pub fn validate(&self) -> Result<(), SamlError> {
175 if self.single_sign_on_service.is_empty() {
176 return Err(SamlError::MissingMetadata("SingleSignOnService".into()));
177 }
178 Ok(())
179 }
180}
181
182#[derive(Debug, Clone)]
184pub struct IdpDescriptor {
185 entity_id: EntityId,
186 metadata_xml: String,
187 metadata: IdpMetadata,
188 trust: AppliedMetadataTrust,
189}
190
191impl IdpDescriptor {
192 pub fn from_metadata_xml_for(
199 expected_entity_id: EntityId,
200 xml: &str,
201 trust: MetadataTrustPolicy<'_>,
202 ) -> Result<Self, SamlError> {
203 validate_entity_id(&expected_entity_id)?;
204 let metadata = IdpMetadata::from_xml(xml)?;
205 let actual_entity_id = metadata_entity_id(&metadata)?;
206 ensure_expected_entity_id(&expected_entity_id, actual_entity_id)?;
207 let trust = ensure_metadata_trust(&metadata, trust)?;
208 Ok(Self {
209 entity_id: expected_entity_id,
210 metadata_xml: xml.to_string(),
211 metadata,
212 trust,
213 })
214 }
215
216 pub fn from_metadata_xml(xml: &str, trust: MetadataTrustPolicy<'_>) -> Result<Self, SamlError> {
225 let metadata = IdpMetadata::from_xml(xml)?;
226 let entity_id = EntityId::try_new(metadata_entity_id(&metadata)?)?;
227 let trust = ensure_metadata_trust(&metadata, trust)?;
228 Ok(Self {
229 entity_id,
230 metadata_xml: xml.to_string(),
231 metadata,
232 trust,
233 })
234 }
235
236 pub fn entity_id(&self) -> &EntityId {
238 &self.entity_id
239 }
240
241 pub fn metadata_xml(&self) -> &str {
243 &self.metadata_xml
244 }
245
246 pub fn metadata(&self) -> &IdpMetadata {
248 &self.metadata
249 }
250
251 pub fn was_verified_with_pinned_certificates(&self) -> bool {
253 matches!(
254 self.trust,
255 AppliedMetadataTrust::SignedByPinnedCertificates { .. }
256 )
257 }
258
259 pub fn signed_entity_descriptor_xml(&self) -> Option<&str> {
261 match &self.trust {
262 AppliedMetadataTrust::SignedByPinnedCertificates {
263 signed_entity_descriptor_xml,
264 } => Some(signed_entity_descriptor_xml.as_str()),
265 AppliedMetadataTrust::UnsignedForCompatibility => None,
266 }
267 }
268}
269
270#[derive(Debug, Clone)]
272pub struct SpDescriptor {
273 entity_id: EntityId,
274 metadata_xml: String,
275 metadata: SpMetadata,
276 trust: AppliedMetadataTrust,
277}
278
279impl SpDescriptor {
280 pub fn from_metadata_xml_for(
287 expected_entity_id: EntityId,
288 xml: &str,
289 trust: MetadataTrustPolicy<'_>,
290 ) -> Result<Self, SamlError> {
291 validate_entity_id(&expected_entity_id)?;
292 let metadata = SpMetadata::from_xml(xml)?;
293 let actual_entity_id = metadata_entity_id(&metadata)?;
294 ensure_expected_entity_id(&expected_entity_id, actual_entity_id)?;
295 let trust = ensure_metadata_trust(&metadata, trust)?;
296 Ok(Self {
297 entity_id: expected_entity_id,
298 metadata_xml: xml.to_string(),
299 metadata,
300 trust,
301 })
302 }
303
304 pub fn from_metadata_xml(xml: &str, trust: MetadataTrustPolicy<'_>) -> Result<Self, SamlError> {
313 let metadata = SpMetadata::from_xml(xml)?;
314 let entity_id = EntityId::try_new(metadata_entity_id(&metadata)?)?;
315 let trust = ensure_metadata_trust(&metadata, trust)?;
316 Ok(Self {
317 entity_id,
318 metadata_xml: xml.to_string(),
319 metadata,
320 trust,
321 })
322 }
323
324 pub fn entity_id(&self) -> &EntityId {
326 &self.entity_id
327 }
328
329 pub fn metadata_xml(&self) -> &str {
331 &self.metadata_xml
332 }
333
334 pub fn metadata(&self) -> &SpMetadata {
336 &self.metadata
337 }
338
339 pub fn was_verified_with_pinned_certificates(&self) -> bool {
341 matches!(
342 self.trust,
343 AppliedMetadataTrust::SignedByPinnedCertificates { .. }
344 )
345 }
346
347 pub fn signed_entity_descriptor_xml(&self) -> Option<&str> {
349 match &self.trust {
350 AppliedMetadataTrust::SignedByPinnedCertificates {
351 signed_entity_descriptor_xml,
352 } => Some(signed_entity_descriptor_xml.as_str()),
353 AppliedMetadataTrust::UnsignedForCompatibility => None,
354 }
355 }
356}