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