1use std::hash::Hash;
6
7use spki::ObjectIdentifier;
8use x509_cert::attr::AttributeTypeAndValue;
9use x509_cert::name::{RdnSequence, RelativeDistinguishedName};
10
11use crate::errors::{ConstraintError, InvalidInput};
12use crate::types::SessionId;
13use crate::types::local_name::LocalName;
14use crate::types::{DomainName, FederationId};
15use crate::{
16 Constrained, OID_RDN_COMMON_NAME, OID_RDN_DOMAIN_COMPONENT, OID_RDN_UID,
17 OID_RDN_UNIQUE_IDENTIFIER,
18};
19
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23pub enum PolyprotoDistinguishedName {
24 ActorDn(ActorDN),
26 HomeServerDn(HomeServerDN),
28}
29
30impl PolyprotoDistinguishedName {
31 pub fn issuer_name(&self) -> &DomainName {
44 match self {
45 PolyprotoDistinguishedName::ActorDn(actor_dn) => &actor_dn.domain_name,
46 PolyprotoDistinguishedName::HomeServerDn(home_server_dn) => &home_server_dn.domain_name,
47 }
48 }
49}
50
51impl From<ActorDN> for PolyprotoDistinguishedName {
52 fn from(value: ActorDN) -> Self {
53 Self::ActorDn(value)
54 }
55}
56
57impl From<HomeServerDN> for PolyprotoDistinguishedName {
58 fn from(value: HomeServerDN) -> Self {
59 Self::HomeServerDn(value)
60 }
61}
62
63impl TryFrom<RdnSequence> for PolyprotoDistinguishedName {
64 type Error = crate::errors::InvalidInput;
65
66 fn try_from(value: RdnSequence) -> Result<Self, Self::Error> {
67 if value
70 .0
71 .iter()
72 .any(|rdn| rdn.0.iter().any(|attr| attr.oid == OID_RDN_UID))
73 {
74 Ok(Self::ActorDn(ActorDN::try_from(value)?))
75 } else {
76 Ok(Self::HomeServerDn(HomeServerDN::try_from(value)?))
77 }
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct ActorDN {
87 pub federation_id: FederationId,
89 pub local_name: LocalName,
91 pub domain_name: DomainName,
93 pub session_id: SessionId,
95 pub additional_fields: RelativeDistinguishedName,
97}
98
99impl ActorDN {
100 pub fn new_validated(
124 federation_id: FederationId,
125 local_name: LocalName,
126 domain_name: DomainName,
127 session_id: SessionId,
128 additional_fields: Option<RelativeDistinguishedName>,
129 ) -> Result<Self, ConstraintError> {
130 let actor_dn = ActorDN {
131 federation_id,
132 local_name,
133 domain_name,
134 session_id,
135 additional_fields: additional_fields.unwrap_or_default(),
136 };
137 actor_dn.validate(Some(crate::certs::Target::Actor))?;
138 Ok(actor_dn)
139 }
140}
141
142impl Hash for ActorDN {
143 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
144 self.federation_id.hash(state);
145 self.domain_name.hash(state);
146 self.session_id.hash(state);
147 self.additional_fields.0.iter().for_each(|item| {
148 item.oid.hash(state);
149 item.value.value().hash(state);
150 });
151 }
152}
153
154#[derive(Debug, Clone, PartialEq, Eq)]
155pub struct HomeServerDN {
160 pub domain_name: DomainName,
162 pub additional_fields: RelativeDistinguishedName,
164}
165
166impl HomeServerDN {
167 pub fn new_validated(
188 domain_name: DomainName,
189 additional_fields: Option<RelativeDistinguishedName>,
190 ) -> Result<Self, ConstraintError> {
191 let home_server_dn = HomeServerDN {
192 domain_name,
193 additional_fields: additional_fields.unwrap_or_default(),
194 };
195 home_server_dn.validate(Some(crate::certs::Target::HomeServer))?;
196 Ok(home_server_dn)
197 }
198}
199
200impl Hash for HomeServerDN {
201 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
202 self.domain_name.hash(state);
203 self.additional_fields.0.iter().for_each(|item| {
204 item.oid.hash(state);
205 item.value.value().hash(state);
206 });
207 }
208}
209
210impl TryFrom<RdnSequence> for ActorDN {
211 type Error = crate::errors::InvalidInput;
212
213 fn try_from(x509_distinguished_name: RdnSequence) -> Result<Self, Self::Error> {
214 check_amount_of_oid_values(
215 range::Range::new(1, 125)?,
216 &x509_distinguished_name,
217 &OID_RDN_DOMAIN_COMPONENT,
218 )?;
219 check_amount_of_oid_values(
220 range::Range::new(1, 1)?,
221 &x509_distinguished_name,
222 &OID_RDN_COMMON_NAME,
223 )?;
224 check_amount_of_oid_values(
225 range::Range::new(1, 1)?,
226 &x509_distinguished_name,
227 &OID_RDN_UID,
228 )?;
229 check_amount_of_oid_values(
230 range::Range::new(1, 1)?,
231 &x509_distinguished_name,
232 &OID_RDN_UNIQUE_IDENTIFIER,
233 )?;
234 let mut maybe_federation_id: Option<AttributeTypeAndValue> = None;
235 let mut maybe_local_name: Option<AttributeTypeAndValue> = None;
236 let mut maybe_domain_names: Vec<AttributeTypeAndValue> = Vec::new();
237 let mut maybe_session_id: Option<AttributeTypeAndValue> = None;
238 let mut maybe_additional_fields: Vec<AttributeTypeAndValue> = Vec::new();
239 for relative_distinguished_name in x509_distinguished_name.0.into_iter() {
240 for attribute_value_and_item in relative_distinguished_name.0.iter() {
241 match attribute_value_and_item.oid {
242 OID_RDN_COMMON_NAME => {
243 make_some_or_error(attribute_value_and_item, &mut maybe_local_name)?
244 }
245 OID_RDN_UID => {
246 make_some_or_error(attribute_value_and_item, &mut maybe_federation_id)?
247 }
248 OID_RDN_UNIQUE_IDENTIFIER => {
249 make_some_or_error(attribute_value_and_item, &mut maybe_session_id)?
250 }
251 OID_RDN_DOMAIN_COMPONENT => {
252 maybe_domain_names.push(attribute_value_and_item.clone())
253 }
254 _other => maybe_additional_fields.push(attribute_value_and_item.clone()),
255 }
256 }
257 }
258 let federation_id = FederationId::try_from(match maybe_federation_id {
259 Some(fid) => fid,
260 None => {
261 return Err(crate::errors::InvalidInput::Malformed(String::from(
262 "Expected Federation ID in ActorDN, found none",
263 )));
264 }
265 })?;
266 let local_name = LocalName::try_from(match maybe_local_name {
267 Some(ln) => ln,
268 None => {
269 return Err(crate::errors::InvalidInput::Malformed(String::from(
270 "Expected Local Name in ActorDN, found none",
271 )));
272 }
273 })?;
274 let domain_name = federation_id.domain_name.clone();
275 let session_id = SessionId::try_from(match maybe_session_id {
276 Some(s_id) => s_id,
277 None => {
278 return Err(crate::errors::InvalidInput::Malformed(String::from(
279 "Expected Local Name in ActorDN, found none",
280 )));
281 }
282 })?;
283 if domain_name != federation_id.domain_name {
284 return Err(InvalidInput::Malformed(format!(
285 "The domain name specified by the DC components does not the equal the domain name described in the federation ID: {domain_name} != {}",
286 federation_id.domain_name,
287 )));
288 }
289 if local_name != federation_id.local_name {
290 return Err(InvalidInput::Malformed(format!(
291 "The local name specified by the OID commonName ({OID_RDN_COMMON_NAME}) does not the equal the local name described in the federation ID (OID UID {OID_RDN_UID}): {local_name} != {}",
292 federation_id.local_name,
293 )));
294 }
295 Ok(ActorDN {
296 federation_id,
297 domain_name,
298 session_id,
299 local_name,
300 additional_fields: RelativeDistinguishedName::try_from(maybe_additional_fields).map_err(|e| crate::errors::InvalidInput::Malformed(format!("Could not parse ActorDN additional_fields: Name attribute contained additional information which was not a valid RelativeDistinguishedName: {e}")))?,
301 })
302 }
303}
304
305impl TryFrom<ActorDN> for RdnSequence {
306 type Error = InvalidInput;
307 fn try_from(value: ActorDN) -> Result<Self, Self::Error> {
308 let mut all_rdns = Vec::new();
309
310 if !value.additional_fields.0.is_empty() {
312 all_rdns.push(value.additional_fields);
313 }
314
315 all_rdns.append(&mut RdnSequence::try_from(value.domain_name)?.0);
316 all_rdns.push(RelativeDistinguishedName::try_from(value.federation_id)?);
317 all_rdns.push(RelativeDistinguishedName::try_from(value.local_name)?);
318 all_rdns.push(RelativeDistinguishedName::try_from(value.session_id)?);
319 Ok(RdnSequence(all_rdns))
320 }
321}
322
323impl TryFrom<RdnSequence> for HomeServerDN {
324 type Error = crate::errors::InvalidInput;
325
326 fn try_from(x509_distinguished_name: RdnSequence) -> Result<Self, Self::Error> {
327 check_amount_of_oid_values(
328 range::Range::new(1, 125)?,
329 &x509_distinguished_name,
330 &OID_RDN_DOMAIN_COMPONENT,
331 )?;
332 check_amount_of_oid_values(
333 range::Range::new(0, 0)?,
334 &x509_distinguished_name,
335 &OID_RDN_COMMON_NAME,
336 )?;
337 check_amount_of_oid_values(
338 range::Range::new(0, 0)?,
339 &x509_distinguished_name,
340 &OID_RDN_UID,
341 )?;
342 check_amount_of_oid_values(
343 range::Range::new(0, 0)?,
344 &x509_distinguished_name,
345 &OID_RDN_UNIQUE_IDENTIFIER,
346 )?;
347 let mut maybe_domain_names: Vec<AttributeTypeAndValue> = Vec::new();
348 let mut maybe_additional_fields: Vec<AttributeTypeAndValue> = Vec::new();
349 for relative_distinguished_name in x509_distinguished_name.0.into_iter() {
350 for attribute_value_and_item in relative_distinguished_name.0.iter() {
351 match attribute_value_and_item.oid {
352 OID_RDN_DOMAIN_COMPONENT => {
353 maybe_domain_names.push(attribute_value_and_item.clone())
354 }
355 _other => maybe_additional_fields.push(attribute_value_and_item.clone()),
356 }
357 }
358 }
359
360 let domain_name = DomainName::try_from(maybe_domain_names.as_slice())?;
361
362 Ok(HomeServerDN {
363 domain_name,
364 additional_fields: RelativeDistinguishedName::try_from(maybe_additional_fields).map_err(|e| crate::errors::InvalidInput::Malformed(format!("Could not parse ActorDN additional_fields: Name attribute contained additional information which was not a valid RelativeDistinguishedName: {e}")))?,
365 })
366 }
367}
368
369impl TryFrom<HomeServerDN> for RdnSequence {
370 type Error = InvalidInput;
371
372 fn try_from(value: HomeServerDN) -> Result<Self, Self::Error> {
373 let mut all_rdns = Vec::new();
374
375 if !value.additional_fields.0.is_empty() {
377 all_rdns.push(value.additional_fields);
378 }
379
380 all_rdns.append(&mut RdnSequence::try_from(value.domain_name)?.0);
381 Ok(RdnSequence(all_rdns))
382 }
383}
384
385impl TryFrom<PolyprotoDistinguishedName> for RdnSequence {
386 type Error = InvalidInput;
387
388 fn try_from(value: PolyprotoDistinguishedName) -> Result<Self, Self::Error> {
389 match value {
390 PolyprotoDistinguishedName::ActorDn(actor_dn) => Self::try_from(actor_dn),
391 PolyprotoDistinguishedName::HomeServerDn(home_server_dn) => {
392 Self::try_from(home_server_dn)
393 }
394 }
395 }
396}
397
398fn make_some_or_error(
404 attribute_value_and_item: &AttributeTypeAndValue,
405 value_to_update: &mut Option<AttributeTypeAndValue>,
406) -> Result<(), crate::errors::InvalidInput> {
407 if value_to_update.is_none() {
408 *value_to_update = Some(attribute_value_and_item.clone());
409 Ok(())
410 } else {
411 Err(crate::errors::InvalidInput::Malformed(
412 "Found multiple entries for same OID, where only one OID is allowed".to_owned(),
413 ))
414 }
415}
416
417pub(crate) mod range {
418 use crate::errors::InvalidInput;
419
420 pub(crate) struct Range {
423 min: u32,
424 max: u32,
425 }
426
427 impl Range {
428 pub(crate) fn new(min: u32, max: u32) -> Result<Self, InvalidInput> {
429 if max < min {
430 Err(InvalidInput::Malformed(
431 "min must be equal to or smaller than max!".to_owned(),
432 ))
433 } else {
434 Ok(Self { min, max })
435 }
436 }
437
438 pub(crate) fn min(&self) -> u32 {
440 self.min
441 }
442
443 pub(crate) fn max(&self) -> u32 {
445 self.max
446 }
447 }
448}
449
450pub(crate) fn check_amount_of_oid_values(
467 min_max: range::Range,
468 value_container: &RdnSequence,
469 oid: &ObjectIdentifier,
470) -> Result<(), InvalidInput> {
471 let mut count = 0u32;
472
473 for rdn in value_container.0.iter() {
474 for attribute_type_and_value in rdn.0.iter() {
475 if attribute_type_and_value.oid == *oid {
476 count = count.saturating_add(1);
477 if count > min_max.max() {
478 return Err(InvalidInput::Length {
479 min_length: min_max.min() as usize,
480 max_length: min_max.max() as usize,
481 actual_length: count.to_string(),
482 });
483 }
484 }
485 }
486 }
487
488 if count < min_max.min() {
489 return Err(InvalidInput::Length {
490 min_length: min_max.min() as usize,
491 max_length: min_max.max() as usize,
492 actual_length: count.to_string(),
493 });
494 }
495
496 Ok(())
497}
498
499#[cfg(test)]
500#[allow(clippy::expect_used)]
501mod tests {
502 use super::*;
503 use der::Any;
504 use x509_cert::ext::pkix::name::DirectoryString;
505
506 fn create_test_attribute(oid: ObjectIdentifier, value: &str) -> AttributeTypeAndValue {
508 let directory_string = DirectoryString::Utf8String(value.to_string());
509 AttributeTypeAndValue {
510 oid,
511 value: Any::encode_from(&directory_string).expect("Valid encoding"),
512 }
513 }
514
515 fn create_test_rdn_sequence(attributes: Vec<AttributeTypeAndValue>) -> RdnSequence {
517 let rdn = RelativeDistinguishedName::try_from(attributes)
518 .expect("Valid RelativeDistinguishedName");
519 RdnSequence(vec![rdn])
520 }
521
522 fn create_multi_rdn_sequence(rdn_attributes: Vec<Vec<AttributeTypeAndValue>>) -> RdnSequence {
524 let rdns: Vec<RelativeDistinguishedName> = rdn_attributes
525 .into_iter()
526 .map(|attrs| RelativeDistinguishedName::try_from(attrs).expect("Valid RDN"))
527 .collect();
528 RdnSequence(rdns)
529 }
530
531 #[test]
532 fn test_valid_count_scenarios() {
533 let range = range::Range::new(1, 3).expect("Valid range");
535 let attributes = vec![
536 create_test_attribute(OID_RDN_COMMON_NAME, "test"),
537 create_test_attribute(OID_RDN_DOMAIN_COMPONENT, "example.com"),
538 ];
539 let rdn_sequence = create_test_rdn_sequence(attributes);
540 let result = check_amount_of_oid_values(range, &rdn_sequence, &OID_RDN_COMMON_NAME);
541 assert!(
542 result.is_ok(),
543 "Should succeed with 1 occurrence within range [1,3]"
544 );
545
546 let range = range::Range::new(2, 4).expect("Valid range");
548 let attributes = vec![
549 create_test_attribute(OID_RDN_COMMON_NAME, "test1"),
550 create_test_attribute(OID_RDN_COMMON_NAME, "test2"),
551 create_test_attribute(OID_RDN_DOMAIN_COMPONENT, "example.com"),
552 ];
553 let rdn_sequence = create_test_rdn_sequence(attributes);
554 let result = check_amount_of_oid_values(range, &rdn_sequence, &OID_RDN_COMMON_NAME);
555 assert!(
556 result.is_ok(),
557 "Should succeed with 2 occurrences within range [2,4]"
558 );
559 }
560
561 #[test]
562 fn test_boundary_conditions() {
563 let range = range::Range::new(2, 5).expect("Valid range");
565 let attributes = vec![
566 create_test_attribute(OID_RDN_UID, "user1"),
567 create_test_attribute(OID_RDN_UID, "user2"),
568 create_test_attribute(OID_RDN_DOMAIN_COMPONENT, "example.com"),
569 ];
570 let rdn_sequence = create_test_rdn_sequence(attributes);
571 let result = check_amount_of_oid_values(range, &rdn_sequence, &OID_RDN_UID);
572 assert!(
573 result.is_ok(),
574 "Should succeed with exactly minimum count (2)"
575 );
576
577 let range = range::Range::new(1, 3).expect("Valid range");
579 let attributes = vec![
580 create_test_attribute(OID_RDN_DOMAIN_COMPONENT, "part1"),
581 create_test_attribute(OID_RDN_DOMAIN_COMPONENT, "part2"),
582 create_test_attribute(OID_RDN_DOMAIN_COMPONENT, "part3"),
583 create_test_attribute(OID_RDN_COMMON_NAME, "test"),
584 ];
585 let rdn_sequence = create_test_rdn_sequence(attributes);
586 let result = check_amount_of_oid_values(range, &rdn_sequence, &OID_RDN_DOMAIN_COMPONENT);
587 assert!(
588 result.is_ok(),
589 "Should succeed with exactly maximum count (3)"
590 );
591 }
592
593 #[test]
594 fn test_error_conditions() {
595 let range = range::Range::new(2, 5).expect("Valid range");
597 let attributes = vec![
598 create_test_attribute(OID_RDN_COMMON_NAME, "test"),
599 create_test_attribute(OID_RDN_DOMAIN_COMPONENT, "example.com"),
600 ];
601 let rdn_sequence = create_test_rdn_sequence(attributes);
602 let result = check_amount_of_oid_values(range, &rdn_sequence, &OID_RDN_COMMON_NAME);
603 assert!(
604 result.is_err(),
605 "Should fail with 1 occurrence below minimum (2)"
606 );
607 if let Err(InvalidInput::Length {
608 min_length,
609 max_length,
610 ..
611 }) = result
612 {
613 assert_eq!(min_length, 2);
614 assert_eq!(max_length, 5);
615 } else {
616 panic!("Expected InvalidInput::Length error");
617 }
618
619 let range = range::Range::new(1, 2).expect("Valid range");
621 let attributes = vec![
622 create_test_attribute(OID_RDN_UNIQUE_IDENTIFIER, "id1"),
623 create_test_attribute(OID_RDN_UNIQUE_IDENTIFIER, "id2"),
624 create_test_attribute(OID_RDN_UNIQUE_IDENTIFIER, "id3"),
625 create_test_attribute(OID_RDN_COMMON_NAME, "test"),
626 ];
627 let rdn_sequence = create_test_rdn_sequence(attributes);
628 let result = check_amount_of_oid_values(range, &rdn_sequence, &OID_RDN_UNIQUE_IDENTIFIER);
629 assert!(
630 result.is_err(),
631 "Should fail with 3 occurrences above maximum (2)"
632 );
633 if let Err(InvalidInput::Length {
634 min_length,
635 max_length,
636 ..
637 }) = result
638 {
639 assert_eq!(min_length, 1);
640 assert_eq!(max_length, 2);
641 } else {
642 panic!("Expected InvalidInput::Length error");
643 }
644 }
645
646 #[test]
647 fn test_edge_cases() {
648 let range = range::Range::new(1, 3).expect("Valid range");
650 let attributes = vec![
651 create_test_attribute(OID_RDN_COMMON_NAME, "test"),
652 create_test_attribute(OID_RDN_DOMAIN_COMPONENT, "example.com"),
653 ];
654 let rdn_sequence = create_test_rdn_sequence(attributes);
655 let result = check_amount_of_oid_values(range, &rdn_sequence, &OID_RDN_UID);
656 assert!(
657 result.is_err(),
658 "Should fail with 0 occurrences below minimum (1)"
659 );
660
661 let range = range::Range::new(0, 2).expect("Valid range");
663 let result = check_amount_of_oid_values(range, &rdn_sequence, &OID_RDN_UID);
664 assert!(
665 result.is_ok(),
666 "Should succeed with 0 occurrences within range [0,2]"
667 );
668
669 let range = range::Range::new(0, 1).expect("Valid range");
671 let empty_sequence = RdnSequence(vec![]);
672 let result = check_amount_of_oid_values(range, &empty_sequence, &OID_RDN_COMMON_NAME);
673 assert!(
674 result.is_ok(),
675 "Should succeed with empty RdnSequence when minimum is 0"
676 );
677
678 let range = range::Range::new(1, 3).expect("Valid range");
680 let result = check_amount_of_oid_values(range, &empty_sequence, &OID_RDN_COMMON_NAME);
681 assert!(
682 result.is_err(),
683 "Should fail with empty RdnSequence when minimum > 0"
684 );
685
686 let range = range::Range::new(2, 4).expect("Valid range");
688 let rdn_attributes = vec![
689 vec![
690 create_test_attribute(OID_RDN_COMMON_NAME, "test1"),
691 create_test_attribute(OID_RDN_DOMAIN_COMPONENT, "part1"),
692 ],
693 vec![
694 create_test_attribute(OID_RDN_COMMON_NAME, "test2"),
695 create_test_attribute(OID_RDN_UID, "user"),
696 ],
697 ];
698 let multi_rdn_sequence = create_multi_rdn_sequence(rdn_attributes);
699 let result = check_amount_of_oid_values(range, &multi_rdn_sequence, &OID_RDN_COMMON_NAME);
700 assert!(
701 result.is_ok(),
702 "Should succeed with 2 occurrences across multiple RDNs"
703 );
704 }
705
706 #[test]
707 fn test_exact_count_constraints() {
708 let range = range::Range::new(1, 1).expect("Valid range");
709
710 let attributes = vec![create_test_attribute(OID_RDN_COMMON_NAME, "single")];
712 let rdn_sequence = create_test_rdn_sequence(attributes);
713 let result = check_amount_of_oid_values(range, &rdn_sequence, &OID_RDN_COMMON_NAME);
714 assert!(
715 result.is_ok(),
716 "Should succeed with exactly 1 occurrence in range [1,1]"
717 );
718
719 let range = range::Range::new(1, 1).expect("Valid range");
721 let attributes = vec![
722 create_test_attribute(OID_RDN_COMMON_NAME, "first"),
723 create_test_attribute(OID_RDN_COMMON_NAME, "second"),
724 ];
725 let rdn_sequence = create_test_rdn_sequence(attributes);
726 let result = check_amount_of_oid_values(range, &rdn_sequence, &OID_RDN_COMMON_NAME);
727 assert!(
728 result.is_err(),
729 "Should fail with 2 occurrences in range [1,1]"
730 );
731 }
732
733 mod constrained_tests {
734 use super::*;
735 use crate::{Constrained, certs::Target};
736 use der::asn1::SetOfVec;
737
738 fn create_valid_federation_id() -> FederationId {
740 FederationId::new("alice@example.com").expect("Valid federation ID")
741 }
742
743 fn create_valid_local_name() -> LocalName {
745 LocalName::new("alice").expect("Valid local name")
746 }
747
748 fn create_valid_domain_name() -> DomainName {
750 DomainName::new("example.com").expect("Valid domain name")
751 }
752
753 fn create_valid_session_id() -> SessionId {
755 SessionId::new_validated("validSessionId123").expect("Valid session ID")
756 }
757
758 fn create_empty_additional_fields() -> RelativeDistinguishedName {
760 RelativeDistinguishedName(SetOfVec::new())
761 }
762
763 fn create_forbidden_oid_additional_fields(
765 oid: ObjectIdentifier,
766 ) -> RelativeDistinguishedName {
767 let attr = create_test_attribute(oid, "forbidden");
768 RelativeDistinguishedName::try_from(vec![attr]).expect("Valid RDN")
769 }
770
771 fn create_valid_actor_dn() -> ActorDN {
773 ActorDN {
774 federation_id: create_valid_federation_id(),
775 local_name: create_valid_local_name(),
776 domain_name: create_valid_domain_name(),
777 session_id: create_valid_session_id(),
778 additional_fields: create_empty_additional_fields(),
779 }
780 }
781
782 fn create_valid_home_server_dn() -> HomeServerDN {
784 HomeServerDN {
785 domain_name: create_valid_domain_name(),
786 additional_fields: create_empty_additional_fields(),
787 }
788 }
789
790 #[test]
791 fn polyproto_dn_actor_with_actor_target_validates() {
792 let actor_dn = create_valid_actor_dn();
793 let pdn = PolyprotoDistinguishedName::ActorDn(actor_dn);
794
795 let result = pdn.validate(Some(Target::Actor));
796 assert!(result.is_ok(), "ActorDn should validate with Actor target");
797 }
798
799 #[test]
800 fn polyproto_dn_actor_with_none_target_validates() {
801 let actor_dn = create_valid_actor_dn();
802 let pdn = PolyprotoDistinguishedName::ActorDn(actor_dn);
803
804 let result = pdn.validate(None);
805 assert!(result.is_ok(), "ActorDn should validate with None target");
806 }
807
808 #[test]
809 fn polyproto_dn_home_server_with_home_server_target_validates() {
810 let home_server_dn = create_valid_home_server_dn();
811 let pdn = PolyprotoDistinguishedName::HomeServerDn(home_server_dn);
812
813 let result = pdn.validate(Some(Target::HomeServer));
814 assert!(
815 result.is_ok(),
816 "HomeServerDn should validate with HomeServer target"
817 );
818 }
819
820 #[test]
821 fn polyproto_dn_home_server_with_none_target_validates() {
822 let home_server_dn = create_valid_home_server_dn();
823 let pdn = PolyprotoDistinguishedName::HomeServerDn(home_server_dn);
824
825 let result = pdn.validate(None);
826 assert!(
827 result.is_ok(),
828 "HomeServerDn should validate with None target"
829 );
830 }
831
832 #[test]
833 fn polyproto_dn_actor_with_home_server_target_fails() {
834 let actor_dn = create_valid_actor_dn();
835 let pdn = PolyprotoDistinguishedName::ActorDn(actor_dn);
836
837 let result = pdn.validate(Some(Target::HomeServer));
838 assert!(
839 result.is_err(),
840 "ActorDn should fail with HomeServer target"
841 );
842
843 if let Err(error) = result {
844 assert!(
845 error.to_string().contains("malformed"),
846 "Error should mention validation failure"
847 );
848 }
849 }
850
851 #[test]
852 fn polyproto_dn_home_server_with_actor_target_fails() {
853 let home_server_dn = create_valid_home_server_dn();
854 let pdn = PolyprotoDistinguishedName::HomeServerDn(home_server_dn);
855
856 let result = pdn.validate(Some(Target::Actor));
857 assert!(
858 result.is_err(),
859 "HomeServerDn should fail with Actor target"
860 );
861
862 if let Err(error) = result {
863 assert!(
864 error.to_string().contains("malformed"),
865 "Error should mention validation failure"
866 );
867 }
868 }
869
870 #[test]
871 fn actor_dn_valid_components_validates() {
872 let actor_dn = create_valid_actor_dn();
873
874 assert!(
875 actor_dn.validate(Some(Target::Actor)).is_ok(),
876 "Valid ActorDN should validate with Actor target"
877 );
878 assert!(
879 actor_dn.validate(None).is_ok(),
880 "Valid ActorDN should validate with None target"
881 );
882 }
883
884 #[test]
885 fn actor_dn_mismatched_domain_name_fails() {
886 let federation_id = create_valid_federation_id();
887 let local_name = create_valid_local_name();
888 let mismatched_domain = DomainName::new("different.com").expect("Valid domain name");
889 let session_id = create_valid_session_id();
890
891 let actor_dn = ActorDN {
892 federation_id,
893 local_name,
894 domain_name: mismatched_domain,
895 session_id,
896 additional_fields: create_empty_additional_fields(),
897 };
898
899 let result = actor_dn.validate(None);
900 assert!(
901 result.is_err(),
902 "ActorDN with mismatched domain name should fail"
903 );
904
905 if let Err(error) = result {
906 let error_str = error.to_string();
907 assert!(
908 error_str.contains("malformed"),
909 "Error should mention validation failure: {error_str}"
910 );
911 }
912 }
913
914 #[test]
915 fn actor_dn_mismatched_local_name_fails() {
916 let federation_id = create_valid_federation_id();
917 let mismatched_local_name = LocalName::new("bob").expect("Valid local name");
918 let domain_name = create_valid_domain_name();
919 let session_id = create_valid_session_id();
920
921 let actor_dn = ActorDN {
922 federation_id,
923 local_name: mismatched_local_name,
924 domain_name,
925 session_id,
926 additional_fields: create_empty_additional_fields(),
927 };
928
929 let result = actor_dn.validate(None);
930 assert!(
931 result.is_err(),
932 "ActorDN with mismatched local name should fail"
933 );
934
935 if let Err(error) = result {
936 let error_str = error.to_string();
937 assert!(
938 error_str.contains("malformed"),
939 "Error should mention validation failure: {error_str}"
940 );
941 }
942 }
943
944 #[test]
945 fn actor_dn_forbidden_domain_component_in_additional_fields_fails() {
946 let mut actor_dn = create_valid_actor_dn();
947 actor_dn.additional_fields =
948 create_forbidden_oid_additional_fields(OID_RDN_DOMAIN_COMPONENT);
949
950 let result = actor_dn.validate(Some(Target::Actor));
951 assert!(
952 result.is_err(),
953 "ActorDN with DOMAIN_COMPONENT in additional_fields should fail"
954 );
955 }
956
957 #[test]
958 fn actor_dn_forbidden_common_name_in_additional_fields_fails() {
959 let mut actor_dn = create_valid_actor_dn();
960 actor_dn.additional_fields =
961 create_forbidden_oid_additional_fields(OID_RDN_COMMON_NAME);
962
963 let result = actor_dn.validate(Some(Target::Actor));
964 assert!(
965 result.is_err(),
966 "ActorDN with COMMON_NAME in additional_fields should fail"
967 );
968 }
969
970 #[test]
971 fn actor_dn_forbidden_uid_in_additional_fields_fails() {
972 let mut actor_dn = create_valid_actor_dn();
973 actor_dn.additional_fields = create_forbidden_oid_additional_fields(OID_RDN_UID);
974
975 let result = actor_dn.validate(Some(Target::Actor));
976 assert!(
977 result.is_err(),
978 "ActorDN with UID in additional_fields should fail"
979 );
980 }
981
982 #[test]
983 fn actor_dn_forbidden_unique_identifier_in_additional_fields_fails() {
984 let mut actor_dn = create_valid_actor_dn();
985 actor_dn.additional_fields =
986 create_forbidden_oid_additional_fields(OID_RDN_UNIQUE_IDENTIFIER);
987
988 let result = actor_dn.validate(Some(Target::Actor));
989 assert!(
990 result.is_err(),
991 "ActorDN with UNIQUE_IDENTIFIER in additional_fields should fail"
992 );
993 }
994
995 #[test]
996 fn actor_dn_multiple_forbidden_oids_in_additional_fields_fails() {
997 let forbidden_attrs = vec![
998 create_test_attribute(OID_RDN_COMMON_NAME, "forbidden1"),
999 create_test_attribute(OID_RDN_UID, "forbidden2"),
1000 ];
1001 let forbidden_fields =
1002 RelativeDistinguishedName::try_from(forbidden_attrs).expect("Valid RDN");
1003
1004 let mut actor_dn = create_valid_actor_dn();
1005 actor_dn.additional_fields = forbidden_fields;
1006
1007 let result = actor_dn.validate(Some(Target::Actor));
1008 assert!(
1009 result.is_err(),
1010 "ActorDN with multiple forbidden OIDs should fail"
1011 );
1012 }
1013
1014 #[test]
1015 fn actor_dn_allowed_custom_oids_in_additional_fields_validates() {
1016 let custom_oid = ObjectIdentifier::new_unwrap("1.2.3.4.5");
1017 let custom_attr = create_test_attribute(custom_oid, "custom_value");
1018 let custom_fields =
1019 RelativeDistinguishedName::try_from(vec![custom_attr]).expect("Valid RDN");
1020
1021 let mut actor_dn = create_valid_actor_dn();
1022 actor_dn.additional_fields = custom_fields;
1023
1024 let result = actor_dn.validate(Some(Target::Actor));
1025 assert!(
1026 result.is_ok(),
1027 "ActorDN with custom OIDs in additional_fields should validate"
1028 );
1029 }
1030
1031 #[test]
1032 fn home_server_dn_valid_components_validates() {
1033 let home_server_dn = create_valid_home_server_dn();
1034
1035 assert!(
1036 home_server_dn.validate(Some(Target::HomeServer)).is_ok(),
1037 "Valid HomeServerDN should validate with HomeServer target"
1038 );
1039 assert!(
1040 home_server_dn.validate(None).is_ok(),
1041 "Valid HomeServerDN should validate with None target"
1042 );
1043 }
1044
1045 #[test]
1046 fn home_server_dn_forbidden_common_name_in_additional_fields_fails() {
1047 let mut home_server_dn = create_valid_home_server_dn();
1048 home_server_dn.additional_fields =
1049 create_forbidden_oid_additional_fields(OID_RDN_COMMON_NAME);
1050
1051 let result = home_server_dn.validate(Some(Target::HomeServer));
1052 assert!(
1053 result.is_err(),
1054 "HomeServerDN with COMMON_NAME in additional_fields should fail"
1055 );
1056 }
1057
1058 #[test]
1059 fn home_server_dn_forbidden_uid_in_additional_fields_fails() {
1060 let mut home_server_dn = create_valid_home_server_dn();
1061 home_server_dn.additional_fields = create_forbidden_oid_additional_fields(OID_RDN_UID);
1062
1063 let result = home_server_dn.validate(Some(Target::HomeServer));
1064 assert!(
1065 result.is_err(),
1066 "HomeServerDN with UID in additional_fields should fail"
1067 );
1068 }
1069
1070 #[test]
1071 fn home_server_dn_forbidden_unique_identifier_in_additional_fields_fails() {
1072 let mut home_server_dn = create_valid_home_server_dn();
1073 home_server_dn.additional_fields =
1074 create_forbidden_oid_additional_fields(OID_RDN_UNIQUE_IDENTIFIER);
1075
1076 let result = home_server_dn.validate(Some(Target::HomeServer));
1077 assert!(
1078 result.is_err(),
1079 "HomeServerDN with UNIQUE_IDENTIFIER in additional_fields should fail"
1080 );
1081 }
1082
1083 #[test]
1084 fn home_server_dn_forbidden_domain_component_in_additional_fields_fails() {
1085 let mut home_server_dn = create_valid_home_server_dn();
1086 home_server_dn.additional_fields =
1087 create_forbidden_oid_additional_fields(OID_RDN_DOMAIN_COMPONENT);
1088
1089 let result = home_server_dn.validate(Some(Target::HomeServer));
1090 assert!(
1091 result.is_err(),
1092 "HomeServerDN with DOMAIN_COMPONENT in additional_fields should fail"
1093 );
1094 }
1095
1096 #[test]
1097 fn home_server_dn_multiple_forbidden_oids_in_additional_fields_fails() {
1098 let forbidden_attrs = vec![
1099 create_test_attribute(OID_RDN_COMMON_NAME, "forbidden1"),
1100 create_test_attribute(OID_RDN_DOMAIN_COMPONENT, "forbidden2"),
1101 create_test_attribute(OID_RDN_UID, "forbidden3"),
1102 ];
1103 let forbidden_fields =
1104 RelativeDistinguishedName::try_from(forbidden_attrs).expect("Valid RDN");
1105
1106 let mut home_server_dn = create_valid_home_server_dn();
1107 home_server_dn.additional_fields = forbidden_fields;
1108
1109 let result = home_server_dn.validate(Some(Target::HomeServer));
1110 assert!(
1111 result.is_err(),
1112 "HomeServerDN with multiple forbidden OIDs should fail"
1113 );
1114 }
1115
1116 #[test]
1117 fn home_server_dn_allowed_custom_oids_in_additional_fields_validates() {
1118 let custom_oids = vec![
1119 ObjectIdentifier::new_unwrap("1.2.3.4.5"),
1120 ObjectIdentifier::new_unwrap("2.5.4.10"), ObjectIdentifier::new_unwrap("2.5.4.11"), ];
1123
1124 let custom_attrs: Vec<_> = custom_oids
1125 .into_iter()
1126 .enumerate()
1127 .map(|(i, oid)| create_test_attribute(oid, &format!("custom_value_{i}")))
1128 .collect();
1129
1130 let custom_fields =
1131 RelativeDistinguishedName::try_from(custom_attrs).expect("Valid RDN");
1132
1133 let mut home_server_dn = create_valid_home_server_dn();
1134 home_server_dn.additional_fields = custom_fields;
1135
1136 let result = home_server_dn.validate(Some(Target::HomeServer));
1137 assert!(
1138 result.is_ok(),
1139 "HomeServerDN with custom OIDs in additional_fields should validate"
1140 );
1141 }
1142
1143 #[test]
1144 fn actor_dn_empty_additional_fields_validates() {
1145 let actor_dn = create_valid_actor_dn();
1146 let result = actor_dn.validate(Some(Target::Actor));
1149 assert!(
1150 result.is_ok(),
1151 "ActorDN with empty additional_fields should validate"
1152 );
1153 }
1154
1155 #[test]
1156 fn home_server_dn_empty_additional_fields_validates() {
1157 let home_server_dn = create_valid_home_server_dn();
1158 let result = home_server_dn.validate(Some(Target::HomeServer));
1161 assert!(
1162 result.is_ok(),
1163 "HomeServerDN with empty additional_fields should validate"
1164 );
1165 }
1166
1167 #[test]
1168 fn actor_dn_new_validated_success() {
1169 let federation_id = create_valid_federation_id();
1170 let local_name = create_valid_local_name();
1171 let domain_name = create_valid_domain_name();
1172 let session_id = create_valid_session_id();
1173
1174 let result =
1175 ActorDN::new_validated(federation_id, local_name, domain_name, session_id, None);
1176
1177 assert!(
1178 result.is_ok(),
1179 "ActorDN::new_validated should succeed with valid components"
1180 );
1181 }
1182
1183 #[test]
1184 fn actor_dn_new_validated_with_additional_fields_success() {
1185 let federation_id = create_valid_federation_id();
1186 let local_name = create_valid_local_name();
1187 let domain_name = create_valid_domain_name();
1188 let session_id = create_valid_session_id();
1189 let custom_oid = ObjectIdentifier::new_unwrap("1.2.3.4.5");
1190 let custom_attr = create_test_attribute(custom_oid, "custom");
1191 let additional_fields =
1192 RelativeDistinguishedName::try_from(vec![custom_attr]).expect("Valid RDN");
1193
1194 let result = ActorDN::new_validated(
1195 federation_id,
1196 local_name,
1197 domain_name,
1198 session_id,
1199 Some(additional_fields),
1200 );
1201
1202 assert!(
1203 result.is_ok(),
1204 "ActorDN::new_validated should succeed with valid additional fields"
1205 );
1206 }
1207
1208 #[test]
1209 fn actor_dn_new_validated_mismatched_components_fails() {
1210 let federation_id = create_valid_federation_id();
1211 let mismatched_local_name = LocalName::new("bob").expect("Valid local name");
1212 let domain_name = create_valid_domain_name();
1213 let session_id = create_valid_session_id();
1214
1215 let result = ActorDN::new_validated(
1216 federation_id,
1217 mismatched_local_name,
1218 domain_name,
1219 session_id,
1220 None,
1221 );
1222
1223 assert!(
1224 result.is_err(),
1225 "ActorDN::new_validated should fail with mismatched components"
1226 );
1227 }
1228
1229 #[test]
1230 fn home_server_dn_new_validated_success() {
1231 let domain_name = create_valid_domain_name();
1232
1233 let result = HomeServerDN::new_validated(domain_name, None);
1234 assert!(
1235 result.is_ok(),
1236 "HomeServerDN::new_validated should succeed with valid components"
1237 );
1238 }
1239
1240 #[test]
1241 fn home_server_dn_new_validated_with_additional_fields_success() {
1242 let domain_name = create_valid_domain_name();
1243 let custom_oid = ObjectIdentifier::new_unwrap("2.5.4.10"); let custom_attr = create_test_attribute(custom_oid, "Example Org");
1245 let additional_fields =
1246 RelativeDistinguishedName::try_from(vec![custom_attr]).expect("Valid RDN");
1247
1248 let result = HomeServerDN::new_validated(domain_name, Some(additional_fields));
1249 assert!(
1250 result.is_ok(),
1251 "HomeServerDN::new_validated should succeed with valid additional fields"
1252 );
1253 }
1254
1255 #[test]
1256 fn home_server_dn_new_validated_forbidden_additional_fields_fails() {
1257 let domain_name = create_valid_domain_name();
1258 let forbidden_attr = create_test_attribute(OID_RDN_COMMON_NAME, "forbidden");
1259 let forbidden_fields =
1260 RelativeDistinguishedName::try_from(vec![forbidden_attr]).expect("Valid RDN");
1261
1262 let result = HomeServerDN::new_validated(domain_name, Some(forbidden_fields));
1263 assert!(
1264 result.is_err(),
1265 "HomeServerDN::new_validated should fail with forbidden additional fields"
1266 );
1267 }
1268 }
1269}