update
This commit is contained in:
210
legal/models.py
Normal file
210
legal/models.py
Normal file
@@ -0,0 +1,210 @@
|
||||
"""
|
||||
Legal compliance and GDPR models.
|
||||
"""
|
||||
from django.db import models
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.utils import timezone
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class ConsentRecord(models.Model):
|
||||
"""
|
||||
Track user consent for GDPR compliance.
|
||||
"""
|
||||
CONSENT_TYPE_CHOICES = [
|
||||
('privacy_policy', 'Privacy Policy'),
|
||||
('terms_of_service', 'Terms of Service'),
|
||||
('data_processing', 'Data Processing'),
|
||||
('marketing', 'Marketing Communications'),
|
||||
('cookies', 'Cookie Consent'),
|
||||
]
|
||||
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='consents',
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
consent_type = models.CharField(
|
||||
max_length=50,
|
||||
choices=CONSENT_TYPE_CHOICES
|
||||
)
|
||||
consent_given = models.BooleanField(default=False)
|
||||
ip_address = models.GenericIPAddressField(null=True, blank=True)
|
||||
user_agent = models.TextField(blank=True)
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
version = models.CharField(
|
||||
max_length=20,
|
||||
blank=True,
|
||||
help_text='Version of the policy/terms'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = 'legal_consentrecord'
|
||||
verbose_name = 'Consent Record'
|
||||
verbose_name_plural = 'Consent Records'
|
||||
ordering = ['-timestamp']
|
||||
indexes = [
|
||||
models.Index(fields=['user', 'consent_type']),
|
||||
models.Index(fields=['consent_type', 'timestamp']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
status = "Given" if self.consent_given else "Not Given"
|
||||
return f"{self.get_consent_type_display()} - {status} - {self.timestamp}"
|
||||
|
||||
|
||||
class DataRequest(models.Model):
|
||||
"""
|
||||
GDPR data subject requests (access, deletion, portability).
|
||||
"""
|
||||
REQUEST_TYPE_CHOICES = [
|
||||
('access', 'Data Access Request'),
|
||||
('deletion', 'Data Deletion Request'),
|
||||
('portability', 'Data Portability Request'),
|
||||
('rectification', 'Data Rectification Request'),
|
||||
('objection', 'Objection to Processing'),
|
||||
('restriction', 'Restriction of Processing'),
|
||||
]
|
||||
|
||||
STATUS_CHOICES = [
|
||||
('pending', 'Pending'),
|
||||
('in_progress', 'In Progress'),
|
||||
('completed', 'Completed'),
|
||||
('rejected', 'Rejected'),
|
||||
]
|
||||
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='data_requests'
|
||||
)
|
||||
request_type = models.CharField(
|
||||
max_length=50,
|
||||
choices=REQUEST_TYPE_CHOICES
|
||||
)
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=STATUS_CHOICES,
|
||||
default='pending'
|
||||
)
|
||||
description = models.TextField(
|
||||
blank=True,
|
||||
help_text='Additional details about the request'
|
||||
)
|
||||
requested_at = models.DateTimeField(auto_now_add=True)
|
||||
completed_at = models.DateTimeField(null=True, blank=True)
|
||||
response_data = models.JSONField(
|
||||
default=dict,
|
||||
blank=True,
|
||||
help_text='Response data (e.g., exported data)'
|
||||
)
|
||||
response_file = models.FileField(
|
||||
upload_to='data_requests/',
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='File containing requested data'
|
||||
)
|
||||
handled_by = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='handled_data_requests',
|
||||
limit_choices_to={'role': 'admin'}
|
||||
)
|
||||
notes = models.TextField(
|
||||
blank=True,
|
||||
help_text='Internal notes about handling the request'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = 'legal_datarequest'
|
||||
verbose_name = 'Data Request'
|
||||
verbose_name_plural = 'Data Requests'
|
||||
ordering = ['-requested_at']
|
||||
indexes = [
|
||||
models.Index(fields=['user', 'status']),
|
||||
models.Index(fields=['request_type', 'status']),
|
||||
models.Index(fields=['status', 'requested_at']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.get_request_type_display()} by {self.user.username} - {self.get_status_display()}"
|
||||
|
||||
|
||||
class SecurityEvent(models.Model):
|
||||
"""
|
||||
Security event logging for compliance and monitoring.
|
||||
"""
|
||||
EVENT_TYPE_CHOICES = [
|
||||
('login_success', 'Successful Login'),
|
||||
('login_failed', 'Failed Login'),
|
||||
('password_change', 'Password Changed'),
|
||||
('account_locked', 'Account Locked'),
|
||||
('suspicious_activity', 'Suspicious Activity'),
|
||||
('data_breach', 'Data Breach'),
|
||||
('unauthorized_access', 'Unauthorized Access Attempt'),
|
||||
('file_upload', 'File Upload'),
|
||||
('data_export', 'Data Export'),
|
||||
('admin_action', 'Admin Action'),
|
||||
]
|
||||
|
||||
SEVERITY_CHOICES = [
|
||||
('low', 'Low'),
|
||||
('medium', 'Medium'),
|
||||
('high', 'High'),
|
||||
('critical', 'Critical'),
|
||||
]
|
||||
|
||||
event_type = models.CharField(
|
||||
max_length=50,
|
||||
choices=EVENT_TYPE_CHOICES
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='security_events'
|
||||
)
|
||||
ip_address = models.GenericIPAddressField(null=True, blank=True)
|
||||
user_agent = models.TextField(blank=True)
|
||||
details = models.JSONField(
|
||||
default=dict,
|
||||
blank=True,
|
||||
help_text='Additional event details'
|
||||
)
|
||||
severity = models.CharField(
|
||||
max_length=20,
|
||||
choices=SEVERITY_CHOICES,
|
||||
default='low'
|
||||
)
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
resolved = models.BooleanField(default=False)
|
||||
resolved_at = models.DateTimeField(null=True, blank=True)
|
||||
resolved_by = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='resolved_security_events',
|
||||
limit_choices_to={'role': 'admin'}
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = 'security_securityevent'
|
||||
verbose_name = 'Security Event'
|
||||
verbose_name_plural = 'Security Events'
|
||||
ordering = ['-timestamp']
|
||||
indexes = [
|
||||
models.Index(fields=['event_type', 'timestamp']),
|
||||
models.Index(fields=['severity', 'timestamp']),
|
||||
models.Index(fields=['user', 'timestamp']),
|
||||
models.Index(fields=['resolved', 'timestamp']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.get_event_type_display()} - {self.get_severity_display()} - {self.timestamp}"
|
||||
Reference in New Issue
Block a user