Loading django/contrib/auth/base_user.py +7 −1 Original line number Diff line number Diff line Loading @@ -4,6 +4,8 @@ not in INSTALLED_APPS. """ from __future__ import unicode_literals import unicodedata from django.contrib.auth import password_validation from django.contrib.auth.hashers import ( check_password, is_password_usable, make_password, Loading @@ -11,7 +13,7 @@ from django.contrib.auth.hashers import ( from django.db import models from django.utils.crypto import get_random_string, salted_hmac from django.utils.deprecation import CallableFalse, CallableTrue from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ Loading @@ -31,6 +33,10 @@ class BaseUserManager(models.Manager): email = '@'.join([email_name, domain_part.lower()]) return email @classmethod def normalize_username(cls, username): return unicodedata.normalize('NFKC', force_text(username)) def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyz' 'ABCDEFGHJKLMNPQRSTUVWXYZ' Loading django/contrib/auth/forms.py +10 −1 Original line number Diff line number Diff line from __future__ import unicode_literals import unicodedata from django import forms from django.contrib.auth import ( authenticate, get_user_model, password_validation, Loading Loading @@ -60,6 +62,11 @@ class ReadOnlyPasswordHashField(forms.Field): return False class UsernameField(forms.CharField): def to_python(self, value): return unicodedata.normalize('NFKC', super(UsernameField, self).to_python(value)) class UserCreationForm(forms.ModelForm): """ A form that creates a user, with no privileges, from the given username and Loading @@ -83,6 +90,7 @@ class UserCreationForm(forms.ModelForm): class Meta: model = User fields = ("username",) field_classes = {'username': UsernameField} def __init__(self, *args, **kwargs): super(UserCreationForm, self).__init__(*args, **kwargs) Loading Loading @@ -121,6 +129,7 @@ class UserChangeForm(forms.ModelForm): class Meta: model = User fields = '__all__' field_classes = {'username': UsernameField} def __init__(self, *args, **kwargs): super(UserChangeForm, self).__init__(*args, **kwargs) Loading @@ -140,7 +149,7 @@ class AuthenticationForm(forms.Form): Base class for authenticating users. Extend this to get a form that accepts username/password logins. """ username = forms.CharField( username = UsernameField( max_length=254, widget=forms.TextInput(attrs={'autofocus': ''}), ) Loading django/contrib/auth/models.py +1 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,7 @@ class UserManager(BaseUserManager): if not username: raise ValueError('The given username must be set') email = self.normalize_email(email) username = self.normalize_username(username) user = self.model(username=username, email=email, **extra_fields) user.set_password(password) user.save(using=self._db) Loading docs/topics/auth/customizing.txt +8 −0 Original line number Diff line number Diff line Loading @@ -726,6 +726,14 @@ utility methods: Normalizes email addresses by lowercasing the domain portion of the email address. .. classmethod:: models.BaseUserManager.normalize_username(email) .. versionadded:: 1.10 Applies NFKC Unicode normalization to usernames so that visually identical characters with different Unicode code points are considered identical. .. method:: models.BaseUserManager.get_by_natural_key(username) Retrieves a user instance using the contents of the field Loading tests/auth_tests/test_basic.py +7 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ from django.apps import apps from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser, User from django.core.exceptions import ImproperlyConfigured from django.db import IntegrityError from django.dispatch import receiver from django.test import TestCase, override_settings from django.test.signals import setting_changed Loading Loading @@ -60,6 +61,12 @@ class BasicTestCase(TestCase): def test_unicode_username(self): User.objects.create_user('jörg') User.objects.create_user('Григорий') # Two equivalent unicode normalized usernames should be duplicates omega_username = 'iamtheΩ' # U+03A9 GREEK CAPITAL LETTER OMEGA ohm_username = 'iamtheΩ' # U+2126 OHM SIGN User.objects.create_user(ohm_username) with self.assertRaises(IntegrityError): User.objects.create_user(omega_username) def test_is_anonymous_authenticated_method_deprecation(self): deprecation_message = ( Loading Loading
django/contrib/auth/base_user.py +7 −1 Original line number Diff line number Diff line Loading @@ -4,6 +4,8 @@ not in INSTALLED_APPS. """ from __future__ import unicode_literals import unicodedata from django.contrib.auth import password_validation from django.contrib.auth.hashers import ( check_password, is_password_usable, make_password, Loading @@ -11,7 +13,7 @@ from django.contrib.auth.hashers import ( from django.db import models from django.utils.crypto import get_random_string, salted_hmac from django.utils.deprecation import CallableFalse, CallableTrue from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ Loading @@ -31,6 +33,10 @@ class BaseUserManager(models.Manager): email = '@'.join([email_name, domain_part.lower()]) return email @classmethod def normalize_username(cls, username): return unicodedata.normalize('NFKC', force_text(username)) def make_random_password(self, length=10, allowed_chars='abcdefghjkmnpqrstuvwxyz' 'ABCDEFGHJKLMNPQRSTUVWXYZ' Loading
django/contrib/auth/forms.py +10 −1 Original line number Diff line number Diff line from __future__ import unicode_literals import unicodedata from django import forms from django.contrib.auth import ( authenticate, get_user_model, password_validation, Loading Loading @@ -60,6 +62,11 @@ class ReadOnlyPasswordHashField(forms.Field): return False class UsernameField(forms.CharField): def to_python(self, value): return unicodedata.normalize('NFKC', super(UsernameField, self).to_python(value)) class UserCreationForm(forms.ModelForm): """ A form that creates a user, with no privileges, from the given username and Loading @@ -83,6 +90,7 @@ class UserCreationForm(forms.ModelForm): class Meta: model = User fields = ("username",) field_classes = {'username': UsernameField} def __init__(self, *args, **kwargs): super(UserCreationForm, self).__init__(*args, **kwargs) Loading Loading @@ -121,6 +129,7 @@ class UserChangeForm(forms.ModelForm): class Meta: model = User fields = '__all__' field_classes = {'username': UsernameField} def __init__(self, *args, **kwargs): super(UserChangeForm, self).__init__(*args, **kwargs) Loading @@ -140,7 +149,7 @@ class AuthenticationForm(forms.Form): Base class for authenticating users. Extend this to get a form that accepts username/password logins. """ username = forms.CharField( username = UsernameField( max_length=254, widget=forms.TextInput(attrs={'autofocus': ''}), ) Loading
django/contrib/auth/models.py +1 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,7 @@ class UserManager(BaseUserManager): if not username: raise ValueError('The given username must be set') email = self.normalize_email(email) username = self.normalize_username(username) user = self.model(username=username, email=email, **extra_fields) user.set_password(password) user.save(using=self._db) Loading
docs/topics/auth/customizing.txt +8 −0 Original line number Diff line number Diff line Loading @@ -726,6 +726,14 @@ utility methods: Normalizes email addresses by lowercasing the domain portion of the email address. .. classmethod:: models.BaseUserManager.normalize_username(email) .. versionadded:: 1.10 Applies NFKC Unicode normalization to usernames so that visually identical characters with different Unicode code points are considered identical. .. method:: models.BaseUserManager.get_by_natural_key(username) Retrieves a user instance using the contents of the field Loading
tests/auth_tests/test_basic.py +7 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ from django.apps import apps from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser, User from django.core.exceptions import ImproperlyConfigured from django.db import IntegrityError from django.dispatch import receiver from django.test import TestCase, override_settings from django.test.signals import setting_changed Loading Loading @@ -60,6 +61,12 @@ class BasicTestCase(TestCase): def test_unicode_username(self): User.objects.create_user('jörg') User.objects.create_user('Григорий') # Two equivalent unicode normalized usernames should be duplicates omega_username = 'iamtheΩ' # U+03A9 GREEK CAPITAL LETTER OMEGA ohm_username = 'iamtheΩ' # U+2126 OHM SIGN User.objects.create_user(ohm_username) with self.assertRaises(IntegrityError): User.objects.create_user(omega_username) def test_is_anonymous_authenticated_method_deprecation(self): deprecation_message = ( Loading