🐛(back) allow ASCII characters in user sub field
All ASCII characters are allowed in a sub, we change the sub validator to reflect this.
This commit is contained in:
@@ -25,6 +25,7 @@ and this project adheres to
|
|||||||
- #1270
|
- #1270
|
||||||
- #1282
|
- #1282
|
||||||
- ♻️(backend) fallback to email identifier when no name #1298
|
- ♻️(backend) fallback to email identifier when no name #1298
|
||||||
|
- 🐛(backend) allow ASCII characters in user sub field #1295
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
import magic
|
import magic
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from core import choices, enums, models, utils
|
from core import choices, enums, models, utils, validators
|
||||||
from core.services.ai_services import AI_ACTIONS
|
from core.services.ai_services import AI_ACTIONS
|
||||||
from core.services.converter_services import (
|
from core.services.converter_services import (
|
||||||
ConversionError,
|
ConversionError,
|
||||||
@@ -422,7 +422,7 @@ class ServerCreateDocumentSerializer(serializers.Serializer):
|
|||||||
content = serializers.CharField(required=True)
|
content = serializers.CharField(required=True)
|
||||||
# User
|
# User
|
||||||
sub = serializers.CharField(
|
sub = serializers.CharField(
|
||||||
required=True, validators=[models.User.sub_validator], max_length=255
|
required=True, validators=[validators.sub_validator], max_length=255
|
||||||
)
|
)
|
||||||
email = serializers.EmailField(required=True)
|
email = serializers.EmailField(required=True)
|
||||||
language = serializers.ChoiceField(
|
language = serializers.ChoiceField(
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import core.validators
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@@ -33,4 +35,17 @@ class Migration(migrations.Migration):
|
|||||||
verbose_name="language",
|
verbose_name="language",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="user",
|
||||||
|
name="sub",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Required. 255 characters or fewer. ASCII characters only.",
|
||||||
|
max_length=255,
|
||||||
|
null=True,
|
||||||
|
unique=True,
|
||||||
|
validators=[core.validators.sub_validator],
|
||||||
|
verbose_name="sub",
|
||||||
|
),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from django.contrib.auth import models as auth_models
|
|||||||
from django.contrib.auth.base_user import AbstractBaseUser
|
from django.contrib.auth.base_user import AbstractBaseUser
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.core import mail, validators
|
from django.core import mail
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
@@ -39,6 +39,7 @@ from .choices import (
|
|||||||
RoleChoices,
|
RoleChoices,
|
||||||
get_equivalent_link_definition,
|
get_equivalent_link_definition,
|
||||||
)
|
)
|
||||||
|
from .validators import sub_validator
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
@@ -136,22 +137,12 @@ class UserManager(auth_models.UserManager):
|
|||||||
class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin):
|
class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin):
|
||||||
"""User model to work with OIDC only authentication."""
|
"""User model to work with OIDC only authentication."""
|
||||||
|
|
||||||
sub_validator = validators.RegexValidator(
|
|
||||||
regex=r"^[\w.@+-:]+\Z",
|
|
||||||
message=_(
|
|
||||||
"Enter a valid sub. This value may contain only letters, "
|
|
||||||
"numbers, and @/./+/-/_/: characters."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
sub = models.CharField(
|
sub = models.CharField(
|
||||||
_("sub"),
|
_("sub"),
|
||||||
help_text=_(
|
help_text=_("Required. 255 characters or fewer. ASCII characters only."),
|
||||||
"Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_/: characters only."
|
|
||||||
),
|
|
||||||
max_length=255,
|
max_length=255,
|
||||||
unique=True,
|
|
||||||
validators=[sub_validator],
|
validators=[sub_validator],
|
||||||
|
unique=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ def test_api_documents_create_for_owner_invalid_sub():
|
|||||||
data = {
|
data = {
|
||||||
"title": "My Document",
|
"title": "My Document",
|
||||||
"content": "Document content",
|
"content": "Document content",
|
||||||
"sub": "123!!",
|
"sub": "invalid süb",
|
||||||
"email": "john.doe@example.com",
|
"email": "john.doe@example.com",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,10 +163,7 @@ def test_api_documents_create_for_owner_invalid_sub():
|
|||||||
assert not Document.objects.exists()
|
assert not Document.objects.exists()
|
||||||
|
|
||||||
assert response.json() == {
|
assert response.json() == {
|
||||||
"sub": [
|
"sub": ["Enter a valid sub. This value should be ASCII only."]
|
||||||
"Enter a valid sub. This value may contain only letters, "
|
|
||||||
"numbers, and @/./+/-/_/: characters."
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -44,3 +44,25 @@ def test_models_users_send_mail_main_missing():
|
|||||||
user.email_user("my subject", "my message")
|
user.email_user("my subject", "my message")
|
||||||
|
|
||||||
assert str(excinfo.value) == "User has no email address."
|
assert str(excinfo.value) == "User has no email address."
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"sub,is_valid",
|
||||||
|
[
|
||||||
|
("valid_sub.@+-:=/", True),
|
||||||
|
("invalid süb", False),
|
||||||
|
(12345, True),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_models_users_sub_validator(sub, is_valid):
|
||||||
|
"""The "sub" field should be validated."""
|
||||||
|
user = factories.UserFactory()
|
||||||
|
user.sub = sub
|
||||||
|
if is_valid:
|
||||||
|
user.full_clean()
|
||||||
|
else:
|
||||||
|
with pytest.raises(
|
||||||
|
ValidationError,
|
||||||
|
match=("Enter a valid sub. This value should be ASCII only."),
|
||||||
|
):
|
||||||
|
user.full_clean()
|
||||||
|
|||||||
9
src/backend/core/validators.py
Normal file
9
src/backend/core/validators.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
"""Custom validators for the core app."""
|
||||||
|
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
def sub_validator(value):
|
||||||
|
"""Validate that the sub is ASCII only."""
|
||||||
|
if not value.isascii():
|
||||||
|
raise ValidationError("Enter a valid sub. This value should be ASCII only.")
|
||||||
Reference in New Issue
Block a user