diff --git a/auth_app/__init__.py b/auth_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth_app/admin.py b/auth_app/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/auth_app/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/auth_app/apps.py b/auth_app/apps.py new file mode 100644 index 0000000..945e148 --- /dev/null +++ b/auth_app/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AuthAppConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'auth_app' diff --git a/auth_app/custom_backend.py b/auth_app/custom_backend.py new file mode 100755 index 0000000..92b3477 --- /dev/null +++ b/auth_app/custom_backend.py @@ -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 \ No newline at end of file diff --git a/auth_app/migrations/__init__.py b/auth_app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth_app/models.py b/auth_app/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/auth_app/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/auth_app/serializer.py b/auth_app/serializer.py new file mode 100755 index 0000000..d0d5d0b --- /dev/null +++ b/auth_app/serializer.py @@ -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 diff --git a/auth_app/tests.py b/auth_app/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/auth_app/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/auth_app/views.py b/auth_app/views.py new file mode 100644 index 0000000..95d71f0 --- /dev/null +++ b/auth_app/views.py @@ -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) + diff --git a/join/settings.py b/join/settings.py index e34e0bb..8eebea3 100644 --- a/join/settings.py +++ b/join/settings.py @@ -55,10 +55,12 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', + 'rest_framework.authtoken', 'corsheaders', 'djangorestframework_camel_case', 'task_app', 'user_app', + 'auth_app', ] MIDDLEWARE = [ @@ -163,3 +165,21 @@ STATIC_URL = 'static/' # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field 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', +) \ No newline at end of file diff --git a/join/urls.py b/join/urls.py index 8f61cf7..823b19d 100644 --- a/join/urls.py +++ b/join/urls.py @@ -3,6 +3,7 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter from task_app.views import TaskViewSet, SubTaskViewSet, AssignedTaskViewSet from user_app.views import UserViewSet +from auth_app.views import LoginView, LogoutView, AuthView router = DefaultRouter() router.register(r'tasks', TaskViewSet) @@ -13,4 +14,8 @@ router.register(r'users', UserViewSet) urlpatterns = [ path('admin/', admin.site.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'), ] \ No newline at end of file diff --git a/user_app/admin.py b/user_app/admin.py index 6b7a580..d53bbf6 100644 --- a/user_app/admin.py +++ b/user_app/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin +from django import forms from .models import User class UserAdmin(admin.ModelAdmin): @@ -6,10 +7,10 @@ class UserAdmin(admin.ModelAdmin): list_display = ('first_name', 'last_name', 'email', 'phone', 'initials', 'color', 'status', 'last_login', 'is_active', 'is_staff') list_filter = ('status', 'is_active', 'is_staff') search_fields = ('email', 'first_name', 'last_name') - + fieldsets = ( (None, { - 'fields': ('first_name', 'last_name', 'email', 'phone', 'password') + 'fields': ('first_name', 'last_name', 'email', 'phone', 'password') #Passwort hier beibehalten. }), ('Additional Info', { 'fields': ('initials', 'color', 'status', 'last_login') @@ -22,4 +23,9 @@ class UserAdmin(admin.ModelAdmin): list_editable = ('is_active', 'is_staff', 'status') 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) \ No newline at end of file