1use std::path::PathBuf;
2
3use serde::{Deserialize, Deserializer, Serialize};
4use serde_with::{serde_as, NoneAsEmptyString};
5use tracing::warn;
6
7#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
22#[serde(rename_all = "kebab-case")]
23#[serde(try_from = "IntermediateAuthConfig")]
24pub enum AuthConfig {
25 Local(LocalConfig),
26 GcpKms(GcpKmsConfig),
27}
28
29impl Default for AuthConfig {
30 fn default() -> Self {
31 AuthConfig::Local(LocalConfig::default())
32 }
33}
34
35#[serde_as]
39#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
40#[serde(rename_all = "kebab-case")]
41pub struct LocalConfig {
42 pub private_keys: Vec<PrivateKey>,
43}
44
45#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
46#[cfg_attr(feature = "testutils", derive(Default))]
47pub struct PrivateKey {
48 #[serde(alias = "Path")]
49 pub path: PathBuf,
50 #[serde(alias = "Password")]
51 pub password: String,
52}
53
54#[serde_as]
58#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
59#[cfg_attr(feature = "testutils", derive(Default))]
60#[serde(rename_all = "kebab-case")]
61pub struct GcpKmsConfig {
62 #[serde(alias = "ProjectId")]
64 #[serde_as(as = "NoneAsEmptyString")]
65 #[serde(default)]
66 pub project_id: Option<String>,
67
68 #[serde(alias = "Location")]
70 #[serde_as(as = "NoneAsEmptyString")]
71 #[serde(default)]
72 pub location: Option<String>,
73
74 #[serde(alias = "Keyring")]
76 #[serde_as(as = "NoneAsEmptyString")]
77 #[serde(default)]
78 pub keyring: Option<String>,
79
80 #[serde_as(as = "NoneAsEmptyString")]
85 #[serde(default)]
86 pub pp_settlement_key_name: Option<String>,
87
88 #[serde(default)]
90 pub pp_settlement_key_version: Option<u64>,
91
92 #[serde_as(as = "NoneAsEmptyString")]
94 #[serde(default)]
95 pub tx_settlement_key_name: Option<String>,
96
97 #[serde(default)]
99 pub tx_settlement_key_version: Option<u64>,
100}
101
102impl<'de> Deserialize<'de> for GcpKmsConfig {
103 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
104 where
105 D: Deserializer<'de>,
106 {
107 #[serde_as]
108 #[derive(Deserialize)]
109 #[cfg_attr(feature = "testutils", derive(Default))]
110 #[serde(rename_all = "kebab-case")]
111 pub struct Intermediate {
112 #[serde(alias = "ProjectId")]
113 #[serde_as(as = "NoneAsEmptyString")]
114 #[serde(default)]
115 pub project_id: Option<String>,
116 #[serde(alias = "Location")]
117 #[serde_as(as = "NoneAsEmptyString")]
118 #[serde(default)]
119 pub location: Option<String>,
120 #[serde(alias = "Keyring")]
121 #[serde_as(as = "NoneAsEmptyString")]
122 #[serde(default)]
123 pub keyring: Option<String>,
124 #[serde_as(as = "NoneAsEmptyString")]
125 #[serde(default)]
126 pub pp_settlement_key_name: Option<String>,
127 #[serde(default)]
128 pub pp_settlement_key_version: Option<u64>,
129 #[serde_as(as = "NoneAsEmptyString")]
130 #[serde(default)]
131 pub tx_settlement_key_name: Option<String>,
132 #[serde(default)]
133 pub tx_settlement_key_version: Option<u64>,
134 #[serde(alias = "KeyName")]
135 #[serde_as(as = "NoneAsEmptyString")]
136 #[serde(default)]
137 pub key_name: Option<String>,
138 #[serde(alias = "KeyVersion")]
139 #[serde(default)]
140 pub key_version: Option<u64>,
141 }
142
143 let d = Intermediate::deserialize(deserializer)?;
144
145 let (
146 pp_settlement_key_name,
147 pp_settlement_key_version,
148 tx_settlement_key_name,
149 tx_settlement_key_version,
150 ) = match (
151 d.pp_settlement_key_name,
152 d.pp_settlement_key_version,
153 d.tx_settlement_key_name,
154 d.tx_settlement_key_version,
155 d.key_name,
156 d.key_version,
157 ) {
158 (Some(pp_k), Some(pp_v), Some(tx_k), Some(tx_v), _, _) => {
159 (Some(pp_k), Some(pp_v), Some(tx_k), Some(tx_v))
160 }
161 (None, None, None, None, Some(k), Some(v)) => {
162 warn!(
163 "'key-name' and 'key-version' are deprecated. Please use \
164 'pp-settlement-key-name','pp-settlement-key-version', \
165 'tx-settlement-key-name', and 'tx-settlement-key-version' instead."
166 );
167 (Some(k.clone()), Some(v), Some(k), Some(v))
168 }
169 _ => {
170 return Err(serde::de::Error::custom(
171 "Either both \
172 ['pp-settlement-key-name','pp-settlement-key-version','\
173 tx-settlement-key-name','tx-settlement-key-version'] or 'key-name' and \
174 'key-version' must be specified",
175 ));
176 }
177 };
178
179 Ok(GcpKmsConfig {
180 project_id: d.project_id,
181 location: d.location,
182 keyring: d.keyring,
183 pp_settlement_key_name,
184 pp_settlement_key_version,
185 tx_settlement_key_name,
186 tx_settlement_key_version,
187 })
188 }
189}
190
191#[derive(Deserialize)]
194struct IntermediateAuthConfig {
195 #[serde(default)]
196 local: Option<LocalConfig>,
197 #[serde(default)]
198 gcpkms: Option<GcpKmsConfig>,
199 #[serde(default, alias = "PrivateKeys")]
200 private_keys: Option<Vec<PrivateKey>>,
201 #[serde(flatten)]
202 kms: Option<GcpKmsConfig>,
203}
204
205impl TryFrom<IntermediateAuthConfig> for AuthConfig {
206 type Error = &'static str;
207
208 fn try_from(intermediate: IntermediateAuthConfig) -> Result<Self, Self::Error> {
209 if let Some(local) = intermediate.local {
210 Ok(AuthConfig::Local(local))
211 } else if let Some(gcpkms) = intermediate.gcpkms {
212 Ok(AuthConfig::GcpKms(gcpkms))
213 } else if let Some(private_keys) = intermediate.private_keys {
214 Ok(AuthConfig::Local(LocalConfig { private_keys }))
215 } else if let Some(kms) = intermediate.kms {
216 Ok(AuthConfig::GcpKms(kms))
217 } else {
218 Err("Invalid auth configuration")
219 }
220 }
221}