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: SecretString::new(&config.project_id),
186 client_email: SecretString::new(&config.client_email),
187 client_id: SecretString::new(&config.client_id),
188 auth_uri: SecretString::new(&config.auth_uri),
189 token_uri: SecretString::new(&config.token_uri),
190 auth_provider_x509_cert_url: SecretString::new(&config.auth_provider_x509_cert_url),
191 client_x509_cert_url: SecretString::new(&config.client_x509_cert_url),
192 universe_domain: SecretString::new(&config.universe_domain),
193 }
194 }
195}
196
197impl From<GoogleCloudKmsSignerKeyRequestConfig> for GoogleCloudKmsSignerKeyConfig {
198 fn from(config: GoogleCloudKmsSignerKeyRequestConfig) -> Self {
199 Self {
200 location: SecretString::new(&config.location),
201 key_ring_id: SecretString::new(&config.key_ring_id),
202 key_id: SecretString::new(&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(Box::new(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: {msg}"
558 );
559 } else {
560 panic!("Expected BadRequest error for type mismatch");
561 }
562 }
563
564 #[test]
566 fn test_valid_aws_kms_create_request() {
567 let request = SignerCreateRequest {
568 id: Some("test-aws-signer".to_string()),
569 signer_type: SignerTypeRequest::AwsKms,
570 config: SignerConfigRequest::AwsKms(AwsKmsSignerRequestConfig {
571 region: "us-east-1".to_string(),
572 key_id: "test-key-id".to_string(),
573 }),
574 };
575
576 let result = Signer::try_from(request);
577 assert!(result.is_ok());
578
579 let signer = result.unwrap();
580 assert_eq!(signer.id, "test-aws-signer");
581 assert_eq!(signer.signer_type(), SignerType::AwsKms);
582
583 if let Some(aws_config) = signer.config.get_aws_kms() {
585 assert_eq!(aws_config.region, Some("us-east-1".to_string()));
586 assert_eq!(aws_config.key_id, "test-key-id");
587 } else {
588 panic!("Expected AWS KMS config");
589 }
590 }
591
592 #[test]
593 fn test_valid_vault_create_request() {
594 let request = SignerCreateRequest {
595 id: Some("test-vault-signer".to_string()),
596 signer_type: SignerTypeRequest::Vault,
597 config: SignerConfigRequest::Vault(VaultSignerRequestConfig {
598 address: "https://vault.example.com".to_string(),
599 namespace: None,
600 role_id: "test-role-id".to_string(),
601 secret_id: "test-secret-id".to_string(),
602 key_name: "test-key".to_string(),
603 mount_point: None,
604 }),
605 };
606
607 let result = Signer::try_from(request);
608 assert!(result.is_ok());
609
610 let signer = result.unwrap();
611 assert_eq!(signer.id, "test-vault-signer");
612 assert_eq!(signer.signer_type(), SignerType::Vault);
613 }
614
615 #[test]
616 fn test_invalid_aws_kms_empty_key_id() {
617 let request = SignerCreateRequest {
618 id: Some("test-signer".to_string()),
619 signer_type: SignerTypeRequest::AwsKms,
620 config: SignerConfigRequest::AwsKms(AwsKmsSignerRequestConfig {
621 region: "us-east-1".to_string(),
622 key_id: "".to_string(), }),
624 };
625
626 let result = Signer::try_from(request);
627 assert!(result.is_err());
628
629 if let Err(ApiError::BadRequest(msg)) = result {
630 assert!(msg.contains("Key ID cannot be empty"));
631 } else {
632 panic!("Expected BadRequest error for empty key ID");
633 }
634 }
635
636 #[test]
637 fn test_invalid_vault_empty_address() {
638 let request = SignerCreateRequest {
639 id: Some("test-signer".to_string()),
640 signer_type: SignerTypeRequest::Vault,
641 config: SignerConfigRequest::Vault(VaultSignerRequestConfig {
642 address: "".to_string(), namespace: None,
644 role_id: "test-role".to_string(),
645 secret_id: "test-secret".to_string(),
646 key_name: "test-key".to_string(),
647 mount_point: None,
648 }),
649 };
650
651 let result = Signer::try_from(request);
652 assert!(result.is_err());
653 }
654
655 #[test]
656 fn test_invalid_vault_invalid_url() {
657 let request = SignerCreateRequest {
658 id: Some("test-signer".to_string()),
659 signer_type: SignerTypeRequest::Vault,
660 config: SignerConfigRequest::Vault(VaultSignerRequestConfig {
661 address: "not-a-url".to_string(), namespace: None,
663 role_id: "test-role".to_string(),
664 secret_id: "test-secret".to_string(),
665 key_name: "test-key".to_string(),
666 mount_point: None,
667 }),
668 };
669
670 let result = Signer::try_from(request);
671 assert!(result.is_err());
672
673 if let Err(ApiError::BadRequest(msg)) = result {
674 assert!(msg.contains("Address must be a valid URL"));
675 } else {
676 panic!("Expected BadRequest error for invalid URL");
677 }
678 }
679
680 #[test]
681 fn test_create_request_generates_uuid_when_no_id() {
682 let request = SignerCreateRequest {
683 id: None,
684 signer_type: SignerTypeRequest::Local,
685 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
686 key: "1111111111111111111111111111111111111111111111111111111111111111".to_string(), }),
688 };
689
690 let result = Signer::try_from(request);
691 assert!(result.is_ok());
692
693 let signer = result.unwrap();
694 assert!(!signer.id.is_empty());
695 assert_eq!(signer.signer_type(), SignerType::Local);
696
697 assert!(uuid::Uuid::parse_str(&signer.id).is_ok());
699 }
700
701 #[test]
702 fn test_invalid_id_format() {
703 let request = SignerCreateRequest {
704 id: Some("invalid@id".to_string()), signer_type: SignerTypeRequest::Local,
706 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
707 key: "2222222222222222222222222222222222222222222222222222222222222222".to_string(), }),
709 };
710
711 let result = Signer::try_from(request);
712 assert!(result.is_err());
713
714 if let Err(ApiError::BadRequest(msg)) = result {
715 assert!(msg.contains("ID must contain only letters, numbers, dashes and underscores"));
716 } else {
717 panic!("Expected BadRequest error with validation message");
718 }
719 }
720
721 #[test]
722 fn test_test_signer_creation() {
723 let request = SignerCreateRequest {
724 id: Some("test-signer".to_string()),
725 signer_type: SignerTypeRequest::Local,
726 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
727 key: "3333333333333333333333333333333333333333333333333333333333333333".to_string(), }),
729 };
730
731 let result = Signer::try_from(request);
732 assert!(result.is_ok());
733
734 let signer = result.unwrap();
735 assert_eq!(signer.id, "test-signer");
736 assert_eq!(signer.signer_type(), SignerType::Local);
737 }
738
739 #[test]
740 fn test_local_signer_creation() {
741 let request = SignerCreateRequest {
742 id: Some("local-signer".to_string()),
743 signer_type: SignerTypeRequest::Local,
744 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
745 key: "4444444444444444444444444444444444444444444444444444444444444444".to_string(), }),
747 };
748
749 let result = Signer::try_from(request);
750 assert!(result.is_ok());
751
752 let signer = result.unwrap();
753 assert_eq!(signer.id, "local-signer");
754 assert_eq!(signer.signer_type(), SignerType::Local);
755 }
756
757 #[test]
758 fn test_valid_turnkey_create_request() {
759 let request = SignerCreateRequest {
760 id: Some("test-turnkey-signer".to_string()),
761 signer_type: SignerTypeRequest::Turnkey,
762 config: SignerConfigRequest::Turnkey(TurnkeySignerRequestConfig {
763 api_public_key: "test-public-key".to_string(),
764 api_private_key: "test-private-key".to_string(),
765 organization_id: "test-org".to_string(),
766 private_key_id: "test-private-key-id".to_string(),
767 public_key: "test-public-key".to_string(),
768 }),
769 };
770
771 let result = Signer::try_from(request);
772 assert!(result.is_ok());
773
774 let signer = result.unwrap();
775 assert_eq!(signer.id, "test-turnkey-signer");
776 assert_eq!(signer.signer_type(), SignerType::Turnkey);
777
778 if let Some(turnkey_config) = signer.config.get_turnkey() {
779 assert_eq!(turnkey_config.api_public_key, "test-public-key");
780 assert_eq!(turnkey_config.organization_id, "test-org");
781 } else {
782 panic!("Expected Turnkey config");
783 }
784 }
785
786 #[test]
787 fn test_valid_vault_transit_create_request() {
788 let request = SignerCreateRequest {
789 id: Some("test-vault-transit-signer".to_string()),
790 signer_type: SignerTypeRequest::VaultTransit,
791 config: SignerConfigRequest::VaultTransit(VaultTransitSignerRequestConfig {
792 key_name: "test-key".to_string(),
793 address: "https://vault.example.com".to_string(),
794 namespace: None,
795 role_id: "test-role".to_string(),
796 secret_id: "test-secret".to_string(),
797 pubkey: "test-pubkey".to_string(),
798 mount_point: None,
799 }),
800 };
801
802 let result = Signer::try_from(request);
803 assert!(result.is_ok());
804
805 let signer = result.unwrap();
806 assert_eq!(signer.id, "test-vault-transit-signer");
807 assert_eq!(signer.signer_type(), SignerType::VaultTransit);
808 }
809
810 #[test]
811 fn test_valid_google_cloud_kms_create_request() {
812 let request = SignerCreateRequest {
813 id: Some("test-gcp-kms-signer".to_string()),
814 signer_type: SignerTypeRequest::GoogleCloudKms,
815 config: SignerConfigRequest::GoogleCloudKms(GoogleCloudKmsSignerRequestConfig {
816 service_account: GoogleCloudKmsSignerServiceAccountRequestConfig {
817 private_key: "test-private-key".to_string(),
818 private_key_id: "test-key-id".to_string(),
819 project_id: "test-project".to_string(),
820 client_email: "test@email.com".to_string(),
821 client_id: "test-client-id".to_string(),
822 auth_uri: "https://accounts.google.com/o/oauth2/auth".to_string(),
823 token_uri: "https://oauth2.googleapis.com/token".to_string(),
824 auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs".to_string(),
825 client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/test%40test.iam.gserviceaccount.com".to_string(),
826 universe_domain: "googleapis.com".to_string(),
827 },
828 key: GoogleCloudKmsSignerKeyRequestConfig {
829 location: "global".to_string(),
830 key_ring_id: "test-ring".to_string(),
831 key_id: "test-key".to_string(),
832 key_version: 1,
833 },
834 }),
835 };
836
837 let result = Signer::try_from(request);
838 assert!(result.is_ok());
839
840 let signer = result.unwrap();
841 assert_eq!(signer.id, "test-gcp-kms-signer");
842 assert_eq!(signer.signer_type(), SignerType::GoogleCloudKms);
843 }
844
845 #[test]
846 fn test_invalid_local_hex_key() {
847 let request = SignerCreateRequest {
848 id: Some("test-signer".to_string()),
849 signer_type: SignerTypeRequest::Local,
850 config: SignerConfigRequest::Local(LocalSignerRequestConfig {
851 key: "invalid-hex".to_string(), }),
853 };
854
855 let result = Signer::try_from(request);
856 assert!(result.is_err());
857 if let Err(ApiError::BadRequest(msg)) = result {
858 assert!(msg.contains("Invalid hex key format"));
859 }
860 }
861
862 #[test]
863 fn test_invalid_turnkey_empty_key() {
864 let request = SignerCreateRequest {
865 id: Some("test-signer".to_string()),
866 signer_type: SignerTypeRequest::Turnkey,
867 config: SignerConfigRequest::Turnkey(TurnkeySignerRequestConfig {
868 api_public_key: "".to_string(), api_private_key: "test-private-key".to_string(),
870 organization_id: "test-org".to_string(),
871 private_key_id: "test-private-key-id".to_string(),
872 public_key: "test-public-key".to_string(),
873 }),
874 };
875
876 let result = Signer::try_from(request);
877 assert!(result.is_err());
878 if let Err(ApiError::BadRequest(msg)) = result {
879 assert!(msg.contains("API public key cannot be empty"));
880 }
881 }
882
883 #[test]
884 fn test_json_deserialization_cdp_signer() {
885 let json = r#"{
886 "id": "test-cdp-signer",
887 "type": "cdp",
888 "config": {
889 "api_key_id": "test-api-key-id",
890 "api_key_secret": "dGVzdC1hcGkta2V5LXNlY3JldA==",
891 "wallet_secret": "dGVzdC13YWxsZXQtc2VjcmV0",
892 "account_address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44f"
893 }
894 }"#;
895
896 let result: Result<SignerCreateRequest, _> = serde_json::from_str(json);
897
898 assert!(
899 result.is_ok(),
900 "Failed to deserialize CDP signer: {:?}",
901 result.err()
902 );
903
904 let request = result.unwrap();
905 assert_eq!(request.id, Some("test-cdp-signer".to_string()));
906
907 match request.config {
908 SignerConfigRequest::Cdp(cdp_config) => {
909 assert_eq!(cdp_config.api_key_id, "test-api-key-id");
910 assert_eq!(cdp_config.api_key_secret, "dGVzdC1hcGkta2V5LXNlY3JldA==");
911 assert_eq!(cdp_config.wallet_secret, "dGVzdC13YWxsZXQtc2VjcmV0");
912 assert_eq!(
913 cdp_config.account_address,
914 "0x742d35Cc6634C0532925a3b844Bc454e4438f44f"
915 );
916 }
917 _ => panic!("Expected CDP config variant"),
918 }
919 }
920
921 #[test]
922 fn test_valid_cdp_create_request() {
923 let request = SignerCreateRequest {
924 id: Some("test-cdp-signer".to_string()),
925 signer_type: SignerTypeRequest::Cdp,
926 config: SignerConfigRequest::Cdp(CdpSignerRequestConfig {
927 api_key_id: "test-api-key-id".to_string(),
928 api_key_secret: "dGVzdC1hcGkta2V5LXNlY3JldA==".to_string(), wallet_secret: "dGVzdC13YWxsZXQtc2VjcmV0".to_string(), account_address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string(),
931 }),
932 };
933
934 let result = Signer::try_from(request);
935 assert!(result.is_ok());
936
937 let signer = result.unwrap();
938 assert_eq!(signer.id, "test-cdp-signer");
939 assert_eq!(signer.signer_type(), SignerType::Cdp);
940
941 if let Some(cdp_config) = signer.config.get_cdp() {
942 assert_eq!(cdp_config.api_key_id, "test-api-key-id");
943 assert_eq!(
944 cdp_config.account_address,
945 "0x742d35Cc6634C0532925a3b844Bc454e4438f44f"
946 );
947 } else {
948 panic!("Expected CDP config");
949 }
950 }
951
952 #[test]
953 fn test_invalid_cdp_empty_api_key_id() {
954 let request = SignerCreateRequest {
955 id: Some("test-signer".to_string()),
956 signer_type: SignerTypeRequest::Cdp,
957 config: SignerConfigRequest::Cdp(CdpSignerRequestConfig {
958 api_key_id: "".to_string(), api_key_secret: "dGVzdC1hcGkta2V5LXNlY3JldA==".to_string(), wallet_secret: "dGVzdC13YWxsZXQtc2VjcmV0".to_string(), account_address: "0x742d35Cc6634C0532925a3b844Bc454e4438f44f".to_string(),
962 }),
963 };
964
965 let result = Signer::try_from(request);
966 assert!(result.is_err());
967 if let Err(ApiError::BadRequest(msg)) = result {
968 assert!(msg.contains("API Key ID cannot be empty"));
969 }
970 }
971}