1mod repository;
15pub use repository::{
16 AwsKmsSignerConfigStorage, GoogleCloudKmsSignerConfigStorage,
17 GoogleCloudKmsSignerKeyConfigStorage, GoogleCloudKmsSignerServiceAccountConfigStorage,
18 LocalSignerConfigStorage, SignerConfigStorage, SignerRepoModel, TurnkeySignerConfigStorage,
19 VaultSignerConfigStorage, VaultTransitSignerConfigStorage,
20};
21
22mod config;
23pub use config::*;
24
25mod request;
26pub use request::*;
27
28mod response;
29pub use response::*;
30
31use crate::{constants::ID_REGEX, models::SecretString};
32use secrets::SecretVec;
33use serde::{Deserialize, Serialize, Serializer};
34use utoipa::ToSchema;
35use validator::Validate;
36
37fn serialize_secret_redacted<S>(_secret: &SecretVec<u8>, serializer: S) -> Result<S::Ok, S::Error>
39where
40 S: Serializer,
41{
42 serializer.serialize_str("[REDACTED]")
43}
44
45#[derive(Debug, Clone, Serialize)]
47pub struct LocalSignerConfig {
48 #[serde(serialize_with = "serialize_secret_redacted")]
49 pub raw_key: SecretVec<u8>,
50}
51
52impl LocalSignerConfig {
53 pub fn validate(&self) -> Result<(), SignerValidationError> {
55 let key_bytes = self.raw_key.borrow();
56
57 if key_bytes.len() != 32 {
59 return Err(SignerValidationError::InvalidConfig(format!(
60 "Raw key must be exactly 32 bytes, got {} bytes",
61 key_bytes.len()
62 )));
63 }
64
65 if key_bytes.iter().all(|&b| b == 0) {
67 return Err(SignerValidationError::InvalidConfig(
68 "Raw key cannot be all zeros".to_string(),
69 ));
70 }
71
72 Ok(())
73 }
74}
75
76impl<'de> Deserialize<'de> for LocalSignerConfig {
77 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
78 where
79 D: serde::Deserializer<'de>,
80 {
81 #[derive(Deserialize)]
82 struct LocalSignerConfigHelper {
83 raw_key: String,
84 }
85
86 let helper = LocalSignerConfigHelper::deserialize(deserializer)?;
87 let raw_key = if helper.raw_key == "[REDACTED]" {
88 SecretVec::zero(32)
90 } else {
91 SecretVec::new(helper.raw_key.len(), |v| {
94 v.copy_from_slice(helper.raw_key.as_bytes())
95 })
96 };
97
98 Ok(LocalSignerConfig { raw_key })
99 }
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
113pub struct AwsKmsSignerConfig {
114 #[validate(length(min = 1, message = "Region cannot be empty"))]
115 pub region: Option<String>,
116 #[validate(length(min = 1, message = "Key ID cannot be empty"))]
117 pub key_id: String,
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
122pub struct VaultSignerConfig {
123 #[validate(url(message = "Address must be a valid URL"))]
124 pub address: String,
125 pub namespace: Option<String>,
126 #[validate(custom(
127 function = "validate_secret_string",
128 message = "Role ID cannot be empty"
129 ))]
130 pub role_id: SecretString,
131 #[validate(custom(
132 function = "validate_secret_string",
133 message = "Secret ID cannot be empty"
134 ))]
135 pub secret_id: SecretString,
136 #[validate(length(min = 1, message = "Vault key name cannot be empty"))]
137 pub key_name: String,
138 pub mount_point: Option<String>,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
143pub struct VaultTransitSignerConfig {
144 #[validate(length(min = 1, message = "Key name cannot be empty"))]
145 pub key_name: String,
146 #[validate(url(message = "Address must be a valid URL"))]
147 pub address: String,
148 pub namespace: Option<String>,
149 #[validate(custom(
150 function = "validate_secret_string",
151 message = "Role ID cannot be empty"
152 ))]
153 pub role_id: SecretString,
154 #[validate(custom(
155 function = "validate_secret_string",
156 message = "Secret ID cannot be empty"
157 ))]
158 pub secret_id: SecretString,
159 #[validate(length(min = 1, message = "pubkey cannot be empty"))]
160 pub pubkey: String,
161 pub mount_point: Option<String>,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
166pub struct TurnkeySignerConfig {
167 #[validate(length(min = 1, message = "API public key cannot be empty"))]
168 pub api_public_key: String,
169 #[validate(custom(
170 function = "validate_secret_string",
171 message = "API private key cannot be empty"
172 ))]
173 pub api_private_key: SecretString,
174 #[validate(length(min = 1, message = "Organization ID cannot be empty"))]
175 pub organization_id: String,
176 #[validate(length(min = 1, message = "Private key ID cannot be empty"))]
177 pub private_key_id: String,
178 #[validate(length(min = 1, message = "Public key cannot be empty"))]
179 pub public_key: String,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
184pub struct GoogleCloudKmsSignerServiceAccountConfig {
185 #[validate(custom(
186 function = "validate_secret_string",
187 message = "Private key cannot be empty"
188 ))]
189 pub private_key: SecretString,
190 #[validate(custom(
191 function = "validate_secret_string",
192 message = "Private key ID cannot be empty"
193 ))]
194 pub private_key_id: SecretString,
195 #[validate(length(min = 1, message = "Project ID cannot be empty"))]
196 pub project_id: String,
197 #[validate(custom(
198 function = "validate_secret_string",
199 message = "Client email cannot be empty"
200 ))]
201 pub client_email: SecretString,
202 #[validate(length(min = 1, message = "Client ID cannot be empty"))]
203 pub client_id: String,
204 #[validate(url(message = "Auth URI must be a valid URL"))]
205 pub auth_uri: String,
206 #[validate(url(message = "Token URI must be a valid URL"))]
207 pub token_uri: String,
208 #[validate(url(message = "Auth provider x509 cert URL must be a valid URL"))]
209 pub auth_provider_x509_cert_url: String,
210 #[validate(url(message = "Client x509 cert URL must be a valid URL"))]
211 pub client_x509_cert_url: String,
212 pub universe_domain: String,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
217pub struct GoogleCloudKmsSignerKeyConfig {
218 pub location: String,
219 #[validate(length(min = 1, message = "Key ring ID cannot be empty"))]
220 pub key_ring_id: String,
221 #[validate(length(min = 1, message = "Key ID cannot be empty"))]
222 pub key_id: String,
223 pub key_version: u32,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
228pub struct GoogleCloudKmsSignerConfig {
229 #[validate(nested)]
230 pub service_account: GoogleCloudKmsSignerServiceAccountConfig,
231 #[validate(nested)]
232 pub key: GoogleCloudKmsSignerKeyConfig,
233}
234
235fn validate_secret_string(secret: &SecretString) -> Result<(), validator::ValidationError> {
237 if secret.to_str().is_empty() {
238 return Err(validator::ValidationError::new("empty_secret"));
239 }
240 Ok(())
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245pub enum SignerConfig {
246 Local(LocalSignerConfig),
247 Vault(VaultSignerConfig),
248 VaultTransit(VaultTransitSignerConfig),
249 AwsKms(AwsKmsSignerConfig),
250 Turnkey(TurnkeySignerConfig),
251 GoogleCloudKms(GoogleCloudKmsSignerConfig),
252}
253
254impl SignerConfig {
255 pub fn validate(&self) -> Result<(), SignerValidationError> {
257 match self {
258 Self::Local(config) => config.validate(),
259 Self::AwsKms(config) => Validate::validate(config).map_err(|e| {
260 SignerValidationError::InvalidConfig(format!(
261 "AWS KMS validation failed: {}",
262 format_validation_errors(&e)
263 ))
264 }),
265 Self::Vault(config) => Validate::validate(config).map_err(|e| {
266 SignerValidationError::InvalidConfig(format!(
267 "Vault validation failed: {}",
268 format_validation_errors(&e)
269 ))
270 }),
271 Self::VaultTransit(config) => Validate::validate(config).map_err(|e| {
272 SignerValidationError::InvalidConfig(format!(
273 "Vault Transit validation failed: {}",
274 format_validation_errors(&e)
275 ))
276 }),
277 Self::Turnkey(config) => Validate::validate(config).map_err(|e| {
278 SignerValidationError::InvalidConfig(format!(
279 "Turnkey validation failed: {}",
280 format_validation_errors(&e)
281 ))
282 }),
283 Self::GoogleCloudKms(config) => Validate::validate(config).map_err(|e| {
284 SignerValidationError::InvalidConfig(format!(
285 "Google Cloud KMS validation failed: {}",
286 format_validation_errors(&e)
287 ))
288 }),
289 }
290 }
291
292 pub fn get_local(&self) -> Option<&LocalSignerConfig> {
294 match self {
295 Self::Local(config) => Some(config),
296 _ => None,
297 }
298 }
299
300 pub fn get_aws_kms(&self) -> Option<&AwsKmsSignerConfig> {
302 match self {
303 Self::AwsKms(config) => Some(config),
304 _ => None,
305 }
306 }
307
308 pub fn get_vault(&self) -> Option<&VaultSignerConfig> {
310 match self {
311 Self::Vault(config) => Some(config),
312 _ => None,
313 }
314 }
315
316 pub fn get_vault_transit(&self) -> Option<&VaultTransitSignerConfig> {
318 match self {
319 Self::VaultTransit(config) => Some(config),
320 _ => None,
321 }
322 }
323
324 pub fn get_turnkey(&self) -> Option<&TurnkeySignerConfig> {
326 match self {
327 Self::Turnkey(config) => Some(config),
328 _ => None,
329 }
330 }
331
332 pub fn get_google_cloud_kms(&self) -> Option<&GoogleCloudKmsSignerConfig> {
334 match self {
335 Self::GoogleCloudKms(config) => Some(config),
336 _ => None,
337 }
338 }
339
340 pub fn get_signer_type(&self) -> SignerType {
342 match self {
343 Self::Local(_) => SignerType::Local,
344 Self::AwsKms(_) => SignerType::AwsKms,
345 Self::Vault(_) => SignerType::Vault,
346 Self::VaultTransit(_) => SignerType::VaultTransit,
347 Self::Turnkey(_) => SignerType::Turnkey,
348 Self::GoogleCloudKms(_) => SignerType::GoogleCloudKms,
349 }
350 }
351}
352
353fn format_validation_errors(errors: &validator::ValidationErrors) -> String {
355 let mut messages = Vec::new();
356
357 for (field, field_errors) in errors.field_errors().iter() {
358 let field_msgs: Vec<String> = field_errors
359 .iter()
360 .map(|error| error.message.clone().unwrap_or_default().to_string())
361 .collect();
362 messages.push(format!("{}: {}", field, field_msgs.join(", ")));
363 }
364
365 for (struct_field, kind) in errors.errors().iter() {
366 if let validator::ValidationErrorsKind::Struct(nested) = kind {
367 let nested_msgs = format_validation_errors(nested);
368 messages.push(format!("{}.{}", struct_field, nested_msgs));
369 }
370 }
371
372 messages.join("; ")
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
377pub struct Signer {
378 #[validate(
379 length(min = 1, max = 36, message = "ID must be between 1 and 36 characters"),
380 regex(
381 path = "*ID_REGEX",
382 message = "ID must contain only letters, numbers, dashes and underscores"
383 )
384 )]
385 pub id: String,
386 pub config: SignerConfig,
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ToSchema)]
391#[serde(rename_all = "lowercase")]
392pub enum SignerType {
393 Local,
394 #[serde(rename = "aws_kms")]
395 AwsKms,
396 #[serde(rename = "google_cloud_kms")]
397 GoogleCloudKms,
398 Vault,
399 #[serde(rename = "vault_transit")]
400 VaultTransit,
401 Turnkey,
402}
403
404impl Signer {
405 pub fn new(id: String, config: SignerConfig) -> Self {
407 Self { id, config }
408 }
409
410 pub fn signer_type(&self) -> SignerType {
412 self.config.get_signer_type()
413 }
414
415 pub fn validate(&self) -> Result<(), SignerValidationError> {
417 Validate::validate(self).map_err(|validation_errors| {
419 for (field, errors) in validation_errors.field_errors() {
422 if let Some(error) = errors.first() {
423 let field_str = field.as_ref();
424 return match (field_str, error.code.as_ref()) {
425 ("id", "length") => SignerValidationError::InvalidIdFormat,
426 ("id", "regex") => SignerValidationError::InvalidIdFormat,
427 _ => SignerValidationError::InvalidIdFormat, };
429 }
430 }
431 SignerValidationError::InvalidIdFormat
433 })?;
434
435 self.config.validate()?;
437
438 Ok(())
439 }
440}
441
442#[derive(Debug, thiserror::Error)]
444pub enum SignerValidationError {
445 #[error("Signer ID cannot be empty")]
446 EmptyId,
447 #[error("Signer ID must contain only letters, numbers, dashes and underscores and must be at most 36 characters long")]
448 InvalidIdFormat,
449 #[error("Invalid signer configuration: {0}")]
450 InvalidConfig(String),
451}
452
453impl From<SignerValidationError> for crate::models::ApiError {
455 fn from(error: SignerValidationError) -> Self {
456 use crate::models::ApiError;
457
458 ApiError::BadRequest(match error {
459 SignerValidationError::EmptyId => "ID cannot be empty".to_string(),
460 SignerValidationError::InvalidIdFormat => {
461 "ID must contain only letters, numbers, dashes and underscores and must be at most 36 characters long".to_string()
462 }
463 SignerValidationError::InvalidConfig(msg) => format!("Invalid signer configuration: {}", msg),
464 })
465 }
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471
472 #[test]
473 fn test_valid_local_signer() {
474 let config = SignerConfig::Local(LocalSignerConfig {
475 raw_key: SecretVec::new(32, |v| v.fill(1)),
476 });
477
478 let signer = Signer::new("valid-id".to_string(), config);
479
480 assert!(signer.validate().is_ok());
481 assert_eq!(signer.signer_type(), SignerType::Local);
482 }
483
484 #[test]
485 fn test_valid_aws_kms_signer() {
486 let config = SignerConfig::AwsKms(AwsKmsSignerConfig {
487 region: Some("us-east-1".to_string()),
488 key_id: "test-key-id".to_string(),
489 });
490
491 let signer = Signer::new("aws-signer".to_string(), config);
492
493 assert!(signer.validate().is_ok());
494 assert_eq!(signer.signer_type(), SignerType::AwsKms);
495 }
496
497 #[test]
498 fn test_empty_id() {
499 let config = SignerConfig::Local(LocalSignerConfig {
500 raw_key: SecretVec::new(32, |v| v.fill(1)), });
502
503 let signer = Signer::new("".to_string(), config);
504
505 assert!(matches!(
506 signer.validate(),
507 Err(SignerValidationError::InvalidIdFormat)
508 ));
509 }
510
511 #[test]
512 fn test_id_too_long() {
513 let config = SignerConfig::Local(LocalSignerConfig {
514 raw_key: SecretVec::new(32, |v| v.fill(1)), });
516
517 let signer = Signer::new("a".repeat(37), config);
518
519 assert!(matches!(
520 signer.validate(),
521 Err(SignerValidationError::InvalidIdFormat)
522 ));
523 }
524
525 #[test]
526 fn test_invalid_id_format() {
527 let config = SignerConfig::Local(LocalSignerConfig {
528 raw_key: SecretVec::new(32, |v| v.fill(1)), });
530
531 let signer = Signer::new("invalid@id".to_string(), config);
532
533 assert!(matches!(
534 signer.validate(),
535 Err(SignerValidationError::InvalidIdFormat)
536 ));
537 }
538
539 #[test]
540 fn test_local_signer_invalid_key_length() {
541 let config = SignerConfig::Local(LocalSignerConfig {
542 raw_key: SecretVec::new(16, |v| v.fill(1)), });
544
545 let signer = Signer::new("valid-id".to_string(), config);
546
547 let result = signer.validate();
548 assert!(result.is_err());
549 if let Err(SignerValidationError::InvalidConfig(msg)) = result {
550 assert!(msg.contains("Raw key must be exactly 32 bytes"));
551 assert!(msg.contains("got 16 bytes"));
552 } else {
553 panic!("Expected InvalidConfig error for invalid key length");
554 }
555 }
556
557 #[test]
558 fn test_local_signer_all_zero_key() {
559 let config = SignerConfig::Local(LocalSignerConfig {
560 raw_key: SecretVec::new(32, |v| v.fill(0)), });
562
563 let signer = Signer::new("valid-id".to_string(), config);
564
565 let result = signer.validate();
566 assert!(result.is_err());
567 if let Err(SignerValidationError::InvalidConfig(msg)) = result {
568 assert_eq!(msg, "Raw key cannot be all zeros");
569 } else {
570 panic!("Expected InvalidConfig error for all-zero key");
571 }
572 }
573
574 #[test]
575 fn test_local_signer_valid_key() {
576 let config = SignerConfig::Local(LocalSignerConfig {
577 raw_key: SecretVec::new(32, |v| v.fill(1)), });
579
580 let signer = Signer::new("valid-id".to_string(), config);
581
582 assert!(signer.validate().is_ok());
583 }
584
585 #[test]
586 fn test_signer_type_serialization() {
587 use serde_json::{from_str, to_string};
588
589 assert_eq!(to_string(&SignerType::Local).unwrap(), "\"local\"");
590 assert_eq!(to_string(&SignerType::AwsKms).unwrap(), "\"aws_kms\"");
591 assert_eq!(
592 to_string(&SignerType::GoogleCloudKms).unwrap(),
593 "\"google_cloud_kms\""
594 );
595 assert_eq!(
596 to_string(&SignerType::VaultTransit).unwrap(),
597 "\"vault_transit\""
598 );
599
600 assert_eq!(
601 from_str::<SignerType>("\"local\"").unwrap(),
602 SignerType::Local
603 );
604 assert_eq!(
605 from_str::<SignerType>("\"aws_kms\"").unwrap(),
606 SignerType::AwsKms
607 );
608 }
609
610 #[test]
611 fn test_config_accessor_methods() {
612 let local_config = LocalSignerConfig {
614 raw_key: SecretVec::new(32, |v| v.fill(1)),
615 };
616 let config = SignerConfig::Local(local_config);
617 assert!(config.get_local().is_some());
618 assert!(config.get_aws_kms().is_none());
619
620 let aws_config = AwsKmsSignerConfig {
622 region: Some("us-east-1".to_string()),
623 key_id: "test-key".to_string(),
624 };
625 let config = SignerConfig::AwsKms(aws_config);
626 assert!(config.get_aws_kms().is_some());
627 assert!(config.get_local().is_none());
628 }
629
630 #[test]
631 fn test_error_conversion_to_api_error() {
632 let error = SignerValidationError::InvalidIdFormat;
633 let api_error: crate::models::ApiError = error.into();
634
635 if let crate::models::ApiError::BadRequest(msg) = api_error {
636 assert!(msg.contains("ID must contain only letters, numbers, dashes and underscores"));
637 } else {
638 panic!("Expected BadRequest error");
639 }
640 }
641
642 #[test]
643 fn test_valid_vault_signer() {
644 let config = SignerConfig::Vault(VaultSignerConfig {
645 address: "https://vault.example.com".to_string(),
646 namespace: Some("test".to_string()),
647 role_id: SecretString::new("role-id"),
648 secret_id: SecretString::new("secret-id"),
649 key_name: "test-key".to_string(),
650 mount_point: None,
651 });
652
653 let signer = Signer::new("vault-signer".to_string(), config);
654 assert!(signer.validate().is_ok());
655 assert_eq!(signer.signer_type(), SignerType::Vault);
656 }
657
658 #[test]
659 fn test_invalid_vault_signer_url() {
660 let config = SignerConfig::Vault(VaultSignerConfig {
661 address: "not-a-url".to_string(),
662 namespace: Some("test".to_string()),
663 role_id: SecretString::new("role-id"),
664 secret_id: SecretString::new("secret-id"),
665 key_name: "test-key".to_string(),
666 mount_point: None,
667 });
668
669 let signer = Signer::new("vault-signer".to_string(), config);
670 let result = signer.validate();
671 assert!(result.is_err());
672 if let Err(SignerValidationError::InvalidConfig(msg)) = result {
673 assert!(msg.contains("Address must be a valid URL"));
674 } else {
675 panic!("Expected InvalidConfig error for invalid URL");
676 }
677 }
678
679 #[test]
680 fn test_valid_google_cloud_kms_signer() {
681 let config = SignerConfig::GoogleCloudKms(GoogleCloudKmsSignerConfig {
682 service_account: GoogleCloudKmsSignerServiceAccountConfig {
683 private_key: SecretString::new("private-key"),
684 private_key_id: SecretString::new("key-id"),
685 project_id: "project".to_string(),
686 client_email: SecretString::new("client@example.com"),
687 client_id: "client-id".to_string(),
688 auth_uri: "https://accounts.google.com/o/oauth2/auth".to_string(),
689 token_uri: "https://oauth2.googleapis.com/token".to_string(),
690 auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
691 .to_string(),
692 client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/test"
693 .to_string(),
694 universe_domain: "googleapis.com".to_string(),
695 },
696 key: GoogleCloudKmsSignerKeyConfig {
697 location: "us-central1".to_string(),
698 key_ring_id: "test-ring".to_string(),
699 key_id: "test-key".to_string(),
700 key_version: 1,
701 },
702 });
703
704 let signer = Signer::new("gcp-kms-signer".to_string(), config);
705 assert!(signer.validate().is_ok());
706 assert_eq!(signer.signer_type(), SignerType::GoogleCloudKms);
707 }
708
709 #[test]
710 fn test_invalid_google_cloud_kms_urls() {
711 let config = SignerConfig::GoogleCloudKms(GoogleCloudKmsSignerConfig {
712 service_account: GoogleCloudKmsSignerServiceAccountConfig {
713 private_key: SecretString::new("private-key"),
714 private_key_id: SecretString::new("key-id"),
715 project_id: "project".to_string(),
716 client_email: SecretString::new("client@example.com"),
717 client_id: "client-id".to_string(),
718 auth_uri: "not-a-url".to_string(), token_uri: "https://oauth2.googleapis.com/token".to_string(),
720 auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
721 .to_string(),
722 client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/test"
723 .to_string(),
724 universe_domain: "googleapis.com".to_string(),
725 },
726 key: GoogleCloudKmsSignerKeyConfig {
727 location: "us-central1".to_string(),
728 key_ring_id: "test-ring".to_string(),
729 key_id: "test-key".to_string(),
730 key_version: 1,
731 },
732 });
733
734 let signer = Signer::new("gcp-kms-signer".to_string(), config);
735 let result = signer.validate();
736 assert!(result.is_err());
737 if let Err(SignerValidationError::InvalidConfig(msg)) = result {
738 assert!(msg.contains("Auth URI must be a valid URL"));
739 } else {
740 panic!("Expected InvalidConfig error for invalid URL");
741 }
742 }
743
744 #[test]
745 fn test_secret_string_validation() {
746 let result = validate_secret_string(&SecretString::new(""));
748 if let Err(e) = result {
749 assert_eq!(e.code, "empty_secret");
750 } else {
751 panic!("Expected validation error for empty secret");
752 }
753
754 let result = validate_secret_string(&SecretString::new("secret"));
756 assert!(result.is_ok());
757 }
758
759 #[test]
760 fn test_validation_error_formatting() {
761 let invalid_config = GoogleCloudKmsSignerConfig {
763 service_account: GoogleCloudKmsSignerServiceAccountConfig {
764 private_key: SecretString::new(""), private_key_id: SecretString::new("key-id"),
766 project_id: "project".to_string(),
767 client_email: SecretString::new("client@example.com"),
768 client_id: "".to_string(), auth_uri: "not-a-url".to_string(), token_uri: "https://oauth2.googleapis.com/token".to_string(),
771 auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
772 .to_string(),
773 client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/test"
774 .to_string(),
775 universe_domain: "googleapis.com".to_string(),
776 },
777 key: GoogleCloudKmsSignerKeyConfig {
778 location: "us-central1".to_string(),
779 key_ring_id: "".to_string(), key_id: "test-key".to_string(),
781 key_version: 1,
782 },
783 };
784
785 let errors = invalid_config.validate().unwrap_err();
786
787 let formatted = format_validation_errors(&errors);
789
790 println!("formatted: {}", formatted);
791
792 assert!(formatted.contains("client_id: Client ID cannot be empty"));
794 assert!(formatted.contains("private_key: Private key cannot be empty"));
795 assert!(formatted.contains("auth_uri: Auth URI must be a valid URL"));
796 assert!(formatted.contains("key_ring_id: Key ring ID cannot be empty"));
797 }
798
799 #[test]
800 fn test_config_type_getters() {
801 let vault_config = VaultSignerConfig {
803 address: "https://vault.example.com".to_string(),
804 namespace: None,
805 role_id: SecretString::new("role"),
806 secret_id: SecretString::new("secret"),
807 key_name: "key".to_string(),
808 mount_point: None,
809 };
810 let config = SignerConfig::Vault(vault_config);
811 assert!(config.get_vault().is_some());
812
813 let vault_transit_config = VaultTransitSignerConfig {
815 key_name: "key".to_string(),
816 address: "https://vault.example.com".to_string(),
817 namespace: None,
818 role_id: SecretString::new("role"),
819 secret_id: SecretString::new("secret"),
820 pubkey: "pubkey".to_string(),
821 mount_point: None,
822 };
823 let config = SignerConfig::VaultTransit(vault_transit_config);
824 assert!(config.get_vault_transit().is_some());
825 assert!(config.get_turnkey().is_none());
826
827 let turnkey_config = TurnkeySignerConfig {
829 api_public_key: "public".to_string(),
830 api_private_key: SecretString::new("private"),
831 organization_id: "org".to_string(),
832 private_key_id: "key-id".to_string(),
833 public_key: "pubkey".to_string(),
834 };
835 let config = SignerConfig::Turnkey(turnkey_config);
836 assert!(config.get_turnkey().is_some());
837 assert!(config.get_google_cloud_kms().is_none());
838
839 let gcp_config = GoogleCloudKmsSignerConfig {
841 service_account: GoogleCloudKmsSignerServiceAccountConfig {
842 private_key: SecretString::new("private-key"),
843 private_key_id: SecretString::new("key-id"),
844 project_id: "project".to_string(),
845 client_email: SecretString::new("client@example.com"),
846 client_id: "client-id".to_string(),
847 auth_uri: "https://accounts.google.com/o/oauth2/auth".to_string(),
848 token_uri: "https://oauth2.googleapis.com/token".to_string(),
849 auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
850 .to_string(),
851 client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/test"
852 .to_string(),
853 universe_domain: "googleapis.com".to_string(),
854 },
855 key: GoogleCloudKmsSignerKeyConfig {
856 location: "us-central1".to_string(),
857 key_ring_id: "test-ring".to_string(),
858 key_id: "test-key".to_string(),
859 key_version: 1,
860 },
861 };
862 let config = SignerConfig::GoogleCloudKms(gcp_config);
863 assert!(config.get_google_cloud_kms().is_some());
864 assert!(config.get_local().is_none());
865 }
866}