Skip to content

Commit d42c94d

Browse files
authored
Merge pull request #72 from PythonBulawayo/auth
Added authentication endpoints
2 parents 16ffbb1 + d984afc commit d42c94d

File tree

14 files changed

+209
-0
lines changed

14 files changed

+209
-0
lines changed

backend/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ FROM python:3.10-slim
22

33
WORKDIR /app
44
COPY requirements.txt /app
5+
# Installing gcc and libc6-dev because docker removes them after building Python,
6+
# so it's impossible to build C extensions afterwards, wcwidth==0.2.6 and cwcwidth==0.1.8 in this case
7+
RUN apt-get update && apt-get install -y \
8+
gcc \
9+
libc6-dev \
10+
&& rm -rf /var/lib/apt/lists/*
511
RUN pip install -r requirements.txt
612
COPY . /app
713

backend/accounts/admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""
22
Module for managing admin functionality related to accounts.
33
"""
4+
45
from django.contrib import admin
56
from django.contrib.auth.admin import UserAdmin
67
from django.contrib.auth.models import Group

backend/api/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@
2525
path("api/posts/<int:pk>/", views.PostDetail.as_view(), name="post-detail"),
2626
path("api/posts/delete/<int:pk>/", views.PostDelete.as_view(), name="post-delete"),
2727
path("api/signup", views.SignUpView.as_view(), name="signup-view"),
28+
path("api/auth/", include("authentication.urls")),
2829
]

backend/authentication/__init__.py

Whitespace-only changes.

backend/authentication/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class AuthenticationConfig(AppConfig):
5+
default_auto_field = "django.db.models.BigAutoField"
6+
name = "authentication"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from rest_framework_simplejwt.authentication import JWTAuthentication
2+
from django.conf import settings
3+
4+
from rest_framework.authentication import CSRFCheck
5+
from rest_framework import exceptions
6+
7+
8+
def enforce_csrf(request):
9+
"""
10+
Enforce CSRF validation.
11+
"""
12+
13+
def dummy_get_response(request):
14+
return None
15+
16+
check = CSRFCheck(dummy_get_response)
17+
check.process_request(request)
18+
reason = check.process_view(request, None, (), {})
19+
if reason:
20+
raise exceptions.PermissionDenied("CSRF Failed: %s" % reason)
21+
22+
23+
class CustomAuthentication(JWTAuthentication):
24+
25+
def authenticate(self, request):
26+
header = self.get_header(request)
27+
28+
if header is None:
29+
raw_token = request.COOKIES.get(settings.SIMPLE_JWT["AUTH_COOKIE"]) or None
30+
else:
31+
raw_token = self.get_raw_token(header)
32+
if raw_token is None:
33+
return None
34+
35+
validated_token = self.get_validated_token(raw_token)
36+
return self.get_user(validated_token), validated_token

backend/authentication/migrations/__init__.py

Whitespace-only changes.

backend/authentication/serializers.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from rest_framework import serializers
2+
3+
from accounts.models import CustomUser
4+
5+
6+
class AuthUserSerializer(serializers.ModelSerializer):
7+
class Meta:
8+
model = CustomUser
9+
fields = ["username", "password"]

backend/authentication/tests.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from django.test import TestCase
2+
from rest_framework import status
3+
from rest_framework.test import APIClient
4+
from accounts.models import CustomUser
5+
from django.conf import settings
6+
7+
8+
class LoginViewTestCase(TestCase):
9+
def setUp(self):
10+
LOGIN_URL = "api/v2/auth/login/"
11+
self.client = APIClient()
12+
self.active_user = CustomUser.objects.create_user(
13+
username="activeuser", password="password123"
14+
)
15+
self.active_user.is_active = True
16+
self.active_user.save()
17+
18+
self.inactive_user = CustomUser.objects.create_user(
19+
username="inactiveuser", password="password123"
20+
)
21+
self.inactive_user.is_active = False
22+
self.inactive_user.save()
23+
24+
self.url = LOGIN_URL
25+
26+
def test_login_successful(self):
27+
response = self.client.post(
28+
self.url, {"username": "activeuser", "password": "password123"}
29+
)
30+
self.assertEqual(response.status_code, status.HTTP_200_OK)
31+
self.assertIn("access", response.data)
32+
self.assertIn(settings.SIMPLE_JWT["AUTH_COOKIE"], response.cookies)
33+
34+
def test_login_inactive_user(self):
35+
response = self.client.post(
36+
self.url, {"username": "inactiveuser", "password": "password123"}
37+
)
38+
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
39+
self.assertEqual(response.data, {"details": "This account is not active."})
40+
41+
def test_login_invalid_credentials(self):
42+
response = self.client.post(
43+
self.url, {"username": "wronguser", "password": "wrongpassword"}
44+
)
45+
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
46+
self.assertEqual(
47+
response.data, {"details": "Account with given credentials not found."}
48+
)
49+
50+
def test_login_missing_fields(self):
51+
response = self.client.post(self.url, {"username": "activeuser"})
52+
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
53+
self.assertEqual(
54+
response.data, {"details": "Account with given credentials not found."}
55+
)

backend/authentication/urls.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from django.urls import path
2+
from . import views
3+
4+
urlpatterns = [
5+
path("login/", views.LoginView.as_view(), name="login"),
6+
path("logout/", views.LogoutView.as_view(), name="logout"),
7+
]

0 commit comments

Comments
 (0)