added user reset password api

This commit is contained in:
Alexander Karpov 2023-09-10 17:38:47 +03:00
parent b59c8fbf8b
commit 3ef20b5eb9
5 changed files with 82 additions and 16 deletions

View File

@ -57,3 +57,24 @@ class Meta:
"is_staff": {"read_only": True}, "is_staff": {"read_only": True},
"is_superuser": {"read_only": True}, "is_superuser": {"read_only": True},
} }
class UserUpdatePassword(serializers.ModelSerializer):
old_password = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ("old_password", "password")
extra_kwargs = {
"password": {"write_only": True},
}
def validate_old_password(self, password: str):
if not self.instance.check_password(password):
raise serializers.ValidationError("Old password is incorrect")
return password
def update(self, instance, validated_data):
instance.set_password(validated_data["password"])
instance.save(update_fields=["password"])
return instance

View File

@ -1,30 +1,36 @@
from django.urls import path from django.urls import path
from .views import ( from .views import (
UserListViewSet, UserListAPIViewSet,
UserRetireUpdateSelfViewSet, UserRetireUpdateSelfAPIViewSet,
UserRetrieveIdViewSet, UserRetrieveAPIViewSet,
UserRetrieveViewSet, UserRetrieveIdAPIAPIView,
UserUpdatePasswordAPIView,
) )
app_name = "users_api" app_name = "users_api"
urlpatterns = [ urlpatterns = [
path("", UserListViewSet.as_view(), name="list"), path("", UserListAPIViewSet.as_view(), name="list"),
path( path(
"self/", "self/",
UserRetireUpdateSelfViewSet.as_view(), UserRetireUpdateSelfAPIViewSet.as_view(),
name="self", name="self",
), ),
path(
"self/password",
UserUpdatePasswordAPIView.as_view(),
name="password",
),
path( path(
"id/<int:pk>", "id/<int:pk>",
UserRetrieveIdViewSet.as_view(), UserRetrieveIdAPIAPIView.as_view(),
name="get_by_id", name="get_by_id",
), ),
path( path(
"<str:username>", "<str:username>",
UserRetrieveViewSet.as_view(), UserRetrieveAPIViewSet.as_view(),
name="get", name="get",
), ),
] ]

View File

@ -9,11 +9,12 @@
UserFullSerializer, UserFullSerializer,
UserPublicInfoSerializer, UserPublicInfoSerializer,
UserRegisterSerializer, UserRegisterSerializer,
UserUpdatePassword,
) )
from akarpov.users.models import User from akarpov.users.models import User
class UserRegisterViewSet(generics.CreateAPIView): class UserRegisterAPIViewSet(generics.CreateAPIView):
"""Creates new user and sends verification email""" """Creates new user and sends verification email"""
serializer_class = UserRegisterSerializer serializer_class = UserRegisterSerializer
@ -26,7 +27,7 @@ def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs) return self.create(request, *args, **kwargs)
class UserEmailValidationViewSet(views.APIView): class UserEmailValidationAPIViewSet(views.APIView):
"""Receives token from email and activates user""" """Receives token from email and activates user"""
permission_classes = [permissions.AllowAny] permission_classes = [permissions.AllowAny]
@ -43,7 +44,7 @@ def post(self, request):
return Response(status=status.HTTP_200_OK) return Response(status=status.HTTP_200_OK)
class UserListViewSet(generics.ListAPIView): class UserListAPIViewSet(generics.ListAPIView):
serializer_class = UserPublicInfoSerializer serializer_class = UserPublicInfoSerializer
pagination_class = SmallResultsSetPagination pagination_class = SmallResultsSetPagination
@ -54,7 +55,7 @@ def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs) return self.list(request, *args, **kwargs)
class UserRetrieveViewSet(generics.RetrieveAPIView): class UserRetrieveAPIViewSet(generics.RetrieveAPIView):
"""Returns user's instance on username""" """Returns user's instance on username"""
serializer_class = UserFullPublicInfoSerializer serializer_class = UserFullPublicInfoSerializer
@ -70,7 +71,7 @@ def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
class UserRetrieveIdViewSet(UserRetrieveViewSet): class UserRetrieveIdAPIAPIView(UserRetrieveAPIViewSet):
"""Returns user's instance on user's id""" """Returns user's instance on user's id"""
lookup_field = "pk" lookup_field = "pk"
@ -82,8 +83,15 @@ def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs) return self.retrieve(request, *args, **kwargs)
class UserRetireUpdateSelfViewSet(generics.RetrieveUpdateDestroyAPIView): class UserRetireUpdateSelfAPIViewSet(generics.RetrieveUpdateDestroyAPIView):
serializer_class = UserFullSerializer serializer_class = UserFullSerializer
def get_object(self): def get_object(self):
return self.request.user return self.request.user
class UserUpdatePasswordAPIView(generics.UpdateAPIView):
serializer_class = UserUpdatePassword
def get_object(self):
return self.request.user

View File

@ -0,0 +1,29 @@
from django.urls import reverse_lazy
from pytest_lambda import lambda_fixture, static_fixture
from rest_framework import status
class TestChangePassword:
url = static_fixture(reverse_lazy("api:users:password"))
new_password = static_fixture("P@ssw0rd123")
user = lambda_fixture(lambda user_factory: user_factory(password="P@ssw0rd"))
def test_ok(self, api_user_client, url, new_password, user):
response = api_user_client.put(
url, {"old_password": "P@ssw0rd", "password": new_password}
)
assert response.status_code == status.HTTP_200_OK
user.refresh_from_db()
assert user.check_password(new_password)
def test_return_err_if_data_is_invalid(
self, api_user_client, url, new_password, user
):
response = api_user_client.put(
url, {"old_password": "123456", "password": new_password}
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
user.refresh_from_db()
assert not user.check_password(new_password)

View File

@ -1,7 +1,7 @@
from django.urls import include, path from django.urls import include, path
from rest_framework.authtoken.views import obtain_auth_token from rest_framework.authtoken.views import obtain_auth_token
from akarpov.users.api.views import UserRegisterViewSet from akarpov.users.api.views import UserRegisterAPIViewSet
app_name = "api" app_name = "api"
@ -11,7 +11,9 @@
include( include(
[ [
path( path(
"register/", UserRegisterViewSet.as_view(), name="user_register_api" "register/",
UserRegisterAPIViewSet.as_view(),
name="user_register_api",
), ),
path("token/", obtain_auth_token), path("token/", obtain_auth_token),
] ]