@@ -77,6 +77,52 @@ impl From<Option<Password>> for PasswordRaw {
77
77
}
78
78
}
79
79
80
+ /// Key config option.
81
+ ///
82
+ /// It either holds the key value directly or references a file where the key is
83
+ /// stored.
84
+ #[ derive( Clone , Debug ) ]
85
+ pub enum Key {
86
+ File ( Utf8PathBuf ) ,
87
+ Value ( String ) ,
88
+ }
89
+
90
+ /// Key fields as serialized in JSON.
91
+ #[ derive( JsonSchema , Serialize , Deserialize , Clone , Debug ) ]
92
+ struct KeyRaw {
93
+ #[ schemars( with = "Option<String>" ) ]
94
+ key_file : Option < Utf8PathBuf > ,
95
+ key : Option < String > ,
96
+ }
97
+
98
+ impl TryFrom < KeyRaw > for Key {
99
+ type Error = anyhow:: Error ;
100
+
101
+ fn try_from ( value : KeyRaw ) -> Result < Key , Self :: Error > {
102
+ match ( value. key , value. key_file ) {
103
+ ( None , None ) => bail ! ( "Missing `key` or `key_file`" ) ,
104
+ ( None , Some ( path) ) => Ok ( Key :: File ( path) ) ,
105
+ ( Some ( key) , None ) => Ok ( Key :: Value ( key) ) ,
106
+ ( Some ( _) , Some ( _) ) => bail ! ( "Cannot specify both `key` and `key_file`" ) ,
107
+ }
108
+ }
109
+ }
110
+
111
+ impl From < Key > for KeyRaw {
112
+ fn from ( value : Key ) -> Self {
113
+ match value {
114
+ Key :: File ( path) => KeyRaw {
115
+ key_file : Some ( path) ,
116
+ key : None ,
117
+ } ,
118
+ Key :: Value ( key) => KeyRaw {
119
+ key_file : None ,
120
+ key : Some ( key) ,
121
+ } ,
122
+ }
123
+ }
124
+ }
125
+
80
126
/// A single key with its key ID and optional password.
81
127
#[ serde_as]
82
128
#[ derive( JsonSchema , Serialize , Deserialize , Clone , Debug ) ]
@@ -88,12 +134,10 @@ pub struct KeyConfig {
88
134
#[ serde( flatten) ]
89
135
password : Option < Password > ,
90
136
91
- #[ serde( skip_serializing_if = "Option::is_none" ) ]
92
- key : Option < String > ,
93
-
94
- #[ serde( skip_serializing_if = "Option::is_none" ) ]
95
- #[ schemars( with = "Option<String>" ) ]
96
- key_file : Option < Utf8PathBuf > ,
137
+ #[ schemars( with = "KeyRaw" ) ]
138
+ #[ serde_as( as = "serde_with::TryFromInto<KeyRaw>" ) ]
139
+ #[ serde( flatten) ]
140
+ key : Key ,
97
141
}
98
142
99
143
impl KeyConfig {
@@ -107,6 +151,16 @@ impl KeyConfig {
107
151
None => None ,
108
152
} )
109
153
}
154
+
155
+ /// Returns the key.
156
+ ///
157
+ /// If `key_file` was given, the key is read from that file.
158
+ async fn key ( & self ) -> anyhow:: Result < Cow < String > > {
159
+ Ok ( match & self . key {
160
+ Key :: File ( path) => Cow :: Owned ( tokio:: fs:: read_to_string ( path) . await ?) ,
161
+ Key :: Value ( key) => Cow :: Borrowed ( key) ,
162
+ } )
163
+ }
110
164
}
111
165
112
166
/// Application secrets
@@ -139,31 +193,13 @@ impl SecretsConfig {
139
193
for item in & self . keys {
140
194
let password = item. password ( ) . await ?;
141
195
142
- // Read the key either embedded in the config file or on disk
143
- let key = match ( & item. key , & item. key_file ) {
144
- ( None , None ) => bail ! ( "Missing `key` or `key_file`" ) ,
145
- ( Some ( _) , Some ( _) ) => bail ! ( "Cannot specify both `key` and `key_file`" ) ,
146
- ( Some ( key) , None ) => {
147
- // If the key was embedded in the config file, assume it is formatted as PEM
148
- if let Some ( password) = password {
149
- PrivateKey :: load_encrypted_pem ( key, password. as_bytes ( ) ) ?
150
- } else {
151
- PrivateKey :: load_pem ( key) ?
152
- }
153
- }
154
- ( None , Some ( path) ) => {
155
- // When reading from disk, it might be either PEM or DER. `PrivateKey::load*`
156
- // will try both.
157
- let key = tokio:: fs:: read ( path) . await ?;
158
- if let Some ( password) = password {
159
- PrivateKey :: load_encrypted ( & key, password. as_bytes ( ) ) ?
160
- } else {
161
- PrivateKey :: load ( & key) ?
162
- }
163
- }
196
+ let key = item. key ( ) . await ?;
197
+ let private_key = match password {
198
+ Some ( password) => PrivateKey :: load_encrypted ( key. as_bytes ( ) , password. as_bytes ( ) ) ?,
199
+ None => PrivateKey :: load ( key. as_bytes ( ) ) ?,
164
200
} ;
165
201
166
- let key = JsonWebKey :: new ( key )
202
+ let key = JsonWebKey :: new ( private_key )
167
203
. with_kid ( item. kid . clone ( ) )
168
204
. with_use ( mas_iana:: jose:: JsonWebKeyUse :: Sig ) ;
169
205
keys. push ( key) ;
@@ -183,34 +219,7 @@ impl SecretsConfig {
183
219
impl ConfigurationSection for SecretsConfig {
184
220
const PATH : Option < & ' static str > = Some ( "secrets" ) ;
185
221
186
- fn validate ( & self , figment : & figment:: Figment ) -> Result < ( ) , figment:: Error > {
187
- for ( index, key) in self . keys . iter ( ) . enumerate ( ) {
188
- let annotate = |mut error : figment:: Error | {
189
- error. metadata = figment
190
- . find_metadata ( & format ! ( "{root}.keys" , root = Self :: PATH . unwrap( ) ) )
191
- . cloned ( ) ;
192
- error. profile = Some ( figment:: Profile :: Default ) ;
193
- error. path = vec ! [
194
- Self :: PATH . unwrap( ) . to_owned( ) ,
195
- "keys" . to_owned( ) ,
196
- index. to_string( ) ,
197
- ] ;
198
- Err ( error)
199
- } ;
200
-
201
- if key. key . is_none ( ) && key. key_file . is_none ( ) {
202
- return annotate ( figment:: Error :: from (
203
- "Missing `key` or `key_file`" . to_owned ( ) ,
204
- ) ) ;
205
- }
206
-
207
- if key. key . is_some ( ) && key. key_file . is_some ( ) {
208
- return annotate ( figment:: Error :: from (
209
- "Cannot specify both `key` and `key_file`" . to_owned ( ) ,
210
- ) ) ;
211
- }
212
- }
213
-
222
+ fn validate ( & self , _figment : & figment:: Figment ) -> Result < ( ) , figment:: Error > {
214
223
Ok ( ( ) )
215
224
}
216
225
}
@@ -236,8 +245,7 @@ impl SecretsConfig {
236
245
let rsa_key = KeyConfig {
237
246
kid : Alphanumeric . sample_string ( & mut rng, 10 ) ,
238
247
password : None ,
239
- key : Some ( rsa_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
240
- key_file : None ,
248
+ key : Key :: Value ( rsa_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
241
249
} ;
242
250
243
251
let span = tracing:: info_span!( "ec_p256" ) ;
@@ -253,8 +261,7 @@ impl SecretsConfig {
253
261
let ec_p256_key = KeyConfig {
254
262
kid : Alphanumeric . sample_string ( & mut rng, 10 ) ,
255
263
password : None ,
256
- key : Some ( ec_p256_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
257
- key_file : None ,
264
+ key : Key :: Value ( ec_p256_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
258
265
} ;
259
266
260
267
let span = tracing:: info_span!( "ec_p384" ) ;
@@ -270,8 +277,7 @@ impl SecretsConfig {
270
277
let ec_p384_key = KeyConfig {
271
278
kid : Alphanumeric . sample_string ( & mut rng, 10 ) ,
272
279
password : None ,
273
- key : Some ( ec_p384_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
274
- key_file : None ,
280
+ key : Key :: Value ( ec_p384_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
275
281
} ;
276
282
277
283
let span = tracing:: info_span!( "ec_k256" ) ;
@@ -287,8 +293,7 @@ impl SecretsConfig {
287
293
let ec_k256_key = KeyConfig {
288
294
kid : Alphanumeric . sample_string ( & mut rng, 10 ) ,
289
295
password : None ,
290
- key : Some ( ec_k256_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
291
- key_file : None ,
296
+ key : Key :: Value ( ec_k256_key. to_pem ( pem_rfc7468:: LineEnding :: LF ) ?. to_string ( ) ) ,
292
297
} ;
293
298
294
299
Ok ( Self {
@@ -301,7 +306,7 @@ impl SecretsConfig {
301
306
let rsa_key = KeyConfig {
302
307
kid : "abcdef" . to_owned ( ) ,
303
308
password : None ,
304
- key : Some (
309
+ key : Key :: Value (
305
310
indoc:: indoc! { r"
306
311
-----BEGIN PRIVATE KEY-----
307
312
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAymS2RkeIZo7pUeEN
@@ -316,12 +321,11 @@ impl SecretsConfig {
316
321
" }
317
322
. to_owned ( ) ,
318
323
) ,
319
- key_file : None ,
320
324
} ;
321
325
let ecdsa_key = KeyConfig {
322
326
kid : "ghijkl" . to_owned ( ) ,
323
327
password : None ,
324
- key : Some (
328
+ key : Key :: Value (
325
329
indoc:: indoc! { r"
326
330
-----BEGIN PRIVATE KEY-----
327
331
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgqfn5mYO/5Qq/wOOiWgHA
@@ -331,7 +335,6 @@ impl SecretsConfig {
331
335
" }
332
336
. to_owned ( ) ,
333
337
) ,
334
- key_file : None ,
335
338
} ;
336
339
337
340
Self {
0 commit comments