use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use async_trait::async_trait;
use symbi_runtime::integrations::policy_engine::types::AccessResult;
use symbi_runtime::integrations::policy_engine::*;
use symbi_runtime::secrets::{Secret, SecretError, SecretStore};
use symbi_runtime::types::*;
fn create_test_agent_metadata() -> AgentMetadata {
AgentMetadata {
version: "1.0.0".to_string(),
author: "test-author".to_string(),
description: "Test agent for policy evaluation".to_string(),
capabilities: vec![Capability::FileSystem, Capability::Network],
dependencies: vec![],
resource_requirements: ResourceRequirements {
min_memory_mb: 256,
max_memory_mb: 512,
min_cpu_cores: 0.5,
max_cpu_cores: 1.0,
disk_space_mb: 1024,
network_bandwidth_mbps: 100,
},
security_requirements: SecurityRequirements {
min_security_tier: SecurityTier::Tier2,
requires_encryption: true,
requires_signature: true,
network_isolation: true,
file_system_isolation: true,
},
custom_fields: HashMap::new(),
}
}
fn create_test_resource_usage() -> ResourceUsage {
ResourceUsage {
memory_used: 256 * 1024 * 1024, cpu_utilization: 0.5,
disk_io_rate: 50 * 1024 * 1024, network_io_rate: 10 * 1024 * 1024, uptime: Duration::from_secs(3600),
}
}
fn create_test_access_context() -> AccessContext {
AccessContext {
agent_metadata: create_test_agent_metadata(),
security_level: SecurityTier::Tier2,
access_history: vec![],
resource_usage: create_test_resource_usage(),
environment: HashMap::new(),
source_info: SourceInfo {
ip_address: Some("127.0.0.1".to_string()),
user_agent: Some("test-agent/1.0".to_string()),
session_id: Some("test-session-123".to_string()),
request_id: "req-123".to_string(),
},
}
}
fn create_test_resource_requirements() -> ResourceRequirements {
ResourceRequirements {
min_memory_mb: 256,
max_memory_mb: 512,
min_cpu_cores: 0.5,
max_cpu_cores: 1.0,
disk_space_mb: 1024,
network_bandwidth_mbps: 100,
}
}
#[tokio::test]
async fn test_policy_enforcement_point_creation() {
let config = ResourceAccessConfig::default();
let enforcement_point = PolicyEnforcementFactory::create_enforcement_point(config).await;
assert!(enforcement_point.is_ok());
}
#[tokio::test]
async fn test_mock_policy_enforcement_point() {
let enforcement_point = PolicyEnforcementFactory::create_mock_enforcement_point();
let agent_id = AgentId::new();
let resource_request = ResourceAccessRequest {
resource_type: ResourceType::File,
resource_id: "/tmp/test.txt".to_string(),
access_type: AccessType::Read,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &resource_request)
.await;
assert!(decision.is_ok());
let access_decision = decision.unwrap();
assert_eq!(access_decision.decision, AccessResult::Allow);
}
#[tokio::test]
async fn test_resource_allocation_validation() {
let enforcement_point = PolicyEnforcementFactory::create_mock_enforcement_point();
let agent_id = AgentId::new();
let allocation_request = ResourceAllocationRequest {
agent_id,
requirements: create_test_resource_requirements(),
priority: Priority::Normal,
justification: Some("Test allocation".to_string()),
max_duration: Some(Duration::from_secs(3600)), timestamp: SystemTime::now(),
};
let decision = enforcement_point
.validate_resource_allocation(agent_id, &allocation_request)
.await;
assert!(decision.is_ok());
let allocation_decision = decision.unwrap();
assert_eq!(allocation_decision.decision, AllocationResult::Approve);
}
#[tokio::test]
async fn test_file_access_policies() {
let config = ResourceAccessConfig::default();
let enforcement_point = PolicyEnforcementFactory::create_enforcement_point(config)
.await
.unwrap();
let agent_id = AgentId::new();
let temp_access = ResourceAccessRequest {
resource_type: ResourceType::File,
resource_id: "/tmp/allowed_file.txt".to_string(),
access_type: AccessType::Read,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &temp_access)
.await;
assert!(decision.is_ok());
let system_access = ResourceAccessRequest {
resource_type: ResourceType::File,
resource_id: "/etc/passwd".to_string(),
access_type: AccessType::Write,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &system_access)
.await;
assert!(decision.is_ok());
}
#[tokio::test]
async fn test_network_access_policies() {
let config = ResourceAccessConfig::default();
let enforcement_point = PolicyEnforcementFactory::create_enforcement_point(config)
.await
.unwrap();
let agent_id = AgentId::new();
let http_access = ResourceAccessRequest {
resource_type: ResourceType::Network,
resource_id: "https://api.example.com".to_string(),
access_type: AccessType::Connect,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &http_access)
.await;
assert!(decision.is_ok());
let local_access = ResourceAccessRequest {
resource_type: ResourceType::Network,
resource_id: "192.168.1.1".to_string(),
access_type: AccessType::Connect,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &local_access)
.await;
assert!(decision.is_ok());
}
#[tokio::test]
async fn test_resource_allocation_limits() {
let config = ResourceAccessConfig::default();
let enforcement_point = PolicyEnforcementFactory::create_enforcement_point(config)
.await
.unwrap();
let agent_id = AgentId::new();
let basic_allocation = ResourceAllocationRequest {
agent_id,
requirements: ResourceRequirements {
min_memory_mb: 128,
max_memory_mb: 256, min_cpu_cores: 0.25,
max_cpu_cores: 0.5, disk_space_mb: 512,
network_bandwidth_mbps: 50,
},
priority: Priority::Normal,
justification: Some("Basic allocation test".to_string()),
max_duration: Some(Duration::from_secs(3600)), timestamp: SystemTime::now(),
};
let decision = enforcement_point
.validate_resource_allocation(agent_id, &basic_allocation)
.await;
assert!(decision.is_ok());
}
#[tokio::test]
async fn test_policy_reload() {
let config = ResourceAccessConfig::default();
let enforcement_point = PolicyEnforcementFactory::create_enforcement_point(config.clone())
.await
.unwrap();
let load_result = enforcement_point.load_policies(&config).await;
assert!(load_result.is_ok());
let reload_result = enforcement_point.reload_policies().await;
assert!(reload_result.is_ok());
}
#[tokio::test]
async fn test_enforcement_statistics() {
let config = ResourceAccessConfig::default();
let enforcement_point = PolicyEnforcementFactory::create_enforcement_point(config)
.await
.unwrap();
let agent_id = AgentId::new();
for i in 0..5 {
let access_request = ResourceAccessRequest {
resource_type: ResourceType::File,
resource_id: format!("/tmp/test_{}.txt", i),
access_type: AccessType::Read,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let _ = enforcement_point
.check_resource_access(agent_id, &access_request)
.await;
}
let stats = enforcement_point.get_enforcement_stats().await;
assert!(stats.is_ok());
let statistics = stats.unwrap();
assert!(statistics.total_requests >= 5);
}
#[tokio::test]
async fn test_conditional_access() {
let enforcement_point = PolicyEnforcementFactory::create_mock_enforcement_point();
let agent_id = AgentId::new();
let resource_request = ResourceAccessRequest {
resource_type: ResourceType::Database,
resource_id: "sensitive_db".to_string(),
access_type: AccessType::Write,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &resource_request)
.await;
assert!(decision.is_ok());
let access_decision = decision.unwrap();
assert!(matches!(
access_decision.decision,
AccessResult::Allow | AccessResult::Conditional
));
}
#[tokio::test]
async fn test_escalation_scenarios() {
let enforcement_point = PolicyEnforcementFactory::create_mock_enforcement_point();
let agent_id = AgentId::new();
let high_privilege_request = ResourceAccessRequest {
resource_type: ResourceType::Command,
resource_id: "/usr/bin/sudo".to_string(),
access_type: AccessType::Execute,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &high_privilege_request)
.await;
assert!(decision.is_ok());
let access_decision = decision.unwrap();
assert!(matches!(
access_decision.decision,
AccessResult::Allow | AccessResult::Deny | AccessResult::Escalate
));
}
#[tokio::test]
async fn test_time_based_access() {
let enforcement_point = PolicyEnforcementFactory::create_mock_enforcement_point();
let agent_id = AgentId::new();
let resource_request = ResourceAccessRequest {
resource_type: ResourceType::Network,
resource_id: "external-api.com".to_string(),
access_type: AccessType::Connect,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &resource_request)
.await;
assert!(decision.is_ok());
let access_decision = decision.unwrap();
if let Some(expires_at) = access_decision.expires_at {
assert!(expires_at > SystemTime::now());
}
}
#[tokio::test]
async fn test_policy_caching() {
let config = ResourceAccessConfig {
enable_caching: true,
cache_ttl_secs: 60,
..Default::default()
};
let enforcement_point = PolicyEnforcementFactory::create_enforcement_point(config)
.await
.unwrap();
let agent_id = AgentId::new();
let resource_request = ResourceAccessRequest {
resource_type: ResourceType::File,
resource_id: "/tmp/cached_test.txt".to_string(),
access_type: AccessType::Read,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision1 = enforcement_point
.check_resource_access(agent_id, &resource_request)
.await;
assert!(decision1.is_ok());
let decision2 = enforcement_point
.check_resource_access(agent_id, &resource_request)
.await;
assert!(decision2.is_ok());
assert_eq!(decision1.unwrap().decision, decision2.unwrap().decision);
}
#[tokio::test]
async fn test_audit_logging() {
let config = ResourceAccessConfig {
enable_audit: true,
..Default::default()
};
let enforcement_point = PolicyEnforcementFactory::create_enforcement_point(config)
.await
.unwrap();
let agent_id = AgentId::new();
let resource_request = ResourceAccessRequest {
resource_type: ResourceType::File,
resource_id: "/tmp/audit_test.txt".to_string(),
access_type: AccessType::Write,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &resource_request)
.await;
assert!(decision.is_ok());
let access_decision = decision.unwrap();
assert!(!access_decision.metadata.is_empty() || access_decision.applied_rule.is_some());
}
#[tokio::test]
async fn test_concurrent_policy_evaluation() {
let config = ResourceAccessConfig::default();
let enforcement_point = Arc::new(
PolicyEnforcementFactory::create_enforcement_point(config)
.await
.unwrap(),
);
let agent_id = AgentId::new();
let mut handles = vec![];
for i in 0..10 {
let ep = enforcement_point.clone();
let handle = tokio::spawn(async move {
let resource_request = ResourceAccessRequest {
resource_type: ResourceType::File,
resource_id: format!("/tmp/concurrent_test_{}.txt", i),
access_type: AccessType::Read,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
ep.check_resource_access(agent_id, &resource_request).await
});
handles.push(handle);
}
for handle in handles {
let result = handle.await.unwrap();
assert!(result.is_ok());
}
}
#[tokio::test]
async fn test_policy_priority_ordering() {
let config = ResourceAccessConfig::default();
let enforcement_point = PolicyEnforcementFactory::create_enforcement_point(config)
.await
.unwrap();
let agent_id = AgentId::new();
let system_write_request = ResourceAccessRequest {
resource_type: ResourceType::File,
resource_id: "/etc/important_config".to_string(),
access_type: AccessType::Write,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &system_write_request)
.await;
assert!(decision.is_ok());
}
#[tokio::test]
async fn test_error_handling() {
let enforcement_point = PolicyEnforcementFactory::create_mock_enforcement_point();
let agent_id = AgentId::new();
let invalid_request = ResourceAccessRequest {
resource_type: ResourceType::Custom("invalid".to_string()),
resource_id: "".to_string(), access_type: AccessType::Read,
context: create_test_access_context(),
timestamp: SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &invalid_request)
.await;
assert!(decision.is_ok() || decision.is_err()); }
#[derive(Debug, Clone)]
pub struct MockSecretStore {
secrets: std::collections::HashMap<String, Result<Secret, SecretError>>,
}
impl Default for MockSecretStore {
fn default() -> Self {
Self::new()
}
}
impl MockSecretStore {
pub fn new() -> Self {
Self {
secrets: std::collections::HashMap::new(),
}
}
pub fn with_secret(mut self, key: &str, value: &str) -> Self {
self.secrets.insert(
key.to_string(),
Ok(Secret::new(key.to_string(), value.to_string())),
);
self
}
pub fn with_missing_secret(mut self, key: &str) -> Self {
self.secrets.insert(
key.to_string(),
Err(SecretError::NotFound {
key: key.to_string(),
}),
);
self
}
pub fn with_denied_secret(mut self, key: &str) -> Self {
self.secrets.insert(
key.to_string(),
Err(SecretError::PermissionDenied {
key: key.to_string(),
}),
);
self
}
}
#[async_trait]
impl SecretStore for MockSecretStore {
async fn get_secret(&self, key: &str) -> Result<Secret, SecretError> {
match self.secrets.get(key) {
Some(result) => result.clone(),
None => Err(SecretError::NotFound {
key: key.to_string(),
}),
}
}
async fn list_secrets(&self) -> Result<Vec<String>, SecretError> {
Ok(self.secrets.keys().cloned().collect())
}
}
#[tokio::test]
async fn test_policy_with_secret_requirement_success() {
use symbi_runtime::integrations::policy_engine::{
DefaultPolicyEnforcementPoint, ResourceAccessConfig,
};
let config = ResourceAccessConfig::default();
let mut enforcement_point = DefaultPolicyEnforcementPoint::new(config).await.unwrap();
let secrets = MockSecretStore::new().with_secret("api_key", "test_val");
enforcement_point.set_secrets(std::sync::Arc::new(secrets));
let agent_id = AgentId::new();
let resource_request = ResourceAccessRequest {
resource_type: ResourceType::Network,
resource_id: "api.example.com".to_string(),
access_type: AccessType::Connect,
context: create_test_access_context(),
timestamp: std::time::SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &resource_request)
.await
.unwrap();
assert!(matches!(
decision.decision,
AccessResult::Allow | AccessResult::Deny
));
}
#[tokio::test]
async fn test_policy_with_secret_requirement_not_found() {
use symbi_runtime::integrations::policy_engine::{
DefaultPolicyEnforcementPoint, ResourceAccessConfig,
};
let config = ResourceAccessConfig::default();
let mut enforcement_point = DefaultPolicyEnforcementPoint::new(config).await.unwrap();
let secrets = MockSecretStore::new().with_missing_secret("api_key");
enforcement_point.set_secrets(std::sync::Arc::new(secrets));
let agent_id = AgentId::new();
let resource_request = ResourceAccessRequest {
resource_type: ResourceType::Network,
resource_id: "api.example.com".to_string(),
access_type: AccessType::Connect,
context: create_test_access_context(),
timestamp: std::time::SystemTime::now(),
};
let decision = enforcement_point
.check_resource_access(agent_id, &resource_request)
.await
.unwrap();
assert!(matches!(
decision.decision,
AccessResult::Allow | AccessResult::Deny
));
}