Source code for authentication.views
from rest_framework.response import Response
from rest_framework.status import (
HTTP_201_CREATED,
HTTP_400_BAD_REQUEST,
HTTP_401_UNAUTHORIZED
)
from django.http import HttpResponse
from django.contrib.auth import logout
from rest_framework.views import APIView
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django.db import IntegrityError
from django.shortcuts import get_object_or_404, redirect
from django.core.exceptions import ObjectDoesNotExist
from django.template import loader
from django.views.generic import CreateView
from django.urls import reverse_lazy
from django import forms
from .serializers import UserSerializer
import re
from django.contrib.auth import authenticate, login
from django.core.mail import EmailMessage
[docs]class CustomUserCreationForm(UserCreationForm):
"""
Custom form for user registration.
Extends UserCreationForm to add custom validation methods for username and password.
"""
[docs] class Meta(UserCreationForm.Meta):
model = User
fields = (
'username',
'password1',
'password2',
'email',
'first_name',
'last_name'
)
labels = {
'username': ('Username'),
'password1': ('Password'),
'password2': ('Confirm Password'),
'email': ('Email'),
'first_name': ('First Name'),
'last_name': ('Last Name')
}
[docs] def username_clean_lenght(self, username):
"""
Validate the length of the username.
:param username: The username to be validated.
:type username: str
:return: True if the username is longer than 150 characters, False otherwise.
:rtype: bool
"""
username = username.lower()
if len(username) > 150:
return True
else:
return False
[docs] def username_clean_exits(self, username):
"""
Check if the username already exists in the database.
:param username: The username to be checked.
:type username: str
:return: True if the username already exists, False otherwise.
:rtype: bool
"""
username = username.lower()
new = User.objects.filter(username=username)
if new.count():
return True
else:
return False
[docs] def username_clean_pattern(self, username):
"""
Validate the username against a specific pattern.
:param username: The username to be validated.
:type username: str
:return: True if the username does not match the pattern, False otherwise.
:rtype: bool
"""
username = username.lower()
username_val_regex = re.search("[^\\w@.\\-_+]", username)
if (username_val_regex is not None):
return True
return False
[docs] def email_clean(self, email):
"""
Check if the email already exists in the database.
:param email: The email to be checked.
:type email: str
:return: True if the email already exists, False otherwise.
:rtype: bool
"""
email = email.lower()
new = User.objects.filter(email=email)
if new.count():
return True
return False
[docs] def clean_confirmation(self, password, confirm_password):
"""
Validate that the entered passwords match.
:param password: The first entered password.
:type password: str
:param confirm_password: The second entered password for confirmation.
:type confirm_password: str
:return: True if the passwords do not match, False otherwise.
:rtype: bool
"""
if password and confirm_password and password != confirm_password:
return True
return False
[docs] def clean_password_lenght(self, password):
"""
Validate the length of the password.
:param password: The password to be validated.
:type password: str
:return: True if the password has fewer than 8 characters, False otherwise.
:rtype: bool
"""
if len(password) < 8:
return True
else:
return False
[docs] def clean_password_common(self, password):
"""
Check if the entered password is common.
:param password: The password to be checked.
:type password: str
:return: True if the password is common, False otherwise.
:rtype: bool
"""
common_passwords = [
'12345678',
'11111111',
'00000000',
'password',
'password0',
'password1',
'decide',
'decide password',
'01234567',
'2345678',
'password123',
'password12',
'cotraseña',
'contraseña123',
'adminadmin',
'admin123',
'1234567890',
'0987654321',
'87654321',
'lorca123',
'lorca_password']
res = False
for c in common_passwords:
if (password == c):
res = True
break
return res
[docs] def clean_password_too_similar(
self,
password,
username,
first_name,
last_name):
"""
Check if the password is too similar to personal data.
:param password: The password to be checked.
:type password: str
:param username: The username of the user.
:type username: str
:param first_name: The first name of the user.
:type first_name: str
:param last_name: The last name of the user.
:type last_name: str
:return: True if the password is too similar to personal data, False otherwise.
:rtype: bool
"""
if (password.__contains__(username) | password.__contains__(
first_name) | password.__contains__(last_name)):
return True
else:
return False
[docs] def clean_password_numeric(self, password):
"""
Check if the password consists only of numeric characters.
:param password: The password to be checked.
:type password: str
:return: True if the password consists only of numeric characters, False otherwise.
:rtype: bool
"""
if (password.isnumeric()):
return True
else:
return False
[docs]class GetUserView(APIView):
"""
API view for retrieving user information.
Handles POST requests to retrieve user information based on the provided user token.
"""
[docs] def post(self, request):
"""
Handle POST requests to retrieve user information.
:param request: The incoming HTTP request containing the user token.
:type request: Request
:return: An HTTP response containing the serialized user data.
:rtype: Response
"""
key = request.data.get('token', '')
tk = get_object_or_404(Token, key=key)
return Response(UserSerializer(tk.user, many=False).data)
[docs]class LogoutView(APIView):
"""
API view for logging out a user.
Handles POST requests to log out a user by deleting their authentication token.
"""
[docs] def post(self, request):
"""
Handle POST requests to log out a user.
:param request: The incoming HTTP request containing the user token.
:type request: Request
:return: An HTTP response indicating the success or failure of the logout operation.
:rtype: Response
"""
key = request.data.get('token', '')
try:
tk = Token.objects.get(key=key)
tk.delete()
except ObjectDoesNotExist:
pass
return Response({})
[docs]class RegisterViewAPI(APIView):
"""
API view for registering a new user.
Handles POST requests to register a new user with admin privileges.
"""
[docs] def post(self, request):
"""
Handle POST requests to register a new user.
:param request: The incoming HTTP request containing the admin token, username, and password.
:type request: Request
:return: An HTTP response indicating the success or failure of the registration.
:rtype: Response
"""
key = request.data.get('token', '')
tk = get_object_or_404(Token, key=key)
if not tk.user.is_superuser:
return Response({}, status=HTTP_401_UNAUTHORIZED)
username = request.data.get('username', '')
pwd = request.data.get('password', '')
if not username or not pwd:
return Response({}, status=HTTP_400_BAD_REQUEST)
try:
user = User(username=username)
user.set_password(pwd)
user.save()
token, _ = Token.objects.get_or_create(user=user)
except IntegrityError:
return Response({}, status=HTTP_400_BAD_REQUEST)
return Response(
{'user_pk': user.pk, 'token': token.key}, HTTP_201_CREATED)
[docs]class LoginView(CreateView):
"""
View for handling user login.
Inherits from CreateView to handle user login. Uses CustomUserCreationForm for customized user registration.
"""
template_name = "authentication/login.html"
form_class = CustomUserCreationForm
model = User
[docs] def post(self, request):
"""
Handle POST requests to log in a user.
:param request: The incoming HTTP request containing the username and password.
:type request: Request
:return: An HTTP response indicating the success or failure of the login operation.
:rtype: HttpResponse
"""
values = request.POST
username = values['username']
password1 = values['password1']
user = authenticate(request, username=username, password=password1)
response = redirect('/')
if user is not None:
login(request, user)
userObject = User.objects.get(username=username)
token, created = Token.objects.get_or_create(user=userObject)
response.set_cookie(key='token', value=token)
else:
incorrect = ["This username or password do not exist"]
template = loader.get_template("authentication/login.html")
context = {"errors": incorrect}
return HttpResponse(template.render(context, request))
return response
[docs]class RegisterView(CreateView):
"""
View for user registration.
Inherits from CreateView to handle user registration. Uses CustomUserCreationForm for customized user registration.
"""
template_name = "authentication/register.html"
form_class = CustomUserCreationForm
model = User
[docs] def get_form(self, form_class=None):
"""
Customize the form widget attributes.
:param form_class: The form class.
:type form_class: CustomUserCreationForm
:return: The customized form.
:rtype: CustomUserCreationForm
"""
form = super(RegisterView, self).get_form()
form.fields['username'].widget = forms.TextInput(
attrs={
'class': 'form-control mb-2',
'placeholder': 'Less than 150 characters',
'required': 'required'})
form.fields['password1'].widget = forms.PasswordInput(
attrs={
'class': 'form-control mb-2',
'placeholder': '8 characters or more',
'required': 'required'})
form.fields['password2'].widget = forms.PasswordInput(
attrs={
'class': 'form-control mb-2',
'placeholder': 'Confirm password',
'required': 'required'})
form.fields['first_name'].widget = forms.TextInput(
attrs={
'class': 'form-control mb-2',
'placeholder': 'Alex',
'required': 'required'})
form.fields['last_name'].widget = forms.TextInput(
attrs={
'class': 'form-control mb-2',
'placeholder': 'Smith',
'required': 'required'})
form.fields['email'].widget = forms.EmailInput(
attrs={
'class': 'form-control mb-2',
'placeholder': 'example@decide.com',
'required': 'required'})
return form
[docs] def post(self, request):
"""
Handle POST requests to register a new user.
:param request: The incoming HTTP request containing user registration data.
:type request: Request
:return: An HTTP response indicating the success or failure of the registration.
:rtype: HttpResponse
"""
values = request.POST
username = values['username']
password1 = values['password1']
password2 = values['password2']
email = values['email']
first_name = values['first_name']
last_name = values['last_name']
form = CustomUserCreationForm()
errors = []
if (form.clean_confirmation(password1, password2)):
errors.append("Passwords must be the same")
if (form.username_clean_lenght(username)):
errors.append("This username is larger than 150 characters")
if (form.username_clean_exits(username)):
errors.append("This username has already taken")
if (form.username_clean_pattern(username)):
errors.append("This username not match with the pattern")
if (form.email_clean(email)):
errors.append("This email has already taken")
if (form.clean_password_lenght(password1)):
errors.append("This password must contain at least 8 characters")
if (form.clean_password_common(password1)):
errors.append("This password is a common password")
if (form.clean_password_too_similar(
password1, username, first_name, last_name)):
errors.append("This password is too similar to your personal data")
if (form.clean_password_numeric(password1)):
errors.append("This password is numeric")
if (len(errors) > 0):
template = loader.get_template("authentication/register.html")
context = {"errors": errors}
return HttpResponse(template.render(context, request))
else:
try:
user = User(username=username)
user.first_name = first_name
user.last_name = last_name
user.email = email
user.set_password(password1)
email = EmailMessage(
"Message from the app Decide",
"This is a confirmation message: the user with name {} and email {} has registered in the app Decide recently.".format(
user.first_name,
user.email),
"",
[
user.email],
reply_to=[email])
email.send()
user.save()
token, _ = Token.objects.get_or_create(user=user)
except IntegrityError:
return HttpResponse(
"Integrity Error raised",
status=HTTP_400_BAD_REQUEST)
return redirect('/')
[docs]def main(request):
"""
Main view for handling authentication.
:param request: The incoming HTTP request.
:type request: Request
:return: An HTTP response with the authentication template.
:rtype: HttpResponse
"""
template = loader.get_template("authentication/authentication.html")
context = {}
is_authenticated = False
if request.user.is_authenticated:
is_authenticated = True
context['username'] = request.user.username
context['authenticated'] = is_authenticated
return HttpResponse(template.render(context, request))
[docs]def logout_view(request):
"""
Logout view to handle user logout.
:param request: The incoming HTTP request.
:type request: Request
:return: An HTTP response redirecting to the home page after logout.
:rtype: HttpResponse
"""
response = redirect('/')
if request.user.is_authenticated:
logout(request)
response.delete_cookie('token')
response.delete_cookie('decide')
return response