Files
ETB/ETB-API/collaboration_war_rooms/services/compliance_integration.py
Iliyan Angelov 6b247e5b9f Updates
2025-09-19 11:58:53 +03:00

352 lines
14 KiB
Python

"""
Compliance Integration Service for Chat
Handles file classification, audit trails, and compliance requirements
"""
from django.utils import timezone
from django.contrib.auth import get_user_model
from typing import Dict, Any, Optional, List
import hashlib
import mimetypes
from ..models import ChatFile, WarRoomMessage
from compliance_governance.models import DataClassification, AuditLog, CompliancePolicy
from security.models import DataClassification as SecurityDataClassification
User = get_user_model()
class ComplianceIntegrationService:
"""Service for handling compliance requirements in chat"""
@staticmethod
def classify_file(file_path: str, filename: str, file_size: int, user: User) -> Dict[str, Any]:
"""Classify a file based on content and context"""
try:
# Get file MIME type
mime_type, _ = mimetypes.guess_type(filename)
# Calculate file hash
file_hash = ComplianceIntegrationService._calculate_file_hash(file_path)
# Determine classification based on file type and content
classification_level = ComplianceIntegrationService._determine_classification(
filename, mime_type, file_size
)
# Get or create data classification
data_classification = ComplianceIntegrationService._get_or_create_classification(
classification_level
)
return {
'classification_level': classification_level,
'data_classification_id': str(data_classification.id) if data_classification else None,
'file_hash': file_hash,
'mime_type': mime_type,
'is_encrypted': classification_level in ['CONFIDENTIAL', 'RESTRICTED', 'TOP_SECRET'],
'retention_period': ComplianceIntegrationService._get_retention_period(classification_level)
}
except Exception as e:
return {
'error': f'Failed to classify file: {str(e)}',
'classification_level': 'PUBLIC', # Default to public on error
'file_hash': None,
'mime_type': None,
'is_encrypted': False
}
@staticmethod
def create_audit_log_entry(
action: str,
user: User,
resource_type: str,
resource_id: str,
details: Dict[str, Any] = None
) -> bool:
"""Create an audit log entry for compliance"""
try:
AuditLog.objects.create(
action=action,
user=user,
resource_type=resource_type,
resource_id=resource_id,
timestamp=timezone.now(),
details=details or {},
ip_address=ComplianceIntegrationService._get_user_ip(user),
user_agent=ComplianceIntegrationService._get_user_agent(user)
)
return True
except Exception as e:
print(f"Error creating audit log entry: {e}")
return False
@staticmethod
def log_chat_message_access(message: WarRoomMessage, user: User, action: str = 'access'):
"""Log access to chat messages for audit trail"""
try:
ComplianceIntegrationService.create_audit_log_entry(
action=f'chat_message_{action}',
user=user,
resource_type='chat_message',
resource_id=str(message.id),
details={
'war_room_id': str(message.war_room.id),
'incident_id': str(message.war_room.incident.id),
'message_type': message.message_type,
'sender': message.sender.username if message.sender else None,
'content_length': len(message.content),
'is_encrypted': message.is_encrypted
}
)
except Exception as e:
print(f"Error logging chat message access: {e}")
@staticmethod
def log_file_access(file_obj: ChatFile, user: User, action: str = 'access'):
"""Log file access for audit trail"""
try:
ComplianceIntegrationService.create_audit_log_entry(
action=f'file_{action}',
user=user,
resource_type='chat_file',
resource_id=str(file_obj.id),
details={
'filename': file_obj.original_filename,
'file_type': file_obj.file_type,
'file_size': file_obj.file_size,
'data_classification': file_obj.data_classification.level if file_obj.data_classification else None,
'is_encrypted': file_obj.is_encrypted,
'message_id': str(file_obj.message.id),
'incident_id': str(file_obj.message.war_room.incident.id)
}
)
except Exception as e:
print(f"Error logging file access: {e}")
@staticmethod
def check_compliance_policies(incident_id: str, user: User) -> Dict[str, Any]:
"""Check compliance policies for incident chat access"""
try:
# Get applicable compliance policies
policies = CompliancePolicy.objects.filter(
is_active=True,
applies_to_incidents=True
)
compliance_status = {
'policies_checked': len(policies),
'violations': [],
'warnings': [],
'recommendations': []
}
for policy in policies:
# Check if policy applies to this incident
if ComplianceIntegrationService._policy_applies_to_incident(policy, incident_id):
violations = ComplianceIntegrationService._check_policy_violations(
policy, incident_id, user
)
if violations:
compliance_status['violations'].extend(violations)
return compliance_status
except Exception as e:
return {
'error': f'Failed to check compliance policies: {str(e)}'
}
@staticmethod
def export_chat_logs_for_compliance(
incident_id: str,
start_date: timezone.datetime,
end_date: timezone.datetime,
user: User
) -> Dict[str, Any]:
"""Export chat logs for compliance reporting"""
try:
from ..models import WarRoom
# Get war room for incident
war_room = WarRoom.objects.filter(incident_id=incident_id).first()
if not war_room:
return {'error': 'War room not found for incident'}
# Get messages in date range
messages = WarRoomMessage.objects.filter(
war_room=war_room,
created_at__gte=start_date,
created_at__lte=end_date
).order_by('created_at')
# Prepare export data
export_data = {
'incident_id': incident_id,
'war_room_id': str(war_room.id),
'export_date': timezone.now().isoformat(),
'exported_by': user.username,
'date_range': {
'start': start_date.isoformat(),
'end': end_date.isoformat()
},
'message_count': messages.count(),
'messages': []
}
for message in messages:
message_data = {
'id': str(message.id),
'timestamp': message.created_at.isoformat(),
'sender': message.sender.username if message.sender else message.sender_name,
'message_type': message.message_type,
'content': message.content,
'is_encrypted': message.is_encrypted,
'is_pinned': message.is_pinned,
'attachments': [
{
'id': str(attachment.id),
'filename': attachment.original_filename,
'file_type': attachment.file_type,
'file_size': attachment.file_size,
'data_classification': attachment.data_classification.level if attachment.data_classification else None,
'is_encrypted': attachment.is_encrypted,
'file_hash': attachment.file_hash
}
for attachment in message.chat_files.all()
]
}
export_data['messages'].append(message_data)
# Log the export action
ComplianceIntegrationService.create_audit_log_entry(
action='export_chat_logs',
user=user,
resource_type='incident',
resource_id=incident_id,
details={
'message_count': export_data['message_count'],
'date_range': export_data['date_range']
}
)
return export_data
except Exception as e:
return {
'error': f'Failed to export chat logs: {str(e)}'
}
@staticmethod
def _calculate_file_hash(file_path: str) -> str:
"""Calculate SHA-256 hash of file"""
try:
hash_sha256 = hashlib.sha256()
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_sha256.update(chunk)
return hash_sha256.hexdigest()
except:
return ""
@staticmethod
def _determine_classification(filename: str, mime_type: str, file_size: int) -> str:
"""Determine data classification level based on file characteristics"""
# Check for sensitive file extensions
sensitive_extensions = ['.key', '.pem', '.p12', '.pfx', '.crt', '.cer']
if any(filename.lower().endswith(ext) for ext in sensitive_extensions):
return 'CONFIDENTIAL'
# Check for log files
if filename.lower().endswith('.log') or 'log' in filename.lower():
return 'INTERNAL'
# Check for configuration files
config_extensions = ['.conf', '.config', '.ini', '.yaml', '.yml', '.json']
if any(filename.lower().endswith(ext) for ext in config_extensions):
return 'INTERNAL'
# Check for database files
db_extensions = ['.db', '.sqlite', '.sql', '.dump']
if any(filename.lower().endswith(ext) for ext in db_extensions):
return 'CONFIDENTIAL'
# Check file size (large files might be sensitive)
if file_size > 100 * 1024 * 1024: # 100MB
return 'INTERNAL'
# Default classification
return 'PUBLIC'
@staticmethod
def _get_or_create_classification(level: str):
"""Get or create data classification object"""
try:
# Try to get from security module first
classification = SecurityDataClassification.objects.filter(level=level).first()
if classification:
return classification
# Create new classification if not found
classification = SecurityDataClassification.objects.create(
level=level,
description=f'Data classification level: {level}',
retention_period_days=ComplianceIntegrationService._get_retention_period(level)
)
return classification
except Exception as e:
print(f"Error getting/creating classification: {e}")
return None
@staticmethod
def _get_retention_period(classification_level: str) -> int:
"""Get retention period in days based on classification level"""
retention_periods = {
'PUBLIC': 365, # 1 year
'INTERNAL': 1095, # 3 years
'CONFIDENTIAL': 2555, # 7 years
'RESTRICTED': 3650, # 10 years
'TOP_SECRET': 3650 # 10 years
}
return retention_periods.get(classification_level, 365)
@staticmethod
def _get_user_ip(user: User) -> str:
"""Get user IP address (placeholder implementation)"""
# This would be implemented based on your request handling
return "127.0.0.1"
@staticmethod
def _get_user_agent(user: User) -> str:
"""Get user agent (placeholder implementation)"""
# This would be implemented based on your request handling
return "Chat System"
@staticmethod
def _policy_applies_to_incident(policy: CompliancePolicy, incident_id: str) -> bool:
"""Check if compliance policy applies to incident"""
# This would check policy conditions against incident
# For now, return True for all active policies
return policy.is_active
@staticmethod
def _check_policy_violations(policy: CompliancePolicy, incident_id: str, user: User) -> List[str]:
"""Check for policy violations"""
violations = []
# Example policy checks
if policy.name == "Data Retention Policy":
# Check if chat logs are being retained properly
pass
if policy.name == "Access Control Policy":
# Check if user has appropriate access
pass
return violations