secrets_core/
backend.rs

1use crate::errors::Result;
2use crate::types::{Scope, SecretListItem, SecretRecord};
3use crate::uri::SecretUri;
4use serde::{Deserialize, Serialize};
5
6#[cfg(feature = "aws")]
7pub mod aws;
8#[cfg(feature = "azure")]
9pub mod azure;
10#[cfg(feature = "env")]
11pub mod env;
12#[cfg(feature = "file")]
13pub mod file;
14#[cfg(feature = "gcp")]
15pub mod gcp;
16#[cfg(feature = "k8s")]
17pub mod k8s;
18
19/// Version metadata describing a specific revision of a secret.
20#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
21pub struct SecretVersion {
22    pub version: u64,
23    pub deleted: bool,
24}
25
26impl SecretVersion {
27    /// Convenience helper to indicate whether the version represents a tombstone.
28    pub fn is_deleted(&self) -> bool {
29        self.deleted
30    }
31}
32
33/// Versioned record returned by backends.
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
35pub struct VersionedSecret {
36    pub version: u64,
37    pub deleted: bool,
38    pub record: Option<SecretRecord>,
39}
40
41impl VersionedSecret {
42    /// Returns a reference to the underlying record when present.
43    pub fn record(&self) -> Option<&SecretRecord> {
44        self.record.as_ref()
45    }
46}
47
48/// Storage interface implemented by provider backends.
49pub trait SecretsBackend: Send + Sync {
50    /// Persist an encrypted record and return the assigned version.
51    fn put(&self, record: SecretRecord) -> Result<SecretVersion>;
52
53    /// Retrieve the latest (or specific) version of a secret.
54    ///
55    /// When `version` is `None`, implementors should return the latest non-deleted
56    /// record, or `None` when the secret does not exist or is tombstoned.
57    /// When `version` is `Some`, tombstoned revisions should be returned with
58    /// `deleted = true` and `record = None`.
59    fn get(&self, uri: &SecretUri, version: Option<u64>) -> Result<Option<VersionedSecret>>;
60
61    /// List secrets scoped to the provided scope, optionally filtered by category/name prefixes.
62    fn list(
63        &self,
64        scope: &Scope,
65        category_prefix: Option<&str>,
66        name_prefix: Option<&str>,
67    ) -> Result<Vec<SecretListItem>>;
68
69    /// Create a tombstone and return the version metadata for the deletion.
70    fn delete(&self, uri: &SecretUri) -> Result<SecretVersion>;
71
72    /// Enumerate all versions for a secret ordered from oldest to newest.
73    fn versions(&self, uri: &SecretUri) -> Result<Vec<SecretVersion>>;
74
75    /// Check whether the latest revision of the secret exists (i.e. is not tombstoned).
76    fn exists(&self, uri: &SecretUri) -> Result<bool>;
77}
78
79impl<T> SecretsBackend for Box<T>
80where
81    T: SecretsBackend + ?Sized,
82{
83    fn put(&self, record: SecretRecord) -> Result<SecretVersion> {
84        (**self).put(record)
85    }
86
87    fn get(&self, uri: &SecretUri, version: Option<u64>) -> Result<Option<VersionedSecret>> {
88        (**self).get(uri, version)
89    }
90
91    fn list(
92        &self,
93        scope: &Scope,
94        category_prefix: Option<&str>,
95        name_prefix: Option<&str>,
96    ) -> Result<Vec<SecretListItem>> {
97        (**self).list(scope, category_prefix, name_prefix)
98    }
99
100    fn delete(&self, uri: &SecretUri) -> Result<SecretVersion> {
101        (**self).delete(uri)
102    }
103
104    fn versions(&self, uri: &SecretUri) -> Result<Vec<SecretVersion>> {
105        (**self).versions(uri)
106    }
107
108    fn exists(&self, uri: &SecretUri) -> Result<bool> {
109        (**self).exists(uri)
110    }
111}
112
113impl<T> SecretsBackend for std::sync::Arc<T>
114where
115    T: SecretsBackend + ?Sized,
116{
117    fn put(&self, record: SecretRecord) -> Result<SecretVersion> {
118        (**self).put(record)
119    }
120
121    fn get(&self, uri: &SecretUri, version: Option<u64>) -> Result<Option<VersionedSecret>> {
122        (**self).get(uri, version)
123    }
124
125    fn list(
126        &self,
127        scope: &Scope,
128        category_prefix: Option<&str>,
129        name_prefix: Option<&str>,
130    ) -> Result<Vec<SecretListItem>> {
131        (**self).list(scope, category_prefix, name_prefix)
132    }
133
134    fn delete(&self, uri: &SecretUri) -> Result<SecretVersion> {
135        (**self).delete(uri)
136    }
137
138    fn versions(&self, uri: &SecretUri) -> Result<Vec<SecretVersion>> {
139        (**self).versions(uri)
140    }
141
142    fn exists(&self, uri: &SecretUri) -> Result<bool> {
143        (**self).exists(uri)
144    }
145}