diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d3e4c84c4add38bb762fc3fd6112d695b6169352..b8a5948a8f63b703f11be397ee1b09e80c0b25ba 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -18,6 +18,7 @@ Added * Create a reusable snippet for avatar content. * Allow to configure if additional field is required * Allow to configure description of additional fields +* Allow configuring regex for allowed usernames Changed ~~~~~~~ diff --git a/aleksis/core/preferences.py b/aleksis/core/preferences.py index c7ad53cec6e0d09406a02da80c43f72fc7865e30..352d5480d179189ad1c92ffd9d44c69fb63732af 100644 --- a/aleksis/core/preferences.py +++ b/aleksis/core/preferences.py @@ -279,6 +279,14 @@ class SignupEnabled(BooleanPreference): verbose_name = _("Enable signup") +@site_preferences_registry.register +class AllowedUsernameRegex(StringPreference): + section = auth + name = "allowed_username_regex" + default = ".+" + verbose_name = _("Regular expression for allowed usernames") + + @site_preferences_registry.register class InviteEnabled(BooleanPreference): section = auth diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py index 087762de88b667f90c942f3596a7df4991faf627..00c0a6605e5c58d0c8078150d567362a73611ee3 100644 --- a/aleksis/core/settings.py +++ b/aleksis/core/settings.py @@ -362,6 +362,9 @@ ACCOUNT_SIGNUP_EMAIL_ENTER_TWICE = True # Enforce uniqueness of email addresses ACCOUNT_UNIQUE_EMAIL = _settings.get("auth.login.registration.unique_email", True) +# Configurable username validators +ACCOUNT_USERNAME_VALIDATORS = "aleksis.core.util.auth_helpers.custom_username_validators" + # Configuration for django-invitations # Use custom account adapter diff --git a/aleksis/core/util/auth_helpers.py b/aleksis/core/util/auth_helpers.py index 056b5156da68233d3b3ee2378ccffdff95cfbcd6..6edfac83373882d077d0a588d822fd3a0d0cc9b4 100644 --- a/aleksis/core/util/auth_helpers.py +++ b/aleksis/core/util/auth_helpers.py @@ -3,6 +3,8 @@ from typing import Any, Optional from django.conf import settings +from django.contrib.auth.validators import ASCIIUsernameValidator +from django.core.validators import RegexValidator from django.http import HttpRequest from allauth.account.adapter import DefaultAccountAdapter @@ -16,6 +18,7 @@ from oauth2_provider.views.mixins import ( from oauthlib.common import Request as OauthlibRequest from .apps import AppConfig +from .core_helpers import get_site_preferences class OurSocialAccountAdapter(DefaultSocialAccountAdapter): @@ -134,3 +137,11 @@ class ClientProtectedResourceMixin(_ClientProtectedResourceMixin): required_scopes = set(self.get_scopes() or []) allowed_scopes = set(AppScopes().get_available_scopes(oauth_request.client) or []) return required_scopes.issubset(allowed_scopes) + + +def validate_username_preference_regex(value: str): + regex = get_site_preferences()["auth__allowed_username_regex"] + return RegexValidator(regex)(value) + + +custom_username_validators = [validate_username_preference_regex, ASCIIUsernameValidator()] diff --git a/docs/admin/22_registration.rst b/docs/admin/22_registration.rst index 2442b5ce6c59314928fb3f6504b2db51eaa02010..510500f2198ec6377c91d97bf1be903411f0daea 100644 --- a/docs/admin/22_registration.rst +++ b/docs/admin/22_registration.rst @@ -26,6 +26,16 @@ signup for everyone. A menu item will be added for public registration. .. warning:: Do not enable this feature unless you intend to run a public AlekSIS instance. +Before enabling registration, you should consider restricting allowed usernames. +By default, all ASCII characters are allowed in usernames. Often, it is advisable +to not allow special characters. This often depends on the systems that will be +linked to AlekSIS. + +To restrict usernames to a certain format, a regular expression can be defined +in the ``Regular expression for allowed usernames`` preference. For example, to +restrict the username to lower case letters and numbers, and beginning with a number, +the regex can be set to ``^[a-z][a-z0-9]+$`. + User invitations ~~~~~~~~~~~~~~~~