converted logistration password reset views from function to DRF APIVIEW (#25582)
* converted logistration password reset views from function to DRF APIVIEW VAN-31
This commit is contained in:
@@ -65,13 +65,13 @@ urlpatterns = [
|
||||
url(r'^account/password$', password_reset.password_change_request_handler, name='password_change_request'),
|
||||
|
||||
# logistration MFE flow
|
||||
url(r'^user_api/v1/account/password_reset/token/validate/$', password_reset.password_reset_token_validate,
|
||||
url(r'^user_api/v1/account/password_reset/token/validate/$', password_reset.PasswordResetTokenValidation.as_view(),
|
||||
name="user_api_password_reset_token_validate"),
|
||||
|
||||
# logistration MFE reset flow
|
||||
url(
|
||||
r'^password/reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
|
||||
password_reset.password_reset_logistration,
|
||||
password_reset.LogistrationPasswordResetView.as_view(),
|
||||
name='logistration_password_reset',
|
||||
),
|
||||
]
|
||||
|
||||
@@ -25,6 +25,7 @@ from edx_ace import ace
|
||||
from edx_ace.recipient import Recipient
|
||||
from eventtracking import tracker
|
||||
from ratelimit.decorators import ratelimit
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from common.djangoapps.edxmako.shortcuts import render_to_string
|
||||
@@ -641,107 +642,106 @@ def password_change_request_handler(request):
|
||||
return HttpResponseBadRequest(_("No email address provided."))
|
||||
|
||||
|
||||
@require_POST
|
||||
@ensure_csrf_cookie
|
||||
def password_reset_token_validate(request):
|
||||
"""HTTP end-point to validate password reset token. """
|
||||
is_valid = False
|
||||
token = request.POST.get('token')
|
||||
try:
|
||||
token = token.split('-', 1)
|
||||
uid_int = base36_to_int(token[0])
|
||||
if request.user.is_authenticated and request.user.id != uid_int:
|
||||
return JsonResponse({'is_valid': is_valid})
|
||||
class PasswordResetTokenValidation(APIView):
|
||||
|
||||
user = User.objects.get(id=uid_int)
|
||||
if UserRetirementRequest.has_user_requested_retirement(user):
|
||||
return JsonResponse({'is_valid': is_valid})
|
||||
def post(self, request):
|
||||
""" HTTP end-point to validate password reset token. """
|
||||
is_valid = False
|
||||
token = request.data.get('token')
|
||||
try:
|
||||
token = token.split('-', 1)
|
||||
uid_int = base36_to_int(token[0])
|
||||
if request.user.is_authenticated and request.user.id != uid_int:
|
||||
return Response({'is_valid': is_valid})
|
||||
|
||||
is_valid = default_token_generator.check_token(user, token[1])
|
||||
if is_valid and not user.is_active:
|
||||
user.is_active = True
|
||||
user.save()
|
||||
except Exception: # pylint: disable=broad-except
|
||||
AUDIT_LOG.exception("Invalid password reset confirm token")
|
||||
user = User.objects.get(id=uid_int)
|
||||
if UserRetirementRequest.has_user_requested_retirement(user):
|
||||
return Response({'is_valid': is_valid})
|
||||
|
||||
return JsonResponse({'is_valid': is_valid})
|
||||
is_valid = default_token_generator.check_token(user, token[1])
|
||||
if is_valid and not user.is_active:
|
||||
user.is_active = True
|
||||
user.save()
|
||||
except Exception: # pylint: disable=broad-except
|
||||
AUDIT_LOG.exception("Invalid password reset confirm token")
|
||||
|
||||
return Response({'is_valid': is_valid})
|
||||
|
||||
|
||||
def _check_token_has_required_values(uidb36, token):
|
||||
"""
|
||||
Helper function to test that token
|
||||
string passed has the required kwargs needed
|
||||
to process token validation.
|
||||
"""
|
||||
class LogistrationPasswordResetView(APIView):
|
||||
|
||||
if not uidb36 or not token:
|
||||
return False, None
|
||||
try:
|
||||
uid_int = base36_to_int(uidb36)
|
||||
except ValueError:
|
||||
return False, None
|
||||
return True, uid_int
|
||||
def post(self, request, **kwargs):
|
||||
""" Reset learner password using passed token and new credentials """
|
||||
|
||||
reset_status = False
|
||||
uidb36 = kwargs.get('uidb36')
|
||||
token = kwargs.get('token')
|
||||
|
||||
@require_POST
|
||||
@ensure_csrf_cookie
|
||||
def password_reset_logistration(request, **kwargs):
|
||||
"""Reset learner password using passed token and new credentials"""
|
||||
has_required_values, uid_int = self._check_token_has_required_values(uidb36, token)
|
||||
if not has_required_values:
|
||||
AUDIT_LOG.exception("Invalid password reset confirm token")
|
||||
return Response({'reset_status': reset_status})
|
||||
|
||||
reset_status = False
|
||||
uidb36 = kwargs.get('uidb36')
|
||||
token = kwargs.get('token')
|
||||
request.data._mutable = True
|
||||
request.data['new_password1'] = normalize_password(request.data['new_password1'])
|
||||
request.data['new_password2'] = normalize_password(request.data['new_password2'])
|
||||
|
||||
has_required_values, uid_int = _check_token_has_required_values(uidb36, token)
|
||||
if not has_required_values:
|
||||
AUDIT_LOG.exception("Invalid password reset confirm token")
|
||||
return JsonResponse({'reset_status': reset_status})
|
||||
password = request.data['new_password1']
|
||||
try:
|
||||
user = User.objects.get(id=uid_int)
|
||||
if not default_token_generator.check_token(user, token):
|
||||
AUDIT_LOG.exception("Token validation failed")
|
||||
return Response({'reset_status': reset_status})
|
||||
|
||||
request.POST = request.POST.copy()
|
||||
request.POST['new_password1'] = normalize_password(request.POST['new_password1'])
|
||||
request.POST['new_password2'] = normalize_password(request.POST['new_password2'])
|
||||
validate_password(password, user=user)
|
||||
form = SetPasswordForm(user, request.data)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
reset_status = True
|
||||
|
||||
password = request.POST['new_password1']
|
||||
try:
|
||||
user = User.objects.get(id=uid_int)
|
||||
if not default_token_generator.check_token(user, token):
|
||||
AUDIT_LOG.exception("Token validation failed")
|
||||
return JsonResponse({'reset_status': reset_status})
|
||||
if 'is_account_recovery' in request.GET:
|
||||
try:
|
||||
old_primary_email = user.email
|
||||
user.email = user.account_recovery.secondary_email
|
||||
user.account_recovery.delete()
|
||||
# emit an event that the user changed their secondary email to the primary email
|
||||
tracker.emit(
|
||||
SETTING_CHANGE_INITIATED,
|
||||
{
|
||||
"setting": "email",
|
||||
"old": old_primary_email,
|
||||
"new": user.email,
|
||||
"user_id": user.id,
|
||||
}
|
||||
)
|
||||
user.save()
|
||||
send_password_reset_success_email(user, request)
|
||||
except ObjectDoesNotExist:
|
||||
err = 'Account recovery process initiated without AccountRecovery instance for user {username}'
|
||||
log.error(err.format(username=user.username))
|
||||
except ValidationError as err:
|
||||
AUDIT_LOG.exception("Password validation failed")
|
||||
error_status = {
|
||||
'reset_status': reset_status,
|
||||
'err_msg': ' '.join(err.messages)
|
||||
}
|
||||
return Response(error_status)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
AUDIT_LOG.exception("Setting new password failed")
|
||||
|
||||
validate_password(password, user=user)
|
||||
form = SetPasswordForm(user, request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
reset_status = True
|
||||
return Response({'reset_status': reset_status})
|
||||
|
||||
if 'is_account_recovery' in request.GET:
|
||||
try:
|
||||
old_primary_email = user.email
|
||||
user.email = user.account_recovery.secondary_email
|
||||
user.account_recovery.delete()
|
||||
# emit an event that the user changed their secondary email to the primary email
|
||||
tracker.emit(
|
||||
SETTING_CHANGE_INITIATED,
|
||||
{
|
||||
"setting": "email",
|
||||
"old": old_primary_email,
|
||||
"new": user.email,
|
||||
"user_id": user.id,
|
||||
}
|
||||
)
|
||||
user.save()
|
||||
send_password_reset_success_email(user, request)
|
||||
except ObjectDoesNotExist:
|
||||
log.error('Account recovery process initiated without AccountRecovery instance for user {username}'
|
||||
.format(username=user.username))
|
||||
except ValidationError as err:
|
||||
AUDIT_LOG.exception("Password validation failed")
|
||||
error_status = {
|
||||
'reset_status': reset_status,
|
||||
'err_msg': ' '.join(err.messages)
|
||||
}
|
||||
return JsonResponse(error_status)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
AUDIT_LOG.exception("Setting new password failed")
|
||||
def _check_token_has_required_values(self, uidb36, token):
|
||||
"""
|
||||
Helper function to test that token
|
||||
string passed has the required kwargs needed
|
||||
to process token validation.
|
||||
"""
|
||||
|
||||
return JsonResponse({'reset_status': reset_status})
|
||||
if not uidb36 or not token:
|
||||
return False, None
|
||||
try:
|
||||
uid_int = base36_to_int(uidb36)
|
||||
except ValueError:
|
||||
return False, None
|
||||
return True, uid_int
|
||||
|
||||
@@ -34,7 +34,7 @@ from openedx.core.djangoapps.user_api.models import UserRetirementRequest
|
||||
from openedx.core.djangoapps.user_api.tests.test_views import UserAPITestCase
|
||||
from openedx.core.djangoapps.user_api.accounts import EMAIL_MAX_LENGTH, EMAIL_MIN_LENGTH
|
||||
from openedx.core.djangoapps.user_authn.views.password_reset import (
|
||||
SETTING_CHANGE_INITIATED, password_reset, password_reset_logistration,
|
||||
SETTING_CHANGE_INITIATED, password_reset, LogistrationPasswordResetView,
|
||||
PasswordResetConfirmWrapper)
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from common.djangoapps.student.tests.factories import TEST_PASSWORD, UserFactory
|
||||
@@ -754,7 +754,7 @@ class ResetPasswordAPITests(EventTestMixin, CacheIsolationTestCase):
|
||||
"logistration_password_reset",
|
||||
kwargs={"uidb36": uidb36, "token": token}
|
||||
) + query_param,
|
||||
request_param
|
||||
request_param, format='json'
|
||||
)
|
||||
return post_request
|
||||
|
||||
@@ -771,7 +771,8 @@ class ResetPasswordAPITests(EventTestMixin, CacheIsolationTestCase):
|
||||
|
||||
post_request = self.create_reset_request(uidb36, token, False)
|
||||
post_request.user = AnonymousUser()
|
||||
json_response = password_reset_logistration(post_request, uidb36=uidb36, token=token)
|
||||
reset_view = LogistrationPasswordResetView.as_view()
|
||||
json_response = reset_view(post_request, uidb36=uidb36, token=token).render()
|
||||
json_response = json.loads(json_response.content.decode('utf-8'))
|
||||
self.assertEqual(json_response.get('reset_status'), status)
|
||||
|
||||
@@ -784,7 +785,8 @@ class ResetPasswordAPITests(EventTestMixin, CacheIsolationTestCase):
|
||||
|
||||
post_request = self.create_reset_request(self.uidb36, self.token, False)
|
||||
post_request.user = AnonymousUser()
|
||||
self.assertRaises(Exception, password_reset_logistration(post_request, uidb36=uidb36, token=token))
|
||||
reset_view = LogistrationPasswordResetView.as_view()
|
||||
self.assertRaises(Exception, reset_view(post_request, uidb36=uidb36, token=token))
|
||||
|
||||
def test_password_mismatch_in_reset_request(self):
|
||||
"""
|
||||
@@ -792,7 +794,8 @@ class ResetPasswordAPITests(EventTestMixin, CacheIsolationTestCase):
|
||||
"""
|
||||
post_request = self.create_reset_request(self.uidb36, self.token, False, 'new_password2')
|
||||
post_request.user = AnonymousUser()
|
||||
json_response = password_reset_logistration(post_request, uidb36=self.uidb36, token=self.token)
|
||||
reset_view = LogistrationPasswordResetView.as_view()
|
||||
json_response = reset_view(post_request, uidb36=self.uidb36, token=self.token).render()
|
||||
json_response = json.loads(json_response.content.decode('utf-8'))
|
||||
self.assertFalse(json_response.get('reset_status'))
|
||||
|
||||
@@ -803,7 +806,8 @@ class ResetPasswordAPITests(EventTestMixin, CacheIsolationTestCase):
|
||||
"""
|
||||
post_request = self.create_reset_request(self.uidb36, self.token, True)
|
||||
post_request.user = AnonymousUser()
|
||||
password_reset_logistration(post_request, uidb36=self.uidb36, token=self.token)
|
||||
reset_view = LogistrationPasswordResetView.as_view()
|
||||
reset_view(post_request, uidb36=self.uidb36, token=self.token)
|
||||
|
||||
updated_user = User.objects.get(id=self.user.id)
|
||||
self.assertEqual(updated_user.email, self.secondary_email)
|
||||
@@ -824,7 +828,8 @@ class ResetPasswordAPITests(EventTestMixin, CacheIsolationTestCase):
|
||||
post_request = self.create_reset_request(self.uidb36, self.token, True)
|
||||
post_request.user = AnonymousUser()
|
||||
post_request.site = Mock(domain='example.com')
|
||||
password_reset_logistration(post_request, uidb36=self.uidb36, token=self.token)
|
||||
reset_view = LogistrationPasswordResetView.as_view()
|
||||
reset_view(post_request, uidb36=self.uidb36, token=self.token)
|
||||
updated_user = User.objects.get(id=self.user.id)
|
||||
|
||||
from_email = configuration_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL)
|
||||
|
||||
Reference in New Issue
Block a user