1use crate::models::{AzureKeyVaultAuthType, Signer, SignerConfig, SignerRepoModel, SignerType};
13use serde::{Deserialize, Serialize};
14use utoipa::ToSchema;
15
16#[derive(Debug, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
19#[serde(untagged)]
20#[serde(rename_all = "lowercase")]
21pub enum SignerConfigResponse {
22 #[serde(rename = "plain")]
23 Vault {
24 address: String,
25 namespace: Option<String>,
26 key_name: String,
27 mount_point: Option<String>,
28 },
31 #[serde(rename = "vault_transit")]
32 VaultTransit {
33 key_name: String,
34 address: String,
35 namespace: Option<String>,
36 pubkey: String,
37 mount_point: Option<String>,
38 },
41 #[serde(rename = "aws_kms")]
42 AwsKms {
43 region: Option<String>,
44 key_id: String,
45 },
46 #[serde(rename = "azure_key_vault")]
47 AzureKeyVault {
48 auth_type: AzureKeyVaultAuthType,
49 tenant_id: Option<String>,
50 client_id: Option<String>,
51 vault_url: String,
52 key_name: String,
53 key_version: Option<String>,
54 },
55 Turnkey {
56 api_public_key: String,
57 organization_id: String,
58 private_key_id: String,
59 public_key: String,
60 },
62 Cdp {
63 api_key_id: String,
64 account_address: String,
65 },
68 #[serde(rename = "google_cloud_kms")]
69 GoogleCloudKms {
70 service_account: GoogleCloudKmsSignerServiceAccountResponseConfig,
71 key: GoogleCloudKmsSignerKeyResponseConfig,
72 },
73 Plain {},
74}
75
76#[derive(Debug, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
77pub struct GoogleCloudKmsSignerServiceAccountResponseConfig {
78 pub project_id: String,
79 pub client_id: String,
80 pub auth_uri: String,
81 pub token_uri: String,
82 pub auth_provider_x509_cert_url: String,
83 pub client_x509_cert_url: String,
84 pub universe_domain: String,
85 }
89
90#[derive(Debug, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
91pub struct GoogleCloudKmsSignerKeyResponseConfig {
92 pub location: String,
93 pub key_ring_id: String,
94 pub key_id: String,
95 pub key_version: u32,
96}
97
98impl From<SignerConfig> for SignerConfigResponse {
99 fn from(config: SignerConfig) -> Self {
100 match config {
101 SignerConfig::Local(_) => SignerConfigResponse::Plain {},
102 SignerConfig::Vault(c) => SignerConfigResponse::Vault {
103 address: c.address,
104 namespace: c.namespace,
105 key_name: c.key_name,
106 mount_point: c.mount_point,
107 },
108 SignerConfig::VaultTransit(c) => SignerConfigResponse::VaultTransit {
109 key_name: c.key_name,
110 address: c.address,
111 namespace: c.namespace,
112 pubkey: c.pubkey,
113 mount_point: c.mount_point,
114 },
115 SignerConfig::AwsKms(c) => SignerConfigResponse::AwsKms {
116 region: c.region,
117 key_id: c.key_id,
118 },
119 SignerConfig::AzureKeyVault(c) => SignerConfigResponse::AzureKeyVault {
120 auth_type: c.auth_type(),
121 tenant_id: c.tenant_id.map(|value| (*value.to_str()).clone()),
122 client_id: c.client_id.map(|value| (*value.to_str()).clone()),
123 vault_url: (*c.vault_url.to_str()).clone(),
124 key_name: (*c.key_name.to_str()).clone(),
125 key_version: c.key_version,
126 },
127 SignerConfig::Turnkey(c) => SignerConfigResponse::Turnkey {
128 api_public_key: c.api_public_key,
129 organization_id: c.organization_id,
130 private_key_id: c.private_key_id,
131 public_key: c.public_key,
132 },
133 SignerConfig::Cdp(c) => SignerConfigResponse::Cdp {
134 api_key_id: c.api_key_id,
135 account_address: c.account_address,
136 },
137 SignerConfig::GoogleCloudKms(c) => SignerConfigResponse::GoogleCloudKms {
138 service_account: GoogleCloudKmsSignerServiceAccountResponseConfig {
139 project_id: (*c.service_account.project_id.to_str()).clone(),
140 client_id: (*c.service_account.client_id.to_str()).clone(),
141 auth_uri: (*c.service_account.auth_uri.to_str()).clone(),
142 token_uri: (*c.service_account.token_uri.to_str()).clone(),
143 auth_provider_x509_cert_url: (*c
144 .service_account
145 .auth_provider_x509_cert_url
146 .to_str())
147 .clone(),
148 client_x509_cert_url: (*c.service_account.client_x509_cert_url.to_str())
149 .clone(),
150 universe_domain: (*c.service_account.universe_domain.to_str()).clone(),
151 },
152 key: GoogleCloudKmsSignerKeyResponseConfig {
153 location: (*c.key.location.to_str()).clone(),
154 key_ring_id: (*c.key.key_ring_id.to_str()).clone(),
155 key_id: (*c.key.key_id.to_str()).clone(),
156 key_version: c.key.key_version,
157 },
158 },
159 }
160 }
161}
162
163#[derive(Debug, Serialize, Deserialize, ToSchema)]
164pub struct SignerResponse {
165 pub id: String,
167 pub r#type: SignerType,
169 pub config: SignerConfigResponse,
171}
172
173impl From<SignerRepoModel> for SignerResponse {
174 fn from(repo_model: SignerRepoModel) -> Self {
175 let domain_signer = Signer::from(repo_model);
177
178 Self {
179 id: domain_signer.id.clone(),
180 r#type: domain_signer.signer_type(),
181 config: SignerConfigResponse::from(domain_signer.config),
182 }
183 }
184}
185
186impl From<Signer> for SignerResponse {
187 fn from(signer: Signer) -> Self {
188 Self {
189 id: signer.id.clone(),
190 r#type: signer.signer_type(),
191 config: SignerConfigResponse::from(signer.config),
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use crate::models::{LocalSignerConfigStorage, SignerConfigStorage};
200 use secrets::SecretVec;
201
202 #[test]
203 fn test_signer_response_from_repo_model() {
204 let repo_model = SignerRepoModel {
205 id: "test-signer".to_string(),
206 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
207 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
208 }),
209 };
210
211 let response = SignerResponse::from(repo_model);
212
213 assert_eq!(response.id, "test-signer");
214 assert_eq!(response.r#type, SignerType::Local);
215 assert_eq!(response.config, SignerConfigResponse::Plain {});
216 }
217
218 #[test]
219 fn test_signer_response_from_domain_model() {
220 use crate::models::signer::{AwsKmsSignerConfig, SignerConfig};
221
222 let aws_config = AwsKmsSignerConfig {
223 key_id: "test-key-id".to_string(),
224 region: Some("us-east-1".to_string()),
225 };
226
227 let signer = crate::models::Signer::new(
228 "domain-signer".to_string(),
229 SignerConfig::AwsKms(aws_config),
230 );
231
232 let response = SignerResponse::from(signer);
233
234 assert_eq!(response.id, "domain-signer");
235 assert_eq!(response.r#type, SignerType::AwsKms);
236 assert_eq!(
237 response.config,
238 SignerConfigResponse::AwsKms {
239 region: Some("us-east-1".to_string()),
240 key_id: "test-key-id".to_string(),
241 }
242 );
243 }
244
245 #[test]
246 fn test_signer_type_mapping_from_config() {
247 let test_cases = vec![
248 (
249 SignerConfigStorage::Local(LocalSignerConfigStorage {
250 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
251 }),
252 SignerType::Local,
253 SignerConfigResponse::Plain {},
254 ),
255 (
256 SignerConfigStorage::AwsKms(crate::models::AwsKmsSignerConfigStorage {
257 region: Some("us-east-1".to_string()),
258 key_id: "test-key".to_string(),
259 }),
260 SignerType::AwsKms,
261 SignerConfigResponse::AwsKms {
262 region: Some("us-east-1".to_string()),
263 key_id: "test-key".to_string(),
264 },
265 ),
266 ];
267
268 for (config, expected_type, expected_config) in test_cases {
269 let repo_model = SignerRepoModel {
270 id: "test".to_string(),
271 config,
272 };
273
274 let response = SignerResponse::from(repo_model);
275 assert_eq!(
276 response.r#type, expected_type,
277 "Type mapping failed for {expected_type:?}"
278 );
279 assert_eq!(response.config, expected_config);
280 }
281 }
282
283 #[test]
284 fn test_response_serialization() {
285 let response = SignerResponse {
286 id: "test-signer".to_string(),
287 r#type: SignerType::Local,
288 config: SignerConfigResponse::Plain {},
289 };
290
291 let json = serde_json::to_string(&response).unwrap();
292 assert!(json.contains("\"id\":\"test-signer\""));
293 assert!(json.contains("\"type\":\"local\""));
294 }
295
296 #[test]
297 fn test_response_deserialization() {
298 let json = r#"{
299 "id": "test-signer",
300 "type": "aws_kms",
301 "config": {
302 "region": "us-east-1",
303 "key_id": "test-key-id"
304 }
305 }"#;
306
307 let response: SignerResponse = serde_json::from_str(json).unwrap();
308 assert_eq!(response.id, "test-signer");
309 assert_eq!(response.r#type, SignerType::AwsKms);
310 assert_eq!(
311 response.config,
312 SignerConfigResponse::AwsKms {
313 region: Some("us-east-1".to_string()),
314 key_id: "test-key-id".to_string(),
315 }
316 );
317 }
318
319 #[test]
320 fn test_response_deserialization_all_types() {
321 let json = r#"{"id": "test", "type": "google_cloud_kms", "config": {"service_account": {"project_id": "proj", "client_id": "cid", "auth_uri": "auth", "token_uri": "token", "auth_provider_x509_cert_url": "cert", "client_x509_cert_url": "client_cert", "universe_domain": "domain"}, "key": {"location": "loc", "key_ring_id": "ring", "key_id": "key", "key_version": 1}}}"#;
322
323 let response: SignerResponse = serde_json::from_str(json).unwrap();
324 assert_eq!(response.r#type, SignerType::GoogleCloudKms);
325 }
326
327 #[test]
328 fn test_cdp_signer_response_conversion() {
329 use crate::models::signer::{CdpSignerConfig, SignerConfig};
330 use crate::models::SecretString;
331
332 let cdp_config = CdpSignerConfig {
333 api_key_id: "test-api-key-id".to_string(),
334 api_key_secret: SecretString::new("secret"),
335 wallet_secret: SecretString::new("wallet-secret"),
336 account_address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string(),
337 };
338
339 let signer =
340 crate::models::Signer::new("cdp-signer".to_string(), SignerConfig::Cdp(cdp_config));
341
342 let response = SignerResponse::from(signer);
343
344 assert_eq!(response.id, "cdp-signer");
345 assert_eq!(response.r#type, SignerType::Cdp);
346 assert_eq!(
347 response.config,
348 SignerConfigResponse::Cdp {
349 api_key_id: "test-api-key-id".to_string(),
350 account_address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string(),
351 }
352 );
353 }
354
355 #[test]
356 fn test_cdp_response_serialization() {
357 let response = SignerResponse {
358 id: "test-cdp-signer".to_string(),
359 r#type: SignerType::Cdp,
360 config: SignerConfigResponse::Cdp {
361 api_key_id: "test-api-key-id".to_string(),
362 account_address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string(),
363 },
364 };
365
366 let json = serde_json::to_string(&response).unwrap();
367 assert!(json.contains("\"id\":\"test-cdp-signer\""));
368 assert!(json.contains("\"type\":\"cdp\""));
369 assert!(json.contains("\"api_key_id\":\"test-api-key-id\""));
370 assert!(json.contains("\"account_address\":\"0x742d35Cc6634C0532925a3b844Bc454e4438f44f\""));
371
372 assert!(!json.contains("api_key_secret"));
374 assert!(!json.contains("wallet_secret"));
375 }
376
377 #[test]
378 fn test_cdp_response_deserialization() {
379 let json = r#"{
380 "id": "test-cdp-signer",
381 "type": "cdp",
382 "config": {
383 "api_key_id": "test-api-key-id",
384 "account_address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44f"
385 }
386 }"#;
387
388 let response: SignerResponse = serde_json::from_str(json).unwrap();
389 assert_eq!(response.id, "test-cdp-signer");
390 assert_eq!(response.r#type, SignerType::Cdp);
391 assert_eq!(
392 response.config,
393 SignerConfigResponse::Cdp {
394 api_key_id: "test-api-key-id".to_string(),
395 account_address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string(),
396 }
397 );
398 }
399}