From ea79fafb08a07d05dc90a9aae2f7f98184b6bd9f Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 6 Mar 2020 12:17:23 +0530 Subject: [PATCH 1/7] Handle unicodes in username + limit username length --- dynamicweb/settings/base.py | 1 + membership/models.py | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index c959c237..9d5cf2bb 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -761,6 +761,7 @@ OTP_VERIFY_ENDPOINT = env('OTP_VERIFY_ENDPOINT') FIRST_VM_ID_AFTER_EU_VAT = int_env('FIRST_VM_ID_AFTER_EU_VAT') PRE_EU_VAT_RATE = float(env('PRE_EU_VAT_RATE')) +MAX_USERNAME_LENGTH = int_env('MAX_USERNAME_LENGTH', 18) if DEBUG: from .local import * # flake8: noqa diff --git a/membership/models.py b/membership/models.py index 703b4800..ab098726 100644 --- a/membership/models.py +++ b/membership/models.py @@ -77,28 +77,54 @@ def get_first_and_last_name(full_name): return first_name, last_name +def limit_username_length(username): + """ + Limit the length of username before the addition of random numbers to + 18 characters + + :param username: the username to limit + :return: + """ + if len(username) > settings.MAX_USERNAME_LENGTH: + username = username[(len(username) - settings.MAX_USERNAME_LENGTH):] + return username.strip() + + def assign_username(user): if not user.username: ldap_manager = LdapManager() # Try to come up with a username first_name, last_name = get_first_and_last_name(user.name) - user.username = unicodedata.normalize('NFKD', first_name + last_name) + user.username = unicodedata.normalize('NFKD', first_name + last_name).encode('ascii', 'ignore') user.username = "".join([char for char in user.username if char.isalnum()]).lower() + if user.username.strip() == "": + try: + # the inferred username from name is empty, hence attempt + # inferring a username from email + logger.debug("Inferred username from name is empty. So, " + "inferring from email now.") + user.username = user.email[0:user.email.index("@")] + except Exception as ex: + logger.debug("Exception %s" % str(ex)) + user.username = get_random_string( + allowed_chars='abcdefghijklmnopqrstuvwxyz' + ) exist = True + user_username = limit_username_length(user.username) while exist: # Check if it exists - exist, entries = ldap_manager.check_user_exists(user.username) + exist, entries = ldap_manager.check_user_exists(user_username) if exist: # If username exists in ldap, come up with a new user name and check it again - user.username = user.username + str(random.randint(0, 2 ** 10)) + user.username = user_username + str(random.randint(0, 2 ** 10)) else: # If username does not exists in ldap, try to save it in database try: user.save() except IntegrityError: # If username exists in database then come up with a new username - user.username = user.username + str(random.randint(0, 2 ** 10)) + user.username = user_username + str(random.randint(0, 2 ** 10)) exist = True From a995f418b29b0fda388c09dee50e3a7a5f6b2b73 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 6 Mar 2020 12:25:42 +0530 Subject: [PATCH 2/7] Remove any non alphanum character present in email for username --- membership/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/membership/models.py b/membership/models.py index ab098726..9a9dfd89 100644 --- a/membership/models.py +++ b/membership/models.py @@ -105,6 +105,9 @@ def assign_username(user): logger.debug("Inferred username from name is empty. So, " "inferring from email now.") user.username = user.email[0:user.email.index("@")] + user.username = "".join( + [char for char in user.username if char.isalnum()] + ).lower() except Exception as ex: logger.debug("Exception %s" % str(ex)) user.username = get_random_string( From 4890c4956f97bec49239d821133bcb99eb21295a Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 6 Mar 2020 15:29:42 +0530 Subject: [PATCH 3/7] Increase MAX_USERNAME_LENGTH to 64 chars --- dynamicweb/settings/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 9d5cf2bb..735cd913 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -761,7 +761,7 @@ OTP_VERIFY_ENDPOINT = env('OTP_VERIFY_ENDPOINT') FIRST_VM_ID_AFTER_EU_VAT = int_env('FIRST_VM_ID_AFTER_EU_VAT') PRE_EU_VAT_RATE = float(env('PRE_EU_VAT_RATE')) -MAX_USERNAME_LENGTH = int_env('MAX_USERNAME_LENGTH', 18) +MAX_USERNAME_LENGTH = int_env('MAX_USERNAME_LENGTH', 64) if DEBUG: from .local import * # flake8: noqa From af70824d6a14a0a945519e7c01fdebbbeab785f7 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 6 Mar 2020 15:30:07 +0530 Subject: [PATCH 4/7] Increase username charfield to 120 chars --- membership/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/membership/models.py b/membership/models.py index 9a9dfd89..e5454839 100644 --- a/membership/models.py +++ b/membership/models.py @@ -145,7 +145,7 @@ class CustomUser(AbstractBaseUser, PermissionsMixin): site = models.ForeignKey(Site, default=1) name = models.CharField(max_length=50, validators=[validate_name]) email = models.EmailField(unique=True) - username = models.CharField(max_length=60, unique=True, null=True) + username = models.CharField(max_length=120, unique=True, null=True) validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0) in_ldap = models.BooleanField(default=False) # By default, we initialize the validation_slug with appropriate value From 2f16f2440ea2695c45e497f92fd4f20f5cc7e1ae Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 6 Mar 2020 15:47:25 +0530 Subject: [PATCH 5/7] Return default value when return value is None --- dynamicweb/settings/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 735cd913..ca8996d1 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -42,7 +42,7 @@ def int_env(val, default_value=0): "details: {}").format( val, str(e))) - return return_value + return return_value if return_value is not None else default_value BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) From 4441ba37ca400201b03533b7a203d2bb669dc9d5 Mon Sep 17 00:00:00 2001 From: PCoder Date: Fri, 6 Mar 2020 15:48:02 +0530 Subject: [PATCH 6/7] Add migration: membership/migrations/0012_auto_20200306_1016.py --- .../migrations/0012_auto_20200306_1016.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 membership/migrations/0012_auto_20200306_1016.py diff --git a/membership/migrations/0012_auto_20200306_1016.py b/membership/migrations/0012_auto_20200306_1016.py new file mode 100644 index 00000000..f38f0780 --- /dev/null +++ b/membership/migrations/0012_auto_20200306_1016.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2020-03-06 10:16 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('membership', '0011_auto_20191218_1050'), + ] + + operations = [ + migrations.AlterField( + model_name='customuser', + name='username', + field=models.CharField(max_length=120, null=True, unique=True), + ), + ] From e18188603a0b28932134c9b7244eb68790ca4e1c Mon Sep 17 00:00:00 2001 From: PCoder Date: Sat, 7 Mar 2020 11:13:57 +0530 Subject: [PATCH 7/7] Decode username back to string from bytes after encode --- membership/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/membership/models.py b/membership/models.py index e5454839..b572833d 100644 --- a/membership/models.py +++ b/membership/models.py @@ -96,7 +96,9 @@ def assign_username(user): # Try to come up with a username first_name, last_name = get_first_and_last_name(user.name) - user.username = unicodedata.normalize('NFKD', first_name + last_name).encode('ascii', 'ignore') + user.username = unicodedata.normalize( + 'NFKD', first_name + last_name + ).encode('ascii', 'ignore').decode('ascii', 'ignore') user.username = "".join([char for char in user.username if char.isalnum()]).lower() if user.username.strip() == "": try: