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