def test_retirement_request_preserved_upon_non_pending_status_delete(setup_retirement_states): # pylint: disable=unused-argument, redefined-outer-name """ Ensure that retirement request record is not deleted upon deletion of a non-PENDING retirement status. """ user = UserFactory() retirement_status = UserRetirementStatus.create_retirement(user) assert UserRetirementRequest.has_user_requested_retirement(user) non_pending = RetirementState.objects.all().order_by('state_execution_order')[1] retirement_status.current_state = non_pending retirement_status.delete() assert UserRetirementRequest.has_user_requested_retirement(user)
def test_retirement_request_deleted_upon_pending_status_delete( setup_retirement_states): # pylint: disable=unused-argument, redefined-outer-name """ Ensure that retirement request record is deleted upon deletion of a PENDING retirement status. """ user = UserFactory() retirement_status = UserRetirementStatus.create_retirement(user) assert UserRetirementRequest.has_user_requested_retirement(user) pending = RetirementState.objects.all().order_by( 'state_execution_order')[0] assert retirement_status.current_state == pending retirement_status.delete() assert not UserRetirementRequest.has_user_requested_retirement(user)
def dispatch(self, *args, **kwargs): self.uidb36 = kwargs.get('uidb36') self.token = kwargs.get('token') self.uidb64 = _uidb36_to_uidb64(self.uidb36) # User can not get this link unless account recovery feature is enabled. if 'is_account_recovery' in self.request.GET and not is_secondary_email_feature_enabled( ): raise Http404 response = self._set_user(self.request) if response: return response if UserRetirementRequest.has_user_requested_retirement(self.user): return self._handle_retired_user(self.request) if self.request.method == 'POST': # Get actual token from session before processing the POST request. # This is needed because django's post process is not called on password reset # post request and the correct token needs to be extracted from session. self.token = self._get_token_from_session(self.request) return self.post(self.request, *args, **kwargs) else: response = super().dispatch(self.request, uidb64=self.uidb64, token=self.token, extra_context=self.platform_name) if hasattr(response, 'context_data'): response_was_successful = response.context_data.get( 'validlink') if response_was_successful and not self.user.is_active: self.user.is_active = True self.user.save() return response
def test_retirement_request_created_upon_status(setup_retirement_states): # pylint: disable=unused-argument, redefined-outer-name """ Ensure that retirement request record is created upon retirement status creation. """ user = UserFactory() UserRetirementStatus.create_retirement(user) assert UserRetirementRequest.has_user_requested_retirement(user)
def test_retirement_request_create_success(): """ Ensure that retirement request record creation succeeds. """ user = UserFactory() UserRetirementRequest.create_retirement_request(user) assert UserRetirementRequest.has_user_requested_retirement(user)
def dispatch(self, *args, **kwargs): self.uidb36 = kwargs.get('uidb36') self.token = kwargs.get('token') self.uidb64 = _uidb36_to_uidb64(self.uidb36) # User can not get this link unless account recovery feature is enabled. if 'is_account_recovery' in self.request.GET and not is_secondary_email_feature_enabled( ): raise Http404 response = self._set_user(self.request) if response: return response if UserRetirementRequest.has_user_requested_retirement(self.user): return self._handle_retired_user(self.request) if self.request.method == 'POST': return self.post(self.request, *args, **kwargs) else: self._set_token_in_session(self.request, self.token) token = self.reset_url_token response = super(PasswordResetConfirmWrapper, self).dispatch(self.request, uidb64=self.uidb64, token=token, extra_context=self.platform_name) response_was_successful = response.context_data.get('validlink') if response_was_successful and not self.user.is_active: self.user.is_active = True self.user.save() return response
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}) user = User.objects.get(id=uid_int) if UserRetirementRequest.has_user_requested_retirement(user): 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") return Response({'is_valid': is_valid})
def password_reset_confirm_wrapper(request, uidb36=None, token=None): """ A wrapper around django.contrib.auth.views.password_reset_confirm. Needed because we want to set the user as active at this step. We also optionally do some additional password policy checks. """ # convert old-style base36-encoded user id to base64 uidb64 = uidb36_to_uidb64(uidb36) platform_name = { "platform_name": configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME) } try: uid_int = base36_to_int(uidb36) user = User.objects.get(id=uid_int) except (ValueError, User.DoesNotExist): # if there's any error getting a user, just let django's # password_reset_confirm function handle it. return password_reset_confirm(request, uidb64=uidb64, token=token, extra_context=platform_name) if UserRetirementRequest.has_user_requested_retirement(user): # Refuse to reset the password of any user that has requested retirement. context = { 'validlink': True, 'form': None, 'title': _('Password reset unsuccessful'), 'err_msg': _('Error in resetting your password.'), } context.update(platform_name) return TemplateResponse(request, 'registration/password_reset_confirm.html', context) if waffle().is_enabled(PREVENT_AUTH_USER_WRITES): context = { 'validlink': False, 'form': None, 'title': _('Password reset unsuccessful'), 'err_msg': SYSTEM_MAINTENANCE_MSG, } context.update(platform_name) return TemplateResponse(request, 'registration/password_reset_confirm.html', context) if request.method == 'POST': password = request.POST['new_password1'] try: validate_password(password, user=user) except ValidationError as err: # We have a password reset attempt which violates some security # policy, or any other validation. Use the existing Django template to communicate that # back to the user. context = { 'validlink': True, 'form': None, 'title': _('Password reset unsuccessful'), 'err_msg': err.message, } context.update(platform_name) return TemplateResponse( request, 'registration/password_reset_confirm.html', context) # remember what the old password hash is before we call down old_password_hash = user.password response = password_reset_confirm(request, uidb64=uidb64, token=token, extra_context=platform_name) # If password reset was unsuccessful a template response is returned (status_code 200). # Check if form is invalid then show an error to the user. # Note if password reset was successful we get response redirect (status_code 302). if response.status_code == 200: form_valid = response.context_data['form'].is_valid( ) if response.context_data['form'] else False if not form_valid: log.warning( u'Unable to reset password for user [%s] because form is not valid. ' u'A possible cause is that the user had an invalid reset token', user.username, ) response.context_data['err_msg'] = _( 'Error in resetting your password. Please try again.') return response # get the updated user updated_user = User.objects.get(id=uid_int) # did the password hash change, if so record it in the PasswordHistory if updated_user.password != old_password_hash: entry = PasswordHistory() entry.create(updated_user) else: response = password_reset_confirm(request, uidb64=uidb64, token=token, extra_context=platform_name) response_was_successful = response.context_data.get('validlink') if response_was_successful and not user.is_active: user.is_active = True user.save() return response
def password_reset_confirm_wrapper(request, uidb36=None, token=None): """ A wrapper around django.contrib.auth.views.password_reset_confirm. Needed because we want to set the user as active at this step. We also optionally do some additional password policy checks. """ # convert old-style base36-encoded user id to base64 uidb64 = uidb36_to_uidb64(uidb36) platform_name = { "platform_name": configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME) } # User can not get this link unless account recovery feature is enabled. if 'is_account_recovery' in request.GET and not is_secondary_email_feature_enabled(): raise Http404 try: uid_int = base36_to_int(uidb36) user = User.objects.get(id=uid_int) except (ValueError, User.DoesNotExist): # if there's any error getting a user, just let django's # password_reset_confirm function handle it. return password_reset_confirm( request, uidb64=uidb64, token=token, extra_context=platform_name ) if UserRetirementRequest.has_user_requested_retirement(user): # Refuse to reset the password of any user that has requested retirement. context = { 'validlink': True, 'form': None, 'title': _('Password reset unsuccessful'), 'err_msg': _('Error in resetting your password.'), } context.update(platform_name) return TemplateResponse( request, 'registration/password_reset_confirm.html', context ) if waffle().is_enabled(PREVENT_AUTH_USER_WRITES): context = { 'validlink': False, 'form': None, 'title': _('Password reset unsuccessful'), 'err_msg': SYSTEM_MAINTENANCE_MSG, } context.update(platform_name) return TemplateResponse( request, 'registration/password_reset_confirm.html', context ) if request.method == 'POST': # We have to make a copy of request.POST because it is a QueryDict object which is immutable until copied. # We have to use request.POST because the password_reset_confirm method takes in the request and a user's # password is set to the request.POST['new_password1'] field. We have to also normalize the new_password2 # field so it passes the equivalence check that new_password1 == new_password2 # In order to switch out of having to do this copy, we would want to move the normalize_password code into # a custom User model's set_password method to ensure it is always happening upon calling set_password. 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']) password = request.POST['new_password1'] try: validate_password(password, user=user) except ValidationError as err: # We have a password reset attempt which violates some security # policy, or any other validation. Use the existing Django template to communicate that # back to the user. context = { 'validlink': True, 'form': None, 'title': _('Password reset unsuccessful'), 'err_msg': ' '.join(err.messages), } context.update(platform_name) return TemplateResponse( request, 'registration/password_reset_confirm.html', context ) # remember what the old password hash is before we call down old_password_hash = user.password if 'is_account_recovery' in request.GET: response = password_reset_confirm( request, uidb64=uidb64, token=token, extra_context=platform_name, template_name='registration/password_reset_confirm.html', post_reset_redirect='signin_user', ) else: response = password_reset_confirm( request, uidb64=uidb64, token=token, extra_context=platform_name ) # If password reset was unsuccessful a template response is returned (status_code 200). # Check if form is invalid then show an error to the user. # Note if password reset was successful we get response redirect (status_code 302). if response.status_code == 200: form_valid = response.context_data['form'].is_valid() if response.context_data['form'] else False if not form_valid: log.warning( u'Unable to reset password for user [%s] because form is not valid. ' u'A possible cause is that the user had an invalid reset token', user.username, ) response.context_data['err_msg'] = _('Error in resetting your password. Please try again.') return response # get the updated user updated_user = User.objects.get(id=uid_int) if 'is_account_recovery' in request.GET: try: updated_user.email = updated_user.account_recovery.secondary_email updated_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": user.email, "new": updated_user.email, "user_id": updated_user.id, } ) except ObjectDoesNotExist: log.error( 'Account recovery process initiated without AccountRecovery instance for user {username}'.format( username=updated_user.username ) ) updated_user.save() if response.status_code == 302 and 'is_account_recovery' in request.GET: messages.success( request, HTML(_( '{html_start}Password Creation Complete{html_end}' 'Your password has been created. {bold_start}{email}{bold_end} is now your primary login email.' )).format( support_url=configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK), html_start=HTML('<p class="message-title">'), html_end=HTML('</p>'), bold_start=HTML('<b>'), bold_end=HTML('</b>'), email=updated_user.email, ), extra_tags='account-recovery aa-icon submission-success' ) else: response = password_reset_confirm( request, uidb64=uidb64, token=token, extra_context=platform_name ) response_was_successful = response.context_data.get('validlink') if response_was_successful and not user.is_active: user.is_active = True user.save() return response
def password_reset_confirm_wrapper(request, uidb36=None, token=None): """ A wrapper around django.contrib.auth.views.password_reset_confirm. Needed because we want to set the user as active at this step. We also optionally do some additional password policy checks. """ # convert old-style base36-encoded user id to base64 uidb64 = uidb36_to_uidb64(uidb36) platform_name = { "platform_name": configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME) } try: uid_int = base36_to_int(uidb36) user = User.objects.get(id=uid_int) except (ValueError, User.DoesNotExist): # if there's any error getting a user, just let django's # password_reset_confirm function handle it. return password_reset_confirm( request, uidb64=uidb64, token=token, extra_context=platform_name ) if UserRetirementRequest.has_user_requested_retirement(user): # Refuse to reset the password of any user that has requested retirement. context = { 'validlink': True, 'form': None, 'title': _('Password reset unsuccessful'), 'err_msg': _('Error in resetting your password.'), } context.update(platform_name) return TemplateResponse( request, 'registration/password_reset_confirm.html', context ) if waffle().is_enabled(PREVENT_AUTH_USER_WRITES): context = { 'validlink': False, 'form': None, 'title': _('Password reset unsuccessful'), 'err_msg': SYSTEM_MAINTENANCE_MSG, } context.update(platform_name) return TemplateResponse( request, 'registration/password_reset_confirm.html', context ) if request.method == 'POST': password = request.POST['new_password1'] try: validate_password(password, user=user) except ValidationError as err: # We have a password reset attempt which violates some security # policy, or any other validation. Use the existing Django template to communicate that # back to the user. context = { 'validlink': True, 'form': None, 'title': _('Password reset unsuccessful'), 'err_msg': err.message, } context.update(platform_name) return TemplateResponse( request, 'registration/password_reset_confirm.html', context ) # remember what the old password hash is before we call down old_password_hash = user.password response = password_reset_confirm( request, uidb64=uidb64, token=token, extra_context=platform_name ) # If password reset was unsuccessful a template response is returned (status_code 200). # Check if form is invalid then show an error to the user. # Note if password reset was successful we get response redirect (status_code 302). if response.status_code == 200: form_valid = response.context_data['form'].is_valid() if response.context_data['form'] else False if not form_valid: log.warning( u'Unable to reset password for user [%s] because form is not valid. ' u'A possible cause is that the user had an invalid reset token', user.username, ) response.context_data['err_msg'] = _('Error in resetting your password. Please try again.') return response # get the updated user updated_user = User.objects.get(id=uid_int) # did the password hash change, if so record it in the PasswordHistory if updated_user.password != old_password_hash: entry = PasswordHistory() entry.create(updated_user) else: response = password_reset_confirm( request, uidb64=uidb64, token=token, extra_context=platform_name ) response_was_successful = response.context_data.get('validlink') if response_was_successful and not user.is_active: user.is_active = True user.save() return response