from django.urls import reverse_lazy from django.views import View from django.views.generic import FormView from django.views.generic.edit import UpdateView from django.views.generic.detail import DetailView from django.views.generic.list import ListView from django.contrib.auth import login,authenticate,logout from django.contrib import messages from django.shortcuts import redirect, render from django.db import IntegrityError import hashlib from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.decorators import login_required from .forms import (UserSignUpForm, SigninForm, CustomPasswordResetForm, ProfilePictureUpdateForm, UpdateProfileForm) from django.contrib.auth.views import (PasswordResetView, PasswordChangeView) from django.http import JsonResponse from django.contrib.messages.views import SuccessMessageMixin from django.contrib.auth.mixins import UserPassesTestMixin from .models import CustomUser from django.utils.encoding import force_str from django.utils.http import urlsafe_base64_decode from .tokens import account_activation_token from django.contrib import messages from django.contrib.auth import authenticate, login from django.utils.http import url_has_allowed_host_and_scheme from .utils import (send_activation_email, redirect_to_next_or_home, ) from at_django_boilerplate.utils.encryption_utils import EncryptionUtils from django.shortcuts import render, redirect from .forms import UserSignUpForm import logging logger = logging.getLogger(__name__) class CheckUsernameAvailability(View): def get(self, request): username = request.GET.get('username', None) is_taken = CustomUser.objects.filter_by_email(username).exists() data = { 'is_taken': is_taken } return JsonResponse(data) class SignupView(FormView): template_name = 'registration/signup.html' form_class = UserSignUpForm success_url = reverse_lazy('home') def post(self, request, *args, **kwargs): next_url = request.GET.get('next') user_form = self.form_class(request.POST) if user_form.is_valid(): email = user_form.cleaned_data['email'].lower() contact_number = user_form.cleaned_data.get('contact_number') email_exists = CustomUser.objects.filter_by_email(email).exists() number_exists = False if contact_number: number_exists = CustomUser.objects.filter_by_contact_number(contact_number).exists() if email_exists: user_form.add_error('email', 'Email is already in use.') elif number_exists: user_form.add_error('contact_number', 'Contact number is already in use.') else: try: user = user_form.save(commit=False) user.set_password(user.password) user.save() login(request, user) user.is_active = True user.save() send_activation_email( request=request, user=user, to_email=user.get_decrypted_email() ) # return redirect(next_url or 'verify-email-done') return redirect('dashboard') except IntegrityError: # Rollback if needed user.delete() return self.render_to_response(self.get_context_data(form=user_form)) def verify_email(request): return render(request, 'registration/verify_email.html') def verify_email_done(request): return render(request, 'registration/verify_email_done.html') def verify_email_confirm(request, uidb64, token): try: uid = force_str(urlsafe_base64_decode(uidb64)) user = CustomUser.objects.get(pk=uid) except(TypeError, ValueError, OverflowError, CustomUser.DoesNotExist): user = None if user is not None and account_activation_token.check_token(user, token): user.is_active = True user.save() messages.success(request, 'Your email has been verified.') return redirect('verify-email-complete') else: messages.warning(request, 'The link is invalid.') return render(request, 'registration/verify_email_confirm.html') def verify_email_complete(request): return render(request, 'registration/verify_email_complete.html') class LoginPageView(View): template_name = 'login.html' form_class = SigninForm def get(self, request): if request.user.is_authenticated: next_url = request.GET.get('next') if next_url and url_has_allowed_host_and_scheme(next_url, allowed_hosts={request.get_host()}): return redirect(next_url) return redirect('home') form = self.form_class() return render(request, self.template_name, {'form': form, 'message': ''}) def post(self, request): print('Login') form = self.form_class(request.POST) message = 'Login failed!' if form.is_valid(): print('Form Valid') username = form.cleaned_data['username'].lower() password = form.cleaned_data['password'] user = authenticate(request, username=username, password=password) # user.backend = 'accounts.auth_backends.CustomAuthBackend' # <--- key line added print(f"Authenticated User: {user}") if user: if user.is_active: login(request, user) return redirect_to_next_or_home(request) else: messages.error(request, 'Please verify your account before logging in.') else: messages.error(request, 'Invalid username or password.') else: messages.error(request, 'Invalid input.') return render(request, self.template_name, { 'form': form, 'message': message, }) class LogoutView(View): def get(self, request): logout(request) return redirect('home') class ResetPasswordView(SuccessMessageMixin, PasswordResetView): template_name = 'reset/password_reset.html' email_template_name = 'reset/password_reset_email.html' html_email_template_name = 'reset/password_reset_email_html.html' subject_template_name = 'reset/password_reset_subject.txt' success_message = "We've emailed you instructions for setting your password, " \ "if an account exists with the email you entered. You should receive them shortly." \ " If you don't receive an email, " \ "please make sure you've entered the address you registered with, and check your spam folder." success_url = reverse_lazy('password_reset_done') token_generator = default_token_generator form_class = CustomPasswordResetForm class ResetPasswordDoneView(View): template_name = 'reset/password_reset_done.html' success_message = "We've emailed you instructions for setting your password, " \ "if an account exists with the email you entered. You should receive them shortly." \ " If you don't receive an email, " \ "please make sure you've entered the address you registered with, and check your spam folder." def get(self, request): return render(request=request,template_name=self.template_name) class ChangePasswordView(SuccessMessageMixin, PasswordChangeView): template_name = 'change_password.html' success_message = "Successfully Changed Your Password" success_url = reverse_lazy('home') class CustomUserListView(ListView): model = CustomUser template_name = 'user_list.html' users = 'CustomUsers' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) users = CustomUser.objects.all() context['users'] = [{ 'username': custom_user.username, 'name': custom_user.get_decrypted_name(), 'email': custom_user.get_decrypted_email(), 'birthday': custom_user.birthday, 'address': custom_user.get_decrypted_address(), 'contact_number': custom_user.get_decrypted_contact_number() } for custom_user in users] return context class ProfileView(DetailView): model = CustomUser template_name = 'profile/profile.html' context_object_name = 'custom_user' def get_object(self): return self.request.user def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Initialize the form with the current user's data form = ProfilePictureUpdateForm(instance=self.get_object()) context['update_profile_photo_form'] = form return context class UpdateProfileView(UserPassesTestMixin, UpdateView): model = CustomUser form_class = UpdateProfileForm template_name = 'profile/update_profile.html' def test_func(self): obj = self.get_object() requested_by_user = self.request.user return (obj == requested_by_user) def get(self, request, *args, **kwargs): self.object = self.get_object() # Decrypt the data before passing it to the form encryption_tool = EncryptionUtils() contact_number = None if self.object.contact_number: contact_number = encryption_tool.decrypt(self.object.contact_number) initial_data = { 'contact_number': contact_number } form = self.form_class(instance=self.object, initial=initial_data) return self.render_to_response(self.get_context_data(form=form)) def post(self, request, *args, **kwargs): self.object = self.get_object() form = self.form_class(request.POST, instance=self.object) if form.is_valid(): obj = form.save(commit=False) encryption_tool = EncryptionUtils() # address = form.cleaned_data['address'] # obj.address = encryption_tool.encrypt(address) contact_number = form.cleaned_data['contact_number'] if all([contact_number!=None,contact_number!='']): obj.contact_number = encryption_tool.encrypt(contact_number) obj.save() return self.form_valid(form) else: # Decrypt the data before re-rendering the form with errors encryption_tool = EncryptionUtils() contact_number = None if self.object.contact_number: contact_number = encryption_tool.decrypt(self.object.contact_number) initial_data = { 'contact_number': contact_number } form = self.form_class(instance=self.object, initial=initial_data) return self.form_invalid(form) @login_required def update_profile_picture(request): if request.user.pk !=None: user_profile = request.user if request.method == 'POST': form = ProfilePictureUpdateForm(request.POST, request.FILES, instance=user_profile) if form.is_valid(): form.save() messages.success(request, 'Your profile picture has been updated successfully!') return redirect('user_profile') else: form = ProfilePictureUpdateForm(instance=user_profile) return render(request, 'profile/update_profile_picture.html', {'form': form}) from django.core.validators import EmailValidator, ValidationError from django.core.cache import cache from .utils import send_otp, verify_otp from .models import CustomUser import re PHONE_REGEX = re.compile(r'^\+?\d{10,15}$') def is_email(identifier): validator = EmailValidator() try: validator(identifier) return True except ValidationError: return False def request_otp_view(request): if request.method == 'POST': identifier = request.POST.get('identifier', '').strip().lower() if not identifier: messages.error(request, "Please enter your email or mobile number.") return redirect('login') # Assuming OTP modal is on the login page print(f"Identifier: {identifier}") user = None if is_email(identifier): print(f"Email: {identifier}") user = CustomUser.objects.filter_by_email(email=identifier).first() else: if not PHONE_REGEX.match(identifier): messages.error(request, "Invalid mobile number format.") return redirect('login') print(f"Mobile hash: {identifier}") user = CustomUser.objects.filter_by_contact_number(contact_number=identifier).first() print(f"User: {user}") if not user: messages.error(request, "No user found with this email or mobile number.") return redirect('login') # Rate limiting cache_key = f"otp_rate_limit_{identifier}" if cache.get(cache_key): messages.error(request, "Please wait before requesting another OTP.") return redirect('login') # Send OTP success, _ = send_otp(identifier, purpose='login') if success: cache.set(cache_key, True, timeout=60) # 60 seconds request.session['otp_identifier'] = identifier messages.success(request, "OTP sent successfully.") return redirect('verify_otp') else: messages.error(request, "Failed to send OTP. Please try again.") return redirect('login') return redirect('login') def verify_otp_view(request): identifier = request.session.get('otp_identifier') if not identifier: messages.error(request, "Session expired. Please request a new OTP.") return redirect('login') if request.method == 'POST': user_otp = request.POST.get('otp', '').strip() if not user_otp: messages.error(request, "Please enter the OTP.") return render(request, 'registration/verify_otp.html') if verify_otp(identifier, user_otp): # Match user if is_email(identifier): print(f"Email: {identifier}") user = CustomUser.objects.get_by_email(email=identifier) else: user = CustomUser.objects.get_by_contact_number(contact_number=identifier) # user.backend = 'django.contrib.auth.backends.ModelBackend' # <--- key line added login(request, user) messages.success(request, "Logged in successfully.") request.session.pop('otp_identifier', None) return redirect('home') else: messages.error(request, "Invalid or expired OTP.") return render(request, 'registration/verify_otp.html')