The sqlx database library has been removed from the project along with associated database-specific code that was no longer being used. This includes removal of various sqlx-related dependencies from Cargo.lock and cleanup of database connection pool references.
518 lines
16 KiB
Rust
518 lines
16 KiB
Rust
//! Policy Checker Module
|
|
//!
|
|
//! Provides automated security policy checking and enforcement
|
|
//! for compliance with organizational and regulatory requirements.
|
|
|
|
use anyhow::{anyhow, Result};
|
|
use chrono::{DateTime, Duration, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
|
|
/// Policy type enumeration
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum PolicyType {
|
|
AccessControl,
|
|
DataRetention,
|
|
PasswordStrength,
|
|
SessionTimeout,
|
|
EncryptionRequired,
|
|
AuditLogging,
|
|
BackupFrequency,
|
|
NetworkSecurity,
|
|
ComplianceStandard,
|
|
}
|
|
|
|
/// Policy status
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum PolicyStatus {
|
|
Active,
|
|
Draft,
|
|
Deprecated,
|
|
Archived,
|
|
}
|
|
|
|
/// Policy severity
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
pub enum PolicySeverity {
|
|
Low,
|
|
Medium,
|
|
High,
|
|
Critical,
|
|
}
|
|
|
|
/// Security policy definition
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SecurityPolicy {
|
|
pub id: Uuid,
|
|
pub name: String,
|
|
pub description: String,
|
|
pub policy_type: PolicyType,
|
|
pub status: PolicyStatus,
|
|
pub severity: PolicySeverity,
|
|
pub rules: Vec<PolicyRule>,
|
|
pub created_at: DateTime<Utc>,
|
|
pub updated_at: DateTime<Utc>,
|
|
pub effective_date: DateTime<Utc>,
|
|
pub expiry_date: Option<DateTime<Utc>>,
|
|
pub tags: Vec<String>,
|
|
}
|
|
|
|
/// Policy rule
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PolicyRule {
|
|
pub id: Uuid,
|
|
pub name: String,
|
|
pub condition: String,
|
|
pub action: PolicyAction,
|
|
pub parameters: HashMap<String, String>,
|
|
}
|
|
|
|
/// Policy action
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum PolicyAction {
|
|
Allow,
|
|
Deny,
|
|
Alert,
|
|
Enforce,
|
|
Log,
|
|
}
|
|
|
|
/// Policy violation
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PolicyViolation {
|
|
pub id: Uuid,
|
|
pub policy_id: Uuid,
|
|
pub rule_id: Uuid,
|
|
pub timestamp: DateTime<Utc>,
|
|
pub user_id: Option<Uuid>,
|
|
pub resource: String,
|
|
pub action_attempted: String,
|
|
pub violation_details: String,
|
|
pub severity: PolicySeverity,
|
|
pub resolved: bool,
|
|
}
|
|
|
|
/// Policy check result
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PolicyCheckResult {
|
|
pub policy_id: Uuid,
|
|
pub passed: bool,
|
|
pub violations: Vec<PolicyViolation>,
|
|
pub warnings: Vec<String>,
|
|
pub timestamp: DateTime<Utc>,
|
|
}
|
|
|
|
/// Policy checker service
|
|
#[derive(Debug, Clone)]
|
|
pub struct PolicyChecker {
|
|
policies: HashMap<Uuid, SecurityPolicy>,
|
|
violations: Vec<PolicyViolation>,
|
|
check_history: Vec<PolicyCheckResult>,
|
|
}
|
|
|
|
impl PolicyChecker {
|
|
/// Create new policy checker
|
|
pub fn new() -> Self {
|
|
let mut checker = Self {
|
|
policies: HashMap::new(),
|
|
violations: Vec::new(),
|
|
check_history: Vec::new(),
|
|
};
|
|
|
|
// Initialize with default policies
|
|
checker.initialize_default_policies();
|
|
checker
|
|
}
|
|
|
|
/// Initialize default security policies
|
|
fn initialize_default_policies(&mut self) {
|
|
// Password policy
|
|
let password_policy = SecurityPolicy {
|
|
id: Uuid::new_v4(),
|
|
name: "Password Strength Policy".to_string(),
|
|
description: "Enforces strong password requirements".to_string(),
|
|
policy_type: PolicyType::PasswordStrength,
|
|
status: PolicyStatus::Active,
|
|
severity: PolicySeverity::High,
|
|
rules: vec![
|
|
PolicyRule {
|
|
id: Uuid::new_v4(),
|
|
name: "Minimum Length".to_string(),
|
|
condition: "password.length >= 12".to_string(),
|
|
action: PolicyAction::Enforce,
|
|
parameters: HashMap::from([("min_length".to_string(), "12".to_string())]),
|
|
},
|
|
PolicyRule {
|
|
id: Uuid::new_v4(),
|
|
name: "Complexity Requirements".to_string(),
|
|
condition: "has_uppercase && has_lowercase && has_digit && has_special"
|
|
.to_string(),
|
|
action: PolicyAction::Enforce,
|
|
parameters: HashMap::new(),
|
|
},
|
|
],
|
|
created_at: Utc::now(),
|
|
updated_at: Utc::now(),
|
|
effective_date: Utc::now(),
|
|
expiry_date: None,
|
|
tags: vec!["security".to_string(), "authentication".to_string()],
|
|
};
|
|
|
|
self.policies.insert(password_policy.id, password_policy);
|
|
|
|
// Session timeout policy
|
|
let session_policy = SecurityPolicy {
|
|
id: Uuid::new_v4(),
|
|
name: "Session Timeout Policy".to_string(),
|
|
description: "Enforces session timeout limits".to_string(),
|
|
policy_type: PolicyType::SessionTimeout,
|
|
status: PolicyStatus::Active,
|
|
severity: PolicySeverity::Medium,
|
|
rules: vec![PolicyRule {
|
|
id: Uuid::new_v4(),
|
|
name: "Maximum Session Duration".to_string(),
|
|
condition: "session.duration <= 8_hours".to_string(),
|
|
action: PolicyAction::Enforce,
|
|
parameters: HashMap::from([
|
|
("max_duration".to_string(), "28800".to_string()), // 8 hours in seconds
|
|
]),
|
|
}],
|
|
created_at: Utc::now(),
|
|
updated_at: Utc::now(),
|
|
effective_date: Utc::now(),
|
|
expiry_date: None,
|
|
tags: vec!["security".to_string(), "session".to_string()],
|
|
};
|
|
|
|
self.policies.insert(session_policy.id, session_policy);
|
|
}
|
|
|
|
/// Add a security policy
|
|
pub fn add_policy(&mut self, policy: SecurityPolicy) -> Result<()> {
|
|
if self.policies.contains_key(&policy.id) {
|
|
return Err(anyhow!("Policy already exists"));
|
|
}
|
|
|
|
log::info!("Adding security policy: {}", policy.name);
|
|
self.policies.insert(policy.id, policy);
|
|
Ok(())
|
|
}
|
|
|
|
/// Update a security policy
|
|
pub fn update_policy(&mut self, policy_id: Uuid, updates: SecurityPolicy) -> Result<()> {
|
|
if let Some(existing) = self.policies.get_mut(&policy_id) {
|
|
*existing = updates;
|
|
existing.updated_at = Utc::now();
|
|
log::info!("Updated policy: {}", existing.name);
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!("Policy not found"))
|
|
}
|
|
}
|
|
|
|
/// Check password against policy
|
|
pub fn check_password_policy(&mut self, password: &str) -> PolicyCheckResult {
|
|
let policy = self
|
|
.policies
|
|
.values()
|
|
.find(|p| {
|
|
p.policy_type == PolicyType::PasswordStrength && p.status == PolicyStatus::Active
|
|
})
|
|
.cloned();
|
|
|
|
if let Some(policy) = policy {
|
|
let mut violations = Vec::new();
|
|
let mut warnings = Vec::new();
|
|
|
|
// Check minimum length
|
|
if password.len() < 12 {
|
|
violations.push(PolicyViolation {
|
|
id: Uuid::new_v4(),
|
|
policy_id: policy.id,
|
|
rule_id: policy.rules[0].id,
|
|
timestamp: Utc::now(),
|
|
user_id: None,
|
|
resource: "password".to_string(),
|
|
action_attempted: "set_password".to_string(),
|
|
violation_details: format!(
|
|
"Password length {} is less than required 12",
|
|
password.len()
|
|
),
|
|
severity: PolicySeverity::High,
|
|
resolved: false,
|
|
});
|
|
}
|
|
|
|
// Check complexity
|
|
let has_uppercase = password.chars().any(|c| c.is_uppercase());
|
|
let has_lowercase = password.chars().any(|c| c.is_lowercase());
|
|
let has_digit = password.chars().any(|c| c.is_numeric());
|
|
let has_special = password.chars().any(|c| !c.is_alphanumeric());
|
|
|
|
if !(has_uppercase && has_lowercase && has_digit && has_special) {
|
|
violations.push(PolicyViolation {
|
|
id: Uuid::new_v4(),
|
|
policy_id: policy.id,
|
|
rule_id: policy.rules[1].id,
|
|
timestamp: Utc::now(),
|
|
user_id: None,
|
|
resource: "password".to_string(),
|
|
action_attempted: "set_password".to_string(),
|
|
violation_details: "Password does not meet complexity requirements".to_string(),
|
|
severity: PolicySeverity::High,
|
|
resolved: false,
|
|
});
|
|
}
|
|
|
|
// Add warnings for common patterns
|
|
if password.to_lowercase().contains("password") {
|
|
warnings.push("Password contains the word 'password'".to_string());
|
|
}
|
|
|
|
let result = PolicyCheckResult {
|
|
policy_id: policy.id,
|
|
passed: violations.is_empty(),
|
|
violations: violations.clone(),
|
|
warnings,
|
|
timestamp: Utc::now(),
|
|
};
|
|
|
|
self.violations.extend(violations);
|
|
self.check_history.push(result.clone());
|
|
|
|
result
|
|
} else {
|
|
PolicyCheckResult {
|
|
policy_id: Uuid::nil(),
|
|
passed: true,
|
|
violations: Vec::new(),
|
|
warnings: vec!["No password policy configured".to_string()],
|
|
timestamp: Utc::now(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Check session against policy
|
|
pub fn check_session_policy(&mut self, session_duration_seconds: u64) -> PolicyCheckResult {
|
|
let policy = self
|
|
.policies
|
|
.values()
|
|
.find(|p| {
|
|
p.policy_type == PolicyType::SessionTimeout && p.status == PolicyStatus::Active
|
|
})
|
|
.cloned();
|
|
|
|
if let Some(policy) = policy {
|
|
let mut violations = Vec::new();
|
|
|
|
if session_duration_seconds > 28800 {
|
|
// 8 hours
|
|
violations.push(PolicyViolation {
|
|
id: Uuid::new_v4(),
|
|
policy_id: policy.id,
|
|
rule_id: policy.rules[0].id,
|
|
timestamp: Utc::now(),
|
|
user_id: None,
|
|
resource: "session".to_string(),
|
|
action_attempted: "extend_session".to_string(),
|
|
violation_details: format!(
|
|
"Session duration {} seconds exceeds maximum 28800 seconds",
|
|
session_duration_seconds
|
|
),
|
|
severity: PolicySeverity::Medium,
|
|
resolved: false,
|
|
});
|
|
}
|
|
|
|
let result = PolicyCheckResult {
|
|
policy_id: policy.id,
|
|
passed: violations.is_empty(),
|
|
violations: violations.clone(),
|
|
warnings: Vec::new(),
|
|
timestamp: Utc::now(),
|
|
};
|
|
|
|
self.violations.extend(violations);
|
|
self.check_history.push(result.clone());
|
|
|
|
result
|
|
} else {
|
|
PolicyCheckResult {
|
|
policy_id: Uuid::nil(),
|
|
passed: true,
|
|
violations: Vec::new(),
|
|
warnings: vec!["No session policy configured".to_string()],
|
|
timestamp: Utc::now(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Check all active policies
|
|
pub fn check_all_policies(&mut self, context: &PolicyContext) -> Vec<PolicyCheckResult> {
|
|
let mut results = Vec::new();
|
|
|
|
for policy in self.policies.values() {
|
|
if policy.status != PolicyStatus::Active {
|
|
continue;
|
|
}
|
|
|
|
let result = self.check_policy(policy.id, context);
|
|
if let Ok(result) = result {
|
|
results.push(result);
|
|
}
|
|
}
|
|
|
|
results
|
|
}
|
|
|
|
/// Check a specific policy
|
|
pub fn check_policy(
|
|
&mut self,
|
|
policy_id: Uuid,
|
|
context: &PolicyContext,
|
|
) -> Result<PolicyCheckResult> {
|
|
let policy = self
|
|
.policies
|
|
.get(&policy_id)
|
|
.ok_or_else(|| anyhow!("Policy not found"))?
|
|
.clone();
|
|
|
|
let mut violations = Vec::new();
|
|
let mut warnings = Vec::new();
|
|
|
|
for rule in &policy.rules {
|
|
if !self.evaluate_rule(rule, context) {
|
|
violations.push(PolicyViolation {
|
|
id: Uuid::new_v4(),
|
|
policy_id: policy.id,
|
|
rule_id: rule.id,
|
|
timestamp: Utc::now(),
|
|
user_id: context.user_id,
|
|
resource: context.resource.clone(),
|
|
action_attempted: context.action.clone(),
|
|
violation_details: format!("Rule '{}' failed", rule.name),
|
|
severity: policy.severity.clone(),
|
|
resolved: false,
|
|
});
|
|
}
|
|
}
|
|
|
|
let result = PolicyCheckResult {
|
|
policy_id: policy.id,
|
|
passed: violations.is_empty(),
|
|
violations: violations.clone(),
|
|
warnings,
|
|
timestamp: Utc::now(),
|
|
};
|
|
|
|
self.violations.extend(violations);
|
|
self.check_history.push(result.clone());
|
|
|
|
Ok(result)
|
|
}
|
|
|
|
/// Evaluate a policy rule
|
|
fn evaluate_rule(&self, rule: &PolicyRule, _context: &PolicyContext) -> bool {
|
|
// Simplified rule evaluation
|
|
// In production, this would use a proper expression evaluator
|
|
match rule.action {
|
|
PolicyAction::Allow => true,
|
|
PolicyAction::Deny => false,
|
|
_ => true, // For Alert, Enforce, Log actions, consider as passed but take action
|
|
}
|
|
}
|
|
|
|
/// Get policy violations
|
|
pub fn get_violations(&self, unresolved_only: bool) -> Vec<PolicyViolation> {
|
|
if unresolved_only {
|
|
self.violations
|
|
.iter()
|
|
.filter(|v| !v.resolved)
|
|
.cloned()
|
|
.collect()
|
|
} else {
|
|
self.violations.clone()
|
|
}
|
|
}
|
|
|
|
/// Resolve a violation
|
|
pub fn resolve_violation(&mut self, violation_id: Uuid) -> Result<()> {
|
|
if let Some(violation) = self.violations.iter_mut().find(|v| v.id == violation_id) {
|
|
violation.resolved = true;
|
|
log::info!("Resolved violation: {}", violation_id);
|
|
Ok(())
|
|
} else {
|
|
Err(anyhow!("Violation not found"))
|
|
}
|
|
}
|
|
|
|
/// Get policy compliance report
|
|
pub fn get_compliance_report(&self) -> PolicyComplianceReport {
|
|
let total_policies = self.policies.len();
|
|
let active_policies = self
|
|
.policies
|
|
.values()
|
|
.filter(|p| p.status == PolicyStatus::Active)
|
|
.count();
|
|
let total_violations = self.violations.len();
|
|
let unresolved_violations = self.violations.iter().filter(|v| !v.resolved).count();
|
|
let critical_violations = self
|
|
.violations
|
|
.iter()
|
|
.filter(|v| v.severity == PolicySeverity::Critical)
|
|
.count();
|
|
|
|
let recent_checks = self
|
|
.check_history
|
|
.iter()
|
|
.filter(|c| c.timestamp > Utc::now() - Duration::days(7))
|
|
.count();
|
|
|
|
let compliance_rate = if !self.check_history.is_empty() {
|
|
let passed = self.check_history.iter().filter(|c| c.passed).count();
|
|
(passed as f64 / self.check_history.len() as f64) * 100.0
|
|
} else {
|
|
100.0
|
|
};
|
|
|
|
PolicyComplianceReport {
|
|
generated_at: Utc::now(),
|
|
total_policies,
|
|
active_policies,
|
|
total_violations,
|
|
unresolved_violations,
|
|
critical_violations,
|
|
recent_checks,
|
|
compliance_rate,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Policy context for evaluation
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PolicyContext {
|
|
pub user_id: Option<Uuid>,
|
|
pub resource: String,
|
|
pub action: String,
|
|
pub parameters: HashMap<String, String>,
|
|
}
|
|
|
|
/// Policy compliance report
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PolicyComplianceReport {
|
|
pub generated_at: DateTime<Utc>,
|
|
pub total_policies: usize,
|
|
pub active_policies: usize,
|
|
pub total_violations: usize,
|
|
pub unresolved_violations: usize,
|
|
pub critical_violations: usize,
|
|
pub recent_checks: usize,
|
|
pub compliance_rate: f64,
|
|
}
|
|
|
|
impl Default for PolicyChecker {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|