From dfaff270abe9119035ec75b18744680e8d4d74b6 Mon Sep 17 00:00:00 2001 From: Eliot Berriot Date: Fri, 14 Feb 2020 13:59:53 +0100 Subject: [PATCH] See #170: apply proper special chars and username blacklist to channel names --- api/funkwhale_api/audio/serializers.py | 10 +++++- api/tests/audio/test_serializers.py | 45 ++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/api/funkwhale_api/audio/serializers.py b/api/funkwhale_api/audio/serializers.py index aaf8a1e68..df98bde32 100644 --- a/api/funkwhale_api/audio/serializers.py +++ b/api/funkwhale_api/audio/serializers.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.db import transaction from rest_framework import serializers @@ -13,6 +14,7 @@ from funkwhale_api.music import models as music_models from funkwhale_api.music import serializers as music_serializers from funkwhale_api.tags import models as tags_models from funkwhale_api.tags import serializers as tags_serializers +from funkwhale_api.users import serializers as users_serializers from . import categories from . import models @@ -52,7 +54,10 @@ class ChannelMetadataSerializer(serializers.Serializer): class ChannelCreateSerializer(serializers.Serializer): name = serializers.CharField(max_length=music_models.MAX_LENGTHS["ARTIST_NAME"]) - username = serializers.CharField(max_length=music_models.MAX_LENGTHS["ARTIST_NAME"]) + username = serializers.CharField( + max_length=music_models.MAX_LENGTHS["ARTIST_NAME"], + validators=[users_serializers.ASCIIUsernameValidator()], + ) description = common_serializers.ContentSerializer(allow_null=True) tags = tags_serializers.TagsListField() content_category = serializers.ChoiceField( @@ -76,6 +81,9 @@ class ChannelCreateSerializer(serializers.Serializer): return validated_data def validate_username(self, value): + if value.lower() in [n.lower() for n in settings.ACCOUNT_USERNAME_BLACKLIST]: + raise serializers.ValidationError("This username is already taken") + matching = federation_models.Actor.objects.local().filter( preferred_username__iexact=value ) diff --git a/api/tests/audio/test_serializers.py b/api/tests/audio/test_serializers.py index ee5a9c1b9..0d664969d 100644 --- a/api/tests/audio/test_serializers.py +++ b/api/tests/audio/test_serializers.py @@ -70,7 +70,7 @@ def test_channel_serializer_create_honor_max_channels_setting(factories, prefere assert serializer.is_valid(raise_exception=True) -def test_channel_serializer_create_validates_username(factories): +def test_channel_serializer_create_validates_username_uniqueness(factories): attributed_to = factories["federation.Actor"](local=True) data = { "name": "My channel", @@ -83,7 +83,48 @@ def test_channel_serializer_create_validates_username(factories): serializer = serializers.ChannelCreateSerializer( data=data, context={"actor": attributed_to} ) - with pytest.raises(serializers.serializers.ValidationError, match=r".*username.*"): + with pytest.raises( + serializers.serializers.ValidationError, match=r".*username is already taken.*" + ): + assert serializer.is_valid(raise_exception=True) + + +def test_channel_serializer_create_validates_username_chars(factories): + attributed_to = factories["federation.Actor"](local=True) + data = { + "name": "My channel", + "username": "hello world", + "description": {"text": "This is my channel", "content_type": "text/markdown"}, + "tags": ["hello", "world"], + "content_category": "other", + } + + serializer = serializers.ChannelCreateSerializer( + data=data, context={"actor": attributed_to} + ) + with pytest.raises( + serializers.serializers.ValidationError, match=r".*Enter a valid username.*" + ): + assert serializer.is_valid(raise_exception=True) + + +def test_channel_serializer_create_validates_blacklisted_username(factories, settings): + settings.ACCOUNT_USERNAME_BLACKLIST = ["forBidden"] + attributed_to = factories["federation.Actor"](local=True) + data = { + "name": "My channel", + "username": "FORBIDDEN", + "description": {"text": "This is my channel", "content_type": "text/markdown"}, + "tags": ["hello", "world"], + "content_category": "other", + } + + serializer = serializers.ChannelCreateSerializer( + data=data, context={"actor": attributed_to} + ) + with pytest.raises( + serializers.serializers.ValidationError, match=r".*username is already taken.*" + ): assert serializer.is_valid(raise_exception=True)