5
5
use der:: Encode ;
6
6
use regex:: Regex ;
7
7
use x509_cert:: attr:: AttributeTypeAndValue ;
8
+ use x509_cert:: name:: RdnSequence ;
8
9
9
10
use crate :: errors:: { ConstraintError , ERR_MSG_FEDERATION_ID_REGEX , InvalidInput } ;
10
- use crate :: { Constrained , OID_RDN_UID } ;
11
+ use crate :: { Constrained , OID_RDN_DOMAIN_COMPONENT , OID_RDN_UID } ;
11
12
12
13
/// The regular expression for a valid `FederationId`.
13
14
pub static REGEX_FEDERATION_ID : & str = r"\b([a-z0-9._%+-]+)@([a-z0-9-]+(\.[a-z0-9-]+)*)$" ;
@@ -51,6 +52,41 @@ impl std::fmt::Display for DomainName {
51
52
}
52
53
}
53
54
55
+ impl TryFrom < & [ AttributeTypeAndValue ] > for DomainName {
56
+ type Error = ConstraintError ;
57
+
58
+ fn try_from ( values : & [ AttributeTypeAndValue ] ) -> Result < Self , Self :: Error > {
59
+ if let Some ( non_rdn) = values
60
+ . iter ( )
61
+ . find ( |element| element. oid != OID_RDN_DOMAIN_COMPONENT )
62
+ {
63
+ return Err ( ConstraintError :: Malformed ( Some ( format ! (
64
+ "Found a value with OID {} when expecting only DomainComponent values of OID {OID_RDN_DOMAIN_COMPONENT}" ,
65
+ non_rdn. oid
66
+ ) ) ) ) ;
67
+ }
68
+ let mut domain_components = Vec :: with_capacity ( values. len ( ) ) ;
69
+ for value in values. iter ( ) {
70
+ let attribute_value = value. value . value ( ) ;
71
+ let string = String :: from_utf8_lossy ( attribute_value) ;
72
+ domain_components. push ( string) ;
73
+ }
74
+ DomainName :: new ( domain_components. join ( "." ) . as_str ( ) )
75
+ }
76
+ }
77
+
78
+ impl From < DomainName > for AttributeTypeAndValue {
79
+ fn from ( value : DomainName ) -> Self {
80
+ todo ! ( )
81
+ }
82
+ }
83
+
84
+ impl From < DomainName > for RdnSequence {
85
+ fn from ( value : DomainName ) -> Self {
86
+ todo ! ( )
87
+ }
88
+ }
89
+
54
90
#[ derive( Debug , Clone , PartialEq , Eq , Hash , PartialOrd , Ord ) ]
55
91
/// A `FederationId` is a globally unique identifier for an actor in the context of polyproto.
56
92
pub struct FederationId {
@@ -248,6 +284,7 @@ impl TryFrom<AttributeTypeAndValue> for FederationId {
248
284
}
249
285
250
286
#[ cfg( test) ]
287
+ #[ allow( clippy:: unwrap_used) ]
251
288
mod test {
252
289
use der:: Any ;
253
290
use x509_cert:: ext:: pkix:: name:: DirectoryString ;
@@ -271,4 +308,69 @@ mod test {
271
308
} ;
272
309
assert ! ( FederationId :: try_from( attribute_and_value) . is_err( ) )
273
310
}
311
+
312
+ #[ test]
313
+ #[ allow( clippy:: unwrap_used) ]
314
+ fn domain_name_from_attribute_type_and_value_success ( ) {
315
+ // Test successful parsing with valid domain components
316
+ let domain_components = vec ! [
317
+ AttributeTypeAndValue {
318
+ oid: OID_RDN_DOMAIN_COMPONENT ,
319
+ value: Any :: encode_from( & DirectoryString :: Utf8String ( "example" . to_string( ) ) )
320
+ . unwrap( ) ,
321
+ } ,
322
+ AttributeTypeAndValue {
323
+ oid: OID_RDN_DOMAIN_COMPONENT ,
324
+ value: Any :: encode_from( & DirectoryString :: Utf8String ( "com" . to_string( ) ) ) . unwrap( ) ,
325
+ } ,
326
+ ] ;
327
+
328
+ let result = DomainName :: try_from ( domain_components. as_slice ( ) ) ;
329
+ assert ! ( result. is_ok( ) ) ;
330
+ let domain_name = result. unwrap ( ) ;
331
+ assert_eq ! ( domain_name. to_string( ) , "example.com" ) ;
332
+ }
333
+
334
+ #[ test]
335
+ #[ allow( clippy:: unwrap_used) ]
336
+ fn domain_name_from_attribute_type_and_value_invalid_oid ( ) {
337
+ // Test error case with invalid OID
338
+ let invalid_components = vec ! [
339
+ AttributeTypeAndValue {
340
+ oid: OID_RDN_DOMAIN_COMPONENT ,
341
+ value: Any :: encode_from( & DirectoryString :: Utf8String ( "example" . to_string( ) ) )
342
+ . unwrap( ) ,
343
+ } ,
344
+ AttributeTypeAndValue {
345
+ oid: OID_RDN_UID , // Wrong OID - should be OID_RDN_DOMAIN_COMPONENT
346
+ value: Any :: encode_from( & DirectoryString :: Utf8String ( "com" . to_string( ) ) ) . unwrap( ) ,
347
+ } ,
348
+ ] ;
349
+
350
+ let result = DomainName :: try_from ( invalid_components. as_slice ( ) ) ;
351
+ assert ! ( result. is_err( ) ) ;
352
+ match result. unwrap_err ( ) {
353
+ ConstraintError :: Malformed ( Some ( msg) ) => {
354
+ assert ! ( msg. contains( "Found a value with OID" ) ) ;
355
+ assert ! ( msg. contains( "when expecting only DomainComponent values" ) ) ;
356
+ }
357
+ _ => panic ! ( "Expected ConstraintError::Malformed with message" ) ,
358
+ }
359
+ }
360
+
361
+ #[ test]
362
+ fn domain_name_from_empty_attribute_array ( ) {
363
+ // Test edge case with empty input
364
+ let empty_components: Vec < AttributeTypeAndValue > = vec ! [ ] ;
365
+ let result = DomainName :: try_from ( empty_components. as_slice ( ) ) ;
366
+
367
+ // Empty input should attempt to create an empty domain name, which should fail validation
368
+ assert ! ( result. is_err( ) ) ;
369
+ match result. unwrap_err ( ) {
370
+ ConstraintError :: Malformed ( Some ( msg) ) => {
371
+ assert ! ( msg. contains( "does not match regex" ) ) ;
372
+ }
373
+ _ => panic ! ( "Expected ConstraintError::Malformed for invalid domain name" ) ,
374
+ }
375
+ }
274
376
}
0 commit comments