def post(self, request): """ POST /api/user/v1/accounts/retire/ { 'username': '******' } Retires the user with the given username. This includes retiring this username, the associates email address, and any other PII associated with this user. """ username = request.data['username'] if is_username_retired(username): return Response(status=status.HTTP_404_NOT_FOUND) try: retirement_status = UserRetirementStatus.get_retirement_for_retirement_action(username) user = retirement_status.user retired_username = retirement_status.retired_username or get_retired_username_by_username(username) retired_email = retirement_status.retired_email or get_retired_email_by_email(user.email) original_email = retirement_status.original_email # Retire core user/profile information self.clear_pii_from_userprofile(user) self.delete_users_profile_images(user) self.delete_users_country_cache(user) # Retire data from Enterprise models self.retire_users_data_sharing_consent(username, retired_username) self.retire_sapsf_data_transmission(user) self.retire_user_from_pending_enterprise_customer_user(user, retired_email) self.retire_entitlement_support_detail(user) # Retire misc. models that may contain PII of this user SoftwareSecurePhotoVerification.retire_user(user.id) PendingEmailChange.delete_by_user_value(user, field='user') UserOrgTag.delete_by_user_value(user, field='user') # Retire any objects linked to the user via their original email CourseEnrollmentAllowed.delete_by_user_value(original_email, field='email') UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field='email') # TODO: Password Reset links - https://openedx.atlassian.net/browse/PLAT-2104 # TODO: Delete OAuth2 records - https://openedx.atlassian.net/browse/EDUCATOR-2703 user.first_name = '' user.last_name = '' user.is_active = False user.username = retired_username user.save() except UserRetirementStatus.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) except RetirementStateError as exc: return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) except Exception as exc: # pylint: disable=broad-except return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(status=status.HTTP_204_NO_CONTENT)
def post(self, request): """ POST /api/user/v1/accounts/retire/ { 'username': '******' } Retires the user with the given username. This includes retiring this username, the associated email address, and any other PII associated with this user. """ username = request.data['username'] try: retirement_status = UserRetirementStatus.get_retirement_for_retirement_action(username) user = retirement_status.user retired_username = retirement_status.retired_username or get_retired_username_by_username(username) retired_email = retirement_status.retired_email or get_retired_email_by_email(user.email) original_email = retirement_status.original_email # Retire core user/profile information self.clear_pii_from_userprofile(user) self.delete_users_profile_images(user) self.delete_users_country_cache(user) # Retire data from Enterprise models self.retire_users_data_sharing_consent(username, retired_username) self.retire_sapsf_data_transmission(user) self.retire_degreed_data_transmission(user) self.retire_user_from_pending_enterprise_customer_user(user, retired_email) self.retire_entitlement_support_detail(user) # Retire misc. models that may contain PII of this user PendingEmailChange.delete_by_user_value(user, field='user') UserOrgTag.delete_by_user_value(user, field='user') # Retire any objects linked to the user via their original email CourseEnrollmentAllowed.delete_by_user_value(original_email, field='email') UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field='email') # This signal allows code in higher points of LMS to retire the user as necessary USER_RETIRE_LMS_CRITICAL.send(sender=self.__class__, user=user) user.first_name = '' user.last_name = '' user.is_active = False user.username = retired_username user.save() except UserRetirementStatus.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) except RetirementStateError as exc: return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST) except Exception as exc: # pylint: disable=broad-except return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(status=status.HTTP_204_NO_CONTENT)
def change_email_request(request): ''' AJAX call from the profile page. User wants a new e-mail. ''' ## Make sure it checks for existing e-mail conflicts if not request.user.is_authenticated: raise Http404 user = request.user if not user.check_password(request.POST['password']): return HttpResponse(json.dumps({'success': False, 'error': 'Invalid password'})) new_email = request.POST['new_email'] try: validate_email(new_email) except ValidationError: return HttpResponse(json.dumps({'success': False, 'error': 'Valid e-mail address required.'})) if User.objects.filter(email=new_email).count() != 0: ## CRITICAL TODO: Handle case sensitivity for e-mails return HttpResponse(json.dumps({'success': False, 'error': 'An account with this e-mail already exists.'})) pec_list = PendingEmailChange.objects.filter(user=request.user) if len(pec_list) == 0: pec = PendingEmailChange() pec.user = user else: pec = pec_list[0] pec.new_email = request.POST['new_email'] pec.activation_key = uuid.uuid4().hex pec.save() if pec.new_email == user.email: pec.delete() return HttpResponse(json.dumps({'success': False, 'error': 'Old email is the same as the new email.'})) d = {'key': pec.activation_key, 'old_email': user.email, 'new_email': pec.new_email} subject = render_to_string('emails/email_change_subject.txt', d) subject = ''.join(subject.splitlines()) message = render_to_string('emails/email_change.txt', d) res = send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [pec.new_email]) return HttpResponse(json.dumps({'success': True}))
def change_email_request(request): ''' AJAX call from the profile page. User wants a new e-mail. ''' ## Make sure it checks for existing e-mail conflicts if not request.user.is_authenticated: raise Http404 user = request.user if not user.check_password(request.POST['password']): return HttpResponse(json.dumps({'success': False, 'error': 'Invalid password'})) new_email = request.POST['new_email'] try: validate_email(new_email) except ValidationError: return HttpResponse(json.dumps({'success': False, 'error': 'Valid e-mail address required.'})) if User.objects.filter(email=new_email).count() != 0: ## CRITICAL TODO: Handle case sensitivity for e-mails return HttpResponse(json.dumps({'success': False, 'error': 'An account with this e-mail already exists.'})) pec_list = PendingEmailChange.objects.filter(user=request.user) if len(pec_list) == 0: pec = PendingEmailChange() pec.user = user else: pec = pec_list[0] pec.new_email = request.POST['new_email'] pec.activation_key = uuid.uuid4().hex pec.save() if pec.new_email == user.email: pec.delete() return HttpResponse(json.dumps({'success': False, 'error': 'Old email is the same as the new email.'})) d = {'key': pec.activation_key, 'old_email': user.email, 'new_email': pec.new_email} subject = render_to_string('emails/email_change_subject.txt', d) subject = ''.join(subject.splitlines()) message = render_to_string('emails/email_change.txt', d) res = send_mail( subject, message, settings.DEFAULT_FROM_EMAIL, [pec.new_email]) return HttpResponse(json.dumps({'success': True}))
def do_email_change_request(user, new_email, activation_key=None): """ Given a new email for a user, does some basic verification of the new address and sends an activation message to the new address. If any issues are encountered with verification or sending the message, a ValueError will be thrown. """ pec_list = PendingEmailChange.objects.filter(user=user) if len(pec_list) == 0: pec = PendingEmailChange() pec.user = user else: pec = pec_list[0] # if activation_key is not passing as an argument, generate a random key if not activation_key: activation_key = uuid.uuid4().hex pec.new_email = new_email pec.activation_key = activation_key pec.save() context = { 'key': pec.activation_key, 'old_email': user.email, 'new_email': pec.new_email } subject = render_to_string('emails/email_change_subject.txt', context) subject = ''.join(subject.splitlines()) message = render_to_string('emails/email_change.txt', context) from_address = configuration_helpers.get_value('email_from_address', settings.DEFAULT_FROM_EMAIL) try: mail.send_mail(subject, message, from_address, [pec.new_email]) except Exception: log.error(u'Unable to send email activation link to user from "%s"', from_address, exc_info=True) raise ValueError( _('Unable to send email activation link. Please try again later.')) # When the email address change is complete, a "edx.user.settings.changed" event will be emitted. # But because changing the email address is multi-step, we also emit an event here so that we can # track where the request was initiated. tracker.emit( SETTING_CHANGE_INITIATED, { "setting": "email", "old": context['old_email'], "new": context['new_email'], "user_id": user.id, })
def do_email_change_request(user, new_email, activation_key=None): """ Given a new email for a user, does some basic verification of the new address and sends an activation message to the new address. If any issues are encountered with verification or sending the message, a ValueError will be thrown. """ pec_list = PendingEmailChange.objects.filter(user=user) if len(pec_list) == 0: pec = PendingEmailChange() pec.user = user else: pec = pec_list[0] # if activation_key is not passing as an argument, generate a random key if not activation_key: activation_key = uuid.uuid4().hex pec.new_email = new_email pec.activation_key = activation_key pec.save() context = { 'key': pec.activation_key, 'old_email': user.email, 'new_email': pec.new_email } subject = render_to_string('emails/email_change_subject.txt', context) subject = ''.join(subject.splitlines()) message = render_to_string('emails/email_change.txt', context) from_address = configuration_helpers.get_value( 'email_from_address', settings.DEFAULT_FROM_EMAIL ) try: mail.send_mail(subject, message, from_address, [pec.new_email]) except Exception: log.error(u'Unable to send email activation link to user from "%s"', from_address, exc_info=True) raise ValueError(_('Unable to send email activation link. Please try again later.')) # When the email address change is complete, a "edx.user.settings.changed" event will be emitted. # But because changing the email address is multi-step, we also emit an event here so that we can # track where the request was initiated. tracker.emit( SETTING_CHANGE_INITIATED, { "setting": "email", "old": context['old_email'], "new": context['new_email'], "user_id": user.id, } )
def test_delete_by_user_no_effect_for_user_with_no_email_change(self): record_was_deleted = PendingEmailChange.delete_by_user_value( self.user2, field='user') self.assertFalse(record_was_deleted) self.assertEqual(1, len(PendingEmailChange.objects.all()))
def test_delete_by_user_removes_pending_email_change(self): record_was_deleted = PendingEmailChange.delete_by_user_value( self.user, field='user') self.assertTrue(record_was_deleted) self.assertEqual(0, len(PendingEmailChange.objects.all()))
def do_email_change_request(user, new_email, activation_key=None): """ Given a new email for a user, does some basic verification of the new address and sends an activation message to the new address. If any issues are encountered with verification or sending the message, a ValueError will be thrown. """ pec_list = PendingEmailChange.objects.filter(user=user) if len(pec_list) == 0: pec = PendingEmailChange() pec.user = user else: pec = pec_list[0] # if activation_key is not passing as an argument, generate a random key if not activation_key: activation_key = uuid.uuid4().hex pec.new_email = new_email pec.activation_key = activation_key pec.save() use_https = theming_helpers.get_current_request().is_secure() site = Site.objects.get_current() message_context = get_base_template_context(site) message_context.update({ 'old_email': user.email, 'new_email': pec.new_email, 'confirm_link': '{protocol}://{site}{link}'.format( protocol='https' if use_https else 'http', site=configuration_helpers.get_value('SITE_NAME', settings.SITE_NAME), link=reverse('confirm_email_change', kwargs={ 'key': pec.activation_key, }), ), }) msg = EmailChange().personalize( recipient=Recipient(user.username, pec.new_email), language=preferences_api.get_user_preference(user, LANGUAGE_KEY), user_context=message_context, ) try: ace.send(msg) except Exception: from_address = configuration_helpers.get_value( 'email_from_address', settings.DEFAULT_FROM_EMAIL) log.error(u'Unable to send email activation link to user from "%s"', from_address, exc_info=True) raise ValueError( _('Unable to send email activation link. Please try again later.')) # When the email address change is complete, a "edx.user.settings.changed" event will be emitted. # But because changing the email address is multi-step, we also emit an event here so that we can # track where the request was initiated. tracker.emit( SETTING_CHANGE_INITIATED, { "setting": "email", "old": message_context['old_email'], "new": message_context['new_email'], "user_id": user.id, })
def test_delete_by_user_no_effect_for_user_with_no_email_change(self): record_was_deleted = PendingEmailChange.delete_by_user_value(self.user2, field='user') self.assertFalse(record_was_deleted) self.assertEqual(1, len(PendingEmailChange.objects.all()))
def test_delete_by_user_removes_pending_email_change(self): record_was_deleted = PendingEmailChange.delete_by_user_value(self.user, field='user') self.assertTrue(record_was_deleted) self.assertEqual(0, len(PendingEmailChange.objects.all()))