""" User management models for the fraud reporting platform. """ from django.contrib.auth.models import AbstractUser from django.db import models from django.utils import timezone from django.core.validators import RegexValidator class User(AbstractUser): """ Custom user model with role-based access control. """ ROLE_CHOICES = [ ('normal', 'Normal User'), ('moderator', 'Moderator'), ('admin', 'Administrator'), ] role = models.CharField( max_length=20, choices=ROLE_CHOICES, default='normal', help_text='User role in the system' ) is_verified = models.BooleanField( default=False, help_text='Email verification status' ) mfa_enabled = models.BooleanField( default=False, help_text='Multi-factor authentication enabled' ) mfa_secret = models.CharField( max_length=32, blank=True, null=True, help_text='MFA secret key' ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) last_login_ip = models.GenericIPAddressField(null=True, blank=True) class Meta: db_table = 'users_user' verbose_name = 'User' verbose_name_plural = 'Users' ordering = ['-created_at'] def __str__(self): return f"{self.username} ({self.role})" def is_moderator(self): return self.role in ['moderator', 'admin'] def is_administrator(self): return self.role == 'admin' class UserProfile(models.Model): """ Extended user profile information. """ user = models.OneToOneField( User, on_delete=models.CASCADE, related_name='profile' ) first_name = models.CharField(max_length=100, blank=True) last_name = models.CharField(max_length=100, blank=True) phone_regex = RegexValidator( regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed." ) phone = models.CharField( validators=[phone_regex], max_length=17, blank=True, null=True, help_text='Encrypted phone number' ) date_of_birth = models.DateField(null=True, blank=True) # GDPR Consent consent_given = models.BooleanField(default=False) consent_date = models.DateTimeField(null=True, blank=True) consent_ip = models.GenericIPAddressField(null=True, blank=True) # Preferences preferred_language = models.CharField( max_length=10, default='bg', choices=[('bg', 'Bulgarian'), ('en', 'English')] ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'users_userprofile' verbose_name = 'User Profile' verbose_name_plural = 'User Profiles' def __str__(self): return f"Profile of {self.user.username}" class ActivityLog(models.Model): """ Log user activities for security and auditing. """ ACTION_CHOICES = [ ('login', 'Login'), ('logout', 'Logout'), ('register', 'Registration'), ('password_change', 'Password Change'), ('profile_update', 'Profile Update'), ('report_create', 'Report Created'), ('report_edit', 'Report Edited'), ('report_delete', 'Report Deleted'), ('security_event', 'Security Event'), ('failed_login', 'Failed Login'), ('suspicious_activity', 'Suspicious Activity'), ] user = models.ForeignKey( User, on_delete=models.SET_NULL, null=True, blank=True, related_name='activity_logs' ) action = models.CharField(max_length=50, choices=ACTION_CHOICES) ip_address = models.GenericIPAddressField(null=True, blank=True) user_agent = models.TextField(blank=True) details = models.JSONField(default=dict, blank=True) timestamp = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'users_activitylog' verbose_name = 'Activity Log' verbose_name_plural = 'Activity Logs' ordering = ['-timestamp'] indexes = [ models.Index(fields=['user', 'timestamp']), models.Index(fields=['action', 'timestamp']), ] def __str__(self): return f"{self.user} - {self.action} at {self.timestamp}" class FailedLoginAttempt(models.Model): """ Track failed login attempts for security. """ email_or_username = models.CharField(max_length=255) ip_address = models.GenericIPAddressField() user_agent = models.TextField(blank=True) timestamp = models.DateTimeField(auto_now_add=True) is_blocked = models.BooleanField(default=False) class Meta: db_table = 'security_failedlogin' verbose_name = 'Failed Login Attempt' verbose_name_plural = 'Failed Login Attempts' ordering = ['-timestamp'] indexes = [ models.Index(fields=['email_or_username', 'timestamp']), models.Index(fields=['ip_address', 'timestamp']), ] def __str__(self): return f"Failed login: {self.email_or_username} from {self.ip_address}"