1
1
use super :: error:: CertError ;
2
2
use crate :: cli:: certificate:: show:: ShowCertCmd ;
3
- use crate :: command:: Command ;
3
+ use crate :: command:: CommandAsync ;
4
4
use crate :: log:: MaybeFancy ;
5
5
use camino:: Utf8PathBuf ;
6
6
use certificate:: KeyCertPair ;
7
7
use certificate:: KeyKind ;
8
8
use certificate:: NewCertificateConfig ;
9
9
use certificate:: PemCertificate ;
10
- use std:: fs:: File ;
11
- use std:: fs:: OpenOptions ;
12
- use std:: io:: prelude:: * ;
10
+ use std:: fs:: Permissions ;
11
+ use std:: os:: unix:: fs:: PermissionsExt ;
13
12
use std:: path:: Path ;
14
- use tedge_utils:: paths:: set_permission;
15
13
use tedge_utils:: paths:: validate_parent_dir_exists;
14
+ use tokio:: fs:: File ;
15
+ use tokio:: fs:: OpenOptions ;
16
+ use tokio:: io:: AsyncWriteExt ;
16
17
17
18
/// Create self-signed device certificate
18
19
pub struct CreateCertCmd {
@@ -30,25 +31,29 @@ pub struct CreateCertCmd {
30
31
pub group : String ,
31
32
}
32
33
33
- impl Command for CreateCertCmd {
34
+ #[ async_trait:: async_trait]
35
+ impl CommandAsync for CreateCertCmd {
34
36
fn description ( & self ) -> String {
35
37
format ! ( "create a test certificate for the device {}." , self . id)
36
38
}
37
39
38
- fn execute ( & self ) -> Result < ( ) , MaybeFancy < anyhow:: Error > > {
40
+ async fn execute ( & self ) -> Result < ( ) , MaybeFancy < anyhow:: Error > > {
39
41
let config = NewCertificateConfig :: default ( ) ;
40
- self . create_test_certificate ( & config) ?;
42
+ self . create_test_certificate ( & config) . await ?;
41
43
eprintln ! ( "Certificate was successfully created\n " ) ;
42
44
let show_cert_cmd = ShowCertCmd {
43
45
cert_path : self . cert_path . clone ( ) ,
44
46
} ;
45
- show_cert_cmd. execute ( ) ?;
47
+ show_cert_cmd. show_certificate ( ) . await ?;
46
48
Ok ( ( ) )
47
49
}
48
50
}
49
51
50
52
impl CreateCertCmd {
51
- pub fn create_test_certificate ( & self , config : & NewCertificateConfig ) -> Result < ( ) , CertError > {
53
+ pub async fn create_test_certificate (
54
+ & self ,
55
+ config : & NewCertificateConfig ,
56
+ ) -> Result < ( ) , CertError > {
52
57
let cert = KeyCertPair :: new_selfsigned_certificate ( config, & self . id , & KeyKind :: New ) ?;
53
58
54
59
let cert_path = & self . cert_path ;
@@ -58,6 +63,7 @@ impl CreateCertCmd {
58
63
& self . user ,
59
64
& self . group ,
60
65
)
66
+ . await
61
67
. map_err ( |err| err. cert_context ( cert_path. clone ( ) ) ) ?;
62
68
63
69
let key_path = & self . key_path ;
@@ -67,99 +73,119 @@ impl CreateCertCmd {
67
73
& self . user ,
68
74
& self . group ,
69
75
)
76
+ . await
70
77
. map_err ( |err| err. key_context ( key_path. clone ( ) ) ) ?;
71
78
Ok ( ( ) )
72
79
}
73
80
}
74
81
75
- pub fn persist_new_public_key (
82
+ pub async fn persist_new_public_key (
76
83
cert_path : & Utf8PathBuf ,
77
84
pem_string : String ,
78
85
user : & str ,
79
86
group : & str ,
80
87
) -> Result < ( ) , CertError > {
81
- validate_parent_dir_exists ( cert_path) . map_err ( CertError :: CertPathError ) ?;
82
- persist_public_key ( create_new_file ( cert_path, user, group) ?, pem_string) ?;
88
+ validate_parent_dir_exists ( cert_path)
89
+ . await
90
+ . map_err ( CertError :: CertPathError ) ?;
91
+ let key_file = create_new_file ( cert_path, user, group) . await ?;
92
+ persist_public_key ( key_file, pem_string) . await ?;
83
93
Ok ( ( ) )
84
94
}
85
95
86
- pub fn persist_new_private_key (
96
+ pub async fn persist_new_private_key (
87
97
key_path : & Utf8PathBuf ,
88
98
key : certificate:: Zeroizing < String > ,
89
99
user : & str ,
90
100
group : & str ,
91
101
) -> Result < ( ) , CertError > {
92
- validate_parent_dir_exists ( key_path) . map_err ( CertError :: KeyPathError ) ?;
93
- persist_private_key ( create_new_file ( key_path, user, group) ?, key) ?;
102
+ validate_parent_dir_exists ( key_path)
103
+ . await
104
+ . map_err ( CertError :: KeyPathError ) ?;
105
+ let key_file = create_new_file ( key_path, user, group) . await ?;
106
+ persist_private_key ( key_file, key) . await ?;
94
107
Ok ( ( ) )
95
108
}
96
109
97
- pub fn override_public_key ( cert_path : & Utf8PathBuf , pem_string : String ) -> Result < ( ) , CertError > {
98
- validate_parent_dir_exists ( cert_path) . map_err ( CertError :: CertPathError ) ?;
99
- persist_public_key ( override_file ( cert_path) ?, pem_string) ?;
110
+ pub async fn override_public_key (
111
+ cert_path : & Utf8PathBuf ,
112
+ pem_string : String ,
113
+ ) -> Result < ( ) , CertError > {
114
+ validate_parent_dir_exists ( cert_path)
115
+ . await
116
+ . map_err ( CertError :: CertPathError ) ?;
117
+ let key_file = override_file ( cert_path) . await ?;
118
+ persist_public_key ( key_file, pem_string) . await ?;
100
119
Ok ( ( ) )
101
120
}
102
121
103
- fn create_new_file (
104
- path : impl AsRef < Path > ,
122
+ async fn create_new_file (
123
+ path : & Utf8PathBuf ,
105
124
user : & str ,
106
125
group : & str ,
107
126
) -> Result < File , std:: io:: Error > {
108
127
let file = OpenOptions :: new ( )
109
128
. write ( true )
110
129
. create_new ( true )
111
- . open ( path. as_ref ( ) ) ?;
130
+ . open ( path)
131
+ . await ?;
112
132
113
133
// Ignore errors - This was the behavior with the now deprecated user manager.
114
134
// - When `tedge cert create` is not run as root, a certificate is created but owned by the user running the command.
115
135
// - A better approach could be to remove this `chown` and run the command as mosquitto.
116
- let _ = tedge_utils:: file:: change_user_and_group ( path. as_ref ( ) , user, group) ;
136
+ let _ = tedge_utils:: file_async:: change_user_and_group (
137
+ path. into ( ) ,
138
+ user. to_string ( ) ,
139
+ group. to_string ( ) ,
140
+ )
141
+ . await ;
117
142
118
143
Ok ( file)
119
144
}
120
145
121
- fn override_file ( path : impl AsRef < Path > ) -> Result < File , std:: io:: Error > {
146
+ async fn override_file ( path : impl AsRef < Path > ) -> Result < File , std:: io:: Error > {
122
147
OpenOptions :: new ( )
123
148
. write ( true )
124
149
. create ( true )
125
150
. truncate ( true )
126
151
. open ( path. as_ref ( ) )
152
+ . await
127
153
}
128
154
129
- pub fn reuse_private_key ( key_path : & Utf8PathBuf ) -> Result < KeyKind , std:: io:: Error > {
130
- std:: fs:: read_to_string ( key_path) . map ( |keypair_pem| KeyKind :: Reuse { keypair_pem } )
155
+ pub async fn reuse_private_key ( key_path : & Utf8PathBuf ) -> Result < KeyKind , std:: io:: Error > {
156
+ tokio:: fs:: read_to_string ( key_path)
157
+ . await
158
+ . map ( |keypair_pem| KeyKind :: Reuse { keypair_pem } )
131
159
}
132
160
133
- fn persist_private_key (
161
+ async fn persist_private_key (
134
162
mut key_file : File ,
135
163
cert_key : certificate:: Zeroizing < String > , // Zero the private key on drop
136
164
) -> Result < ( ) , std:: io:: Error > {
137
165
// Make sure the key is secret, before write
138
- set_permission ( & key_file, 0o600 ) ?;
139
- key_file. write_all ( cert_key. as_bytes ( ) ) ?;
140
- key_file. sync_all ( ) ?;
166
+ File :: set_permissions ( & key_file, Permissions :: from_mode ( 0o600 ) ) . await ?;
167
+ key_file. write_all ( cert_key. as_bytes ( ) ) . await ?;
168
+ key_file. sync_all ( ) . await ?;
141
169
142
170
// Prevent the key to be overwritten
143
- set_permission ( & key_file, 0o400 ) ?;
171
+ File :: set_permissions ( & key_file, Permissions :: from_mode ( 0o400 ) ) . await ?;
144
172
Ok ( ( ) )
145
173
}
146
174
147
- fn persist_public_key ( mut key_file : File , cert_pem : String ) -> Result < ( ) , std:: io:: Error > {
148
- key_file. write_all ( cert_pem. as_bytes ( ) ) ?;
149
- key_file. sync_all ( ) ?;
175
+ async fn persist_public_key ( mut key_file : File , cert_pem : String ) -> Result < ( ) , std:: io:: Error > {
176
+ key_file. write_all ( cert_pem. as_bytes ( ) ) . await ?;
177
+ key_file. sync_all ( ) . await ?;
150
178
151
179
// Make the file public
152
- set_permission ( & key_file, 0o444 ) ?;
180
+ File :: set_permissions ( & key_file, Permissions :: from_mode ( 0o444 ) ) . await ?;
153
181
Ok ( ( ) )
154
182
}
155
183
156
- pub fn cn_of_self_signed_certificate ( cert_path : & Utf8PathBuf ) -> Result < String , CertError > {
157
- let pem = PemCertificate :: from_pem_file ( cert_path) . map_err ( |err| match err {
158
- certificate:: CertificateError :: IoError { error, .. } => {
159
- CertError :: IoError ( error) . cert_context ( cert_path. clone ( ) )
160
- }
161
- from => CertError :: CertificateError ( from) ,
162
- } ) ?;
184
+ pub async fn cn_of_self_signed_certificate ( cert_path : & Utf8PathBuf ) -> Result < String , CertError > {
185
+ let cert = tokio:: fs:: read_to_string ( cert_path)
186
+ . await
187
+ . map_err ( |err| CertError :: IoError ( err) . cert_context ( cert_path. clone ( ) ) ) ?;
188
+ let pem = PemCertificate :: from_pem_string ( & cert) ?;
163
189
164
190
if pem. issuer ( ) ? == pem. subject ( ) ? {
165
191
Ok ( pem. subject_common_name ( ) ?)
@@ -177,8 +203,8 @@ mod tests {
177
203
use std:: fs;
178
204
use tempfile:: * ;
179
205
180
- #[ test]
181
- fn basic_usage ( ) {
206
+ #[ tokio :: test]
207
+ async fn basic_usage ( ) {
182
208
let dir = tempdir ( ) . unwrap ( ) ;
183
209
let cert_path = temp_file_path ( & dir, "my-device-cert.pem" ) ;
184
210
let key_path = temp_file_path ( & dir, "my-device-key.pem" ) ;
@@ -193,15 +219,16 @@ mod tests {
193
219
} ;
194
220
195
221
assert_matches ! (
196
- cmd. create_test_certificate( & NewCertificateConfig :: default ( ) ) ,
222
+ cmd. create_test_certificate( & NewCertificateConfig :: default ( ) )
223
+ . await ,
197
224
Ok ( ( ) )
198
225
) ;
199
226
assert_eq ! ( parse_pem_file( & cert_path) . unwrap( ) . tag, "CERTIFICATE" ) ;
200
227
assert_eq ! ( parse_pem_file( & key_path) . unwrap( ) . tag, "PRIVATE KEY" ) ;
201
228
}
202
229
203
- #[ test]
204
- fn check_certificate_is_not_overwritten ( ) {
230
+ #[ tokio :: test]
231
+ async fn check_certificate_is_not_overwritten ( ) {
205
232
let dir = tempdir ( ) . unwrap ( ) ;
206
233
207
234
let cert_path = temp_file_path ( & dir, "my-device-cert.pem" ) ;
@@ -223,15 +250,16 @@ mod tests {
223
250
224
251
assert ! ( cmd
225
252
. create_test_certificate( & NewCertificateConfig :: default ( ) )
253
+ . await
226
254
. ok( )
227
255
. is_none( ) ) ;
228
256
229
257
assert_eq ! ( fs:: read( & cert_path) . unwrap( ) , cert_content. as_bytes( ) ) ;
230
258
assert_eq ! ( fs:: read( & key_path) . unwrap( ) , key_content. as_bytes( ) ) ;
231
259
}
232
260
233
- #[ test]
234
- fn create_certificate_in_non_existent_directory ( ) {
261
+ #[ tokio :: test]
262
+ async fn create_certificate_in_non_existent_directory ( ) {
235
263
let dir = tempdir ( ) . unwrap ( ) ;
236
264
let key_path = temp_file_path ( & dir, "my-device-key.pem" ) ;
237
265
let cert_path = Utf8PathBuf :: from ( "/non/existent/cert/path" ) ;
@@ -246,12 +274,13 @@ mod tests {
246
274
247
275
let cert_error = cmd
248
276
. create_test_certificate ( & NewCertificateConfig :: default ( ) )
277
+ . await
249
278
. unwrap_err ( ) ;
250
279
assert_matches ! ( cert_error, CertError :: CertPathError { .. } ) ;
251
280
}
252
281
253
- #[ test]
254
- fn create_key_in_non_existent_directory ( ) {
282
+ #[ tokio :: test]
283
+ async fn create_key_in_non_existent_directory ( ) {
255
284
let dir = tempdir ( ) . unwrap ( ) ;
256
285
let cert_path = temp_file_path ( & dir, "my-device-cert.pem" ) ;
257
286
let key_path = Utf8PathBuf :: from ( "/non/existent/key/path" ) ;
@@ -266,6 +295,7 @@ mod tests {
266
295
267
296
let cert_error = cmd
268
297
. create_test_certificate ( & NewCertificateConfig :: default ( ) )
298
+ . await
269
299
. unwrap_err ( ) ;
270
300
assert_matches ! ( cert_error, CertError :: KeyPathError { .. } ) ;
271
301
}
0 commit comments