From 0a0df72fc9842008ccaa737bf49c51c6113faa7a Mon Sep 17 00:00:00 2001 From: Dillon Dumesnil Date: Mon, 15 Oct 2018 09:55:33 -0400 Subject: [PATCH] Cleaning up unicode waffle flag --- common/djangoapps/student/helpers.py | 8 ++--- .../student/tests/test_password_policy.py | 9 +++--- .../student/tests/test_reset_password.py | 29 ++++++++--------- common/djangoapps/student/views/management.py | 23 ++++++------- .../third_party_auth/tests/specs/base.py | 4 +-- .../util/password_policy_validators.py | 12 +------ .../tests/test_password_policy_validators.py | 11 ++----- .../core/djangoapps/user_api/accounts/api.py | 4 +-- .../user_api/accounts/tests/test_api.py | 2 -- .../core/djangoapps/user_api/config/waffle.py | 4 +-- .../core/djangoapps/user_authn/views/login.py | 5 +-- .../user_authn/views/tests/test_login.py | 32 ++++++------------- .../user_authn/views/tests/test_register.py | 7 ++-- .../user_authn/views/tests/test_views.py | 4 +-- 14 files changed, 53 insertions(+), 101 deletions(-) diff --git a/common/djangoapps/student/helpers.py b/common/djangoapps/student/helpers.py index 3a159dcd8c..aa4d6c796e 100644 --- a/common/djangoapps/student/helpers.py +++ b/common/djangoapps/student/helpers.py @@ -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() diff --git a/common/djangoapps/student/tests/test_password_policy.py b/common/djangoapps/student/tests/test_password_policy.py index 366fdf11ae..7b4d015165 100644 --- a/common/djangoapps/student/tests/test_password_policy.py +++ b/common/djangoapps/student/tests/test_password_policy.py @@ -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'thisShouldPass២33' 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. diff --git a/common/djangoapps/student/tests/test_reset_password.py b/common/djangoapps/student/tests/test_reset_password.py index 6601553c70..7f676f3a5a 100644 --- a/common/djangoapps/student/tests/test_reset_password.py +++ b/common/djangoapps/student/tests/test_reset_password.py @@ -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}), diff --git a/common/djangoapps/student/views/management.py b/common/djangoapps/student/views/management.py index 217ffec4a9..9044018684 100644 --- a/common/djangoapps/student/views/management.py +++ b/common/djangoapps/student/views/management.py @@ -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'] diff --git a/common/djangoapps/third_party_auth/tests/specs/base.py b/common/djangoapps/third_party_auth/tests/specs/base.py index dcc943515b..edd2eb7eb5 100644 --- a/common/djangoapps/third_party_auth/tests/specs/base.py +++ b/common/djangoapps/third_party_auth/tests/specs/base.py @@ -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. diff --git a/common/djangoapps/util/password_policy_validators.py b/common/djangoapps/util/password_policy_validators.py index 6369abb643..58238a99d1 100644 --- a/common/djangoapps/util/password_policy_validators.py +++ b/common/djangoapps/util/password_policy_validators.py @@ -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) diff --git a/common/djangoapps/util/tests/test_password_policy_validators.py b/common/djangoapps/util/tests/test_password_policy_validators.py index 9f451ac169..0ce7fe2c96 100644 --- a/common/djangoapps/util/tests/test_password_policy_validators.py +++ b/common/djangoapps/util/tests/test_password_policy_validators.py @@ -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})], diff --git a/openedx/core/djangoapps/user_api/accounts/api.py b/openedx/core/djangoapps/user_api/accounts/api.py index 907be5f815..42c02ba5cd 100644 --- a/openedx/core/djangoapps/user_api/accounts/api.py +++ b/openedx/core/djangoapps/user_api/accounts/api.py @@ -23,7 +23,6 @@ from util.password_policy_validators import validate_password, normalize_passwor 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.config.waffle import ( - PASSWORD_UNICODE_NORMALIZE_FLAG, PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle, @@ -336,8 +335,7 @@ def create_account(username, password, email): # Create the user account, setting them to "inactive" until they activate their account. user = User(username=username, email=email, is_active=False) - if PASSWORD_UNICODE_NORMALIZE_FLAG.is_enabled(): - password = normalize_password(password) + password = normalize_password(password) user.set_password(password) try: diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_api.py b/openedx/core/djangoapps/user_api/accounts/tests/test_api.py index fe7c8e2bfe..cf10013123 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_api.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_api.py @@ -41,7 +41,6 @@ from openedx.core.djangoapps.user_api.accounts.tests.testutils import ( VALID_USERNAMES_UNICODE ) from openedx.core.djangoapps.user_api.config.waffle import ( - PASSWORD_UNICODE_NORMALIZE_FLAG, PREVENT_AUTH_USER_WRITES, SYSTEM_MAINTENANCE_MSG, waffle @@ -361,7 +360,6 @@ class AccountSettingsOnCreationTest(TestCase): 'extended_profile': [], }) - @override_waffle_flag(PASSWORD_UNICODE_NORMALIZE_FLAG, active=True) def test_normalize_password(self): """ Test that unicode normalization on passwords is happening when a user is created. diff --git a/openedx/core/djangoapps/user_api/config/waffle.py b/openedx/core/djangoapps/user_api/config/waffle.py index 9292bb0143..fc1166bd41 100644 --- a/openedx/core/djangoapps/user_api/config/waffle.py +++ b/openedx/core/djangoapps/user_api/config/waffle.py @@ -5,12 +5,10 @@ from __future__ import absolute_import from django.utils.translation import ugettext_lazy as _ -from openedx.core.djangoapps.waffle_utils import WaffleSwitchNamespace, WaffleFlagNamespace, WaffleFlag +from openedx.core.djangoapps.waffle_utils import WaffleSwitchNamespace SYSTEM_MAINTENANCE_MSG = _(u'System maintenance in progress. Please try again later.') WAFFLE_NAMESPACE = u'user_api' -_WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(WAFFLE_NAMESPACE) -PASSWORD_UNICODE_NORMALIZE_FLAG = WaffleFlag(_WAFFLE_FLAG_NAMESPACE, u'password_unicode_normalize') # Switches PREVENT_AUTH_USER_WRITES = u'prevent_auth_user_writes' diff --git a/openedx/core/djangoapps/user_authn/views/login.py b/openedx/core/djangoapps/user_authn/views/login.py index 4dcc7e1697..a84ffd10a6 100644 --- a/openedx/core/djangoapps/user_authn/views/login.py +++ b/openedx/core/djangoapps/user_authn/views/login.py @@ -27,7 +27,6 @@ from openedx.core.djangoapps.external_auth.models import ExternalAuthMap from openedx.core.djangoapps.password_policy import compliance as password_policy_compliance from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.util.user_messages import PageLevelMessages -from openedx.core.djangoapps.user_api.config.waffle import PASSWORD_UNICODE_NORMALIZE_FLAG from student.models import ( LoginFailures, PasswordHistory, @@ -213,9 +212,7 @@ def _authenticate_first_party(request, unauthenticated_user): username = unauthenticated_user.username if unauthenticated_user else "" try: - password = request.POST['password'] - if PASSWORD_UNICODE_NORMALIZE_FLAG.is_enabled(): - password = normalize_password(password) + password = normalize_password(request.POST['password']) return authenticate( username=username, password=password, diff --git a/openedx/core/djangoapps/user_authn/views/tests/test_login.py b/openedx/core/djangoapps/user_authn/views/tests/test_login.py index d9584cadb3..bfb6db0069 100644 --- a/openedx/core/djangoapps/user_authn/views/tests/test_login.py +++ b/openedx/core/djangoapps/user_authn/views/tests/test_login.py @@ -23,11 +23,7 @@ from openedx.core.djangoapps.password_policy.compliance import ( NonCompliantPasswordException, NonCompliantPasswordWarning ) -from openedx.core.djangoapps.user_api.config.waffle import ( - PASSWORD_UNICODE_NORMALIZE_FLAG, - PREVENT_AUTH_USER_WRITES, - waffle -) +from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, waffle from openedx.core.djangoapps.user_authn.cookies import jwt_cookies from openedx.core.djangoapps.user_authn.tests.utils import setup_login_oauth_client from openedx.core.djangoapps.user_authn.waffle import JWT_COOKIES_FLAG @@ -491,31 +487,23 @@ class LoginTest(CacheIsolationTestCase): self.assertTrue(response_content.get('success')) @ddt.data( - ('test_password', 'test_password', True, True), + ('test_password', 'test_password', True), (unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), - unicodedata.normalize('NFKC', u'Ṗŕệṿïệẅ Ṯệẍt'), False, True), + unicodedata.normalize('NFKC', u'Ṗŕệṿïệẅ Ṯệẍt'), False), (unicodedata.normalize('NFKC', u'Ṗŕệṿïệẅ Ṯệẍt'), - unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), True, True), + unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), True), (unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), - unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), False, True), - ('test_password', 'test_password', True, False), - (unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), - unicodedata.normalize('NFKC', u'Ṗŕệṿïệẅ Ṯệẍt'), False, False), - (unicodedata.normalize('NFKC', u'Ṗŕệṿïệẅ Ṯệẍt'), - unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), False, False), - (unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), - unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), True, False), + unicodedata.normalize('NFKD', u'Ṗŕệṿïệẅ Ṯệẍt'), False), ) @ddt.unpack - def test_password_unicode_normalization_login(self, password, password_entered, login_success, waffle_flag): + def test_password_unicode_normalization_login(self, password, password_entered, login_success): """ Tests unicode normalization on user's passwords on login. """ - with PASSWORD_UNICODE_NORMALIZE_FLAG.override(active=waffle_flag): - self.user.set_password(password) - self.user.save() - response, _ = self._login_response(self.user.email, password_entered) - self._assert_response(response, success=login_success) + self.user.set_password(password) + self.user.save() + response, _ = self._login_response(self.user.email, password_entered) + self._assert_response(response, success=login_success) def _login_response(self, email, password, patched_audit_log=None, extra_post_params=None): """ diff --git a/openedx/core/djangoapps/user_authn/views/tests/test_register.py b/openedx/core/djangoapps/user_authn/views/tests/test_register.py index 1e7330ce75..749b76e8ec 100644 --- a/openedx/core/djangoapps/user_authn/views/tests/test_register.py +++ b/openedx/core/djangoapps/user_authn/views/tests/test_register.py @@ -31,9 +31,7 @@ from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin from openedx.core.djangoapps.user_api.accounts import ( USERNAME_BAD_LENGTH_MSG, USERNAME_INVALID_CHARS_ASCII, USERNAME_INVALID_CHARS_UNICODE ) -from openedx.core.djangoapps.user_api.config.waffle import ( - PASSWORD_UNICODE_NORMALIZE_FLAG, PREVENT_AUTH_USER_WRITES, waffle -) +from openedx.core.djangoapps.user_api.config.waffle import PREVENT_AUTH_USER_WRITES, waffle from openedx.core.djangoapps.user_api.preferences.api import get_user_preference from student.models import UserAttribute from student.tests.factories import UserFactory @@ -104,7 +102,7 @@ class TestCreateAccount(SiteMixin, TestCase): self.params = { "username": self.username, "email": "test@example.org", - "password": "testpass", + "password": u"testpass", "name": "Test User", "honor_code": "true", "terms_of_service": "true", @@ -135,7 +133,6 @@ class TestCreateAccount(SiteMixin, TestCase): user = User.objects.get(username=self.username) return user.profile - @override_waffle_flag(PASSWORD_UNICODE_NORMALIZE_FLAG, active=True) def test_create_account_and_normalize_password(self): """ Test that unicode normalization on passwords is happening when a user registers. diff --git a/openedx/core/djangoapps/user_authn/views/tests/test_views.py b/openedx/core/djangoapps/user_authn/views/tests/test_views.py index 6cf9cec630..5f1039c79d 100644 --- a/openedx/core/djangoapps/user_authn/views/tests/test_views.py +++ b/openedx/core/djangoapps/user_authn/views/tests/test_views.py @@ -58,7 +58,7 @@ class UserAccountUpdateTest(CacheIsolationTestCase, UrlResetMixin): USERNAME = u"heisenberg" ALTERNATE_USERNAME = u"walt" OLD_PASSWORD = u"ḅḷüëṡḳÿ" - NEW_PASSWORD = u"🄱🄸🄶🄱🄻🅄🄴" + NEW_PASSWORD = u"B🄸🄶B🄻🅄🄴" OLD_EMAIL = u"walter@graymattertech.com" NEW_EMAIL = u"walt@savewalterwhite.com" @@ -292,7 +292,7 @@ class LoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMixin, ModuleSto shard = 7 USERNAME = "bob" EMAIL = "bob@example.com" - PASSWORD = "password" + PASSWORD = u"password" URLCONF_MODULES = ['openedx.core.djangoapps.embargo']