Skip to main content

static_credstore_plugin/
config.rs

1use serde::Deserialize;
2use uuid::Uuid;
3
4use credstore_sdk::SharingMode;
5
6/// Plugin configuration.
7#[derive(Debug, Clone, Deserialize)]
8#[serde(default, deny_unknown_fields)]
9pub struct StaticCredStorePluginConfig {
10    /// Vendor name for GTS instance registration.
11    pub vendor: String,
12
13    /// Plugin priority (lower = higher priority).
14    pub priority: i16,
15
16    /// Static secrets served by this plugin.
17    pub secrets: Vec<SecretConfig>,
18}
19
20impl Default for StaticCredStorePluginConfig {
21    fn default() -> Self {
22        Self {
23            vendor: "hyperspot".to_owned(),
24            priority: 100,
25            secrets: Vec::new(),
26        }
27    }
28}
29
30/// A single secret entry in the plugin configuration.
31#[derive(Clone, Deserialize)]
32#[serde(deny_unknown_fields)]
33pub struct SecretConfig {
34    /// Tenant that owns this secret.
35    pub tenant_id: Uuid,
36
37    /// Owner (subject) of this secret.
38    pub owner_id: Uuid,
39
40    /// Secret reference key (validated as `SecretRef` at init).
41    pub key: String,
42
43    /// Secret value (plaintext string, converted to bytes at init).
44    pub value: String,
45
46    /// Sharing mode for this secret.
47    #[serde(default)]
48    pub sharing: SharingMode,
49}
50
51impl core::fmt::Debug for SecretConfig {
52    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53        f.debug_struct("SecretConfig")
54            .field("tenant_id", &self.tenant_id)
55            .field("owner_id", &self.owner_id)
56            .field("key", &self.key)
57            .field("value", &"<redacted>")
58            .field("sharing", &self.sharing)
59            .finish()
60    }
61}
62
63#[cfg(test)]
64#[cfg_attr(coverage_nightly, coverage(off))]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn config_defaults_are_applied() {
70        let yaml = r#"
71secrets:
72  - tenant_id: "00000000-0000-0000-0000-000000000001"
73    owner_id: "00000000-0000-0000-0000-000000000002"
74    key: "openai_api_key"
75    value: "sk-test-123"
76"#;
77
78        let parsed: Result<StaticCredStorePluginConfig, _> = serde_saphyr::from_str(yaml);
79        assert!(parsed.is_ok());
80
81        let cfg = match parsed {
82            Ok(cfg) => cfg,
83            Err(e) => panic!("failed to parse config: {e}"),
84        };
85
86        assert_eq!(cfg.vendor, "hyperspot");
87        assert_eq!(cfg.priority, 100);
88        assert_eq!(cfg.secrets.len(), 1);
89        assert_eq!(cfg.secrets[0].sharing, SharingMode::Tenant);
90    }
91
92    #[test]
93    fn config_rejects_unknown_fields() {
94        let yaml = r#"
95vendor: "hyperspot"
96priority: 100
97unexpected: true
98"#;
99
100        let parsed: Result<StaticCredStorePluginConfig, _> = serde_saphyr::from_str(yaml);
101        assert!(parsed.is_err());
102    }
103
104    #[test]
105    fn config_allows_empty_secrets() {
106        let parsed: Result<StaticCredStorePluginConfig, _> = serde_saphyr::from_str("{}");
107        assert!(parsed.is_ok());
108
109        let cfg = match parsed {
110            Ok(cfg) => cfg,
111            Err(e) => panic!("failed to parse config: {e}"),
112        };
113        assert!(cfg.secrets.is_empty());
114        assert_eq!(cfg.vendor, "hyperspot");
115        assert_eq!(cfg.priority, 100);
116    }
117}