""" 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