Cleaning up unicode waffle flag

This commit is contained in:
Dillon Dumesnil
2018-10-15 09:55:33 -04:00
parent 0fe187dfac
commit 0a0df72fc9
14 changed files with 53 additions and 101 deletions

View File

@@ -35,7 +35,6 @@ from lms.djangoapps.verify_student.utils import is_verification_expiring_soon, v
from openedx.core.djangoapps.certificates.api import certificates_viewable_for_course
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.theming import helpers as theming_helpers
from openedx.core.djangoapps.user_api.config.waffle import PASSWORD_UNICODE_NORMALIZE_FLAG
from openedx.core.djangoapps.theming.helpers import get_themes
from openedx.core.djangoapps.user_authn.utils import is_safe_login_or_logout_redirect
from student.models import (
@@ -414,8 +413,7 @@ def authenticate_new_user(request, username, password):
logged in until they close the browser. They can't log in again until they click
the activation link from the email.
"""
if PASSWORD_UNICODE_NORMALIZE_FLAG.is_enabled():
password = normalize_password(password)
password = normalize_password(password)
backend = load_backend(NEW_USER_AUTH_BACKEND)
user = backend.authenticate(request=request, username=username, password=password)
user.backend = NEW_USER_AUTH_BACKEND
@@ -614,9 +612,7 @@ def do_create_account(form, custom_form=None):
email=form.cleaned_data["email"],
is_active=False
)
password = form.cleaned_data["password"]
if PASSWORD_UNICODE_NORMALIZE_FLAG.is_enabled():
password = normalize_password(password)
password = normalize_password(form.cleaned_data["password"])
user.set_password(password)
registration = Registration()

View File

@@ -144,7 +144,8 @@ class TestPasswordPolicy(TestCase):
create_validator_config('util.password_policy_validators.NumericValidator', {'min_numeric': 3})
])
def test_not_enough_numeric_characters(self):
self.url_params['password'] = u'thishouldfail½2'
# The unicode ២ is the number 2 in Khmer and the ٧ is the Arabic-Indic number 7
self.url_params['password'] = u'thisShouldFail២٧'
response = self.client.post(self.url, self.url_params)
self.assertEqual(response.status_code, 400)
obj = json.loads(response.content)
@@ -157,8 +158,8 @@ class TestPasswordPolicy(TestCase):
create_validator_config('util.password_policy_validators.NumericValidator', {'min_numeric': 3})
])
def test_enough_numeric_characters(self):
# This unicode 1/2 should count as a numeric value here
self.url_params['password'] = u'thisShouldPass½33'
# The unicode ២ is the number 2 in Khmer
self.url_params['password'] = u'thisShouldPass33'
response = self.client.post(self.url, self.url_params)
self.assertEqual(response.status_code, 200)
obj = json.loads(response.content)
@@ -260,7 +261,7 @@ class TestPasswordPolicy(TestCase):
"""
Tests that even if password policy is enforced, ext_auth registrations aren't subject to it
"""
self.url_params['password'] = 'aaa' # shouldn't pass validation
self.url_params['password'] = u'aaa' # shouldn't pass validation
request = self.request_factory.post(self.url, self.url_params)
request.site = SiteFactory.create()
# now indicate we are doing ext_auth by setting 'ExternalAuthMap' in the session.

View File

@@ -25,9 +25,7 @@ from provider.oauth2 import models as dop_models
from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.user_api.models import UserRetirementRequest
from openedx.core.djangoapps.user_api.config.waffle import (
PASSWORD_UNICODE_NORMALIZE_FLAG, PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle
)
from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
from student.tests.factories import UserFactory
from student.tests.test_email import mock_render_to_string
@@ -360,21 +358,20 @@ class ResetPasswordTests(EventTestMixin, CacheIsolationTestCase):
method of NFKC.
In this test, the input password is u'p\u212bssword'. It should be normalized to u'p\xc5ssword'
"""
with PASSWORD_UNICODE_NORMALIZE_FLAG.override(active=True):
url = reverse(
"password_reset_confirm",
kwargs={"uidb36": self.uidb36, "token": self.token}
)
url = reverse(
"password_reset_confirm",
kwargs={"uidb36": self.uidb36, "token": self.token}
)
password = u'p\u212bssword'
request_params = {'new_password1': password, 'new_password2': password}
confirm_request = self.request_factory.post(url, data=request_params)
response = password_reset_confirm_wrapper(confirm_request, self.uidb36, self.token)
password = u'p\u212bssword'
request_params = {'new_password1': password, 'new_password2': password}
confirm_request = self.request_factory.post(url, data=request_params)
response = password_reset_confirm_wrapper(confirm_request, self.uidb36, self.token)
user = User.objects.get(pk=self.user.pk)
salt_val = user.password.split('$')[1]
expected_user_password = make_password(unicodedata.normalize('NFKC', u'p\u212bssword'), salt_val)
self.assertEqual(expected_user_password, user.password)
user = User.objects.get(pk=self.user.pk)
salt_val = user.password.split('$')[1]
expected_user_password = make_password(unicodedata.normalize('NFKC', u'p\u212bssword'), salt_val)
self.assertEqual(expected_user_password, user.password)
@override_settings(AUTH_PASSWORD_VALIDATORS=[
create_validator_config('util.password_policy_validators.MinimumLengthValidator', {'min_length': 2}),

View File

@@ -57,9 +57,7 @@ from openedx.core.djangoapps.programs.models import ProgramsApiConfig
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.theming import helpers as theming_helpers
from openedx.core.djangoapps.theming.helpers import get_current_site
from openedx.core.djangoapps.user_api.config.waffle import (
PASSWORD_UNICODE_NORMALIZE_FLAG, PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle
)
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 UserNotFound, UserAPIInternalError
from openedx.core.djangoapps.user_api.models import UserRetirementRequest
from openedx.core.djangoapps.user_api.preferences import api as preferences_api
@@ -829,16 +827,15 @@ def password_reset_confirm_wrapper(request, uidb36=None, token=None):
)
if request.method == 'POST':
if PASSWORD_UNICODE_NORMALIZE_FLAG.is_enabled():
# 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'])
# 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']

View File

@@ -759,11 +759,11 @@ class IntegrationTest(testutil.TestCase, test.TestCase, HelperMixin):
def test_first_party_auth_trumps_third_party_auth_and_fails_when_credentials_bad(self):
self.assert_first_party_auth_trumps_third_party_auth(
email='user@example.com', password='password', success=False)
email='user@example.com', password=u'password', success=False)
def test_first_party_auth_trumps_third_party_auth_and_succeeds_when_credentials_good(self):
self.assert_first_party_auth_trumps_third_party_auth(
email='user@example.com', password='password', success=True)
email='user@example.com', password=u'password', success=True)
def test_full_pipeline_succeeds_registering_new_account(self):
# First, create, the request and strategy that store pipeline state.

View File

@@ -14,7 +14,6 @@ from django.contrib.auth.password_validation import (
)
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _, ungettext
from openedx.core.djangoapps.user_api.config.waffle import PASSWORD_UNICODE_NORMALIZE_FLAG
from six import text_type
log = logging.getLogger(__name__)
@@ -124,16 +123,7 @@ def validate_password(password, user=None):
Raises:
ValidationError if any of the password validators fail.
"""
if not isinstance(password, text_type):
try:
# some checks rely on unicode semantics (e.g. length)
password = text_type(password, encoding='utf8')
except UnicodeDecodeError:
# no reason to get into weeds
raise ValidationError([_('Invalid password.')])
if PASSWORD_UNICODE_NORMALIZE_FLAG.is_enabled():
password = normalize_password(password)
password = normalize_password(password)
django_validate_password(password, user)

View File

@@ -9,15 +9,13 @@ from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.test.utils import override_settings
from openedx.core.djangoapps.user_api.config.waffle import PASSWORD_UNICODE_NORMALIZE_FLAG
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
from util.password_policy_validators import (
create_validator_config, validate_password, password_validators_instruction_texts,
)
@ddt
class PasswordPolicyValidatorsTestCase(CacheIsolationTestCase):
class PasswordPolicyValidatorsTestCase(unittest.TestCase):
"""
Tests for password validator utility functions
@@ -66,14 +64,11 @@ class PasswordPolicyValidatorsTestCase(CacheIsolationTestCase):
# s ̣ ̇ (s with combining dot below and combining dot above)
not_normalized_password = u'\u0073\u0323\u0307'
self.assertEqual(len(not_normalized_password), 3)
# When the flag is not set, the validation should succeed since len > 2
self.validation_errors_checker(not_normalized_password, None)
# When we normalize we expect the not_normalized password to fail
# because it should be normalized to u'\u1E69' -> ṩ
with PASSWORD_UNICODE_NORMALIZE_FLAG.override(active=True):
self.validation_errors_checker(not_normalized_password,
'This password is too short. It must contain at least 2 characters.')
self.validation_errors_checker(not_normalized_password,
'This password is too short. It must contain at least 2 characters.')
@data(
([create_validator_config('util.password_policy_validators.MinimumLengthValidator', {'min_length': 2})],