Skip to main content

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}