Features: 1) Enforced password validation using Django's validate_password in password reset flows; 2) Added handling for ValidationError during password validation;
Fixes: 1) Removed redundant import for `ValidationError` from `rest_framework.exceptions`; 2) Fixed request data handling in `confirm_password_reset` to directly use incoming data instead of serialized data; Extra: 1) Minor adjustments to error messages for consistency; 2) Cleaned up unused variables in `confirm_password_reset`.
This commit is contained in:
parent
0db69018e2
commit
7b5585ea9f
2 changed files with 16 additions and 14 deletions
|
|
@ -3,7 +3,7 @@ from hmac import compare_digest
|
||||||
|
|
||||||
from django.contrib.auth.password_validation import validate_password
|
from django.contrib.auth.password_validation import validate_password
|
||||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||||
from django.core.exceptions import BadRequest, PermissionDenied
|
from django.core.exceptions import BadRequest, PermissionDenied, ValidationError
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.utils.http import urlsafe_base64_decode
|
from django.utils.http import urlsafe_base64_decode
|
||||||
|
|
@ -11,7 +11,6 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from graphene import UUID, Boolean, Field, List, String
|
from graphene import UUID, Boolean, Field, List, String
|
||||||
from graphene.types.generic import GenericScalar
|
from graphene.types.generic import GenericScalar
|
||||||
from graphene_file_upload.scalars import Upload
|
from graphene_file_upload.scalars import Upload
|
||||||
from rest_framework.exceptions import ValidationError
|
|
||||||
|
|
||||||
from core.graphene import BaseMutation
|
from core.graphene import BaseMutation
|
||||||
from core.utils.messages import permission_denied_message
|
from core.utils.messages import permission_denied_message
|
||||||
|
|
@ -123,8 +122,8 @@ class UpdateUser(BaseMutation):
|
||||||
password = kwargs.get("password", "")
|
password = kwargs.get("password", "")
|
||||||
confirm_password = kwargs.get("confirm_password", "")
|
confirm_password = kwargs.get("confirm_password", "")
|
||||||
|
|
||||||
if compare_digest(password.lower(), email.lower()):
|
if password:
|
||||||
raise BadRequest(_("password too weak"))
|
validate_password(password=password, user=user)
|
||||||
|
|
||||||
if not compare_digest(password, "") and compare_digest(password, confirm_password):
|
if not compare_digest(password, "") and compare_digest(password, confirm_password):
|
||||||
user.set_password(password)
|
user.set_password(password)
|
||||||
|
|
@ -314,13 +313,15 @@ class ConfirmResetPassword(BaseMutation):
|
||||||
if not password_reset_token.check_token(user, token):
|
if not password_reset_token.check_token(user, token):
|
||||||
raise BadRequest(_("token is invalid!"))
|
raise BadRequest(_("token is invalid!"))
|
||||||
|
|
||||||
|
validate_password(password=password, user=user)
|
||||||
|
|
||||||
user.set_password(password)
|
user.set_password(password)
|
||||||
|
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
return ConfirmResetPassword(success=True)
|
return ConfirmResetPassword(success=True)
|
||||||
|
|
||||||
except (TypeError, ValueError, OverflowError, User.DoesNotExist) as e:
|
except (TypeError, ValueError, OverflowError, ValidationError, User.DoesNotExist) as e:
|
||||||
raise BadRequest(_(f"something went wrong: {e!s}"))
|
raise BadRequest(_(f"something went wrong: {e!s}"))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ import traceback
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from secrets import compare_digest
|
from secrets import compare_digest
|
||||||
|
|
||||||
|
from django.contrib.auth.password_validation import validate_password
|
||||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.http import urlsafe_base64_decode
|
from django.utils.http import urlsafe_base64_decode
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
@ -20,7 +22,6 @@ from evibes.settings import DEBUG
|
||||||
from vibes_auth.docs.drf.viewsets import USER_SCHEMA
|
from vibes_auth.docs.drf.viewsets import USER_SCHEMA
|
||||||
from vibes_auth.models import User
|
from vibes_auth.models import User
|
||||||
from vibes_auth.serializers import (
|
from vibes_auth.serializers import (
|
||||||
ConfirmPasswordResetSerializer,
|
|
||||||
UserSerializer,
|
UserSerializer,
|
||||||
)
|
)
|
||||||
from vibes_auth.utils.emailing import send_reset_password_email_task
|
from vibes_auth.utils.emailing import send_reset_password_email_task
|
||||||
|
|
@ -65,32 +66,32 @@ class UserViewSet(
|
||||||
@action(detail=False, methods=["post"])
|
@action(detail=False, methods=["post"])
|
||||||
@method_decorator(ratelimit(key="ip", rate="2/h" if not DEBUG else "888/h"))
|
@method_decorator(ratelimit(key="ip", rate="2/h" if not DEBUG else "888/h"))
|
||||||
def confirm_password_reset(self, request, *args, **kwargs):
|
def confirm_password_reset(self, request, *args, **kwargs):
|
||||||
serializer_data = None
|
|
||||||
try:
|
try:
|
||||||
serializer_data = ConfirmPasswordResetSerializer(request.data).data
|
|
||||||
|
|
||||||
if not compare_digest(serializer_data.get("password"), serializer_data.get("confirm_password")):
|
if not compare_digest(request.data.get("password"), request.data.get("confirm_password")):
|
||||||
return Response(
|
return Response(
|
||||||
{"error": _("passwords do not match")},
|
{"error": _("passwords do not match")},
|
||||||
status=status.HTTP_400_BAD_REQUEST,
|
status=status.HTTP_400_BAD_REQUEST,
|
||||||
)
|
)
|
||||||
|
|
||||||
uuid = urlsafe_base64_decode(serializer_data.get("uidb64")).decode()
|
uuid = urlsafe_base64_decode(request.data.get("uidb64")).decode()
|
||||||
user = User.objects.get(pk=uuid)
|
user = User.objects.get(pk=uuid)
|
||||||
|
|
||||||
|
validate_password(password=request.data.get("password"), user=user)
|
||||||
|
|
||||||
password_reset_token = PasswordResetTokenGenerator()
|
password_reset_token = PasswordResetTokenGenerator()
|
||||||
if not password_reset_token.check_token(user, serializer_data.get("token")):
|
if not password_reset_token.check_token(user, request.data.get("token")):
|
||||||
return Response({"error": _("token is invalid!")}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({"error": _("token is invalid!")}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
user.set_password(serializer_data.get("password"))
|
user.set_password(request.data.get("password"))
|
||||||
user.save()
|
user.save()
|
||||||
return Response({"message": _("password reset successfully")}, status=status.HTTP_200_OK)
|
return Response({"message": _("password reset successfully")}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
except (TypeError, ValueError, OverflowError, User.DoesNotExist) as e:
|
except (TypeError, ValueError, OverflowError, ValidationError, User.DoesNotExist) as e:
|
||||||
data = {"error": str(e)}
|
data = {"error": str(e)}
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
data["detail"] = str(traceback.format_exc())
|
data["detail"] = str(traceback.format_exc())
|
||||||
data["received"] = str(serializer_data)
|
data["received"] = str(request.data)
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@method_decorator(ratelimit(key="ip", rate="3/h" if not DEBUG else "888/h"))
|
@method_decorator(ratelimit(key="ip", rate="3/h" if not DEBUG else "888/h"))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue