regent_sdk/secrets/mod.rs
1//! Secret management
2//!
3//! This module provides comprehensive secret management capabilities for Regent SDK.
4//! It supports multiple secret providers including local files, environment variables,
5//! and cloud-based secret managers (AWS, GCP).
6//!
7//! ## Features
8//!
9//! - **Multiple Providers**: Files, environment variables, AWS Secrets Manager, GCP Secret Manager
10//! - **Secret Pool**: Manage multiple providers with a unified interface
11//! - **Typed Secrets**: Retrieve secrets as specific types (string, structs, etc.)
12//! - **Builder Pattern**: Easy configuration of secret provider pools
13//!
14//! ## Quick Start
15//!
16//! ```no_run
17//! use regent_sdk::secrets::{SecretProvider, SecretProvidersPool, SecretProvidersPoolBuilder, SecretReference};
18//!
19//! // Create a pool with file-based secrets
20//! let files = SecretProvider::files("files");
21//! let pool = SecretProvidersPoolBuilder::new()
22//! .with_default_provider(&files)
23//! .build()
24//! .unwrap();
25//!
26//! // Refer to a secret of this provider
27//! let ssh_key = SecretReference::from_provider("/path/to/key", &files);
28//!
29//! // Or use environment variables
30//! let pool = SecretProvidersPool::with_default_provider(&SecretProvider::env_var("env"));
31//! ```
32
33pub mod local;
34pub mod remote;
35
36#[cfg(feature = "aws-secretsmanager")]
37use aws_config::SdkConfig as AwsConfig;
38use serde::de::DeserializeOwned;
39use serde::{Deserialize, Serialize};
40use std::collections::HashMap;
41use std::fmt::Debug;
42use std::sync::Arc;
43use tokio::sync::Mutex;
44#[allow(unused)]
45use tracing::{debug, error, info, trace, warn};
46
47use crate::error::RegentError;
48use crate::secrets::local::environment_variables::EnvVarSecretProvider;
49use crate::secrets::local::files::FilesSecretProvider;
50#[cfg(feature = "aws-secretsmanager")]
51use crate::secrets::remote::aws_secrets_manager::AwsSecretsManagerProvider;
52#[cfg(feature = "gcp-secretmanager")]
53use crate::secrets::remote::gcp_secret_manager::GcpSecretProvider;
54
55/// A cache for storing resolved secrets to ensure idempotency.
56///
57/// This cache stores secrets that have been retrieved from providers, ensuring
58/// that the same secret value is used throughout a run,
59/// even if the underlying secret is rotated during the operation.
60#[derive(Debug, Clone)]
61pub struct SecretCache {
62 cache: Arc<Mutex<HashMap<String, String>>>,
63}
64
65impl SecretCache {
66 pub fn new() -> Self {
67 Self {
68 cache: Arc::new(Mutex::new(HashMap::new())),
69 }
70 }
71
72 pub async fn insert(&self, secret_ref: String, value: String) {
73 let mut cache = self.cache.lock().await;
74 cache.insert(secret_ref, value);
75 }
76
77 pub async fn get(&self, secret_ref: &str) -> Option<String> {
78 let cache = self.cache.lock().await;
79 cache.get(secret_ref).cloned()
80 }
81
82 pub async fn contains(&self, secret_ref: &str) -> bool {
83 let cache = self.cache.lock().await;
84 cache.contains_key(secret_ref)
85 }
86}
87
88/// A named secret provider. The name identifies the provider in a [`SecretProvidersPool`]: it is the `Provider` of the
89/// [`SecretReference`]s pointing to its secrets. This wrapper type allows to map the "YAML API" with the rust code, meaning
90/// an artbitrary user-defined name with an actual rust item.
91#[derive(Clone)]
92pub struct SecretProvider {
93 name: String,
94 backend: SecretBackend,
95}
96
97#[derive(Clone)]
98enum SecretBackend {
99 /// Secret provider that retrieves secrets from files on disk.
100 Files(FilesSecretProvider),
101 /// Secret provider that retrieves secrets from environment variables.
102 EnvironmentVariable(EnvVarSecretProvider),
103 /// AWS Secrets Manager provider.
104 /// Requires the `aws-secretsmanager` feature to be enabled.
105 #[cfg(feature = "aws-secretsmanager")]
106 AwsSecretsManager(AwsSecretsManagerProvider),
107 /// Google Cloud Secret Manager provider.
108 /// Requires the `gcp-secretmanager` feature to be enabled.
109 #[cfg(feature = "gcp-secretmanager")]
110 GcpSecretManager(GcpSecretProvider),
111 // DelineaSecretServer,
112 // HashicorpVault,
113}
114
115impl SecretProvider {
116 /// Secrets are retrieved from files on the filesystem.
117 /// The secret reference must be the file path.
118 pub fn files(name: &str) -> Self {
119 Self::new(name, SecretBackend::Files(FilesSecretProvider::new()))
120 }
121
122 /// Secrets are retrieved from environment variables.
123 /// The secret reference must be the environment variable name.
124 pub fn env_var(name: &str) -> Self {
125 Self::new(
126 name,
127 SecretBackend::EnvironmentVariable(EnvVarSecretProvider::new()),
128 )
129 }
130
131 /// Create an AWS Secrets Manager provider.
132 ///
133 /// The secret reference must be the ARN of the secret.
134 /// Requires the `aws-secretsmanager` feature to be enabled.
135 #[cfg(feature = "aws-secretsmanager")]
136 pub fn aws_secretsmanager(name: &str, aws_config: AwsConfig) -> Self {
137 Self::new(
138 name,
139 SecretBackend::AwsSecretsManager(AwsSecretsManagerProvider::from(aws_config)),
140 )
141 }
142
143 /// Create a Google Cloud Secret Manager provider.
144 ///
145 /// The secret reference must be the Ressource Name of the secret.
146 /// Requires the `gcp-secretmanager` feature to be enabled.
147 #[cfg(feature = "gcp-secretmanager")]
148 pub async fn gcp_secretmanager(name: &str) -> Result<Self, RegentError> {
149 match GcpSecretProvider::new().await {
150 Ok(gcp_secret_provider) => Ok(Self::new(
151 name,
152 SecretBackend::GcpSecretManager(gcp_secret_provider),
153 )),
154 Err(details) => Err(RegentError::SecretsIssue(format!(
155 "Failed to create a GCP SecretManager client : {}",
156 details
157 ))),
158 }
159 }
160
161 fn new(name: &str, backend: SecretBackend) -> Self {
162 Self {
163 name: name.to_string(),
164 backend,
165 }
166 }
167
168 pub fn name(&self) -> &str {
169 &self.name
170 }
171
172 pub async fn get_secret_typed<T: DeserializeOwned>(
173 &self,
174 secret_reference: &str,
175 ) -> Result<Secret<T>, RegentError> {
176 match &self.backend {
177 SecretBackend::Files(secret_provider) => {
178 secret_provider.get_secret_typed(secret_reference).await
179 }
180 SecretBackend::EnvironmentVariable(secret_provider) => {
181 secret_provider.get_secret_typed(secret_reference).await
182 }
183 #[cfg(feature = "aws-secretsmanager")]
184 SecretBackend::AwsSecretsManager(secret_provider) => {
185 secret_provider.get_secret_typed(secret_reference).await
186 }
187 #[cfg(feature = "gcp-secretmanager")]
188 SecretBackend::GcpSecretManager(secret_provider) => {
189 secret_provider.get_secret_typed(secret_reference).await
190 } // SecretBackend::DelineaSecretServer => {}
191 // SecretBackend::HashicorpVault => {}
192 }
193 }
194
195 pub async fn get_secret_raw(
196 &self,
197 secret_reference: &str,
198 ) -> Result<Secret<String>, RegentError> {
199 match &self.backend {
200 SecretBackend::Files(secret_provider) => {
201 secret_provider.get_secret_raw(secret_reference).await
202 }
203 SecretBackend::EnvironmentVariable(secret_provider) => {
204 secret_provider.get_secret_raw(secret_reference).await
205 }
206 #[cfg(feature = "aws-secretsmanager")]
207 SecretBackend::AwsSecretsManager(secret_provider) => {
208 secret_provider.get_secret_raw(secret_reference).await
209 }
210 #[cfg(feature = "gcp-secretmanager")]
211 SecretBackend::GcpSecretManager(secret_provider) => {
212 secret_provider.get_secret_raw(secret_reference).await
213 } // SecretBackend::DelineaSecretServer => {}
214 // SecretBackend::HashicorpVault => {}
215 }
216 }
217}
218
219/// Trait for synchronous secret providers.
220///
221/// Implement this trait for secret providers that can operate synchronously.
222///
223/// # Requirements
224///
225/// Implementers must provide methods for connecting to the secret backend
226/// and retrieving secrets in both typed and raw string formats.
227pub trait SecretProvidingSolution {
228 /// Connect to the secret provider backend.
229 async fn connect(&mut self) -> Result<(), RegentError>;
230
231 /// Retrieve a secret as a specific type.
232 async fn get_secret_typed<T: DeserializeOwned>(
233 &self,
234 secret_reference: &str,
235 ) -> Result<Secret<T>, RegentError>;
236
237 /// Retrieve a secret as a raw string.
238 async fn get_secret_raw(&self, secret_reference: &str) -> Result<Secret<String>, RegentError>;
239}
240
241/// Trait for asynchronous secret providers.
242///
243/// Similar to [`SecretProvidingSolution`] but for providers that require async operations.
244///
245/// # Requirements
246///
247/// Implementers must provide methods for connecting to the secret backend
248/// and retrieving secrets in both typed and raw string formats.
249pub trait AsyncSecretProvidingSolution {
250 /// Connect to the secret provider backend.
251 async fn connect(&mut self) -> Result<(), RegentError>;
252
253 /// Retrieve a secret as a specific type.
254 async fn get_secret_typed<T: DeserializeOwned>(
255 &self,
256 secret_reference: &str,
257 ) -> Result<Secret<T>, RegentError>;
258
259 /// Retrieve a secret as a raw string.
260 async fn get_secret_raw(&self, secret_reference: &str) -> Result<Secret<String>, RegentError>;
261}
262
263/// A wrapper type that holds secret content and prevents accidental leaking.
264///
265/// This type wraps secret values and implements a custom `Debug` formatter that
266/// redacts the actual secret content, preventing accidental exposure in logs.
267///
268/// # Type Parameters
269///
270/// * `T` - The type of the secret value
271///
272/// # Example
273///
274/// ```no_run
275/// use regent_sdk::secrets::Secret;
276///
277/// let password = Secret::from("db_password", "super_secret_123".to_string());
278///
279/// // The secret content is redacted in Debug output
280/// println!("{:?}", password); // Prints: Secret { sec_ref: "db_password", inner: <redacted> }
281///
282/// // Access the inner value when needed
283/// let actual_password = password.inner();
284/// ```
285// Wrapper type which holds secrets content and helps to avoid leaking secrets (usual or debug logging in general...)
286#[derive(Clone, PartialEq, Serialize, Deserialize)]
287pub struct Secret<T> {
288 /// Reference identifier for this secret (used for auditing and debugging).
289 sec_ref: String,
290 /// The actual secret value.
291 inner: T,
292}
293
294impl<T> Debug for Secret<T>
295where
296 T: Debug,
297{
298 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299 f.debug_struct("Secret")
300 .field("sec_ref", &self.sec_ref)
301 .field("inner", &format_args!("<redacted>"))
302 .finish()
303 }
304}
305
306impl<T> Secret<T> {
307 /// Create a new secret wrapper.
308 ///
309 /// # Example
310 ///
311 /// ```no_run
312 /// use regent_sdk::secrets::Secret;
313 ///
314 /// let secret = Secret::from("api_key", "sk-1234567890");
315 /// ```
316 pub fn from(sec_ref: &str, inner: T) -> Self {
317 Self {
318 sec_ref: sec_ref.to_string(),
319 inner,
320 }
321 }
322
323 /// Consume the secret wrapper and return the inner value.
324 ///
325 /// **Warning**: This exposes the secret value. Use with caution.
326 ///
327 /// # Example
328 ///
329 /// ```no_run
330 /// use regent_sdk::secrets::Secret;
331 ///
332 /// let secret = Secret::from("password", "my_password");
333 /// let password: String = secret.inner();
334 /// ```
335 pub fn inner(self) -> T {
336 self.inner
337 }
338}
339
340/// Reference to a secret in a secret provider.
341///
342/// This struct is used to identify secrets without containing the actual secret value.
343/// It consists of a secret reference string and an optional provider name.
344///
345/// # Example
346///
347/// ```no_run
348/// use regent_sdk::secrets::SecretReference;
349///
350/// // Reference to a secret in the default provider
351/// let ref1 = SecretReference::from("my_secret", None);
352///
353/// // Reference to a secret in a specific provider
354/// let ref2 = SecretReference::from("my_secret", Some("aws".to_string()));
355/// ```
356#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
357#[serde(rename_all = "PascalCase")]
358#[serde(deny_unknown_fields)]
359pub struct SecretReference {
360 /// The reference/identifier for the secret (e.g., file path, env var name, secret name).
361 sec_ref: String,
362 /// Optional name of the secret provider to use.
363 /// If `None`, the default provider will be used.
364 provider: Option<String>,
365}
366
367impl SecretReference {
368 /// Create a new secret reference.
369 ///
370 /// # Example
371 ///
372 /// ```no_run
373 /// use regent_sdk::secrets::SecretReference;
374 ///
375 /// // Use default provider
376 /// let ref1 = SecretReference::from("password", None);
377 ///
378 /// // Use specific provider
379 /// let ref2 = SecretReference::from("api_key", Some("aws".to_string()));
380 /// ```
381 pub fn from(sec_ref: &str, provider: Option<String>) -> Self {
382 Self {
383 sec_ref: sec_ref.to_string(),
384 provider,
385 }
386 }
387
388 /// Create a reference to a secret of the given provider.
389 ///
390 /// # Example
391 ///
392 /// ```no_run
393 /// use regent_sdk::secrets::{SecretProvider, SecretReference};
394 ///
395 /// let env = SecretProvider::env_var("env");
396 /// let api_key = SecretReference::from_provider("API_KEY", &env);
397 /// ```
398 pub fn from_provider(sec_ref: &str, provider: &SecretProvider) -> Self {
399 Self::from(sec_ref, Some(provider.name.clone()))
400 }
401
402 /// Get the secret reference string.
403 pub fn sec_ref(&self) -> &str {
404 &self.sec_ref
405 }
406
407 /// Get the optional provider name.
408 pub fn provider(&self) -> &Option<String> {
409 &self.provider
410 }
411}
412
413/// Builder for creating [`SecretProvidersPool`] instances.
414///
415/// This builder allows you to configure multiple secret providers and set a default.
416/// Use the builder pattern to add providers and then call [`SecretProvidersPoolBuilder::build()`] to create the pool.
417///
418/// # Example
419///
420/// ```no_run
421/// use regent_sdk::secrets::{SecretProvider, SecretProvidersPoolBuilder};
422///
423/// let pool = SecretProvidersPoolBuilder::new()
424/// .add_provider(&SecretProvider::files("files"))
425/// .with_default_provider(&SecretProvider::env_var("env"))
426/// .build()
427/// .unwrap();
428/// ```
429pub struct SecretProvidersPoolBuilder {
430 providers: HashMap<String, SecretProvider>,
431 default_provider: Option<String>,
432}
433
434impl SecretProvidersPoolBuilder {
435 /// Create a new, empty secret providers pool builder.
436 ///
437 /// # Example
438 ///
439 /// ```no_run
440 /// use regent_sdk::secrets::SecretProvidersPoolBuilder;
441 ///
442 /// let builder = SecretProvidersPoolBuilder::new();
443 /// ```
444 pub fn new() -> Self {
445 Self {
446 providers: HashMap::new(),
447 default_provider: None,
448 }
449 }
450
451 /// Add a secret provider to the pool.
452 ///
453 /// # Example
454 ///
455 /// ```no_run
456 /// use regent_sdk::secrets::{SecretProvider, SecretProvidersPoolBuilder};
457 ///
458 /// let builder = SecretProvidersPoolBuilder::new()
459 /// .add_provider(&SecretProvider::files("files"));
460 /// ```
461 pub fn add_provider(mut self, provider: &SecretProvider) -> Self {
462 if let Some(_old_secret_provider) = self
463 .providers
464 .insert(provider.name.clone(), provider.clone())
465 {
466 warn!(
467 "You just overrided a secret provider in the pool, also identified by the name {}",
468 provider.name
469 );
470 }
471 self
472 }
473
474 /// Add a secret provider and set it as the default.
475 ///
476 /// This is a convenience method that combines `add_provider` and `set_default`.
477 ///
478 /// # Example
479 ///
480 /// ```no_run
481 /// use regent_sdk::secrets::{SecretProvider, SecretProvidersPoolBuilder};
482 ///
483 /// let builder = SecretProvidersPoolBuilder::new()
484 /// .with_default_provider(&SecretProvider::files("files"));
485 /// ```
486 pub fn with_default_provider(self, provider: &SecretProvider) -> Self {
487 let mut builder = self.add_provider(provider);
488 builder.default_provider = Some(provider.name.clone());
489 builder
490 }
491
492 /// Set the default provider.
493 ///
494 /// The provider must have been previously added with `add_provider`.
495 ///
496 /// # Example
497 ///
498 /// ```no_run
499 /// use regent_sdk::secrets::{SecretProvider, SecretProvidersPoolBuilder};
500 ///
501 /// let files = SecretProvider::files("files");
502 /// let builder = SecretProvidersPoolBuilder::new()
503 /// .add_provider(&files)
504 /// .add_provider(&SecretProvider::env_var("env"))
505 /// .set_default(&files);
506 /// ```
507 pub fn set_default(mut self, provider: &SecretProvider) -> Self {
508 self.default_provider = Some(provider.name.clone());
509 self
510 }
511
512 /// Build the secret providers pool.
513 ///
514 /// This consumes the builder and returns a new [`SecretProvidersPool`] if validation passes.
515 ///
516 /// # Example
517 ///
518 /// ```no_run
519 /// use regent_sdk::secrets::{SecretProvider, SecretProvidersPoolBuilder};
520 ///
521 /// let pool = SecretProvidersPoolBuilder::new()
522 /// .with_default_provider(&SecretProvider::files("files"))
523 /// .build()
524 /// .unwrap();
525 /// ```
526 pub fn build(self) -> Result<SecretProvidersPool, RegentError> {
527 match self.default_provider {
528 Some(default_provider_name) => match self.providers.get(&default_provider_name) {
529 Some(_secrets_provider) => Ok(SecretProvidersPool {
530 providers: self.providers,
531 default_provider: default_provider_name,
532 secret_cache: None,
533 }),
534 None => {
535 error!(
536 "Default secrets provider ({}) is not set",
537 default_provider_name
538 );
539 return Err(RegentError::SecretsIssue(format!(
540 "Default secrets provider ({}) is not set",
541 default_provider_name
542 )));
543 }
544 },
545 None => {
546 error!("No default secrets provider set");
547 return Err(RegentError::SecretsIssue(
548 "No default secrets provider set".to_string(),
549 ));
550 }
551 }
552 }
553}
554
555/// A pool of multiple secret providers with a unified interface.
556///
557/// This struct manages multiple [`SecretProvider`] instances and provides a single
558/// interface for retrieving secrets. When a secret is requested, the pool uses
559/// either the provider specified in the secret reference or the default provider.
560///
561/// # Example
562///
563/// ```no_run
564/// use regent_sdk::secrets::{SecretProvider, SecretProvidersPool, SecretReference};
565///
566/// // Create a pool with a single provider
567/// let pool = SecretProvidersPool::with_default_provider(&SecretProvider::files("files"));
568///
569/// // Retrieve a secret
570/// let secret_ref = SecretReference::from("/path/to/secret", None);
571/// let secret = pool.get_secret_raw(&secret_ref).await.unwrap();
572/// ```
573#[derive(Clone)]
574pub struct SecretProvidersPool {
575 /// Map of provider names to provider instances.
576 providers: HashMap<String, SecretProvider>,
577 /// Name of the default provider to use when none is specified.
578 default_provider: String,
579 /// Optional cache for storing resolved secrets to ensure idempotency.
580 /// When `Some`, secrets are cached after first retrieval and subsequent
581 /// requests for the same secret will return the cached value.
582 secret_cache: Option<SecretCache>,
583}
584
585impl SecretProvidersPool {
586 /// Create a pool holding a single provider, used as the default provider.
587 ///
588 /// To hold several providers, use [`SecretProvidersPoolBuilder`].
589 ///
590 /// # Example
591 ///
592 /// ```no_run
593 /// use regent_sdk::secrets::{SecretProvider, SecretProvidersPool};
594 ///
595 /// let pool = SecretProvidersPool::with_default_provider(&SecretProvider::files("files"));
596 /// ```
597 pub fn with_default_provider(secret_provider: &SecretProvider) -> Self {
598 let mut providers: HashMap<String, SecretProvider> = HashMap::new();
599 providers.insert(secret_provider.name.clone(), secret_provider.clone());
600 Self {
601 providers,
602 default_provider: secret_provider.name.clone(),
603 secret_cache: None,
604 }
605 }
606
607 /// Enable secret caching for this pool.
608 ///
609 /// When caching is enabled, secrets are stored after first retrieval and
610 /// subsequent requests for the same secret will return the cached value.
611 /// This ensures idempotency by preventing secret rotation from affecting
612 /// compliance operations that are in progress.
613 ///
614 /// # Example
615 ///
616 /// ```no_run
617 /// use regent_sdk::secrets::{SecretProvider, SecretProvidersPool};
618 ///
619 /// let mut pool = SecretProvidersPool::with_default_provider(&SecretProvider::files("files"));
620 /// pool.enable_caching();
621 /// ```
622 pub fn enable_caching(&mut self) {
623 self.secret_cache = Some(SecretCache::new());
624 }
625
626 /// Disable secret caching for this pool.
627 ///
628 /// # Example
629 ///
630 /// ```no_run
631 /// use regent_sdk::secrets::{SecretProvider, SecretProvidersPool};
632 ///
633 /// let mut pool = SecretProvidersPool::with_default_provider(&SecretProvider::files("files"));
634 /// pool.disable_caching();
635 /// ```
636 pub fn disable_caching(&mut self) {
637 self.secret_cache = None;
638 }
639
640 /// Check if caching is enabled for this pool.
641 pub fn is_caching_enabled(&self) -> bool {
642 self.secret_cache.is_some()
643 }
644
645 /// Create a cache key from a secret reference.
646 ///
647 /// This creates a unique key that combines the provider name and secret reference
648 /// to ensure proper caching across different providers.
649 pub fn create_cache_key(&self, secret_reference: &SecretReference) -> String {
650 let provider = match secret_reference.provider() {
651 Some(user_defined_provider) => user_defined_provider.clone(),
652 None => self.default_provider.clone(),
653 };
654 format!("{}:{}", provider, secret_reference.sec_ref())
655 }
656
657 /// Retrieve a secret as a specific type.
658 ///
659 /// # Example
660 ///
661 /// ```no_run
662 /// use regent_sdk::secrets::{SecretProvidersPool, SecretReference};
663 /// use serde::Deserialize;
664 ///
665 /// #[derive(Deserialize)]
666 /// struct ApiCredentials {
667 /// key: String,
668 /// secret: String,
669 /// }
670 ///
671 /// let pool = SecretProvidersPool::with_default_provider(&SecretProvider::files("files"));
672 /// let secret_ref = SecretReference::from("api_creds.json", None);
673 /// let creds: Secret<ApiCredentials> = pool.get_secret_typed(&secret_ref).await.unwrap();
674 /// ```
675 pub async fn get_secret_typed<T: DeserializeOwned>(
676 &self,
677 secret_reference: &SecretReference,
678 ) -> Result<Secret<T>, RegentError> {
679 let provider = match secret_reference.provider() {
680 Some(user_defined_provider) => user_defined_provider,
681 None => &self.default_provider,
682 };
683
684 match self.providers.get(provider) {
685 Some(secret_provider) => {
686 secret_provider
687 .get_secret_typed(secret_reference.sec_ref())
688 .await
689 }
690 None => {
691 error!(
692 "Default secrets provider {} not found. Was the SecretProvidersPoolBuilder type used to build this SecretProvidersPool ?",
693 self.default_provider
694 );
695 return Err(RegentError::SecretsIssue(format!(
696 "Default secrets provider {} not found. Was the SecretProvidersPoolBuilder type used to build this SecretProvidersPool ?",
697 self.default_provider
698 )));
699 }
700 }
701 }
702
703 /// Retrieve a secret as a raw string.
704 ///
705 /// # Example
706 ///
707 /// ```no_run
708 /// use regent_sdk::secrets::{SecretProvidersPool, SecretReference};
709 ///
710 /// let pool = SecretProvidersPool::with_default_provider(&SecretProvider::files("files"));
711 /// let secret_ref = SecretReference::from("/path/to/password.txt", None);
712 /// let password = pool.get_secret_raw(&secret_ref).await.unwrap();
713 /// let password_str: String = password.inner();
714 /// ```
715 pub async fn get_secret_raw(
716 &self,
717 secret_reference: &SecretReference,
718 ) -> Result<Secret<String>, RegentError> {
719 let cache_key = self.create_cache_key(secret_reference);
720
721 // Check if the secret is in cache
722 if let Some(cache) = &self.secret_cache {
723 if let Some(cached_value) = cache.get(&cache_key).await {
724 debug!("Secret cache hit for: {}", cache_key);
725 return Ok(Secret::from(secret_reference.sec_ref(), cached_value));
726 }
727 }
728
729 // Secret not in cache, fetch from provider
730 let provider = match secret_reference.provider() {
731 Some(user_defined_provider) => user_defined_provider,
732 None => &self.default_provider,
733 };
734
735 let secret_result = match self.providers.get(provider) {
736 Some(secret_provider) => {
737 secret_provider
738 .get_secret_raw(secret_reference.sec_ref())
739 .await
740 }
741 None => {
742 error!(
743 "Default secrets provider {} not found. Was the SecretProvidersPoolBuilder type used to build this SecretProvidersPool ?",
744 self.default_provider
745 );
746 return Err(RegentError::SecretsIssue(format!(
747 "Default secrets provider {} not found. Was the SecretProvidersPoolBuilder type used to build this SecretProvidersPool ?",
748 self.default_provider
749 )));
750 }
751 };
752
753 // Store in cache if caching is enabled
754 if let Some(cache) = &self.secret_cache {
755 if let Ok(secret) = &secret_result {
756 let secret_value = secret.clone().inner();
757 cache.insert(cache_key.clone(), secret_value).await;
758 debug!("Secret cached for: {}", cache_key);
759 }
760 }
761
762 secret_result
763 }
764}