This commit is contained in:
Iliyan Angelov
2025-11-26 22:32:20 +02:00
commit ed94dd22dd
150 changed files with 14058 additions and 0 deletions

175
accounts/models.py Normal file
View File

@@ -0,0 +1,175 @@
"""
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}"