@@ -27,17 +27,66 @@ fn example_secret() -> &'static str {
27
27
"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff"
28
28
}
29
29
30
+ /// Password config option.
31
+ ///
32
+ /// It either holds the password value directly or references a file where the
33
+ /// password is stored.
34
+ #[ derive( Clone , Debug ) ]
35
+ pub enum Password {
36
+ File ( Utf8PathBuf ) ,
37
+ Value ( String ) ,
38
+ }
39
+
40
+ /// Password fields as serialized in JSON.
41
+ #[ derive( JsonSchema , Serialize , Deserialize , Clone , Debug ) ]
42
+ struct PasswordRaw {
43
+ #[ schemars( with = "Option<String>" ) ]
44
+ password_file : Option < Utf8PathBuf > ,
45
+ password : Option < String > ,
46
+ }
47
+
48
+ impl TryFrom < PasswordRaw > for Option < Password > {
49
+ type Error = anyhow:: Error ;
50
+
51
+ fn try_from ( value : PasswordRaw ) -> Result < Self , Self :: Error > {
52
+ match ( value. password , value. password_file ) {
53
+ ( None , None ) => Ok ( None ) ,
54
+ ( None , Some ( path) ) => Ok ( Some ( Password :: File ( path) ) ) ,
55
+ ( Some ( password) , None ) => Ok ( Some ( Password :: Value ( password) ) ) ,
56
+ ( Some ( _) , Some ( _) ) => bail ! ( "Cannot specify both `password` and `password_file`" ) ,
57
+ }
58
+ }
59
+ }
60
+
61
+ impl From < Option < Password > > for PasswordRaw {
62
+ fn from ( value : Option < Password > ) -> Self {
63
+ match value {
64
+ Some ( Password :: File ( path) ) => PasswordRaw {
65
+ password_file : Some ( path) ,
66
+ password : None ,
67
+ } ,
68
+ Some ( Password :: Value ( password) ) => PasswordRaw {
69
+ password_file : None ,
70
+ password : Some ( password) ,
71
+ } ,
72
+ None => PasswordRaw {
73
+ password_file : None ,
74
+ password : None ,
75
+ } ,
76
+ }
77
+ }
78
+ }
79
+
30
80
/// A single key with its key ID and optional password.
81
+ #[ serde_as]
31
82
#[ derive( JsonSchema , Serialize , Deserialize , Clone , Debug ) ]
32
83
pub struct KeyConfig {
33
84
kid : String ,
34
85
35
- #[ serde( skip_serializing_if = "Option::is_none" ) ]
36
- password : Option < String > ,
37
-
38
- #[ serde( skip_serializing_if = "Option::is_none" ) ]
39
- #[ schemars( with = "Option<String>" ) ]
40
- password_file : Option < Utf8PathBuf > ,
86
+ #[ schemars( with = "PasswordRaw" ) ]
87
+ #[ serde_as( as = "serde_with::TryFromInto<PasswordRaw>" ) ]
88
+ #[ serde( flatten) ]
89
+ password : Option < Password > ,
41
90
42
91
#[ serde( skip_serializing_if = "Option::is_none" ) ]
43
92
key : Option < String > ,
@@ -47,6 +96,19 @@ pub struct KeyConfig {
47
96
key_file : Option < Utf8PathBuf > ,
48
97
}
49
98
99
+ impl KeyConfig {
100
+ /// Returns the password in case any is provided.
101
+ ///
102
+ /// If `password_file` was given, the password is read from that file.
103
+ async fn password ( & self ) -> anyhow:: Result < Option < Cow < String > > > {
104
+ Ok ( match & self . password {
105
+ Some ( Password :: File ( path) ) => Some ( Cow :: Owned ( tokio:: fs:: read_to_string ( path) . await ?) ) ,
106
+ Some ( Password :: Value ( password) ) => Some ( Cow :: Borrowed ( password) ) ,
107
+ None => None ,
108
+ } )
109
+ }
110
+ }
111
+
50
112
/// Application secrets
51
113
#[ serde_as]
52
114
#[ derive( Debug , Clone , Serialize , Deserialize , JsonSchema ) ]
@@ -75,14 +137,7 @@ impl SecretsConfig {
75
137
pub async fn key_store ( & self ) -> anyhow:: Result < Keystore > {
76
138
let mut keys = Vec :: with_capacity ( self . keys . len ( ) ) ;
77
139
for item in & self . keys {
78
- let password = match ( & item. password , & item. password_file ) {
79
- ( None , None ) => None ,
80
- ( Some ( _) , Some ( _) ) => {
81
- bail ! ( "Cannot specify both `password` and `password_file`" )
82
- }
83
- ( Some ( password) , None ) => Some ( Cow :: Borrowed ( password) ) ,
84
- ( None , Some ( path) ) => Some ( Cow :: Owned ( tokio:: fs:: read_to_string ( path) . await ?) ) ,
85
- } ;
140
+ let password = item. password ( ) . await ?;
86
141
87
142
// Read the key either embedded in the config file or on disk
88
143
let key = match ( & item. key , & item. key_file ) {
@@ -154,12 +209,6 @@ impl ConfigurationSection for SecretsConfig {
154
209
"Cannot specify both `key` and `key_file`" . to_owned ( ) ,
155
210
) ) ;
156
211
}
157
-
158
- if key. password . is_some ( ) && key. password_file . is_some ( ) {
159
- return annotate ( figment:: Error :: from (
160
- "Cannot specify both `password` and `password_file`" . to_owned ( ) ,
161
- ) ) ;
162
- }
163
212
}
164
213
165
214
Ok ( ( ) )
@@ -187,7 +236,6 @@ impl SecretsConfig {
187
236
let rsa_key = KeyConfig {
188
237
kid : Alphanumeric . sample_string ( & mut rng, 10 ) ,
189
238
password : None ,
190
- password_file : None ,
191
239
key : Some ( rsa_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
192
240
key_file : None ,
193
241
} ;
@@ -205,7 +253,6 @@ impl SecretsConfig {
205
253
let ec_p256_key = KeyConfig {
206
254
kid : Alphanumeric . sample_string ( & mut rng, 10 ) ,
207
255
password : None ,
208
- password_file : None ,
209
256
key : Some ( ec_p256_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
210
257
key_file : None ,
211
258
} ;
@@ -223,7 +270,6 @@ impl SecretsConfig {
223
270
let ec_p384_key = KeyConfig {
224
271
kid : Alphanumeric . sample_string ( & mut rng, 10 ) ,
225
272
password : None ,
226
- password_file : None ,
227
273
key : Some ( ec_p384_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
228
274
key_file : None ,
229
275
} ;
@@ -241,7 +287,6 @@ impl SecretsConfig {
241
287
let ec_k256_key = KeyConfig {
242
288
kid : Alphanumeric . sample_string ( & mut rng, 10 ) ,
243
289
password : None ,
244
- password_file : None ,
245
290
key : Some ( ec_k256_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
246
291
key_file : None ,
247
292
} ;
@@ -256,7 +301,6 @@ impl SecretsConfig {
256
301
let rsa_key = KeyConfig {
257
302
kid : "abcdef" . to_owned ( ) ,
258
303
password : None ,
259
- password_file : None ,
260
304
key : Some (
261
305
indoc:: indoc! { r"
262
306
-----BEGIN PRIVATE KEY-----
@@ -277,7 +321,6 @@ impl SecretsConfig {
277
321
let ecdsa_key = KeyConfig {
278
322
kid : "ghijkl" . to_owned ( ) ,
279
323
password : None ,
280
- password_file : None ,
281
324
key : Some (
282
325
indoc:: indoc! { r"
283
326
-----BEGIN PRIVATE KEY-----
0 commit comments