use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct PolicySet {
pub version: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<PolicyMetadata>,
pub defaults: PolicyDefaults,
pub rules: Vec<PolicyRule>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct PolicyMetadata {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub author: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub modified: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct PolicyDefaults {
#[serde(default = "default_allow_methods")]
pub allow_methods: Vec<String>,
#[serde(default = "default_true")]
pub deny_external_refs: bool,
#[serde(default = "default_true")]
pub require_auth: bool,
#[serde(default = "default_audit_level")]
pub audit_level: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct PolicyRule {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub pattern: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub conditions: Option<Vec<PolicyCondition>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub allow: Option<PolicyAction>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deny: Option<PolicyAction>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub audit: Option<AuditConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub explain: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct PolicyCondition {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auth_profile: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub time_window: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_ip: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct PolicyAction {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub operations: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub methods: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub all: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AuditConfig {
#[serde(default = "default_audit_level")]
pub level: String,
#[serde(default)]
pub include_body: bool,
#[serde(default)]
pub include_response: bool,
}
fn default_allow_methods() -> Vec<String> {
vec!["GET".to_string(), "HEAD".to_string(), "OPTIONS".to_string()]
}
fn default_true() -> bool {
true
}
fn default_audit_level() -> String {
"basic".to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_policy_defaults() {
let defaults = PolicyDefaults {
allow_methods: default_allow_methods(),
deny_external_refs: true,
require_auth: true,
audit_level: "basic".to_string(),
};
assert_eq!(defaults.allow_methods, vec!["GET", "HEAD", "OPTIONS"]);
assert!(defaults.deny_external_refs);
assert!(defaults.require_auth);
}
#[test]
fn test_policy_rule_serialization() {
let rule = PolicyRule {
name: "test-rule".to_string(),
description: Some("Test rule".to_string()),
pattern: "*.example.com/*".to_string(),
conditions: None,
allow: Some(PolicyAction {
methods: Some(vec!["GET".to_string()]),
operations: None,
all: None,
tags: None,
}),
deny: None,
audit: None,
explain: None,
};
let json = serde_json::to_string_pretty(&rule).unwrap();
assert!(json.contains("test-rule"));
assert!(json.contains("*.example.com/*"));
}
}