Added sign-in view and simple protected view

This commit is contained in:
ITQ
2024-02-29 20:42:06 +03:00
parent fcf4499787
commit 2ea64e2bcb
6 changed files with 161 additions and 68 deletions
+6 -1
View File
@@ -112,7 +112,12 @@ DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
REST_FRAMEWORK = { REST_FRAMEWORK = {
"DEFAULT_FILTER_BACKENDS": [ "DEFAULT_FILTER_BACKENDS": [
"django_filters.rest_framework.DjangoFilterBackend" "django_filters.rest_framework.DjangoFilterBackend"
] ],
"DEFAULT_AUTHENTICATION_CLASSES": (
"users.authentication.JWTAuthentication",
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.BasicAuthentication",
),
} }
APPEND_SLASH = False APPEND_SLASH = False
+29
View File
@@ -0,0 +1,29 @@
import jwt
from django.conf import settings
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from users.models import Profile
class JWTAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.headers.get("Authorization", "").split("Bearer ")[-1]
if not token:
return None
try:
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=["HS256"]
)
user = Profile.objects.get(login=payload["login"])
return (user, None)
except Profile.DoesNotExist:
raise AuthenticationFailed("Invalid token")
except jwt.ExpiredSignatureError:
raise AuthenticationFailed("Token has expired")
except jwt.InvalidTokenError:
raise AuthenticationFailed("Invalid token")
+3
View File
@@ -34,5 +34,8 @@ class Profile(models.Model):
) )
image = models.URLField(max_length=200, blank=True, null=True) image = models.URLField(max_length=200, blank=True, null=True)
def is_authenticated(self):
return True
def __str__(self): def __str__(self):
return self.login return self.login
+10 -1
View File
@@ -5,7 +5,16 @@ import users.views
urlpatterns = [ urlpatterns = [
path( path(
"register", "register",
users.views.register_user, users.views.RegisterUserApiView.as_view(),
name="register", name="register",
), ),
path(
"sign-in",
users.views.SigninUserApiView.as_view(),
name="sign-in",
),
path(
"protected-view",
users.views.ProtectedView.as_view(),
)
] ]
+112 -66
View File
@@ -1,9 +1,14 @@
import re import re
from datetime import timedelta
import bcrypt import bcrypt
import jwt
from django.conf import settings
from django.utils import timezone
from rest_framework import status from rest_framework import status
from rest_framework.decorators import api_view from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView
from users.models import Profile from users.models import Profile
from users.serializers import UserSerializer from users.serializers import UserSerializer
@@ -12,76 +17,117 @@ MIN_PASSWORD_LEN = 6
MAX_PASSWORD_LEN = 100 MAX_PASSWORD_LEN = 100
@api_view(["POST"]) class RegisterUserApiView(APIView):
def register_user(request): def post(self, request):
serializer = UserSerializer(data=request.data) serializer = UserSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
if ( if (
Profile.objects.filter( Profile.objects.filter(
login=serializer.validated_data["login"] login=serializer.validated_data["login"]
).first() ).first()
is not None is not None
): ):
return Response( return Response(
{"error": "User with this login already exists"}, {"error": "User with this login already exists"},
status=status.HTTP_409_CONFLICT, status=status.HTTP_409_CONFLICT,
) )
if ( if (
Profile.objects.filter( Profile.objects.filter(
email=serializer.validated_data["email"] email=serializer.validated_data["email"]
).first() ).first()
is not None is not None
): ):
return Response( return Response(
{"error": "User with this email already exists"}, {"error": "User with this email already exists"},
status=status.HTTP_409_CONFLICT, status=status.HTTP_409_CONFLICT,
) )
if ( if (
Profile.objects.filter( Profile.objects.filter(
phone=serializer.validated_data["phone"] phone=serializer.validated_data["phone"]
).first() ).first()
is not None is not None
): ):
return Response( return Response(
{"error": "User with this phone already exists"}, {"error": "User with this phone already exists"},
status=status.HTTP_409_CONFLICT, status=status.HTTP_409_CONFLICT,
)
password = serializer.validated_data["password"]
password_pattern = re.compile(
r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{6,100}$"
) )
password = serializer.validated_data["password"] if not (bool(re.match(password_pattern, password))):
password_pattern = re.compile( error = {
r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{6,100}$" "message": "Your password does not meet our requirements"
}
return Response(
error,
status=status.HTTP_400_BAD_REQUEST,
)
password_hash = bcrypt.hashpw(
password.encode("utf-8"), bcrypt.gensalt()
).decode("utf-8")
serializer.validated_data["password"] = password_hash
user = serializer.save()
profile = {
"profile": {
"login": user.login,
"email": user.email,
"countryCode": user.countryCode,
"isPublic": user.isPublic,
}
}
if user.phone is not None:
profile["profile"]["phone"] = user.phone
if user.image is not None:
profile["profile"]["image"] = user.image
return Response(profile, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class SigninUserApiView(APIView):
def post(self, request):
login = request.data.get("login")
password = request.data.get("password")
user = Profile.objects.filter(login=login).first()
if user is not None:
if not bcrypt.checkpw(
password.encode("utf-8"), user.password.encode("utf-8")
):
return Response(
{"error": "Invalid credentials"},
status=status.HTTP_401_UNAUTHORIZED,
)
else:
return Response(
{"error": "Invalid credentials"},
status=status.HTTP_401_UNAUTHORIZED,
)
token = jwt.encode(
{
"login": login,
"password": password,
"exp": timezone.now() + timedelta(hours=24),
},
settings.SECRET_KEY,
algorithm="HS256",
) )
if not ( return Response({"token": token})
bool(re.match(password_pattern, password))
):
error = {"message": "Your password does not meet our requirements"}
return Response(
error,
status=status.HTTP_400_BAD_REQUEST,
)
password_hash = bcrypt.hashpw(
password.encode("utf-8"), bcrypt.gensalt()
).decode("utf-8")
serializer.validated_data["password"] = password_hash
user = serializer.save() class ProtectedView(APIView):
permission_classes = [IsAuthenticated]
profile = { def get(self, request):
"profile": { user = request.user
"login": user.login, return Response({"message": "Authenticated", "user": str(user)})
"email": user.email,
"countryCode": user.countryCode,
"isPublic": user.isPublic,
}
}
if user.phone is not None:
profile["profile"]["phone"] = user.phone
if user.image is not None:
profile["profile"]["image"] = user.image
return Response(profile, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+1
View File
@@ -5,3 +5,4 @@ psycopg2-binary==2.9.9
dj-database-url==2.1.0 dj-database-url==2.1.0
django-filter==23.5 django-filter==23.5
bcrypt==4.1.2 bcrypt==4.1.2
djangorestframework-jwt==1.11.0