352 lines
14 KiB
Python
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
|