feat: create auth app with login, logout, and auth check, including custom backend for email login
This commit is contained in:
parent
5ba7863913
commit
0ac3a5049c
12 changed files with 129 additions and 2 deletions
0
auth_app/__init__.py
Normal file
0
auth_app/__init__.py
Normal file
3
auth_app/admin.py
Normal file
3
auth_app/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
6
auth_app/apps.py
Normal file
6
auth_app/apps.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AuthAppConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'auth_app'
|
||||||
12
auth_app/custom_backend.py
Executable file
12
auth_app/custom_backend.py
Executable file
|
|
@ -0,0 +1,12 @@
|
||||||
|
from user_app.models import User
|
||||||
|
from django.contrib.auth.backends import BaseBackend
|
||||||
|
|
||||||
|
class EmailBackend(BaseBackend):
|
||||||
|
def authenticate(self, request, email=None, password=None):
|
||||||
|
try:
|
||||||
|
user = User.objects.get(email=email)
|
||||||
|
if user.check_password(password):
|
||||||
|
return user
|
||||||
|
return None
|
||||||
|
except User.DoesNotExist:
|
||||||
|
return None
|
||||||
0
auth_app/migrations/__init__.py
Normal file
0
auth_app/migrations/__init__.py
Normal file
3
auth_app/models.py
Normal file
3
auth_app/models.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
17
auth_app/serializer.py
Executable file
17
auth_app/serializer.py
Executable file
|
|
@ -0,0 +1,17 @@
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
class LoginSerializer(serializers.Serializer):
|
||||||
|
email = serializers.EmailField()
|
||||||
|
password = serializers.CharField(write_only=True)
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
email = data.get('email')
|
||||||
|
password = data.get('password')
|
||||||
|
|
||||||
|
if not email:
|
||||||
|
raise serializers.ValidationError("Email is required.")
|
||||||
|
|
||||||
|
if not password:
|
||||||
|
raise serializers.ValidationError("Password is required.")
|
||||||
|
|
||||||
|
return data
|
||||||
3
auth_app/tests.py
Normal file
3
auth_app/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
52
auth_app/views.py
Normal file
52
auth_app/views.py
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
from django.shortcuts import render, redirect
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import status, authentication
|
||||||
|
from rest_framework.authtoken.models import Token
|
||||||
|
from rest_framework.authtoken.views import ObtainAuthToken
|
||||||
|
from django.contrib.auth import authenticate
|
||||||
|
from .serializer import LoginSerializer
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
|
||||||
|
class LoginView(APIView):
|
||||||
|
serializer_class = LoginSerializer
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
serializer = self.serializer_class(data=request.data)
|
||||||
|
|
||||||
|
if serializer.is_valid():
|
||||||
|
email = serializer.validated_data['email']
|
||||||
|
password = serializer.validated_data['password']
|
||||||
|
user = authenticate(request, email=email, password=password)
|
||||||
|
|
||||||
|
if user:
|
||||||
|
if not user.is_active:
|
||||||
|
return Response({'error': 'Account is inactive, please check your mails'}, status=status.HTTP_403_FORBIDDEN)
|
||||||
|
return self._create_token_response(user)
|
||||||
|
return Response({'error': 'Unable to login with provided credentials.'}, status=status.HTTP_401_UNAUTHORIZED)
|
||||||
|
|
||||||
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def _create_token_response(self, user):
|
||||||
|
token, created = Token.objects.get_or_create(user=user)
|
||||||
|
return Response({'token': token.key}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
class LogoutView(APIView):
|
||||||
|
authentication_classes = [authentication.TokenAuthentication]
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
try:
|
||||||
|
request.user.auth_token.delete()
|
||||||
|
return Response({"message": "Successfully logged out"}, status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return Response({"error": "Logout failed"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
class AuthView(APIView):
|
||||||
|
authentication_classes = [authentication.TokenAuthentication]
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
return Response({"user_id": request.user.id}, status=status.HTTP_200_OK)
|
||||||
|
return Response({"error": "User is not logged in"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||||
|
|
||||||
|
|
@ -55,10 +55,12 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
|
'rest_framework.authtoken',
|
||||||
'corsheaders',
|
'corsheaders',
|
||||||
'djangorestframework_camel_case',
|
'djangorestframework_camel_case',
|
||||||
'task_app',
|
'task_app',
|
||||||
'user_app',
|
'user_app',
|
||||||
|
'auth_app',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|
@ -163,3 +165,21 @@ STATIC_URL = 'static/'
|
||||||
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
|
||||||
|
# Auth
|
||||||
|
|
||||||
|
AUTH_USER_MODEL = 'user_app.User'
|
||||||
|
AUTH_EMAIL_VERIFICATION = True
|
||||||
|
|
||||||
|
# Rest Framework
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||||
|
'rest_framework.authentication.TokenAuthentication',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
AUTHENTICATION_BACKENDS = (
|
||||||
|
'auth_app.custom_backend.EmailBackend',
|
||||||
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
|
)
|
||||||
|
|
@ -3,6 +3,7 @@ from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from task_app.views import TaskViewSet, SubTaskViewSet, AssignedTaskViewSet
|
from task_app.views import TaskViewSet, SubTaskViewSet, AssignedTaskViewSet
|
||||||
from user_app.views import UserViewSet
|
from user_app.views import UserViewSet
|
||||||
|
from auth_app.views import LoginView, LogoutView, AuthView
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register(r'tasks', TaskViewSet)
|
router.register(r'tasks', TaskViewSet)
|
||||||
|
|
@ -13,4 +14,8 @@ router.register(r'users', UserViewSet)
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('api/', include(router.urls)),
|
path('api/', include(router.urls)),
|
||||||
|
|
||||||
|
path('auth/', AuthView.as_view(), name='auth'),
|
||||||
|
path('auth/login/', LoginView.as_view(), name='login'),
|
||||||
|
path('auth/logout/', LogoutView.as_view(), name='logout'),
|
||||||
]
|
]
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django import forms
|
||||||
from .models import User
|
from .models import User
|
||||||
|
|
||||||
class UserAdmin(admin.ModelAdmin):
|
class UserAdmin(admin.ModelAdmin):
|
||||||
|
|
@ -9,7 +10,7 @@ class UserAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(None, {
|
||||||
'fields': ('first_name', 'last_name', 'email', 'phone', 'password')
|
'fields': ('first_name', 'last_name', 'email', 'phone', 'password') #Passwort hier beibehalten.
|
||||||
}),
|
}),
|
||||||
('Additional Info', {
|
('Additional Info', {
|
||||||
'fields': ('initials', 'color', 'status', 'last_login')
|
'fields': ('initials', 'color', 'status', 'last_login')
|
||||||
|
|
@ -22,4 +23,9 @@ class UserAdmin(admin.ModelAdmin):
|
||||||
list_editable = ('is_active', 'is_staff', 'status')
|
list_editable = ('is_active', 'is_staff', 'status')
|
||||||
ordering = ('last_name',)
|
ordering = ('last_name',)
|
||||||
|
|
||||||
|
def save_model(self, request, obj, form, change):
|
||||||
|
if 'password' in form.changed_data:
|
||||||
|
obj.set_password(form.cleaned_data['password'])
|
||||||
|
super().save_model(request, obj, form, change)
|
||||||
|
|
||||||
admin.site.register(User, UserAdmin)
|
admin.site.register(User, UserAdmin)
|
||||||
Loading…
Reference in a new issue