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

139 lines
4.5 KiB
Python

"""
Forms for accounts app.
"""
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django_otp.plugins.otp_totp.models import TOTPDevice
from django.utils import timezone
from .models import User, UserProfile
from .security import InputSanitizer, PasswordSecurity
from .form_mixins import BotProtectionMixin
class UserRegistrationForm(BotProtectionMixin, UserCreationForm):
"""User registration form with security validation and bot protection."""
email = forms.EmailField(required=True)
consent_given = forms.BooleanField(
required=True,
label='I agree to the Privacy Policy and Terms of Service'
)
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2', 'consent_given')
def clean_username(self):
username = self.cleaned_data.get('username')
if username:
# Sanitize username
username = InputSanitizer.sanitize_html(username)
# Check for SQL injection patterns
sanitized = InputSanitizer.sanitize_sql(username)
if sanitized is None:
raise forms.ValidationError('Invalid username format.')
return username
def clean_email(self):
email = self.cleaned_data.get('email')
if email:
# Validate email format
if not InputSanitizer.validate_email(email):
raise forms.ValidationError('Invalid email format.')
# Sanitize email
email = InputSanitizer.sanitize_html(email)
return email
def clean_password1(self):
password = self.cleaned_data.get('password1')
if password:
is_strong, message = PasswordSecurity.check_password_strength(password)
if not is_strong:
raise forms.ValidationError(message)
return password
def save(self, commit=True):
user = super().save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
# Create profile with consent
profile = UserProfile.objects.create(
user=user,
consent_given=self.cleaned_data['consent_given']
)
return user
class UserProfileForm(forms.ModelForm):
"""User profile edit form."""
first_name = forms.CharField(max_length=100, required=False)
last_name = forms.CharField(max_length=100, required=False)
class Meta:
model = UserProfile
fields = ('first_name', 'last_name', 'phone', 'preferred_language')
widgets = {
'phone': forms.TextInput(attrs={'placeholder': '+359...'}),
}
class MFAVerifyForm(forms.Form):
"""MFA verification form."""
token = forms.CharField(
max_length=6,
min_length=6,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': '000000',
'autofocus': True,
'pattern': '[0-9]{6}',
'maxlength': '6'
}),
label='Verification Code',
help_text='Enter the 6-digit code from your authenticator app'
)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
def verify_token(self):
"""Verify the TOTP token."""
if not self.user:
return False
token = self.cleaned_data.get('token')
if not token:
return False
# Get the TOTP device (for login, use confirmed device; for setup, use unconfirmed)
try:
# Try confirmed device first (for login)
device = TOTPDevice.objects.get(user=self.user, name='default', confirmed=True)
except TOTPDevice.DoesNotExist:
# Try unconfirmed device (for setup)
try:
device = TOTPDevice.objects.get(user=self.user, name='default', confirmed=False)
except TOTPDevice.DoesNotExist:
return False
return device.verify_token(token)
class MFASetupForm(forms.Form):
"""MFA setup form (for confirmation)."""
token = forms.CharField(
max_length=6,
min_length=6,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': '000000',
'autofocus': True,
'pattern': '[0-9]{6}',
'maxlength': '6'
}),
label='Verification Code',
help_text='Enter the 6-digit code from your authenticator app to confirm setup'
)