Disallow registration when the proposed email is half-retired

Our learner retirement implementation shall allow re-use of email
addresses, but we currently do not disallow re-use of emails for
learners whose retirement is still in-progress (i.e. their retirement
state is between PENDING and LMS_COMPLETE inclusive).

The time between a user initiating retirement, and the jenkins job
actually picking up the user and driving their account retirement might
be as long as 1 hour, so this is a serious concern.

Addresses EDUCATOR-2824.
This commit is contained in:
Troy Sankey
2018-05-04 13:36:52 -04:00
parent fca9ac10bc
commit e9276ba246
5 changed files with 49 additions and 3 deletions

View File

@@ -21,6 +21,7 @@ from util.password_policy_validators import validate_password
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.user_api import errors, accounts, forms, helpers
from openedx.core.djangoapps.user_api.accounts.utils import email_exists
from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle
from openedx.core.djangoapps.user_api.errors import (
AccountUpdateError,
@@ -692,7 +693,7 @@ def _validate_email_doesnt_exist(email):
:return: None
:raises: errors.AccountEmailAlreadyExists
"""
if email is not None and User.objects.filter(email=email).exists():
if email is not None and email_exists(email):
raise errors.AccountEmailAlreadyExists(_(accounts.EMAIL_CONFLICT_MSG).format(email_address=email))

View File

@@ -6,6 +6,7 @@ import re
import string
from urlparse import urlparse
from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
@@ -192,3 +193,29 @@ def generate_password(length=12, chars=string.letters + string.digits):
password += choice(string.letters)
password += ''.join([choice(chars) for _i in xrange(length - 2)])
return password
def email_exists(email):
"""
Check an email against the User and UserRetirementStatus models for
existence.
"""
exists = False
# Normal case, check users in the auth_user table.
if User.objects.filter(email=email).exists():
exists = True
else:
# Handle case where another user with the same email address has
# initiated retirement (account deletion), but they are still in
# the retirement queue in any state which is not COMPLETE.
UserRetirementStatus = apps.get_model('user_api', 'UserRetirementStatus')
try:
if (UserRetirementStatus.objects
.select_related('current_state')
.filter(original_email=email)
.exclude(current_state__state_name='COMPLETE')
.exists()):
exists = True
except UserRetirementStatus.DoesNotExist:
pass
return exists