From 33264cab7edb9cbcea69ddb4b0cdb3fb233097fc Mon Sep 17 00:00:00 2001 From: Muhammad Faraz Maqsood Date: Tue, 16 Apr 2024 14:28:06 +0500 Subject: [PATCH] feat!: create change password API for drupal --- .../core/djangoapps/user_authn/serializers.py | 5 +++ .../core/djangoapps/user_authn/urls_common.py | 2 ++ .../user_authn/views/password_reset.py | 31 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/openedx/core/djangoapps/user_authn/serializers.py b/openedx/core/djangoapps/user_authn/serializers.py index 9bbed367e6e2..8e3a96dc4c96 100644 --- a/openedx/core/djangoapps/user_authn/serializers.py +++ b/openedx/core/djangoapps/user_authn/serializers.py @@ -80,3 +80,8 @@ class MFEContextSerializer(serializers.Serializer): 'extended_profile': [] } ) + + +class ChangePasswordSerializer(serializers.Serializer): + current_password = serializers.CharField(required=True) + new_password = serializers.CharField(required=True) diff --git a/openedx/core/djangoapps/user_authn/urls_common.py b/openedx/core/djangoapps/user_authn/urls_common.py index bb735f7b2973..41136b4b1817 100644 --- a/openedx/core/djangoapps/user_authn/urls_common.py +++ b/openedx/core/djangoapps/user_authn/urls_common.py @@ -74,6 +74,8 @@ # Password reset api views. path('password_reset/', password_reset.password_reset, name='password_reset'), + path('api/user/v1/account/change_password/', password_reset.ChangePasswordAPIView.as_view(), + name='user_change_password_api'), re_path( r'^password_reset_confirm/(?P[0-9A-Za-z]+)-(?P.+)/$', PasswordResetConfirmWrapper.as_view(), diff --git a/openedx/core/djangoapps/user_authn/views/password_reset.py b/openedx/core/djangoapps/user_authn/views/password_reset.py index 5cb0e7ac79f9..a71510a07cdc 100644 --- a/openedx/core/djangoapps/user_authn/views/password_reset.py +++ b/openedx/core/djangoapps/user_authn/views/password_reset.py @@ -23,8 +23,11 @@ from django.views.decorators.http import require_POST from edx_ace import ace from edx_ace.recipient import Recipient +from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication +from edx_rest_framework_extensions.auth.session.authentication import SessionAuthenticationAllowInactiveUser from eventtracking import tracker from django_ratelimit.decorators import ratelimit +from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.throttling import AnonRateThrottle from rest_framework.views import APIView @@ -36,6 +39,7 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.theming.helpers import get_current_request, get_current_site from openedx.core.djangoapps.user_api import accounts, errors, helpers +from openedx.core.djangoapps.user_authn.serializers import ChangePasswordSerializer from openedx.core.djangoapps.user_authn.toggles import should_redirect_to_authn_microfrontend from openedx.core.djangoapps.user_api.accounts.utils import is_secondary_email_feature_enabled from openedx.core.djangoapps.user_api.helpers import FormDescription @@ -44,6 +48,7 @@ from openedx.core.djangoapps.user_authn.message_types import PasswordReset, PasswordResetSuccess from openedx.core.djangoapps.user_authn.utils import check_pwned_password from openedx.core.djangolib.markup import HTML +from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser from common.djangoapps.student.forms import send_account_recovery_email_for_user from common.djangoapps.student.models import AccountRecovery, LoginFailures from common.djangoapps.util.json_request import JsonResponse @@ -244,6 +249,32 @@ def get(self, request): return HttpResponse(get_password_reset_form().to_json(), content_type="application/json") # lint-amnesty, pylint: disable=http-response-with-content-type-json +class ChangePasswordAPIView(APIView): + authentication_classes = ( + JwtAuthentication, BearerAuthenticationAllowInactiveUser, SessionAuthenticationAllowInactiveUser + ) + permission_classes = (IsAuthenticated,) + + def post(self, request): + serializer = ChangePasswordSerializer(data=request.data) + if serializer.is_valid(): + current_password = serializer.validated_data['current_password'] + new_password = serializer.validated_data['new_password'] + user = request.user + + # Check if the current password provided matches the user's actual password + if not user.check_password(current_password): + log.info(f"\nCurrent password is incorrect.") + return Response({"error": "Current password is incorrect."}, status=400) + + # Change the user's password + user.set_password(new_password) + user.save() + log.info(f"\nPassword changed successfully.") + return Response({"message": "Password changed successfully."}, status=200) + else: + return Response(serializer.errors, status=400) + @helpers.intercept_errors(errors.UserAPIInternalError, ignore_errors=[errors.UserAPIRequestError]) def request_password_change(email, is_secure): """Email a single-use link for performing a password reset.