1use crate::{
13 config::ConfigFileError,
14 models::signer::{
15 AwsKmsSignerConfig, AzureKeyVaultSignerConfig, CdpSignerConfig, GoogleCloudKmsSignerConfig,
16 GoogleCloudKmsSignerKeyConfig, GoogleCloudKmsSignerServiceAccountConfig, LocalSignerConfig,
17 Signer, SignerConfig, TurnkeySignerConfig, VaultSignerConfig, VaultTransitSignerConfig,
18 },
19 models::PlainOrEnvValue,
20};
21use secrets::SecretVec;
22use serde::{Deserialize, Serialize};
23use std::{collections::HashSet, path::Path};
24
25#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
26#[serde(deny_unknown_fields)]
27pub struct LocalSignerFileConfig {
28 pub path: String,
29 pub passphrase: PlainOrEnvValue,
30}
31
32#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
33#[serde(deny_unknown_fields)]
34pub struct AwsKmsSignerFileConfig {
35 pub region: String,
36 pub key_id: String,
37}
38
39#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
40#[serde(deny_unknown_fields)]
41pub struct AzureKeyVaultSignerFileConfig {
42 pub auth_type: Option<crate::models::AzureKeyVaultAuthType>,
43 pub tenant_id: Option<PlainOrEnvValue>,
44 pub client_id: Option<PlainOrEnvValue>,
45 pub client_secret: Option<PlainOrEnvValue>,
46 pub federated_token_file: Option<PlainOrEnvValue>,
47 pub vault_url: String,
48 pub key_name: String,
49 pub key_version: Option<String>,
50}
51
52#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
53#[serde(deny_unknown_fields)]
54pub struct TurnkeySignerFileConfig {
55 pub api_public_key: String,
56 pub api_private_key: PlainOrEnvValue,
57 pub organization_id: String,
58 pub private_key_id: String,
59 pub public_key: String,
60}
61
62#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
63#[serde(deny_unknown_fields)]
64pub struct CdpSignerFileConfig {
65 pub api_key_id: String,
66 pub api_key_secret: PlainOrEnvValue,
67 pub wallet_secret: PlainOrEnvValue,
68 pub account_address: String,
69}
70
71#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
72#[serde(deny_unknown_fields)]
73pub struct VaultSignerFileConfig {
74 pub address: String,
75 pub namespace: Option<String>,
76 pub role_id: PlainOrEnvValue,
77 pub secret_id: PlainOrEnvValue,
78 pub key_name: String,
79 pub mount_point: Option<String>,
80}
81
82#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
83#[serde(deny_unknown_fields)]
84pub struct VaultTransitSignerFileConfig {
85 pub key_name: String,
86 pub address: String,
87 pub role_id: PlainOrEnvValue,
88 pub secret_id: PlainOrEnvValue,
89 pub pubkey: String,
90 pub mount_point: Option<String>,
91 pub namespace: Option<String>,
92}
93
94fn google_cloud_default_auth_uri() -> String {
95 "https://accounts.google.com/o/oauth2/auth".to_string()
96}
97
98fn google_cloud_default_token_uri() -> String {
99 "https://oauth2.googleapis.com/token".to_string()
100}
101
102fn google_cloud_default_auth_provider_x509_cert_url() -> String {
103 "https://www.googleapis.com/oauth2/v1/certs".to_string()
104}
105
106fn google_cloud_default_client_x509_cert_url() -> String {
107 "https://www.googleapis.com/robot/v1/metadata/x509/solana-signer%40forward-emitter-459820-r7.iam.gserviceaccount.com".to_string()
108}
109
110fn google_cloud_default_universe_domain() -> String {
111 "googleapis.com".to_string()
112}
113
114fn google_cloud_default_key_version() -> u32 {
115 1
116}
117
118fn google_cloud_default_location() -> String {
119 "global".to_string()
120}
121
122#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
123#[serde(deny_unknown_fields)]
124pub struct GoogleCloudKmsServiceAccountFileConfig {
125 pub project_id: String,
126 pub private_key_id: PlainOrEnvValue,
127 pub private_key: PlainOrEnvValue,
128 pub client_email: PlainOrEnvValue,
129 pub client_id: String,
130 #[serde(default = "google_cloud_default_auth_uri")]
131 pub auth_uri: String,
132 #[serde(default = "google_cloud_default_token_uri")]
133 pub token_uri: String,
134 #[serde(default = "google_cloud_default_auth_provider_x509_cert_url")]
135 pub auth_provider_x509_cert_url: String,
136 #[serde(default = "google_cloud_default_client_x509_cert_url")]
137 pub client_x509_cert_url: String,
138 #[serde(default = "google_cloud_default_universe_domain")]
139 pub universe_domain: String,
140}
141
142#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
143#[serde(deny_unknown_fields)]
144pub struct GoogleCloudKmsKeyFileConfig {
145 #[serde(default = "google_cloud_default_location")]
146 pub location: String,
147 pub key_ring_id: String,
148 pub key_id: String,
149 #[serde(default = "google_cloud_default_key_version")]
150 pub key_version: u32,
151}
152
153#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
154#[serde(deny_unknown_fields)]
155pub struct GoogleCloudKmsSignerFileConfig {
156 pub service_account: GoogleCloudKmsServiceAccountFileConfig,
157 pub key: GoogleCloudKmsKeyFileConfig,
158}
159
160#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
162#[serde(tag = "type", rename_all = "lowercase", content = "config")]
163pub enum SignerFileConfigEnum {
164 Local(LocalSignerFileConfig),
165 #[serde(rename = "aws_kms")]
166 AwsKms(AwsKmsSignerFileConfig),
167 #[serde(rename = "azure_key_vault")]
168 AzureKeyVault(AzureKeyVaultSignerFileConfig),
169 Turnkey(TurnkeySignerFileConfig),
170 Cdp(CdpSignerFileConfig),
171 Vault(VaultSignerFileConfig),
172 #[serde(rename = "vault_transit")]
173 VaultTransit(VaultTransitSignerFileConfig),
174 #[serde(rename = "google_cloud_kms")]
175 GoogleCloudKms(GoogleCloudKmsSignerFileConfig),
176}
177
178#[derive(Debug, Serialize, Deserialize, Clone)]
180#[serde(deny_unknown_fields)]
181pub struct SignerFileConfig {
182 pub id: String,
183 #[serde(flatten)]
184 pub config: SignerFileConfigEnum,
185}
186
187#[derive(Debug, Serialize, Deserialize, Clone)]
189#[serde(deny_unknown_fields)]
190pub struct SignersFileConfig {
191 pub signers: Vec<SignerFileConfig>,
192}
193
194impl SignerFileConfig {
195 pub fn validate_basic(&self) -> Result<(), ConfigFileError> {
196 if self.id.is_empty() {
197 return Err(ConfigFileError::InvalidIdLength(
198 "Signer ID cannot be empty".into(),
199 ));
200 }
201 Ok(())
202 }
203}
204
205impl SignersFileConfig {
206 pub fn new(signers: Vec<SignerFileConfig>) -> Self {
207 Self { signers }
208 }
209
210 pub fn validate(&self) -> Result<(), ConfigFileError> {
211 if self.signers.is_empty() {
212 return Ok(());
213 }
214
215 let mut ids = HashSet::new();
216 for signer in &self.signers {
217 signer.validate_basic()?;
218 if !ids.insert(signer.id.clone()) {
219 return Err(ConfigFileError::DuplicateId(signer.id.clone()));
220 }
221 }
222 Ok(())
223 }
224}
225
226impl TryFrom<LocalSignerFileConfig> for LocalSignerConfig {
227 type Error = ConfigFileError;
228
229 fn try_from(config: LocalSignerFileConfig) -> Result<Self, Self::Error> {
230 if config.path.is_empty() {
231 return Err(ConfigFileError::InvalidIdLength(
232 "Signer path cannot be empty".into(),
233 ));
234 }
235
236 let path = Path::new(&config.path);
237 if !path.exists() {
238 return Err(ConfigFileError::FileNotFound(format!(
239 "Signer file not found at path: {}",
240 path.display()
241 )));
242 }
243
244 if !path.is_file() {
245 return Err(ConfigFileError::InvalidFormat(format!(
246 "Path exists but is not a file: {}",
247 path.display()
248 )));
249 }
250
251 let passphrase = config.passphrase.get_value().map_err(|e| {
252 ConfigFileError::InvalidFormat(format!("Failed to get passphrase value: {e}"))
253 })?;
254
255 if passphrase.is_empty() {
256 return Err(ConfigFileError::InvalidFormat(
257 "Local signer passphrase cannot be empty".into(),
258 ));
259 }
260
261 let raw_key = SecretVec::new(32, |buffer| {
262 let loaded = oz_keystore::LocalClient::load(
263 Path::new(&config.path).to_path_buf(),
264 passphrase.to_str().as_str().to_string(),
265 );
266 buffer.copy_from_slice(&loaded);
267 });
268
269 Ok(LocalSignerConfig { raw_key })
270 }
271}
272
273impl TryFrom<AwsKmsSignerFileConfig> for AwsKmsSignerConfig {
274 type Error = ConfigFileError;
275
276 fn try_from(config: AwsKmsSignerFileConfig) -> Result<Self, Self::Error> {
277 Ok(AwsKmsSignerConfig {
278 region: Some(config.region),
279 key_id: config.key_id,
280 })
281 }
282}
283
284impl TryFrom<AzureKeyVaultSignerFileConfig> for AzureKeyVaultSignerConfig {
285 type Error = ConfigFileError;
286
287 fn try_from(config: AzureKeyVaultSignerFileConfig) -> Result<Self, Self::Error> {
288 let tenant_id = config
289 .tenant_id
290 .map(|value| {
291 value.get_value().map_err(|e| {
292 ConfigFileError::InvalidFormat(format!("Failed to get tenant_id value: {e}"))
293 })
294 })
295 .transpose()?;
296 let client_id = config
297 .client_id
298 .map(|value| {
299 value.get_value().map_err(|e| {
300 ConfigFileError::InvalidFormat(format!("Failed to get client_id value: {e}"))
301 })
302 })
303 .transpose()?;
304 let client_secret = config
305 .client_secret
306 .map(|value| {
307 value.get_value().map_err(|e| {
308 ConfigFileError::InvalidFormat(format!(
309 "Failed to get client_secret value: {e}"
310 ))
311 })
312 })
313 .transpose()?;
314 let federated_token_file = config
315 .federated_token_file
316 .map(|value| {
317 value.get_value().map_err(|e| {
318 ConfigFileError::InvalidFormat(format!(
319 "Failed to get federated_token_file value: {e}"
320 ))
321 })
322 })
323 .transpose()?;
324
325 Ok(AzureKeyVaultSignerConfig {
326 auth_type: config.auth_type,
327 tenant_id,
328 client_id,
329 client_secret,
330 federated_token_file,
331 vault_url: crate::models::SecretString::new(&config.vault_url),
332 key_name: crate::models::SecretString::new(&config.key_name),
333 key_version: config.key_version,
334 })
335 }
336}
337
338impl TryFrom<TurnkeySignerFileConfig> for TurnkeySignerConfig {
339 type Error = ConfigFileError;
340
341 fn try_from(config: TurnkeySignerFileConfig) -> Result<Self, Self::Error> {
342 let api_private_key = config.api_private_key.get_value().map_err(|e| {
343 ConfigFileError::InvalidFormat(format!("Failed to get API private key: {e}"))
344 })?;
345
346 Ok(TurnkeySignerConfig {
347 api_public_key: config.api_public_key,
348 api_private_key,
349 organization_id: config.organization_id,
350 private_key_id: config.private_key_id,
351 public_key: config.public_key,
352 })
353 }
354}
355
356impl TryFrom<CdpSignerFileConfig> for CdpSignerConfig {
357 type Error = ConfigFileError;
358
359 fn try_from(config: CdpSignerFileConfig) -> Result<Self, Self::Error> {
360 let api_key_secret = config.api_key_secret.get_value().map_err(|e| {
361 ConfigFileError::InvalidFormat(format!("Failed to get API key secret: {e}"))
362 })?;
363
364 let wallet_secret = config.wallet_secret.get_value().map_err(|e| {
365 ConfigFileError::InvalidFormat(format!("Failed to get wallet secret: {e}"))
366 })?;
367
368 Ok(CdpSignerConfig {
369 api_key_id: config.api_key_id,
370 api_key_secret,
371 wallet_secret,
372 account_address: config.account_address,
373 })
374 }
375}
376
377impl TryFrom<VaultSignerFileConfig> for VaultSignerConfig {
378 type Error = ConfigFileError;
379
380 fn try_from(config: VaultSignerFileConfig) -> Result<Self, Self::Error> {
381 let role_id = config
382 .role_id
383 .get_value()
384 .map_err(|e| ConfigFileError::InvalidFormat(format!("Failed to get role ID: {e}")))?;
385
386 let secret_id = config
387 .secret_id
388 .get_value()
389 .map_err(|e| ConfigFileError::InvalidFormat(format!("Failed to get secret ID: {e}")))?;
390
391 Ok(VaultSignerConfig {
392 address: config.address,
393 namespace: config.namespace,
394 role_id,
395 secret_id,
396 key_name: config.key_name,
397 mount_point: config.mount_point,
398 })
399 }
400}
401
402impl TryFrom<VaultTransitSignerFileConfig> for VaultTransitSignerConfig {
403 type Error = ConfigFileError;
404
405 fn try_from(config: VaultTransitSignerFileConfig) -> Result<Self, Self::Error> {
406 let role_id = config
407 .role_id
408 .get_value()
409 .map_err(|e| ConfigFileError::InvalidFormat(format!("Failed to get role ID: {e}")))?;
410
411 let secret_id = config
412 .secret_id
413 .get_value()
414 .map_err(|e| ConfigFileError::InvalidFormat(format!("Failed to get secret ID: {e}")))?;
415
416 Ok(VaultTransitSignerConfig {
417 key_name: config.key_name,
418 address: config.address,
419 namespace: config.namespace,
420 role_id,
421 secret_id,
422 pubkey: config.pubkey,
423 mount_point: config.mount_point,
424 })
425 }
426}
427
428impl TryFrom<GoogleCloudKmsSignerFileConfig> for GoogleCloudKmsSignerConfig {
429 type Error = ConfigFileError;
430
431 fn try_from(config: GoogleCloudKmsSignerFileConfig) -> Result<Self, Self::Error> {
432 use crate::models::SecretString;
433
434 let private_key = config
435 .service_account
436 .private_key
437 .get_value()
438 .map_err(|e| {
439 ConfigFileError::InvalidFormat(format!("Failed to get private key: {e}"))
440 })?;
441
442 let private_key_id = config
443 .service_account
444 .private_key_id
445 .get_value()
446 .map_err(|e| {
447 ConfigFileError::InvalidFormat(format!("Failed to get private key ID: {e}"))
448 })?;
449
450 let client_email = config
451 .service_account
452 .client_email
453 .get_value()
454 .map_err(|e| {
455 ConfigFileError::InvalidFormat(format!("Failed to get client email: {e}"))
456 })?;
457
458 let service_account = GoogleCloudKmsSignerServiceAccountConfig {
459 private_key,
460 private_key_id,
461 project_id: SecretString::new(&config.service_account.project_id),
462 client_email,
463 client_id: SecretString::new(&config.service_account.client_id),
464 auth_uri: SecretString::new(&config.service_account.auth_uri),
465 token_uri: SecretString::new(&config.service_account.token_uri),
466 auth_provider_x509_cert_url: SecretString::new(
467 &config.service_account.auth_provider_x509_cert_url,
468 ),
469 client_x509_cert_url: SecretString::new(&config.service_account.client_x509_cert_url),
470 universe_domain: SecretString::new(&config.service_account.universe_domain),
471 };
472
473 let key = GoogleCloudKmsSignerKeyConfig {
474 location: SecretString::new(&config.key.location),
475 key_ring_id: SecretString::new(&config.key.key_ring_id),
476 key_id: SecretString::new(&config.key.key_id),
477 key_version: config.key.key_version,
478 };
479
480 Ok(GoogleCloudKmsSignerConfig {
481 service_account,
482 key,
483 })
484 }
485}
486
487impl TryFrom<SignerFileConfigEnum> for SignerConfig {
488 type Error = ConfigFileError;
489
490 fn try_from(config: SignerFileConfigEnum) -> Result<Self, Self::Error> {
491 match config {
492 SignerFileConfigEnum::Local(local) => {
493 Ok(SignerConfig::Local(LocalSignerConfig::try_from(local)?))
494 }
495 SignerFileConfigEnum::AwsKms(aws_kms) => {
496 Ok(SignerConfig::AwsKms(AwsKmsSignerConfig::try_from(aws_kms)?))
497 }
498 SignerFileConfigEnum::AzureKeyVault(azure_key_vault) => Ok(
499 SignerConfig::AzureKeyVault(AzureKeyVaultSignerConfig::try_from(azure_key_vault)?),
500 ),
501 SignerFileConfigEnum::Turnkey(turnkey) => Ok(SignerConfig::Turnkey(
502 TurnkeySignerConfig::try_from(turnkey)?,
503 )),
504 SignerFileConfigEnum::Cdp(cdp) => {
505 Ok(SignerConfig::Cdp(CdpSignerConfig::try_from(cdp)?))
506 }
507 SignerFileConfigEnum::Vault(vault) => {
508 Ok(SignerConfig::Vault(VaultSignerConfig::try_from(vault)?))
509 }
510 SignerFileConfigEnum::VaultTransit(vault_transit) => Ok(SignerConfig::VaultTransit(
511 VaultTransitSignerConfig::try_from(vault_transit)?,
512 )),
513 SignerFileConfigEnum::GoogleCloudKms(gcp_kms) => Ok(SignerConfig::GoogleCloudKms(
514 Box::new(GoogleCloudKmsSignerConfig::try_from(gcp_kms)?),
515 )),
516 }
517 }
518}
519
520impl TryFrom<SignerFileConfig> for Signer {
521 type Error = ConfigFileError;
522
523 fn try_from(config: SignerFileConfig) -> Result<Self, Self::Error> {
524 config.validate_basic()?;
525
526 let signer_config = SignerConfig::try_from(config.config)?;
527
528 let signer = Signer::new(config.id, signer_config);
530
531 signer.validate().map_err(|e| match e {
533 crate::models::signer::SignerValidationError::EmptyId => {
534 ConfigFileError::MissingField("signer id".into())
535 }
536 crate::models::signer::SignerValidationError::InvalidIdFormat => {
537 ConfigFileError::InvalidFormat("Invalid signer ID format".into())
538 }
539 crate::models::signer::SignerValidationError::InvalidConfig(msg) => {
540 ConfigFileError::InvalidFormat(format!("Invalid signer configuration: {msg}"))
541 }
542 })?;
543
544 Ok(signer)
545 }
546}
547
548#[cfg(test)]
549mod tests {
550 use super::*;
551 use crate::models::SecretString;
552
553 #[test]
554 fn test_aws_kms_conversion() {
555 let config = AwsKmsSignerFileConfig {
556 region: "us-east-1".to_string(),
557 key_id: "test-key-id".to_string(),
558 };
559
560 let result = AwsKmsSignerConfig::try_from(config);
561 assert!(result.is_ok());
562
563 let aws_config = result.unwrap();
564 assert_eq!(aws_config.region, Some("us-east-1".to_string()));
565 assert_eq!(aws_config.key_id, "test-key-id");
566 }
567
568 #[test]
569 fn test_turnkey_conversion() {
570 let config = TurnkeySignerFileConfig {
571 api_public_key: "test-public-key".to_string(),
572 api_private_key: PlainOrEnvValue::Plain {
573 value: SecretString::new("test-private-key"),
574 },
575 organization_id: "test-org".to_string(),
576 private_key_id: "test-private-key-id".to_string(),
577 public_key: "test-public-key".to_string(),
578 };
579
580 let result = TurnkeySignerConfig::try_from(config);
581 assert!(result.is_ok());
582
583 let turnkey_config = result.unwrap();
584 assert_eq!(turnkey_config.api_public_key, "test-public-key");
585 assert_eq!(turnkey_config.organization_id, "test-org");
586 }
587
588 #[test]
589 fn test_signer_file_config_validation() {
590 let signer_config = SignerFileConfig {
591 id: "test-signer".to_string(),
592 config: SignerFileConfigEnum::Local(LocalSignerFileConfig {
593 path: "test-path".to_string(),
594 passphrase: PlainOrEnvValue::Plain {
595 value: SecretString::new("test-passphrase"),
596 },
597 }),
598 };
599
600 assert!(signer_config.validate_basic().is_ok());
601 }
602
603 #[test]
604 fn test_empty_signer_id() {
605 let signer_config = SignerFileConfig {
606 id: "".to_string(),
607 config: SignerFileConfigEnum::Local(LocalSignerFileConfig {
608 path: "test-path".to_string(),
609 passphrase: PlainOrEnvValue::Plain {
610 value: SecretString::new("test-passphrase"),
611 },
612 }),
613 };
614
615 assert!(signer_config.validate_basic().is_err());
616 }
617
618 #[test]
619 fn test_signers_config_validation() {
620 let configs = SignersFileConfig::new(vec![
621 SignerFileConfig {
622 id: "signer1".to_string(),
623 config: SignerFileConfigEnum::Local(LocalSignerFileConfig {
624 path: "test-path".to_string(),
625 passphrase: PlainOrEnvValue::Plain {
626 value: SecretString::new("test-passphrase"),
627 },
628 }),
629 },
630 SignerFileConfig {
631 id: "signer2".to_string(),
632 config: SignerFileConfigEnum::Local(LocalSignerFileConfig {
633 path: "test-path".to_string(),
634 passphrase: PlainOrEnvValue::Plain {
635 value: SecretString::new("test-passphrase"),
636 },
637 }),
638 },
639 ]);
640
641 assert!(configs.validate().is_ok());
642 }
643
644 #[test]
645 fn test_duplicate_signer_ids() {
646 let configs = SignersFileConfig::new(vec![
647 SignerFileConfig {
648 id: "signer1".to_string(),
649 config: SignerFileConfigEnum::Local(LocalSignerFileConfig {
650 path: "test-path".to_string(),
651 passphrase: PlainOrEnvValue::Plain {
652 value: SecretString::new("test-passphrase"),
653 },
654 }),
655 },
656 SignerFileConfig {
657 id: "signer1".to_string(), config: SignerFileConfigEnum::Local(LocalSignerFileConfig {
659 path: "test-path".to_string(),
660 passphrase: PlainOrEnvValue::Plain {
661 value: SecretString::new("test-passphrase"),
662 },
663 }),
664 },
665 ]);
666
667 assert!(matches!(
668 configs.validate(),
669 Err(ConfigFileError::DuplicateId(_))
670 ));
671 }
672
673 #[test]
674 fn test_local_conversion_invalid_path() {
675 let config = LocalSignerFileConfig {
676 path: "non-existent-path".to_string(),
677 passphrase: PlainOrEnvValue::Plain {
678 value: SecretString::new("test-passphrase"),
679 },
680 };
681
682 let result = LocalSignerConfig::try_from(config);
683 assert!(result.is_err());
684 if let Err(ConfigFileError::FileNotFound(msg)) = result {
685 assert!(msg.contains("Signer file not found"));
686 } else {
687 panic!("Expected FileNotFound error");
688 }
689 }
690
691 #[test]
692 fn test_vault_conversion() {
693 let config = VaultSignerFileConfig {
694 address: "https://vault.example.com".to_string(),
695 namespace: Some("test-namespace".to_string()),
696 role_id: PlainOrEnvValue::Plain {
697 value: SecretString::new("test-role"),
698 },
699 secret_id: PlainOrEnvValue::Plain {
700 value: SecretString::new("test-secret"),
701 },
702 key_name: "test-key".to_string(),
703 mount_point: Some("test-mount".to_string()),
704 };
705
706 let result = VaultSignerConfig::try_from(config);
707 assert!(result.is_ok());
708
709 let vault_config = result.unwrap();
710 assert_eq!(vault_config.address, "https://vault.example.com");
711 assert_eq!(vault_config.namespace, Some("test-namespace".to_string()));
712 }
713
714 #[test]
715 fn test_google_cloud_kms_conversion() {
716 let config = GoogleCloudKmsSignerFileConfig {
717 service_account: GoogleCloudKmsServiceAccountFileConfig {
718 project_id: "test-project".to_string(),
719 private_key_id: PlainOrEnvValue::Plain {
720 value: SecretString::new("test-key-id"),
721 },
722 private_key: PlainOrEnvValue::Plain {
723 value: SecretString::new("test-private-key"),
724 },
725 client_email: PlainOrEnvValue::Plain {
726 value: SecretString::new("test@email.com"),
727 },
728 client_id: "test-client-id".to_string(),
729 auth_uri: google_cloud_default_auth_uri(),
730 token_uri: google_cloud_default_token_uri(),
731 auth_provider_x509_cert_url: google_cloud_default_auth_provider_x509_cert_url(),
732 client_x509_cert_url: google_cloud_default_client_x509_cert_url(),
733 universe_domain: google_cloud_default_universe_domain(),
734 },
735 key: GoogleCloudKmsKeyFileConfig {
736 location: google_cloud_default_location(),
737 key_ring_id: "test-ring".to_string(),
738 key_id: "test-key".to_string(),
739 key_version: google_cloud_default_key_version(),
740 },
741 };
742
743 let result = GoogleCloudKmsSignerConfig::try_from(config);
744 assert!(result.is_ok());
745
746 let gcp_config = result.unwrap();
747 assert_eq!(gcp_config.key.key_id.to_str().as_str(), "test-key");
748 assert_eq!(
749 gcp_config.service_account.project_id.to_str().as_str(),
750 "test-project"
751 );
752 }
753
754 #[test]
755 fn test_cdp_file_config_conversion() {
756 use crate::models::SecretString;
757 let cfg = CdpSignerFileConfig {
758 api_key_id: "id".into(),
759 api_key_secret: PlainOrEnvValue::Plain {
760 value: SecretString::new("asecret"),
761 },
762 wallet_secret: PlainOrEnvValue::Plain {
763 value: SecretString::new("wsecret"),
764 },
765 account_address: "0x0000000000000000000000000000000000000000".into(),
766 };
767 let res = CdpSignerConfig::try_from(cfg);
768 assert!(res.is_ok());
769 let c = res.unwrap();
770 assert_eq!(c.api_key_id, "id");
771 assert_eq!(
772 c.account_address,
773 "0x0000000000000000000000000000000000000000"
774 );
775 }
776
777 #[test]
778 fn test_cdp_file_config_conversion_api_key_secret_error() {
779 let cfg = CdpSignerFileConfig {
780 api_key_id: "id".into(),
781 api_key_secret: PlainOrEnvValue::Env {
782 value: "NONEXISTENT_ENV_VAR".into(),
783 },
784 wallet_secret: PlainOrEnvValue::Plain {
785 value: SecretString::new("wsecret"),
786 },
787 account_address: "0x0000000000000000000000000000000000000000".into(),
788 };
789 let res = CdpSignerConfig::try_from(cfg);
790 assert!(res.is_err());
791 let err = res.unwrap_err();
792 assert!(matches!(err, ConfigFileError::InvalidFormat(_)));
793 if let ConfigFileError::InvalidFormat(msg) = err {
794 assert!(msg.contains("Failed to get API key secret"));
795 }
796 }
797
798 #[test]
799 fn test_cdp_file_config_conversion_wallet_secret_error() {
800 let cfg = CdpSignerFileConfig {
801 api_key_id: "id".into(),
802 api_key_secret: PlainOrEnvValue::Plain {
803 value: SecretString::new("asecret"),
804 },
805 wallet_secret: PlainOrEnvValue::Env {
806 value: "NONEXISTENT_ENV_VAR".into(),
807 },
808 account_address: "0x0000000000000000000000000000000000000000".into(),
809 };
810 let res = CdpSignerConfig::try_from(cfg);
811 assert!(res.is_err());
812 let err = res.unwrap_err();
813 assert!(matches!(err, ConfigFileError::InvalidFormat(_)));
814 if let ConfigFileError::InvalidFormat(msg) = err {
815 assert!(msg.contains("Failed to get wallet secret"));
816 }
817 }
818}