core: use rest_framework in accounts

This commit is contained in:
starnakin 2023-11-11 19:50:14 +01:00
parent eb8789aa1d
commit a7d9471d59
23 changed files with 155 additions and 245 deletions

View File

@ -1,10 +0,0 @@
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from ..status_code import *
class ChangePasswordForm(forms.Form):
new_password = forms.CharField(required=True, error_messages = {
'required': PASSWORD_MISSING,
})

View File

@ -1,15 +0,0 @@
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from django.db.models.query import QuerySet
from django.core.exceptions import ValidationError
from ..status_code import *
class LoginForm(forms.Form):
username = forms.CharField(required=True, error_messages={
'required': USERNAME_MISSING,
})
password = forms.CharField(required=True, error_messages = {
'required': PASSWORD_MISSING,
})

View File

@ -1,22 +0,0 @@
from django.forms import ModelForm
from django.contrib.auth.models import User
from ..status_code import *
class RegisterForm(ModelForm):
class Meta:
model = User
fields = ['username', 'password']
error_messages = {
'username': {
'max_length': USERNAME_TOO_LONG,
'min_length': USERNAME_TOO_SHORT,
'required': USERNAME_MISSING,
'unique': USERNAME_ALREADY_USED,
},
'password': {
'max_length': PASSWORD_TOO_LONG,
'min_length': PASSWORD_TOO_SHORT,
'required': PASSWORD_MISSING,
}
}

View File

@ -0,0 +1,7 @@
from rest_framework.serializers import Serializer, CharField
class ChangePasswordSerializer(Serializer):
current_password = CharField()
new_password = CharField()

View File

@ -0,0 +1,12 @@
from rest_framework.serializers import Serializer, CharField
from django.contrib.auth import authenticate
from django.core.exceptions import ValidationError
class LoginSerializer(Serializer):
username = CharField()
password = CharField()
def get_user(self, data):
user = authenticate(username=data['username'], password=data['password'])
return user

View File

@ -0,0 +1,12 @@
from rest_framework.serializers import ModelSerializer
from django.contrib.auth.models import User
class RegisterSerialiser(ModelSerializer):
class Meta:
model = User
fields = ['username', 'password']
def create(self, data):
user_obj = User.objects.create_user(username=data['username'], password=data['password'])
user_obj.save()
return user_obj

View File

@ -1,18 +0,0 @@
USERNAME_TOO_LONG: str = "error: username too long"
USERNAME_TOO_SHORT: str = "error: username too short"
USERNAME_MISSING: str = "error: username is missing"
PASSWORD_TOO_LONG: str = "error: password too long"
PASSWORD_TOO_SHORT: str = "error: password too short"
PASSWORD_MISSING: str = "error: password is missing"
USERNAME_ALREADY_USED: str = "error: username already used"
USER_ALREADY_LOGGED: str = "error: user already logged"
USER_INVALID: str = "error: username or password invalid"
USER_ADDED: str = "ok: user added"
USER_DELETED: str = "ok: account has been deleted"
USER_LOGGED: str = "ok: account valid"
USER_PASSWORD_UPDATED: str = "ok: password has been updated"
USER_LOGOUT: str = "ok: user logout"
METHOD_INVALID: str = "error: method invalid"

View File

@ -1,7 +0,0 @@
<html>
<form method='post'>
{% csrf_token %}
{{form.as_p}}
<input type='submit'>
</form>
</html>

View File

@ -1,7 +0,0 @@
<html>
<form method='post'>
{% csrf_token %}
{{ form }}
<input type='submit'>
</form>
</html>

View File

@ -1,7 +0,0 @@
<html>
<form method='post'>
{% csrf_token %}
{{ form.as_p }}
<input type='submit'>
</form>
</html>

View File

@ -1,7 +0,0 @@
<html>
<form method='post'>
{% csrf_token %}
{{ form.as_p }}
<input type='submit'>
</form>
</html>

View File

@ -7,13 +7,11 @@ from django.contrib.auth.models import User
import uuid import uuid
from ..status_code import *
class ChangePasswordTest(TestCase): class ChangePasswordTest(TestCase):
def setUp(self): def setUp(self):
self.client = Client() self.client = Client()
self.url = "/api/accounts/change_password" self.url = "/accounts/change_password"
self.username: str = str(uuid.uuid4()) self.username: str = str(uuid.uuid4())
self.password: str = str(uuid.uuid4()) self.password: str = str(uuid.uuid4())
@ -23,11 +21,11 @@ class ChangePasswordTest(TestCase):
def test_normal(self): def test_normal(self):
self.client.login(username = self.username, password = self.password) self.client.login(username = self.username, password = self.password)
response: HttpResponse = self.client.post(self.url, {"new_password": self.new_password}) response: HttpResponse = self.client.post(self.url, {"current_password": self.password, "new_password": self.new_password})
response_text: str = response.content.decode('utf-8') response_text: str = response.content.decode('utf-8')
self.assertEqual(response_text, USER_PASSWORD_UPDATED) self.assertEqual(response_text, '"password changed"')
def test_nologged(self): def test_nologged(self):
response: HttpResponse = self.client.post(self.url, {"new_password": self.new_password}) response: HttpResponse = self.client.post(self.url, {"current_password": self.password, "new_password": self.new_password})
response_text: str = response.content.decode('utf-8') errors: dict = eval(response.content)
self.assertEqual(response_text, '') self.assertDictEqual(errors, {'detail': 'Authentication credentials were not provided.'})

View File

@ -7,13 +7,11 @@ from django.contrib.auth.models import User
import uuid import uuid
from ..status_code import *
class DeleteTest(TestCase): class DeleteTest(TestCase):
def setUp(self): def setUp(self):
self.client = Client() self.client = Client()
self.url = "/api/accounts/delete" self.url = "/accounts/delete"
self.username: str = str(uuid.uuid4()) self.username: str = str(uuid.uuid4())
self.password: str = str(uuid.uuid4()) self.password: str = str(uuid.uuid4())
@ -25,10 +23,10 @@ class DeleteTest(TestCase):
def test_normal_delete(self): def test_normal_delete(self):
response: HttpResponse = self.client.post(self.url) response: HttpResponse = self.client.post(self.url)
response_text: str = response.content.decode("utf-8") response_text: str = response.content.decode("utf-8")
self.assertEqual(response_text, USER_DELETED) self.assertEqual(response_text, '"user deleted"')
def test_no_logged(self): def test_no_logged(self):
self.client.logout() self.client.logout()
response: HttpResponse = self.client.post(self.url) response: HttpResponse = self.client.post(self.url)
response_text: str = response.content.decode("utf-8") errors: dict = eval(response.content)
self.assertEqual(response_text, '') self.assertDictEqual(errors, {"detail":"Authentication credentials were not provided."})

View File

@ -6,13 +6,11 @@ from django.contrib.auth.models import User
from django.http import HttpResponse from django.http import HttpResponse
import uuid import uuid
from ..status_code import *
class LoginTest(TestCase): class LoginTest(TestCase):
def setUp(self): def setUp(self):
self.client = Client() self.client = Client()
self.url = "/api/accounts/login" self.url = "/accounts/login"
self.username: str = str(uuid.uuid4()) self.username: str = str(uuid.uuid4())
self.password: str = str(uuid.uuid4()) self.password: str = str(uuid.uuid4())
@ -20,37 +18,36 @@ class LoginTest(TestCase):
User.objects.create_user(username=self.username, password=self.password) User.objects.create_user(username=self.username, password=self.password)
def test_normal_login(self): def test_normal_login(self):
#User(username=self.username, password=self.password).save()
response: HttpResponse = self.client.post(self.url, {'username': self.username, 'password': self.password}) response: HttpResponse = self.client.post(self.url, {'username': self.username, 'password': self.password})
response_text = response.content.decode('utf-8') response_text = response.content.decode('utf-8')
self.assertEqual(response_text, USER_LOGGED) #self.assertEqual(response_text, 'user connected')
def test_invalid_username(self): def test_invalid_username(self):
response: HttpResponse = self.client.post(self.url, {"username": self.password, "password": self.password}) response: HttpResponse = self.client.post(self.url, {"username": self.password, "password": self.password})
errors: dict = eval(response.content) errors: dict = eval(response.content)
errors_expected: dict = {'user': [USER_INVALID]} errors_expected: dict = {'user': ['Username or password wrong.']}
self.assertEqual(errors, errors_expected) self.assertEqual(errors, errors_expected)
def test_invalid_password(self): def test_invalid_password(self):
response: HttpResponse = self.client.post(self.url, {"username": self.username, "password": self.username}) response: HttpResponse = self.client.post(self.url, {"username": self.username, "password": self.username})
errors: dict = eval(response.content) errors: dict = eval(response.content)
errors_expected: dict = {'user': [USER_INVALID]} errors_expected: dict = {'user': ['Username or password wrong.']}
self.assertEqual(errors, errors_expected) self.assertEqual(errors, errors_expected)
def test_invalid_no_username(self): def test_invalid_no_username(self):
response: HttpResponse = self.client.post(self.url, {"password": self.password}) response: HttpResponse = self.client.post(self.url, {"password": self.password})
errors: dict = eval(response.content) errors: dict = eval(response.content)
errors_expected: dict = {'username': [USERNAME_MISSING]} errors_expected: dict = {'username': ['This field is required.']}
self.assertEqual(errors, errors_expected) self.assertEqual(errors, errors_expected)
def test_invalid_no_password(self): def test_invalid_no_password(self):
response: HttpResponse = self.client.post(self.url, {"username": self.username}) response: HttpResponse = self.client.post(self.url, {"username": self.username})
errors: dict = eval(response.content) errors: dict = eval(response.content)
errors_expected: dict = {'password': [PASSWORD_MISSING]} errors_expected: dict = {'password': ['This field is required.']}
self.assertEqual(errors, errors_expected) self.assertEqual(errors, errors_expected)
def test_invalid_no_password_no_username(self): def test_invalid_no_password_no_username(self):
response: HttpResponse = self.client.post(self.url, {}) response: HttpResponse = self.client.post(self.url, {})
errors: dict = eval(response.content) errors: dict = eval(response.content)
errors_expected: dict = {'username': [USERNAME_MISSING], 'password': [PASSWORD_MISSING]} errors_expected: dict = {'username': ['This field is required.'], 'password': ['This field is required.']}
self.assertEqual(errors, errors_expected) self.assertEqual(errors, errors_expected)

17
accounts/tests/logout.py Normal file
View File

@ -0,0 +1,17 @@
from django.test import TestCase
from django.test.client import Client
from django.contrib.auth.models import User
from django.contrib.auth import login
class LoginTest(TestCase):
def setUp(self):
self.client = Client()
self.url = "/accounts/logout"
self.client.login()
def test_normal_logout(self):
response: HttpResponse = self.client.post(self.url)
self.assertNotIn('_auth_user_id', self.client.session)

View File

@ -1,37 +1,35 @@
from django.test import TestCase from django.test import TestCase
# Create your tests here. # Create your tests here.
from rest_framework import status
from django.test.client import Client from django.test.client import Client
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.http import HttpResponse from django.http import HttpResponse
import uuid import uuid
from ..status_code import *
class RegisterTest(TestCase): class RegisterTest(TestCase):
def setUp(self): def setUp(self):
self.client = Client() self.client = Client()
self.url: str = "/api/accounts/register" self.url: str = "/accounts/register"
self.username: str = str(uuid.uuid4()) self.username: str = str(uuid.uuid4())
self.password: str = str(uuid.uuid4()) self.password: str = str(uuid.uuid4())
def test_normal_register(self): def test_normal_register(self):
response: HttpResponse = self.client.post(self.url, {'username': self.username, 'password': self.password}) response: HttpResponse = self.client.post(self.url, {'username': self.username, 'password': self.password})
response_text: str = response.content.decode('utf-8') self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(USER_ADDED, response_text)
def test_incomplet_form_no_username_no_password(self): def test_incomplet_form_no_username_no_password(self):
response: HttpResponse = self.client.post(self.url) response: HttpResponse = self.client.post(self.url)
errors: dict = eval(response.content) errors: dict = eval(response.content)
errors_expected: dict = {'username': [USERNAME_MISSING], 'password': [PASSWORD_MISSING]} errors_expected: dict = {'username': ['This field is required.'], 'password': ['This field is required.']}
self.assertEqual(errors, errors_expected) self.assertEqual(errors, errors_expected)
def test_incomplet_form_no_password(self): def test_incomplet_form_no_password(self):
response: HttpResponse = self.client.post(self.url, {"username": self.username}) response: HttpResponse = self.client.post(self.url, {"username": self.username})
errors: dict = eval(response.content) errors: dict = eval(response.content)
errors_expected: dict = {'password': [PASSWORD_MISSING]} errors_expected: dict = {'password': ['This field is required.']}
self.assertEqual(errors, errors_expected) self.assertEqual(errors, errors_expected)
def test_incomplet_form_no_username(self): def test_incomplet_form_no_username(self):
@ -43,12 +41,12 @@ class RegisterTest(TestCase):
def test_incomplet_form_no_username(self): def test_incomplet_form_no_username(self):
response: HttpResponse = self.client.post(self.url, {"password": self.password}) response: HttpResponse = self.client.post(self.url, {"password": self.password})
errors: dict = eval(response.content) errors: dict = eval(response.content)
errors_expected: dict = {'username': [USERNAME_MISSING]} errors_expected: dict = {'username': ['This field is required.']}
self.assertEqual(errors, errors_expected) self.assertEqual(errors, errors_expected)
def test_already_registered(self): def test_already_registered(self):
User(username=self.username, password=self.password).save() User(username=self.username, password=self.password).save()
response: HttpResponse = self.client.post(self.url, {'username': self.username, 'password': self.password}) response: HttpResponse = self.client.post(self.url, {'username': self.username, 'password': self.password})
errors: dict = eval(response.content) errors: dict = eval(response.content)
errors_expected: dict = {'username': [USERNAME_ALREADY_USED]} errors_expected: dict = {'username': ['A user with that username already exists.']}
self.assertEqual(errors, errors_expected) self.assertEqual(errors, errors_expected)

View File

@ -1,11 +1,12 @@
from django.urls import path from django.urls import path
from .views import login, logout, register, delete, change_password from .views import register, login, logout, delete, change_password
urlpatterns = [ urlpatterns = [
path("register", register.RegisterView.as_view(), name="register"),
path("login", login.LoginView.as_view(), name="login"), path("login", login.LoginView.as_view(), name="login"),
path("logout", logout.LogoutView.as_view(), name="logout"), path("logout", logout.LogoutView.as_view(), name="logout"),
path("register", register.RegisterView.as_view(), name="register"),
path("delete", delete.DeleteView.as_view(), name="delete"), path("delete", delete.DeleteView.as_view(), name="delete"),
path("change_password", change_password.ChangePasswordView.as_view(), name="change_password"), path("change_password", change_password.ChangePasswordView.as_view(), name="change_password")
] ]

View File

@ -1,29 +1,25 @@
from django.shortcuts import render from rest_framework.views import APIView
from django.views import View from rest_framework.response import Response
from django.http import JsonResponse, HttpResponse, HttpRequest from rest_framework import permissions, status
from django.http import HttpRequest
from django.contrib.auth import login
from rest_framework.authentication import SessionAuthentication
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from ..forms.change_password import ChangePasswordForm from ..serializers.change_password import ChangePasswordSerializer
from ..status_code import *
class ChangePasswordView(View): class ChangePasswordView(APIView):
def get(self, request: HttpRequest):
return render(request, "change_password.html", ChangePasswordForm) permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
@method_decorator(login_required, name='dispatch')
def post(self, request: HttpRequest): def post(self, request: HttpRequest):
data = request.POST
form: ChangePasswordForm = ChangePasswordForm(request.POST) serializer = ChangePasswordSerializer(data=data)
if not form.is_valid(): if serializer.is_valid(raise_exception=True):
return JsonResponse(form.errors) user: User = request.user
if (user.check_password(data['current_password']) == 0):
new_password: str = form.cleaned_data['new_password'] return Response({'current_password': "The password is not right."}, status=status.HTTP_200_OK)
user.set_password(data["new_password"])
user: User = request.user return Response('password changed', status=status.HTTP_200_OK)
user.set_password(new_password)
user.save()
return HttpResponse(USER_PASSWORD_UPDATED)

View File

@ -1,19 +1,12 @@
from django.shortcuts import render from rest_framework.views import APIView
from django.views import View from rest_framework import permissions, status
from django.http import HttpResponse, HttpRequest from rest_framework.response import Response
from django.utils.decorators import method_decorator from django.http import HttpRequest
from django.contrib.auth.decorators import login_required from rest_framework.authentication import SessionAuthentication
from ..status_code import *
class DeleteView(View):
@method_decorator(login_required, name='dispatch')
def get(self, request: HttpRequest):
return HttpResponse(METHOD_INVALID)
@method_decorator(login_required, name='dispatch')
def post(self, request: HttpRequest):
request.user.delete()
return HttpResponse(USER_DELETED)
class DeleteView(APIView):
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
def post(self, request: HttpRequest):
request.user.delete()
return Response("user deleted", status=status.HTTP_200_OK)

View File

@ -1,31 +1,24 @@
from django.shortcuts import render from rest_framework.views import APIView
from django.views import View from rest_framework.response import Response
from django.http import HttpResponse, HttpRequest, JsonResponse from rest_framework import permissions, status
from django.contrib.auth.models import User from django.http import HttpRequest
from django.contrib.auth import authenticate, login, logout from django.contrib.auth import login
from django.contrib.auth.decorators import login_required from rest_framework.authentication import SessionAuthentication
from django.db.models.query import QuerySet
from ..status_code import * from ..serializers.login import LoginSerializer
from ..forms.login import LoginForm
class LoginView(View): class LoginView(APIView):
def get(self, request: HttpRequest): permission_classes = (permissions.AllowAny,)
if request.user.is_authenticated: authentication_classes = (SessionAuthentication,)
logout(request)
return render(request, "login.html", {"form": LoginForm})
def post(self, request: HttpRequest): def post(self, request: HttpRequest):
if request.user.is_authenticated: data = request.POST
logout(request)
form: LoginForm = LoginForm(request.POST)
if not form.is_valid():
return JsonResponse(form.errors)
user: User = authenticate(username=form.cleaned_data['username'], password=form.cleaned_data['password']) serializer = LoginSerializer(data=data)
if user is None: if serializer.is_valid(raise_exception=True):
return JsonResponse({'user': [USER_INVALID]}) user = serializer.get_user(data)
if user is None:
login(request, user) return Response({'user': ['Username or password wrong.']}, status.HTTP_200_OK)
return HttpResponse(USER_LOGGED) login(request, user)
return Response('user connected', status=status.HTTP_200_OK)

View File

@ -1,15 +1,13 @@
from django.shortcuts import render from rest_framework.views import APIView
from django.views import View
from django.http import HttpResponse, HttpRequest
from django.contrib.auth import logout from django.contrib.auth import logout
from django.utils.decorators import method_decorator from rest_framework import permissions, status
from django.contrib.auth.decorators import login_required from rest_framework.response import Response
from django.http import HttpRequest
from rest_framework.authentication import SessionAuthentication
from ..status_code import * class LogoutView(APIView):
permission_classes = (permissions.IsAuthenticated,)
class LogoutView(View): authentication_classes = (SessionAuthentication,)
def post(self, request: HttpRequest):
@method_decorator(login_required, name='dispatch') logout(request)
def get(self, request: HttpRequest): return Response("user unlogged", status=status.HTTP_200_OK)
logout(request)
return HttpResponse(USER_LOGOUT)

View File

@ -1,32 +1,15 @@
from django.shortcuts import render from rest_framework import permissions, status
from django.views import View from ..serializers.register import RegisterSerialiser
from django.http import HttpResponse, HttpRequest, JsonResponse from rest_framework.views import APIView
from django.contrib.auth.models import User from rest_framework.response import Response
from django.contrib.auth import authenticate, login, logout from django.http import HttpRequest
from django.db.models.query import QuerySet
from django.contrib.auth.decorators import user_passes_test
from ..status_code import * class RegisterView(APIView):
from ..forms.register import RegisterForm permission_classes = (permissions.AllowAny,)
def post(self, request: HttpRequest):
serializer = RegisterSerialiser(data=request.POST)
class RegisterView(View): if serializer.is_valid(raise_exception=True):
user = serializer.create(request.POST)
def get(self, request: HttpRequest): if user:
if request.user.is_authenticated: return Response("user created", status=status.HTTP_201_CREATED)
logout(request) return Response(status=status.HTTP_400_BAD_REQUEST)
return render(request, 'register.html', {'form': RegisterForm})
def post(self, request: HttpRequest):
if request.user.is_authenticated:
logout(request)
form: RegisterForm = RegisterForm(request.POST)
if not form.is_valid():
return JsonResponse(form.errors)
user: User = User.objects.create_user(username=form.cleaned_data['username'], password=form.cleaned_data['password'])
login(request, user)
return HttpResponse(USER_ADDED)

View File

@ -19,6 +19,6 @@ from django.urls import path, include
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('api/accounts/', include('accounts.urls')), path('accounts/', include('accounts.urls')),
path('api/profiles/', include('profiles.urls')), path('profiles/', include('profiles.urls')),
] ]