Skip to content

Commit e92d16c

Browse files
committed
Refactor password options in secret config
Signed-off-by: Kai A. Hiller <[email protected]>
1 parent b0fcf0b commit e92d16c

File tree

2 files changed

+73
-30
lines changed

2 files changed

+73
-30
lines changed

crates/config/src/sections/secrets.rs

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,66 @@ fn example_secret() -> &'static str {
2727
"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff"
2828
}
2929

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+
3080
/// A single key with its key ID and optional password.
81+
#[serde_as]
3182
#[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)]
3283
pub struct KeyConfig {
3384
kid: String,
3485

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>,
4190

4291
#[serde(skip_serializing_if = "Option::is_none")]
4392
key: Option<String>,
@@ -47,6 +96,19 @@ pub struct KeyConfig {
4796
key_file: Option<Utf8PathBuf>,
4897
}
4998

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+
50112
/// Application secrets
51113
#[serde_as]
52114
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
@@ -75,14 +137,7 @@ impl SecretsConfig {
75137
pub async fn key_store(&self) -> anyhow::Result<Keystore> {
76138
let mut keys = Vec::with_capacity(self.keys.len());
77139
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?;
86141

87142
// Read the key either embedded in the config file or on disk
88143
let key = match (&item.key, &item.key_file) {
@@ -154,12 +209,6 @@ impl ConfigurationSection for SecretsConfig {
154209
"Cannot specify both `key` and `key_file`".to_owned(),
155210
));
156211
}
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-
}
163212
}
164213

165214
Ok(())
@@ -187,7 +236,6 @@ impl SecretsConfig {
187236
let rsa_key = KeyConfig {
188237
kid: Alphanumeric.sample_string(&mut rng, 10),
189238
password: None,
190-
password_file: None,
191239
key: Some(rsa_key.to_pem(pem_rfc7468::LineEnding::LF)?.to_string()),
192240
key_file: None,
193241
};
@@ -205,7 +253,6 @@ impl SecretsConfig {
205253
let ec_p256_key = KeyConfig {
206254
kid: Alphanumeric.sample_string(&mut rng, 10),
207255
password: None,
208-
password_file: None,
209256
key: Some(ec_p256_key.to_pem(pem_rfc7468::LineEnding::LF)?.to_string()),
210257
key_file: None,
211258
};
@@ -223,7 +270,6 @@ impl SecretsConfig {
223270
let ec_p384_key = KeyConfig {
224271
kid: Alphanumeric.sample_string(&mut rng, 10),
225272
password: None,
226-
password_file: None,
227273
key: Some(ec_p384_key.to_pem(pem_rfc7468::LineEnding::LF)?.to_string()),
228274
key_file: None,
229275
};
@@ -241,7 +287,6 @@ impl SecretsConfig {
241287
let ec_k256_key = KeyConfig {
242288
kid: Alphanumeric.sample_string(&mut rng, 10),
243289
password: None,
244-
password_file: None,
245290
key: Some(ec_k256_key.to_pem(pem_rfc7468::LineEnding::LF)?.to_string()),
246291
key_file: None,
247292
};
@@ -256,7 +301,6 @@ impl SecretsConfig {
256301
let rsa_key = KeyConfig {
257302
kid: "abcdef".to_owned(),
258303
password: None,
259-
password_file: None,
260304
key: Some(
261305
indoc::indoc! {r"
262306
-----BEGIN PRIVATE KEY-----
@@ -277,7 +321,6 @@ impl SecretsConfig {
277321
let ecdsa_key = KeyConfig {
278322
kid: "ghijkl".to_owned(),
279323
password: None,
280-
password_file: None,
281324
key: Some(
282325
indoc::indoc! {r"
283326
-----BEGIN PRIVATE KEY-----

docs/config.schema.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,16 +1547,16 @@
15471547
"kid": {
15481548
"type": "string"
15491549
},
1550-
"password": {
1550+
"key": {
15511551
"type": "string"
15521552
},
1553-
"password_file": {
1553+
"key_file": {
15541554
"type": "string"
15551555
},
1556-
"key": {
1556+
"password_file": {
15571557
"type": "string"
15581558
},
1559-
"key_file": {
1559+
"password": {
15601560
"type": "string"
15611561
}
15621562
}

0 commit comments

Comments
 (0)