1use crate::models::{
13 ApiError, AwsKmsSignerConfig, AzureKeyVaultAuthType, AzureKeyVaultSignerConfig,
14 CdpSignerConfig, GoogleCloudKmsSignerConfig, GoogleCloudKmsSignerKeyConfig,
15 GoogleCloudKmsSignerServiceAccountConfig, LocalSignerConfig, SecretString, Signer,
16 SignerConfig, TurnkeySignerConfig, VaultSignerConfig, VaultTransitSignerConfig,
17};
18use secrets::SecretVec;
19use serde::{Deserialize, Serialize};
20use utoipa::ToSchema;
21use zeroize::Zeroize;
22
23#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
25#[serde(deny_unknown_fields)]
26pub struct LocalSignerRequestConfig {
27 pub key: String,
28}
29
30#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
32#[serde(deny_unknown_fields)]
33pub struct AwsKmsSignerRequestConfig {
34 pub region: String,
35 pub key_id: String,
36}
37
38#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
40#[serde(deny_unknown_fields)]
41pub struct AzureKeyVaultSignerRequestConfig {
42 pub auth_type: Option<AzureKeyVaultAuthType>,
43 pub tenant_id: Option<String>,
44 pub client_id: Option<String>,
45 pub client_secret: Option<String>,
46 pub federated_token_file: Option<String>,
47 pub vault_url: String,
48 pub key_name: String,
49 #[schema(nullable = false)]
50 pub key_version: Option<String>,
51}
52
53#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
55#[serde(deny_unknown_fields)]
56pub struct VaultSignerRequestConfig {
57 pub address: String,
58 #[schema(nullable = false)]
59 pub namespace: Option<String>,
60 pub role_id: String,
61 pub secret_id: String,
62 pub key_name: String,
63 #[schema(nullable = false)]
64 pub mount_point: Option<String>,
65}
66
67#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
69#[serde(deny_unknown_fields)]
70pub struct VaultTransitSignerRequestConfig {
71 pub key_name: String,
72 pub address: String,
73 #[schema(nullable = false)]
74 pub namespace: Option<String>,
75 pub role_id: String,
76 pub secret_id: String,
77 pub pubkey: String,
78 #[schema(nullable = false)]
79 pub mount_point: Option<String>,
80}
81
82#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
84#[serde(deny_unknown_fields)]
85pub struct TurnkeySignerRequestConfig {
86 pub api_public_key: String,
87 pub api_private_key: String,
88 pub organization_id: String,
89 pub private_key_id: String,
90 pub public_key: String,
91}
92
93#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
95#[serde(deny_unknown_fields)]
96pub struct GoogleCloudKmsSignerServiceAccountRequestConfig {
97 pub private_key: String,
98 pub private_key_id: String,
99 pub project_id: String,
100 pub client_email: String,
101 pub client_id: String,
102 pub auth_uri: String,
103 pub token_uri: String,
104 pub auth_provider_x509_cert_url: String,
105 pub client_x509_cert_url: String,
106 pub universe_domain: String,
107}
108
109#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
111#[serde(deny_unknown_fields)]
112pub struct GoogleCloudKmsSignerKeyRequestConfig {
113 pub location: String,
114 pub key_ring_id: String,
115 pub key_id: String,
116 pub key_version: u32,
117}
118
119#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
121#[serde(deny_unknown_fields)]
122pub struct GoogleCloudKmsSignerRequestConfig {
123 pub service_account: GoogleCloudKmsSignerServiceAccountRequestConfig,
124 pub key: GoogleCloudKmsSignerKeyRequestConfig,
125}
126
127#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
129#[serde(deny_unknown_fields)]
130pub struct CdpSignerRequestConfig {
131 pub api_key_id: String,
132 pub api_key_secret: String,
133 pub wallet_secret: String,
134 pub account_address: String,
135}
136
137#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
139#[serde(untagged)]
140pub enum SignerConfigRequest {
141 Local(LocalSignerRequestConfig),
142 AwsKms(AwsKmsSignerRequestConfig),
143 AzureKeyVault(AzureKeyVaultSignerRequestConfig),
144 Vault(VaultSignerRequestConfig),
145 VaultTransit(VaultTransitSignerRequestConfig),
146 Turnkey(TurnkeySignerRequestConfig),
147 Cdp(CdpSignerRequestConfig),
148 GoogleCloudKms(GoogleCloudKmsSignerRequestConfig),
149}
150
151#[derive(Debug, Serialize, Deserialize, ToSchema)]
153#[serde(rename_all = "lowercase")]
154pub enum SignerTypeRequest {
155 #[serde(rename = "plain")]
156 Local,
157 #[serde(rename = "aws_kms")]
158 AwsKms,
159 #[serde(rename = "azure_key_vault")]
160 AzureKeyVault,
161 Vault,
162 #[serde(rename = "vault_transit")]
163 VaultTransit,
164 Turnkey,
165 Cdp,
166 #[serde(rename = "google_cloud_kms")]
167 GoogleCloudKms,
168}
169
170impl zeroize::Zeroize for SignerTypeRequest {
171 fn zeroize(&mut self) {
172 }
174}
175
176#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
178#[serde(deny_unknown_fields)]
179pub struct SignerCreateRequest {
180 #[schema(nullable = false)]
182 pub id: Option<String>,
183 #[serde(rename = "type")]
185 pub signer_type: SignerTypeRequest,
186 pub config: SignerConfigRequest,
188}
189
190#[derive(Debug, Serialize, Deserialize, ToSchema, Zeroize)]
193#[serde(deny_unknown_fields)]
194pub struct SignerUpdateRequest {}
195
196impl From<GoogleCloudKmsSignerServiceAccountRequestConfig>
197 for GoogleCloudKmsSignerServiceAccountConfig
198{
199 fn from(config: GoogleCloudKmsSignerServiceAccountRequestConfig) -> Self {
200 Self {
201 private_key: SecretString::new(&config.private_key),
202 private_key_id: SecretString::new(&config.private_key_id),
203 project_id: SecretString::new(&config.project_id),
204 client_email: SecretString::new(&config.client_email),
205 client_id: SecretString::new(&config.client_id),
206 auth_uri: SecretString::new(&config.auth_uri),
207 token_uri: SecretString::new(&config.token_uri),
208 auth_provider_x509_cert_url: SecretString::new(&config.auth_provider_x509_cert_url),
209 client_x509_cert_url: SecretString::new(&config.client_x509_cert_url),
210 universe_domain: SecretString::new(&config.universe_domain),
211 }
212 }
213}
214
215impl From<GoogleCloudKmsSignerKeyRequestConfig> for GoogleCloudKmsSignerKeyConfig {
216 fn from(config: GoogleCloudKmsSignerKeyRequestConfig) -> Self {
217 Self {
218 location: SecretString::new(&config.location),
219 key_ring_id: SecretString::new(&config.key_ring_id),
220 key_id: SecretString::new(&config.key_id),
221 key_version: config.key_version,
222 }
223 }
224}
225
226impl TryFrom<SignerConfigRequest> for SignerConfig {
227 type Error = ApiError;
228
229 fn try_from(config: SignerConfigRequest) -> Result<Self, Self::Error> {
230 let domain_config = match config {
231 SignerConfigRequest::Local(local_config) => {
232 let key_bytes = hex::decode(&local_config.key)
234 .map_err(|e| ApiError::BadRequest(format!(
235 "Invalid hex key format: {e}. Key must be a 64-character hex string (32 bytes)."
236 )))?;
237
238 let raw_key = SecretVec::new(key_bytes.len(), |buffer| {
239 buffer.copy_from_slice(&key_bytes);
240 });
241
242 SignerConfig::Local(LocalSignerConfig { raw_key })
243 }
244 SignerConfigRequest::AwsKms(aws_config) => SignerConfig::AwsKms(AwsKmsSignerConfig {
245 region: Some(aws_config.region),
246 key_id: aws_config.key_id,
247 }),
248 SignerConfigRequest::AzureKeyVault(azure_config) => {
249 SignerConfig::AzureKeyVault(AzureKeyVaultSignerConfig {
250 auth_type: azure_config.auth_type,
251 tenant_id: azure_config
252 .tenant_id
253 .map(|value| SecretString::new(&value)),
254 client_id: azure_config
255 .client_id
256 .map(|value| SecretString::new(&value)),
257 client_secret: azure_config
258 .client_secret
259 .map(|value| SecretString::new(&value)),
260 federated_token_file: azure_config
261 .federated_token_file
262 .map(|value| SecretString::new(&value)),
263 vault_url: SecretString::new(&azure_config.vault_url),
264 key_name: SecretString::new(&azure_config.key_name),
265 key_version: azure_config.key_version,
266 })
267 }
268 SignerConfigRequest::Vault(vault_config) => SignerConfig::Vault(VaultSignerConfig {
269 address: vault_config.address,
270 namespace: vault_config.namespace,
271 role_id: SecretString::new(&vault_config.role_id),
272 secret_id: SecretString::new(&vault_config.secret_id),
273 key_name: vault_config.key_name,
274 mount_point: vault_config.mount_point,
275 }),
276 SignerConfigRequest::VaultTransit(vault_transit_config) => {
277 SignerConfig::VaultTransit(VaultTransitSignerConfig {
278 key_name: vault_transit_config.key_name,
279 address: vault_transit_config.address,
280 namespace: vault_transit_config.namespace,
281 role_id: SecretString::new(&vault_transit_config.role_id),
282 secret_id: SecretString::new(&vault_transit_config.secret_id),
283 pubkey: vault_transit_config.pubkey,
284 mount_point: vault_transit_config.mount_point,
285 })
286 }
287 SignerConfigRequest::Turnkey(turnkey_config) => {
288 SignerConfig::Turnkey(TurnkeySignerConfig {
289 api_public_key: turnkey_config.api_public_key,
290 api_private_key: SecretString::new(&turnkey_config.api_private_key),
291 organization_id: turnkey_config.organization_id,
292 private_key_id: turnkey_config.private_key_id,
293 public_key: turnkey_config.public_key,
294 })
295 }
296 SignerConfigRequest::Cdp(cdp_config) => SignerConfig::Cdp(CdpSignerConfig {
297 api_key_id: cdp_config.api_key_id,
298 api_key_secret: SecretString::new(&cdp_config.api_key_secret),
299 wallet_secret: SecretString::new(&cdp_config.wallet_secret),
300 account_address: cdp_config.account_address,
301 }),
302 SignerConfigRequest::GoogleCloudKms(gcp_kms_config) => {
303 SignerConfig::GoogleCloudKms(Box::new(GoogleCloudKmsSignerConfig {
304 service_account: gcp_kms_config.service_account.into(),
305 key: gcp_kms_config.key.into(),
306 }))
307 }
308 };
309
310 domain_config.validate().map_err(ApiError::from)?;
312
313 Ok(domain_config)
314 }
315}
316
317impl TryFrom<SignerCreateRequest> for Signer {
318 type Error = ApiError;
319
320 fn try_from(request: SignerCreateRequest) -> Result<Self, Self::Error> {
321 let id = request
323 .id
324 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
325
326 let config_matches_type = matches!(
328 (&request.signer_type, &request.config),
329 (SignerTypeRequest::Local, SignerConfigRequest::Local(_))
330 | (SignerTypeRequest::AwsKms, SignerConfigRequest::AwsKms(_))
331 | (
332 SignerTypeRequest::AzureKeyVault,
333 SignerConfigRequest::AzureKeyVault(_)
334 )
335 | (SignerTypeRequest::Vault, SignerConfigRequest::Vault(_))
336 | (
337 SignerTypeRequest::VaultTransit,
338 SignerConfigRequest::VaultTransit(_)
339 )
340 | (SignerTypeRequest::Turnkey, SignerConfigRequest::Turnkey(_))
341 | (SignerTypeRequest::Cdp, SignerConfigRequest::Cdp(_))
342 | (
343 SignerTypeRequest::GoogleCloudKms,
344 SignerConfigRequest::GoogleCloudKms(_)
345 )
346 );
347
348 if !config_matches_type {
349 return Err(ApiError::BadRequest(format!(
350 "Signer type '{:?}' does not match the provided configuration",
351 request.signer_type
352 )));
353 }
354
355 let config = SignerConfig::try_from(request.config)?;
357
358 let signer = Signer::new(id, config);
360
361 signer.validate().map_err(ApiError::from)?;
363
364 Ok(signer)
365 }
366}
367
368#[cfg(test)]
369mod tests {
370 use super::*;
371 use crate::models::signer::SignerType;
372
373 #[test]
374 fn test_json_deserialization_local_signer() {
375 let json = r#"{
376 "id": "test-local-signer",
377 "type": "plain",
378 "config": {
379 "key": "1111111111111111111111111111111111111111111111111111111111111111"
380 }
381 }"#;
382
383 let result: Result<SignerCreateRequest, _> = serde_json::from_str(json);
384
385 assert!(
386 result.is_ok(),
387 "Failed to deserialize local signer: {:?}",
388 result.err()
389 );
390
391 let request = result.unwrap();
392 assert_eq!(request.id, Some("test-local-signer".to_string()));
393
394 match request.config {
395 SignerConfigRequest::Local(local_config) => {
396 assert_eq!(
397 local_config.key,
398 "1111111111111111111111111111111111111111111111111111111111111111"
399 );
400 }
401 _ => panic!("Expected Local config variant"),
402 }
403 }
404
405 #[test]
406 fn test_json_deserialization_aws_kms_signer() {
407 let json = r#"{
408 "id": "test-aws-signer",
409 "type": "aws_kms",
410 "config": {
411 "region": "us-east-1",
412 "key_id": "test-key-id"
413 }
414 }"#;
415
416 let result: Result<SignerCreateRequest, _> = serde_json::from_str(json);
417
418 assert!(
419 result.is_ok(),
420 "Failed to deserialize AWS KMS signer: {:?}",
421 result.err()
422 );
423
424 let request = result.unwrap();
425 assert_eq!(request.id, Some("test-aws-signer".to_string()));
426
427 match request.config {
428 SignerConfigRequest::AwsKms(aws_config) => {
429 assert_eq!(aws_config.region, "us-east-1");
430 assert_eq!(aws_config.key_id, "test-key-id");
431 }
432 _ => panic!("Expected AwsKms config variant"),
433 }
434 }
435
436 #[test]
437 fn test_json_deserialization_vault_signer() {
438 let json = r#"{
439 "id": "test-vault-signer",
440 "type": "vault",
441 "config": {
442 "address": "https://vault.example.com",
443 "namespace": null,
444 "role_id": "test-role-id",
445 "secret_id": "test-secret-id",
446 "key_name": "test-key",
447 "mount_point": null
448 }
449 }"#;
450
451 let result: Result<SignerCreateRequest, _> = serde_json::from_str(json);
452
453 assert!(
454 result.is_ok(),
455 "Failed to deserialize Vault signer: {:?}",
456 result.err()
457 );
458
459 let request = result.unwrap();
460 assert_eq!(request.id, Some("test-vault-signer".to_string()));
461
462 match request.config {
463 SignerConfigRequest::Vault(vault_config) => {
464 assert_eq!(vault_config.address, "https://vault.example.com");
465 assert_eq!(vault_config.namespace, None);
466 assert_eq!(vault_config.role_id, "test-role-id");
467 assert_eq!(vault_config.secret_id, "test-secret-id");
468 assert_eq!(vault_config.key_name, "test-key");
469 assert_eq!(vault_config.mount_point, None);
470 }
471 _ => panic!("Expected Vault config variant"),
472 }
473 }
474
475 #[test]
476 fn test_json_deserialization_turnkey_signer() {
477 let json = r#"{
478 "id": "test-turnkey-signer",
479 "type": "turnkey",
480 "config": {
481 "api_public_key": "test-public-key",
482 "api_private_key": "test-private-key",
483 "organization_id": "test-org",
484 "private_key_id": "test-private-key-id",
485 "public_key": "test-public-key"
486 }
487 }"#;
488
489 let result: Result<SignerCreateRequest, _> = serde_json::from_str(json);
490
491 assert!(
492 result.is_ok(),
493 "Failed to deserialize Turnkey signer: {:?}",
494 result.err()
495 );
496
497 let request = result.unwrap();
498 assert_eq!(request.id, Some("test-turnkey-signer".to_string()));
499
500 match request.config {
501 SignerConfigRequest::Turnkey(turnkey_config) => {
502 assert_eq!(turnkey_config.api_public_key, "test-public-key");
503 assert_eq!(turnkey_config.api_private_key, "test-private-key");
504 assert_eq!(turnkey_config.organization_id, "test-org");
505 assert_eq!(turnkey_config.private_key_id, "test-private-key-id");
506 assert_eq!(turnkey_config.public_key, "test-public-key");
507 }
508 _ => panic!("Expected Turnkey config variant"),
509 }
510 }
511
512 #[test]
513 fn test_json_serialization_local_signer() {
514 let request = SignerCreateRequest {
515 id: Some("test-local-signer".to_string()),
516 signer_type: SignerTypeRequest::Local,
517 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
518 key: "1111111111111111111111111111111111111111111111111111111111111111".to_string(),
519 }),
520 };
521
522 let json_result = serde_json::to_string_pretty(&request);
523
524 assert!(
525 json_result.is_ok(),
526 "Failed to serialize local signer: {:?}",
527 json_result.err()
528 );
529
530 let json = json_result.unwrap();
531
532 let deserialize_result: Result<SignerCreateRequest, _> = serde_json::from_str(&json);
534 assert!(
535 deserialize_result.is_ok(),
536 "Failed to deserialize back: {:?}",
537 deserialize_result.err()
538 );
539 }
540
541 #[test]
542 fn test_json_serialization_aws_kms_signer() {
543 let request = SignerCreateRequest {
544 id: Some("test-aws-signer".to_string()),
545 signer_type: SignerTypeRequest::AwsKms,
546 config: SignerConfigRequest::AwsKms(AwsKmsSignerRequestConfig {
547 region: "us-east-1".to_string(),
548 key_id: "test-key-id".to_string(),
549 }),
550 };
551
552 let json_result = serde_json::to_string_pretty(&request);
553
554 assert!(
555 json_result.is_ok(),
556 "Failed to serialize AWS KMS signer: {:?}",
557 json_result.err()
558 );
559
560 let json = json_result.unwrap();
561
562 let deserialize_result: Result<SignerCreateRequest, _> = serde_json::from_str(&json);
564 assert!(
565 deserialize_result.is_ok(),
566 "Failed to deserialize back: {:?}",
567 deserialize_result.err()
568 );
569 }
570
571 #[test]
572 fn test_type_config_mismatch_validation() {
573 let json = r#"{
575 "id": "test-mismatch-signer",
576 "type": "aws_kms",
577 "config": {
578 "key": "1111111111111111111111111111111111111111111111111111111111111111"
579 }
580 }"#;
581
582 let result: Result<SignerCreateRequest, _> = serde_json::from_str(json);
583
584 assert!(result.is_ok(), "JSON deserialization should succeed");
586
587 let request = result.unwrap();
588
589 let signer_result = Signer::try_from(request);
591 assert!(
592 signer_result.is_err(),
593 "Type mismatch should cause validation error"
594 );
595
596 if let Err(ApiError::BadRequest(msg)) = signer_result {
597 assert!(
598 msg.contains("does not match"),
599 "Error should mention type mismatch: {msg}"
600 );
601 } else {
602 panic!("Expected BadRequest error for type mismatch");
603 }
604 }
605
606 #[test]
608 fn test_valid_aws_kms_create_request() {
609 let request = SignerCreateRequest {
610 id: Some("test-aws-signer".to_string()),
611 signer_type: SignerTypeRequest::AwsKms,
612 config: SignerConfigRequest::AwsKms(AwsKmsSignerRequestConfig {
613 region: "us-east-1".to_string(),
614 key_id: "test-key-id".to_string(),
615 }),
616 };
617
618 let result = Signer::try_from(request);
619 assert!(result.is_ok());
620
621 let signer = result.unwrap();
622 assert_eq!(signer.id, "test-aws-signer");
623 assert_eq!(signer.signer_type(), SignerType::AwsKms);
624
625 if let Some(aws_config) = signer.config.get_aws_kms() {
627 assert_eq!(aws_config.region, Some("us-east-1".to_string()));
628 assert_eq!(aws_config.key_id, "test-key-id");
629 } else {
630 panic!("Expected AWS KMS config");
631 }
632 }
633
634 #[test]
635 fn test_valid_vault_create_request() {
636 let request = SignerCreateRequest {
637 id: Some("test-vault-signer".to_string()),
638 signer_type: SignerTypeRequest::Vault,
639 config: SignerConfigRequest::Vault(VaultSignerRequestConfig {
640 address: "https://vault.example.com".to_string(),
641 namespace: None,
642 role_id: "test-role-id".to_string(),
643 secret_id: "test-secret-id".to_string(),
644 key_name: "test-key".to_string(),
645 mount_point: None,
646 }),
647 };
648
649 let result = Signer::try_from(request);
650 assert!(result.is_ok());
651
652 let signer = result.unwrap();
653 assert_eq!(signer.id, "test-vault-signer");
654 assert_eq!(signer.signer_type(), SignerType::Vault);
655 }
656
657 #[test]
658 fn test_invalid_aws_kms_empty_key_id() {
659 let request = SignerCreateRequest {
660 id: Some("test-signer".to_string()),
661 signer_type: SignerTypeRequest::AwsKms,
662 config: SignerConfigRequest::AwsKms(AwsKmsSignerRequestConfig {
663 region: "us-east-1".to_string(),
664 key_id: "".to_string(), }),
666 };
667
668 let result = Signer::try_from(request);
669 assert!(result.is_err());
670
671 if let Err(ApiError::BadRequest(msg)) = result {
672 assert!(msg.contains("Key ID cannot be empty"));
673 } else {
674 panic!("Expected BadRequest error for empty key ID");
675 }
676 }
677
678 #[test]
679 fn test_invalid_vault_empty_address() {
680 let request = SignerCreateRequest {
681 id: Some("test-signer".to_string()),
682 signer_type: SignerTypeRequest::Vault,
683 config: SignerConfigRequest::Vault(VaultSignerRequestConfig {
684 address: "".to_string(), namespace: None,
686 role_id: "test-role".to_string(),
687 secret_id: "test-secret".to_string(),
688 key_name: "test-key".to_string(),
689 mount_point: None,
690 }),
691 };
692
693 let result = Signer::try_from(request);
694 assert!(result.is_err());
695 }
696
697 #[test]
698 fn test_invalid_vault_invalid_url() {
699 let request = SignerCreateRequest {
700 id: Some("test-signer".to_string()),
701 signer_type: SignerTypeRequest::Vault,
702 config: SignerConfigRequest::Vault(VaultSignerRequestConfig {
703 address: "not-a-url".to_string(), namespace: None,
705 role_id: "test-role".to_string(),
706 secret_id: "test-secret".to_string(),
707 key_name: "test-key".to_string(),
708 mount_point: None,
709 }),
710 };
711
712 let result = Signer::try_from(request);
713 assert!(result.is_err());
714
715 if let Err(ApiError::BadRequest(msg)) = result {
716 assert!(msg.contains("Address must be a valid URL"));
717 } else {
718 panic!("Expected BadRequest error for invalid URL");
719 }
720 }
721
722 #[test]
723 fn test_create_request_generates_uuid_when_no_id() {
724 let request = SignerCreateRequest {
725 id: None,
726 signer_type: SignerTypeRequest::Local,
727 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
728 key: "1111111111111111111111111111111111111111111111111111111111111111".to_string(), }),
730 };
731
732 let result = Signer::try_from(request);
733 assert!(result.is_ok());
734
735 let signer = result.unwrap();
736 assert!(!signer.id.is_empty());
737 assert_eq!(signer.signer_type(), SignerType::Local);
738
739 assert!(uuid::Uuid::parse_str(&signer.id).is_ok());
741 }
742
743 #[test]
744 fn test_invalid_id_format() {
745 let request = SignerCreateRequest {
746 id: Some("invalid@id".to_string()), signer_type: SignerTypeRequest::Local,
748 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
749 key: "2222222222222222222222222222222222222222222222222222222222222222".to_string(), }),
751 };
752
753 let result = Signer::try_from(request);
754 assert!(result.is_err());
755
756 if let Err(ApiError::BadRequest(msg)) = result {
757 assert!(msg.contains("ID must contain only letters, numbers, dashes and underscores"));
758 } else {
759 panic!("Expected BadRequest error with validation message");
760 }
761 }
762
763 #[test]
764 fn test_test_signer_creation() {
765 let request = SignerCreateRequest {
766 id: Some("test-signer".to_string()),
767 signer_type: SignerTypeRequest::Local,
768 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
769 key: "3333333333333333333333333333333333333333333333333333333333333333".to_string(), }),
771 };
772
773 let result = Signer::try_from(request);
774 assert!(result.is_ok());
775
776 let signer = result.unwrap();
777 assert_eq!(signer.id, "test-signer");
778 assert_eq!(signer.signer_type(), SignerType::Local);
779 }
780
781 #[test]
782 fn test_local_signer_creation() {
783 let request = SignerCreateRequest {
784 id: Some("local-signer".to_string()),
785 signer_type: SignerTypeRequest::Local,
786 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
787 key: "4444444444444444444444444444444444444444444444444444444444444444".to_string(), }),
789 };
790
791 let result = Signer::try_from(request);
792 assert!(result.is_ok());
793
794 let signer = result.unwrap();
795 assert_eq!(signer.id, "local-signer");
796 assert_eq!(signer.signer_type(), SignerType::Local);
797 }
798
799 #[test]
800 fn test_valid_turnkey_create_request() {
801 let request = SignerCreateRequest {
802 id: Some("test-turnkey-signer".to_string()),
803 signer_type: SignerTypeRequest::Turnkey,
804 config: SignerConfigRequest::Turnkey(TurnkeySignerRequestConfig {
805 api_public_key: "test-public-key".to_string(),
806 api_private_key: "test-private-key".to_string(),
807 organization_id: "test-org".to_string(),
808 private_key_id: "test-private-key-id".to_string(),
809 public_key: "test-public-key".to_string(),
810 }),
811 };
812
813 let result = Signer::try_from(request);
814 assert!(result.is_ok());
815
816 let signer = result.unwrap();
817 assert_eq!(signer.id, "test-turnkey-signer");
818 assert_eq!(signer.signer_type(), SignerType::Turnkey);
819
820 if let Some(turnkey_config) = signer.config.get_turnkey() {
821 assert_eq!(turnkey_config.api_public_key, "test-public-key");
822 assert_eq!(turnkey_config.organization_id, "test-org");
823 } else {
824 panic!("Expected Turnkey config");
825 }
826 }
827
828 #[test]
829 fn test_valid_vault_transit_create_request() {
830 let request = SignerCreateRequest {
831 id: Some("test-vault-transit-signer".to_string()),
832 signer_type: SignerTypeRequest::VaultTransit,
833 config: SignerConfigRequest::VaultTransit(VaultTransitSignerRequestConfig {
834 key_name: "test-key".to_string(),
835 address: "https://vault.example.com".to_string(),
836 namespace: None,
837 role_id: "test-role".to_string(),
838 secret_id: "test-secret".to_string(),
839 pubkey: "test-pubkey".to_string(),
840 mount_point: None,
841 }),
842 };
843
844 let result = Signer::try_from(request);
845 assert!(result.is_ok());
846
847 let signer = result.unwrap();
848 assert_eq!(signer.id, "test-vault-transit-signer");
849 assert_eq!(signer.signer_type(), SignerType::VaultTransit);
850 }
851
852 #[test]
853 fn test_valid_google_cloud_kms_create_request() {
854 let request = SignerCreateRequest {
855 id: Some("test-gcp-kms-signer".to_string()),
856 signer_type: SignerTypeRequest::GoogleCloudKms,
857 config: SignerConfigRequest::GoogleCloudKms(GoogleCloudKmsSignerRequestConfig {
858 service_account: GoogleCloudKmsSignerServiceAccountRequestConfig {
859 private_key: "test-private-key".to_string(),
860 private_key_id: "test-key-id".to_string(),
861 project_id: "test-project".to_string(),
862 client_email: "test@email.com".to_string(),
863 client_id: "test-client-id".to_string(),
864 auth_uri: "https://accounts.google.com/o/oauth2/auth".to_string(),
865 token_uri: "https://oauth2.googleapis.com/token".to_string(),
866 auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs".to_string(),
867 client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/test%40test.iam.gserviceaccount.com".to_string(),
868 universe_domain: "googleapis.com".to_string(),
869 },
870 key: GoogleCloudKmsSignerKeyRequestConfig {
871 location: "global".to_string(),
872 key_ring_id: "test-ring".to_string(),
873 key_id: "test-key".to_string(),
874 key_version: 1,
875 },
876 }),
877 };
878
879 let result = Signer::try_from(request);
880 assert!(result.is_ok());
881
882 let signer = result.unwrap();
883 assert_eq!(signer.id, "test-gcp-kms-signer");
884 assert_eq!(signer.signer_type(), SignerType::GoogleCloudKms);
885 }
886
887 #[test]
888 fn test_invalid_local_hex_key() {
889 let request = SignerCreateRequest {
890 id: Some("test-signer".to_string()),
891 signer_type: SignerTypeRequest::Local,
892 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
893 key: "invalid-hex".to_string(), }),
895 };
896
897 let result = Signer::try_from(request);
898 assert!(result.is_err());
899 if let Err(ApiError::BadRequest(msg)) = result {
900 assert!(msg.contains("Invalid hex key format"));
901 }
902 }
903
904 #[test]
905 fn test_invalid_turnkey_empty_key() {
906 let request = SignerCreateRequest {
907 id: Some("test-signer".to_string()),
908 signer_type: SignerTypeRequest::Turnkey,
909 config: SignerConfigRequest::Turnkey(TurnkeySignerRequestConfig {
910 api_public_key: "".to_string(), api_private_key: "test-private-key".to_string(),
912 organization_id: "test-org".to_string(),
913 private_key_id: "test-private-key-id".to_string(),
914 public_key: "test-public-key".to_string(),
915 }),
916 };
917
918 let result = Signer::try_from(request);
919 assert!(result.is_err());
920 if let Err(ApiError::BadRequest(msg)) = result {
921 assert!(msg.contains("API public key cannot be empty"));
922 }
923 }
924
925 #[test]
926 fn test_json_deserialization_cdp_signer() {
927 let json = r#"{
928 "id": "test-cdp-signer",
929 "type": "cdp",
930 "config": {
931 "api_key_id": "test-api-key-id",
932 "api_key_secret": "dGVzdC1hcGkta2V5LXNlY3JldA==",
933 "wallet_secret": "dGVzdC13YWxsZXQtc2VjcmV0",
934 "account_address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44f"
935 }
936 }"#;
937
938 let result: Result<SignerCreateRequest, _> = serde_json::from_str(json);
939
940 assert!(
941 result.is_ok(),
942 "Failed to deserialize CDP signer: {:?}",
943 result.err()
944 );
945
946 let request = result.unwrap();
947 assert_eq!(request.id, Some("test-cdp-signer".to_string()));
948
949 match request.config {
950 SignerConfigRequest::Cdp(cdp_config) => {
951 assert_eq!(cdp_config.api_key_id, "test-api-key-id");
952 assert_eq!(cdp_config.api_key_secret, "dGVzdC1hcGkta2V5LXNlY3JldA==");
953 assert_eq!(cdp_config.wallet_secret, "dGVzdC13YWxsZXQtc2VjcmV0");
954 assert_eq!(
955 cdp_config.account_address,
956 "0x742d35Cc6634C0532925a3b844Bc454e4438f44f"
957 );
958 }
959 _ => panic!("Expected CDP config variant"),
960 }
961 }
962
963 #[test]
964 fn test_valid_cdp_create_request() {
965 let request = SignerCreateRequest {
966 id: Some("test-cdp-signer".to_string()),
967 signer_type: SignerTypeRequest::Cdp,
968 config: SignerConfigRequest::Cdp(CdpSignerRequestConfig {
969 api_key_id: "test-api-key-id".to_string(),
970 api_key_secret: "dGVzdC1hcGkta2V5LXNlY3JldA==".to_string(), wallet_secret: "dGVzdC13YWxsZXQtc2VjcmV0".to_string(), account_address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string(),
973 }),
974 };
975
976 let result = Signer::try_from(request);
977 assert!(result.is_ok());
978
979 let signer = result.unwrap();
980 assert_eq!(signer.id, "test-cdp-signer");
981 assert_eq!(signer.signer_type(), SignerType::Cdp);
982
983 if let Some(cdp_config) = signer.config.get_cdp() {
984 assert_eq!(cdp_config.api_key_id, "test-api-key-id");
985 assert_eq!(
986 cdp_config.account_address,
987 "0x742d35Cc6634C0532925a3b844Bc454e4438f44f"
988 );
989 } else {
990 panic!("Expected CDP config");
991 }
992 }
993
994 #[test]
995 fn test_invalid_cdp_empty_api_key_id() {
996 let request = SignerCreateRequest {
997 id: Some("test-signer".to_string()),
998 signer_type: SignerTypeRequest::Cdp,
999 config: SignerConfigRequest::Cdp(CdpSignerRequestConfig {
1000 api_key_id: "".to_string(), api_key_secret: "dGVzdC1hcGkta2V5LXNlY3JldA==".to_string(), wallet_secret: "dGVzdC13YWxsZXQtc2VjcmV0".to_string(), account_address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string(),
1004 }),
1005 };
1006
1007 let result = Signer::try_from(request);
1008 assert!(result.is_err());
1009 if let Err(ApiError::BadRequest(msg)) = result {
1010 assert!(msg.contains("API Key ID cannot be empty"));
1011 }
1012 }
1013}