Files
OSINT/reports/models.py
Iliyan Angelov ed94dd22dd update
2025-11-26 22:32:20 +02:00

412 lines
13 KiB
Python

"""
Scam and fraud report models.
"""
from django.db import models
from django.contrib.auth import get_user_model
from django.urls import reverse
from django.utils.text import slugify
from django.core.cache import cache
from accounts.security import DataEncryption
User = get_user_model()
class SiteSettings(models.Model):
"""
Site-wide settings that can be managed from admin.
Uses singleton pattern - only one instance should exist.
"""
contact_email = models.EmailField(
default='support@fraudplatform.bg',
help_text='Основен имейл за контакти и поддръжка'
)
contact_phone = models.CharField(
max_length=50,
blank=True,
default='',
help_text='Телефонен номер за контакти (незадължително)'
)
contact_address = models.CharField(
max_length=200,
blank=True,
default='',
help_text='Адрес за контакти (незадължително)'
)
# Email Server Settings
email_backend = models.CharField(
max_length=100,
default='django.core.mail.backends.smtp.EmailBackend',
choices=[
('django.core.mail.backends.smtp.EmailBackend', 'SMTP'),
('django.core.mail.backends.console.EmailBackend', 'Console (Development)'),
('django.core.mail.backends.filebased.EmailBackend', 'File Based'),
],
help_text='Тип на имейл сървъра'
)
email_host = models.CharField(
max_length=255,
blank=True,
default='',
help_text='SMTP сървър (напр. smtp.gmail.com)'
)
email_port = models.IntegerField(
default=587,
help_text='SMTP порт (обикновено 587 за TLS или 465 за SSL)'
)
email_use_tls = models.BooleanField(
default=True,
help_text='Използване на TLS (за порт 587)'
)
email_use_ssl = models.BooleanField(
default=False,
help_text='Използване на SSL (за порт 465)'
)
email_host_user = models.CharField(
max_length=255,
blank=True,
default='',
help_text='SMTP потребителско име / имейл'
)
email_host_password = models.CharField(
max_length=255,
blank=True,
default='',
help_text='SMTP парола (ще бъде криптирана)'
)
default_from_email = models.EmailField(
default='noreply@fraudplatform.bg',
help_text='Имейл адрес по подразбиране за изпращане'
)
email_timeout = models.IntegerField(
default=10,
help_text='Таймаут за имейл връзка (секунди)'
)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'Настройки на Сайта'
verbose_name_plural = 'Настройки на Сайта'
db_table = 'reports_sitesettings'
def __str__(self):
return 'Настройки на Сайта'
def save(self, *args, **kwargs):
# Ensure only one instance exists
self.pk = 1
# Encrypt email password if it's provided and not already encrypted
if self.email_host_password:
# Check if it's already encrypted by trying to decrypt it
# If decryption succeeds, it's already encrypted, so keep original
# If decryption fails, it's plain text, so encrypt it
is_encrypted = False
try:
# Try to decrypt - if it succeeds, it's already encrypted
DataEncryption.decrypt(self.email_host_password)
is_encrypted = True
except (Exception, ValueError, TypeError):
# Decryption failed, so it's plain text
is_encrypted = False
# Only encrypt if it's not already encrypted
if not is_encrypted:
self.email_host_password = DataEncryption.encrypt(self.email_host_password)
super().save(*args, **kwargs)
# Clear cache when settings are updated
cache.delete('site_settings')
def get_email_password(self):
"""Get decrypted email password."""
if not self.email_host_password:
return ''
try:
return DataEncryption.decrypt(self.email_host_password)
except:
# If decryption fails, return as-is (might be plain text from migration)
return self.email_host_password
def delete(self, *args, **kwargs):
# Prevent deletion - settings should always exist
pass
@classmethod
def get_settings(cls):
"""Get site settings with caching."""
settings = cache.get('site_settings')
if settings is None:
settings, created = cls.objects.get_or_create(pk=1)
cache.set('site_settings', settings, 3600) # Cache for 1 hour
return settings
class ScamTag(models.Model):
"""
Tags for categorizing scam reports.
"""
name = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=100, unique=True, blank=True)
description = models.TextField(blank=True)
color = models.CharField(
max_length=7,
default='#007bff',
help_text='Hex color code for display'
)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'reports_scamtag'
verbose_name = 'Scam Tag'
verbose_name_plural = 'Scam Tags'
ordering = ['name']
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def __str__(self):
return self.name
class ScamReport(models.Model):
"""
Main scam/fraud report model.
"""
SCAM_TYPE_CHOICES = [
('phishing', 'Phishing'),
('fake_website', 'Fake Website'),
('romance_scam', 'Romance Scam'),
('investment_scam', 'Investment Scam'),
('tech_support_scam', 'Tech Support Scam'),
('identity_theft', 'Identity Theft'),
('fake_product', 'Fake Product'),
('advance_fee', 'Advance Fee Fraud'),
('other', 'Other'),
]
STATUS_CHOICES = [
('pending', 'Pending Review'),
('under_review', 'Under Review'),
('verified', 'Verified'),
('rejected', 'Rejected'),
('archived', 'Archived'),
]
# Reporter information
reporter = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='reports'
)
is_anonymous = models.BooleanField(
default=False,
help_text='Report submitted anonymously'
)
# Report details
title = models.CharField(max_length=200)
description = models.TextField()
scam_type = models.CharField(
max_length=50,
choices=SCAM_TYPE_CHOICES,
default='other'
)
# Reported entities
reported_url = models.URLField(blank=True, null=True, max_length=500)
reported_email = models.EmailField(blank=True, null=True)
reported_phone = models.CharField(max_length=20, blank=True, null=True)
reported_company = models.CharField(max_length=200, blank=True, null=True)
# Evidence
evidence_files = models.JSONField(
default=list,
blank=True,
help_text='List of file paths for evidence'
)
# Status and verification
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='pending'
)
verification_score = models.IntegerField(
default=0,
help_text='OSINT verification confidence score (0-100)'
)
# Visibility
is_public = models.BooleanField(
default=True,
help_text='Visible in public database'
)
is_auto_discovered = models.BooleanField(
default=False,
help_text='Automatically discovered by OSINT system'
)
# Metadata
tags = models.ManyToManyField(ScamTag, blank=True, related_name='reports')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
verified_at = models.DateTimeField(null=True, blank=True)
# IP tracking for anonymous reports
reporter_ip = models.GenericIPAddressField(null=True, blank=True)
class Meta:
db_table = 'reports_scamreport'
verbose_name = 'Scam Report'
verbose_name_plural = 'Scam Reports'
ordering = ['-created_at']
indexes = [
models.Index(fields=['status', 'created_at']),
models.Index(fields=['scam_type', 'status']),
models.Index(fields=['reported_url']),
models.Index(fields=['reported_email']),
models.Index(fields=['reported_phone']),
]
def __str__(self):
return f"{self.title} - {self.get_status_display()}"
def get_absolute_url(self):
return reverse('reports:detail', kwargs={'pk': self.pk})
def get_reporter_display(self):
if self.is_anonymous:
return "Anonymous"
return self.reporter.username if self.reporter else "Unknown"
class ScamVerification(models.Model):
"""
OSINT verification data for scam reports.
"""
VERIFICATION_METHOD_CHOICES = [
('whois', 'WHOIS Lookup'),
('dns', 'DNS Records'),
('ssl', 'SSL Certificate'),
('archive', 'Wayback Machine'),
('email_check', 'Email Validation'),
('phone_check', 'Phone Validation'),
('business_registry', 'Business Registry'),
('social_media', 'Social Media'),
('manual', 'Manual Review'),
]
report = models.ForeignKey(
ScamReport,
on_delete=models.CASCADE,
related_name='verifications'
)
verification_method = models.CharField(
max_length=50,
choices=VERIFICATION_METHOD_CHOICES
)
verification_data = models.JSONField(
default=dict,
help_text='Raw verification data'
)
confidence_score = models.IntegerField(
default=0,
help_text='Confidence score for this verification (0-100)'
)
verified_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='verifications'
)
notes = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'reports_scamverification'
verbose_name = 'Scam Verification'
verbose_name_plural = 'Scam Verifications'
ordering = ['-created_at']
def __str__(self):
return f"Verification for {self.report.title} via {self.get_verification_method_display()}"
class TakedownRequest(models.Model):
"""
Request to take down a scam report by the accused party.
"""
STATUS_CHOICES = [
('pending', 'Pending Review'),
('under_review', 'Under Review'),
('approved', 'Approved'),
('rejected', 'Rejected'),
]
report = models.ForeignKey(
ScamReport,
on_delete=models.CASCADE,
related_name='takedown_requests'
)
requester_name = models.CharField(
max_length=200,
help_text='Име на заявителя'
)
requester_email = models.EmailField(
help_text='Имейл на заявителя'
)
requester_phone = models.CharField(
max_length=50,
blank=True,
help_text='Телефон на заявителя (незадължително)'
)
reason = models.TextField(
help_text='Причина за заявката за премахване'
)
evidence = models.TextField(
blank=True,
help_text='Доказателства или допълнителна информация'
)
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='pending'
)
reviewed_by = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='reviewed_takedown_requests',
limit_choices_to={'role__in': ['moderator', 'admin']}
)
review_notes = models.TextField(
blank=True,
help_text='Бележки от модератора'
)
ip_address = models.GenericIPAddressField(null=True, blank=True)
user_agent = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
reviewed_at = models.DateTimeField(null=True, blank=True)
class Meta:
db_table = 'reports_takedownrequest'
verbose_name = 'Заявка за Премахване'
verbose_name_plural = 'Заявки за Премахване'
ordering = ['-created_at']
indexes = [
models.Index(fields=['report', 'status']),
models.Index(fields=['status', 'created_at']),
]
def __str__(self):
return f"Takedown request for {self.report.title} by {self.requester_name}"