1+ // This Source Code Form is subject to the terms of the Mozilla Public
2+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+ #![ cfg( feature = "types" ) ]
6+
7+ use std:: str:: FromStr ;
8+
9+ use polyproto:: certs:: Target ;
10+ use polyproto:: types:: pdn:: ActorDN ;
11+ use polyproto:: Name ;
12+ use x509_cert:: Certificate ;
13+
14+ use crate :: common:: { actor_id_cert, actor_subject, gen_priv_key, init_logger, test_all_platforms} ;
15+
16+ test_all_platforms ! {
17+ fn test_actor_dn_from_valid_id_cert_name( ) {
18+ init_logger( ) ;
19+
20+ // Generate a valid actor ID certificate
21+ let cert = actor_id_cert( "testuser" ) ;
22+
23+ // Convert to x509_cert::Certificate to access the subject
24+ let x509_cert = Certificate :: try_from( cert) . unwrap( ) ;
25+ let subject_name = Name :: from( x509_cert. tbs_certificate. subject) ;
26+
27+ // Test the TryFrom<Name> conversion - should succeed
28+ let result = ActorDN :: try_from( subject_name) ;
29+ assert!( result. is_ok( ) , "Failed to convert valid certificate subject to ActorDN" ) ;
30+
31+ let _actor_dn = result. unwrap( ) ;
32+ // Since ActorDN fields are private and no getters exist,
33+ // we can only test that the conversion succeeds for valid input
34+ }
35+ }
36+
37+ test_all_platforms ! {
38+ fn test_actor_dn_from_different_usernames( ) {
39+ init_logger( ) ;
40+
41+ let test_cases = [ "alice" , "bob" , "charlie123" ] ;
42+
43+ for username in test_cases. iter( ) {
44+ // Generate ID cert for each username
45+ let cert = actor_id_cert( username) ;
46+ let x509_cert = Certificate :: try_from( cert) . unwrap( ) ;
47+ let subject_name = Name :: from( x509_cert. tbs_certificate. subject) ;
48+
49+ // Convert to ActorDN - should succeed for all valid usernames
50+ let result = ActorDN :: try_from( subject_name) ;
51+ assert!( result. is_ok( ) , "Failed to convert valid certificate for user: {}" , username) ;
52+ }
53+ }
54+ }
55+
56+ test_all_platforms ! {
57+ fn test_actor_dn_custom_subject_name( ) {
58+ init_logger( ) ;
59+
60+ let priv_key = gen_priv_key( ) ;
61+
62+ // Create a custom subject with specific components
63+ let subject = actor_subject( "customuser" ) ;
64+
65+ let csr = polyproto:: certs:: idcsr:: IdCsr :: new(
66+ & subject,
67+ & priv_key,
68+ & polyproto:: certs:: capabilities:: Capabilities :: default_actor( ) ,
69+ Some ( Target :: Actor ) ,
70+ ) . unwrap( ) ;
71+
72+ let cert = polyproto:: certs:: idcert:: IdCert :: from_actor_csr(
73+ csr,
74+ & priv_key,
75+ polyproto:: types:: x509_cert:: SerialNumber :: from_bytes_be( & [ 1 ] ) . unwrap( ) ,
76+ crate :: common:: home_server_subject( ) ,
77+ crate :: common:: default_validity( ) ,
78+ ) . unwrap( ) ;
79+
80+ // Extract subject and convert
81+ let x509_cert = Certificate :: try_from( cert) . unwrap( ) ;
82+ let subject_name = Name :: from( x509_cert. tbs_certificate. subject) ;
83+ let result = ActorDN :: try_from( subject_name) ;
84+
85+ // Should succeed with custom user
86+ assert!( result. is_ok( ) , "Failed to convert custom certificate subject to ActorDN" ) ;
87+ }
88+ }
89+
90+ test_all_platforms ! {
91+ fn test_actor_dn_missing_federation_id( ) {
92+ init_logger( ) ;
93+
94+ // Create a malformed subject missing the UID (Federation ID)
95+ let malformed_subject = Name :: from_str( "CN=testuser,DC=polyphony,DC=chat,uniqueIdentifier=client1" ) . unwrap( ) ;
96+
97+ // Should fail because Federation ID (UID) is missing
98+ let result = ActorDN :: try_from( malformed_subject) ;
99+ assert!( result. is_err( ) ) ;
100+
101+ let error = result. unwrap_err( ) ;
102+ println!( "Missing Federation ID error: {}" , error) ;
103+ // This fails at the Name validation level before reaching ActorDN parsing
104+ assert!( error. to_string( ) . contains( "malformed" ) || error. to_string( ) . contains( "validation" ) ) ;
105+ }
106+ }
107+
108+ test_all_platforms ! {
109+ fn test_actor_dn_missing_local_name( ) {
110+ init_logger( ) ;
111+
112+ // Create a malformed subject missing the CN (Local Name)
113+ let malformed_subject =
Name :: from_str
( "DC=polyphony,DC=chat,[email protected] ,uniqueIdentifier=client1" ) . unwrap
( ) ; 114+
115+ // Should fail because Local Name (CN) is missing
116+ let result = ActorDN :: try_from( malformed_subject) ;
117+ assert!( result. is_err( ) ) ;
118+
119+ let error = result. unwrap_err( ) ;
120+ assert!( error. to_string( ) . contains( "Expected Local Name in ActorDN, found none" ) ) ;
121+ }
122+ }
123+
124+ test_all_platforms ! {
125+ fn test_actor_dn_missing_session_id( ) {
126+ init_logger( ) ;
127+
128+ // Create a malformed subject missing the uniqueIdentifier (Session ID)
129+ let malformed_subject =
Name :: from_str
( "CN=testuser,DC=polyphony,DC=chat,[email protected] " ) . unwrap
( ) ; 130+
131+ // Should fail because Session ID (uniqueIdentifier) is missing
132+ let result = ActorDN :: try_from( malformed_subject) ;
133+ assert!( result. is_err( ) ) ;
134+
135+ let error = result. unwrap_err( ) ;
136+ println!( "Missing Session ID error: {}" , error) ;
137+ // This fails at the Name validation level before reaching ActorDN parsing
138+ assert!( error. to_string( ) . contains( "expected to be between" ) || error. to_string( ) . contains( "malformed" ) ) ;
139+ }
140+ }
141+
142+ test_all_platforms ! {
143+ fn test_actor_dn_duplicate_oid_error( ) {
144+ init_logger( ) ;
145+
146+ // Create a malformed subject with duplicate CN values
147+ let malformed_subject =
Name :: from_str
( "CN=testuser,CN=duplicate,DC=polyphony,DC=chat,[email protected] ,uniqueIdentifier=client1" ) . unwrap
( ) ; 148+
149+ // Should fail because of duplicate OID
150+ let result = ActorDN :: try_from( malformed_subject) ;
151+ assert!( result. is_err( ) ) ;
152+
153+ let error = result. unwrap_err( ) ;
154+ println!( "Duplicate OID error: {}" , error) ;
155+ // This fails at the Name validation level before reaching ActorDN parsing
156+ assert!( error. to_string( ) . contains( "expected to be between" ) || error. to_string( ) . contains( "malformed" ) ) ;
157+ }
158+ }
159+
160+ test_all_platforms ! {
161+ fn test_actor_dn_complex_distinguished_name( ) {
162+ init_logger( ) ;
163+
164+ // Create a more complex distinguished name with additional fields
165+ let complex_subject = Name :: from_str(
166+ "CN=testuser,O=TestOrg,OU=TestUnit,DC=polyphony,DC=chat,[email protected] ,uniqueIdentifier=client1" 167+ ) . unwrap( ) ;
168+
169+ let result = ActorDN :: try_from( complex_subject) ;
170+
171+ // This should succeed, with additional fields captured
172+ assert!( result. is_ok( ) , "Complex distinguished name should be parsed successfully" ) ;
173+ }
174+ }
175+
176+ test_all_platforms ! {
177+ fn test_actor_dn_roundtrip_conversion( ) {
178+ init_logger( ) ;
179+
180+ // Generate multiple certificates and ensure they convert properly
181+ let test_users = [ "alice" , "bob" , "charlie" ] ;
182+
183+ for user in test_users. iter( ) {
184+ // Create certificate
185+ let cert = actor_id_cert( user) ;
186+ let x509_cert = Certificate :: try_from( cert) . unwrap( ) ;
187+ let original_subject = Name :: from( x509_cert. tbs_certificate. subject) ;
188+
189+ // Convert to ActorDN - should succeed for all valid users
190+ let result = ActorDN :: try_from( original_subject. clone( ) ) ;
191+ assert!( result. is_ok( ) , "Roundtrip conversion should succeed for user: {}" , user) ;
192+ }
193+ }
194+ }
0 commit comments