llm_shield_cloud/
config.rs

1//! Configuration structures for cloud integrations.
2//!
3//! Provides unified configuration for all cloud providers.
4
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8/// Cloud provider selection.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
10#[serde(rename_all = "lowercase")]
11pub enum CloudProvider {
12    /// Amazon Web Services
13    AWS,
14    /// Google Cloud Platform
15    GCP,
16    /// Microsoft Azure
17    Azure,
18    /// No cloud provider (local/development mode)
19    None,
20}
21
22impl CloudProvider {
23    /// Returns the provider name as a string.
24    pub fn as_str(&self) -> &str {
25        match self {
26            CloudProvider::AWS => "aws",
27            CloudProvider::GCP => "gcp",
28            CloudProvider::Azure => "azure",
29            CloudProvider::None => "none",
30        }
31    }
32
33    /// Checks if cloud integration is enabled.
34    pub fn is_enabled(&self) -> bool {
35        !matches!(self, CloudProvider::None)
36    }
37}
38
39/// Root cloud configuration.
40#[derive(Debug, Clone, Deserialize, Serialize)]
41pub struct CloudConfig {
42    /// Selected cloud provider.
43    #[serde(default = "default_provider")]
44    pub provider: CloudProvider,
45
46    /// AWS-specific configuration.
47    #[serde(default)]
48    pub aws: AwsConfig,
49
50    /// GCP-specific configuration.
51    #[serde(default)]
52    pub gcp: GcpConfig,
53
54    /// Azure-specific configuration.
55    #[serde(default)]
56    pub azure: AzureConfig,
57}
58
59fn default_provider() -> CloudProvider {
60    CloudProvider::None
61}
62
63impl Default for CloudConfig {
64    fn default() -> Self {
65        Self {
66            provider: CloudProvider::None,
67            aws: AwsConfig::default(),
68            gcp: GcpConfig::default(),
69            azure: AzureConfig::default(),
70        }
71    }
72}
73
74// ============================================================================
75// AWS Configuration
76// ============================================================================
77
78/// AWS-specific configuration.
79#[derive(Debug, Clone, Default, Deserialize, Serialize)]
80pub struct AwsConfig {
81    /// AWS region (e.g., "us-east-1").
82    #[serde(default = "default_aws_region")]
83    pub region: String,
84
85    /// Secrets Manager configuration.
86    #[serde(default)]
87    pub secrets_manager: AwsSecretsManagerConfig,
88
89    /// S3 storage configuration.
90    #[serde(default)]
91    pub s3: AwsS3Config,
92
93    /// CloudWatch configuration.
94    #[serde(default)]
95    pub cloudwatch: AwsCloudWatchConfig,
96
97    /// X-Ray tracing configuration.
98    #[serde(default)]
99    pub xray: AwsXRayConfig,
100}
101
102fn default_aws_region() -> String {
103    "us-east-1".to_string()
104}
105
106/// AWS Secrets Manager configuration.
107#[derive(Debug, Clone, Deserialize, Serialize)]
108pub struct AwsSecretsManagerConfig {
109    /// Enable Secrets Manager integration.
110    #[serde(default)]
111    pub enabled: bool,
112
113    /// Cache TTL in seconds.
114    #[serde(default = "default_cache_ttl")]
115    pub cache_ttl_seconds: u64,
116}
117
118impl Default for AwsSecretsManagerConfig {
119    fn default() -> Self {
120        Self {
121            enabled: false,
122            cache_ttl_seconds: 300,
123        }
124    }
125}
126
127fn default_cache_ttl() -> u64 {
128    300
129}
130
131/// AWS S3 configuration.
132#[derive(Debug, Clone, Default, Deserialize, Serialize)]
133pub struct AwsS3Config {
134    /// S3 bucket name.
135    #[serde(default)]
136    pub bucket: String,
137
138    /// Prefix for models.
139    #[serde(default = "default_models_prefix")]
140    pub models_prefix: String,
141
142    /// Prefix for scan results.
143    #[serde(default = "default_results_prefix")]
144    pub results_prefix: String,
145}
146
147fn default_models_prefix() -> String {
148    "models/".to_string()
149}
150
151fn default_results_prefix() -> String {
152    "scan-results/".to_string()
153}
154
155/// AWS CloudWatch configuration.
156#[derive(Debug, Clone, Deserialize, Serialize)]
157pub struct AwsCloudWatchConfig {
158    /// Enable CloudWatch integration.
159    #[serde(default)]
160    pub enabled: bool,
161
162    /// CloudWatch namespace for metrics.
163    #[serde(default = "default_cw_namespace")]
164    pub namespace: String,
165
166    /// CloudWatch log group.
167    #[serde(default = "default_cw_log_group")]
168    pub log_group: String,
169}
170
171impl Default for AwsCloudWatchConfig {
172    fn default() -> Self {
173        Self {
174            enabled: false,
175            namespace: "LLMShield".to_string(),
176            log_group: "/llm-shield/api".to_string(),
177        }
178    }
179}
180
181fn default_cw_namespace() -> String {
182    "LLMShield".to_string()
183}
184
185fn default_cw_log_group() -> String {
186    "/llm-shield/api".to_string()
187}
188
189/// AWS X-Ray configuration.
190#[derive(Debug, Clone, Deserialize, Serialize)]
191pub struct AwsXRayConfig {
192    /// Enable X-Ray tracing.
193    #[serde(default)]
194    pub enabled: bool,
195}
196
197impl Default for AwsXRayConfig {
198    fn default() -> Self {
199        Self { enabled: false }
200    }
201}
202
203// ============================================================================
204// GCP Configuration
205// ============================================================================
206
207/// GCP-specific configuration.
208#[derive(Debug, Clone, Default, Deserialize, Serialize)]
209pub struct GcpConfig {
210    /// GCP project ID.
211    #[serde(default)]
212    pub project_id: String,
213
214    /// Secret Manager configuration.
215    #[serde(default)]
216    pub secret_manager: GcpSecretManagerConfig,
217
218    /// Cloud Storage configuration.
219    #[serde(default)]
220    pub cloud_storage: GcpCloudStorageConfig,
221
222    /// Cloud Logging configuration.
223    #[serde(default)]
224    pub cloud_logging: GcpCloudLoggingConfig,
225
226    /// Cloud Trace configuration.
227    #[serde(default)]
228    pub cloud_trace: GcpCloudTraceConfig,
229}
230
231/// GCP Secret Manager configuration.
232#[derive(Debug, Clone, Deserialize, Serialize)]
233pub struct GcpSecretManagerConfig {
234    /// Enable Secret Manager integration.
235    #[serde(default)]
236    pub enabled: bool,
237
238    /// Cache TTL in seconds.
239    #[serde(default = "default_cache_ttl")]
240    pub cache_ttl_seconds: u64,
241}
242
243impl Default for GcpSecretManagerConfig {
244    fn default() -> Self {
245        Self {
246            enabled: false,
247            cache_ttl_seconds: 300,
248        }
249    }
250}
251
252/// GCP Cloud Storage configuration.
253#[derive(Debug, Clone, Default, Deserialize, Serialize)]
254pub struct GcpCloudStorageConfig {
255    /// Cloud Storage bucket name.
256    #[serde(default)]
257    pub bucket: String,
258}
259
260/// GCP Cloud Logging configuration.
261#[derive(Debug, Clone, Deserialize, Serialize)]
262pub struct GcpCloudLoggingConfig {
263    /// Enable Cloud Logging integration.
264    #[serde(default)]
265    pub enabled: bool,
266
267    /// Log name.
268    #[serde(default = "default_gcp_log_name")]
269    pub log_name: String,
270}
271
272impl Default for GcpCloudLoggingConfig {
273    fn default() -> Self {
274        Self {
275            enabled: false,
276            log_name: "llm-shield-api".to_string(),
277        }
278    }
279}
280
281fn default_gcp_log_name() -> String {
282    "llm-shield-api".to_string()
283}
284
285/// GCP Cloud Trace configuration.
286#[derive(Debug, Clone, Deserialize, Serialize)]
287pub struct GcpCloudTraceConfig {
288    /// Enable Cloud Trace integration.
289    #[serde(default)]
290    pub enabled: bool,
291}
292
293impl Default for GcpCloudTraceConfig {
294    fn default() -> Self {
295        Self { enabled: false }
296    }
297}
298
299// ============================================================================
300// Azure Configuration
301// ============================================================================
302
303/// Azure-specific configuration.
304#[derive(Debug, Clone, Default, Deserialize, Serialize)]
305pub struct AzureConfig {
306    /// Azure subscription ID.
307    #[serde(default)]
308    pub subscription_id: String,
309
310    /// Azure resource group.
311    #[serde(default)]
312    pub resource_group: String,
313
314    /// Key Vault configuration.
315    #[serde(default)]
316    pub key_vault: AzureKeyVaultConfig,
317
318    /// Blob Storage configuration.
319    #[serde(default)]
320    pub blob_storage: AzureBlobStorageConfig,
321
322    /// Azure Monitor configuration.
323    #[serde(default)]
324    pub monitor: AzureMonitorConfig,
325
326    /// Application Insights configuration.
327    #[serde(default)]
328    pub application_insights: AzureApplicationInsightsConfig,
329}
330
331/// Azure Key Vault configuration.
332#[derive(Debug, Clone, Deserialize, Serialize)]
333pub struct AzureKeyVaultConfig {
334    /// Key Vault URL (e.g., "https://my-vault.vault.azure.net/").
335    #[serde(default)]
336    pub vault_url: String,
337
338    /// Cache TTL in seconds.
339    #[serde(default = "default_cache_ttl")]
340    pub cache_ttl_seconds: u64,
341}
342
343impl Default for AzureKeyVaultConfig {
344    fn default() -> Self {
345        Self {
346            vault_url: String::new(),
347            cache_ttl_seconds: 300,
348        }
349    }
350}
351
352/// Azure Blob Storage configuration.
353#[derive(Debug, Clone, Default, Deserialize, Serialize)]
354pub struct AzureBlobStorageConfig {
355    /// Storage account name.
356    #[serde(default)]
357    pub account: String,
358
359    /// Container name.
360    #[serde(default)]
361    pub container: String,
362}
363
364/// Azure Monitor configuration.
365#[derive(Debug, Clone, Deserialize, Serialize)]
366pub struct AzureMonitorConfig {
367    /// Enable Azure Monitor integration.
368    #[serde(default)]
369    pub enabled: bool,
370
371    /// Log Analytics workspace ID.
372    #[serde(default)]
373    pub workspace_id: String,
374}
375
376impl Default for AzureMonitorConfig {
377    fn default() -> Self {
378        Self {
379            enabled: false,
380            workspace_id: String::new(),
381        }
382    }
383}
384
385/// Azure Application Insights configuration.
386#[derive(Debug, Clone, Deserialize, Serialize)]
387pub struct AzureApplicationInsightsConfig {
388    /// Instrumentation key.
389    #[serde(default)]
390    pub instrumentation_key: String,
391
392    /// Enable Application Insights.
393    #[serde(default)]
394    pub enabled: bool,
395}
396
397impl Default for AzureApplicationInsightsConfig {
398    fn default() -> Self {
399        Self {
400            instrumentation_key: String::new(),
401            enabled: false,
402        }
403    }
404}
405
406#[cfg(test)]
407mod tests {
408    use super::*;
409
410    #[test]
411    fn test_cloud_provider_as_str() {
412        assert_eq!(CloudProvider::AWS.as_str(), "aws");
413        assert_eq!(CloudProvider::GCP.as_str(), "gcp");
414        assert_eq!(CloudProvider::Azure.as_str(), "azure");
415        assert_eq!(CloudProvider::None.as_str(), "none");
416    }
417
418    #[test]
419    fn test_cloud_provider_is_enabled() {
420        assert!(CloudProvider::AWS.is_enabled());
421        assert!(CloudProvider::GCP.is_enabled());
422        assert!(CloudProvider::Azure.is_enabled());
423        assert!(!CloudProvider::None.is_enabled());
424    }
425
426    #[test]
427    fn test_cloud_config_default() {
428        let config = CloudConfig::default();
429        assert_eq!(config.provider, CloudProvider::None);
430        assert!(!config.provider.is_enabled());
431    }
432
433    #[test]
434    fn test_aws_config_defaults() {
435        let config = AwsConfig::default();
436        assert_eq!(config.region, "us-east-1");
437        assert!(!config.secrets_manager.enabled);
438        assert_eq!(config.secrets_manager.cache_ttl_seconds, 300);
439    }
440
441    #[test]
442    fn test_config_deserialization() {
443        let yaml = r#"
444provider: aws
445aws:
446  region: us-west-2
447  secrets_manager:
448    enabled: true
449    cache_ttl_seconds: 600
450  s3:
451    bucket: my-bucket
452"#;
453
454        let config: CloudConfig = serde_yaml::from_str(yaml).unwrap();
455        assert_eq!(config.provider, CloudProvider::AWS);
456        assert_eq!(config.aws.region, "us-west-2");
457        assert!(config.aws.secrets_manager.enabled);
458        assert_eq!(config.aws.secrets_manager.cache_ttl_seconds, 600);
459        assert_eq!(config.aws.s3.bucket, "my-bucket");
460    }
461
462    #[test]
463    fn test_config_serialization() {
464        let config = CloudConfig {
465            provider: CloudProvider::GCP,
466            gcp: GcpConfig {
467                project_id: "my-project".to_string(),
468                secret_manager: GcpSecretManagerConfig {
469                    enabled: true,
470                    cache_ttl_seconds: 300,
471                },
472                ..Default::default()
473            },
474            ..Default::default()
475        };
476
477        let yaml = serde_yaml::to_string(&config).unwrap();
478        assert!(yaml.contains("provider: gcp"));
479        assert!(yaml.contains("project_id: my-project"));
480    }
481}