Updates
This commit is contained in:
@@ -0,0 +1,351 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user