diff --git a/misago/core/decorators.py b/misago/core/decorators.py index f2c20d5e24..b7e505e42c 100644 --- a/misago/core/decorators.py +++ b/misago/core/decorators.py @@ -1,3 +1,5 @@ +from rest_framework import serializers + from .errorpages import not_allowed @@ -19,3 +21,15 @@ def decorator(request, *args, **kwargs): return f(request, *args, **kwargs) return decorator + + +def require_dict_data(f): + def decorator(request, *args, **kwargs): + if request.method == 'POST': + DummySerializer(data=request.data).is_valid(raise_exception=True) + return f(request, *args, **kwargs) + return decorator + + +class DummySerializer(serializers.Serializer): + pass diff --git a/misago/users/api/auth.py b/misago/users/api/auth.py index ff517508bb..74b3681cbe 100644 --- a/misago/users/api/auth.py +++ b/misago/users/api/auth.py @@ -9,6 +9,7 @@ from django.views.decorators.csrf import csrf_protect from misago.conf import settings +from misago.core.decorators import require_dict_data from misago.core.mail import mail_user from misago.users.bans import get_user_ban from misago.users.forms.auth import AuthenticationForm, ResendActivationForm, ResetPasswordForm @@ -32,6 +33,7 @@ def gateway(request): @api_view(['POST']) @permission_classes((UnbannedAnonOnly, )) @csrf_protect +@require_dict_data def login(request): """ POST /auth/ with CSRF, username and password @@ -85,6 +87,7 @@ def get_criteria(request): @api_view(['POST']) @permission_classes((UnbannedAnonOnly, )) @csrf_protect +@require_dict_data def send_activation(request): """ POST /auth/send-activation/ with CSRF token and email @@ -123,6 +126,7 @@ def send_activation(request): @api_view(['POST']) @permission_classes((UnbannedOnly, )) @csrf_protect +@require_dict_data def send_password_form(request): """ POST /auth/send-password-form/ with CSRF token and email @@ -167,6 +171,7 @@ class PasswordChangeFailed(Exception): @api_view(['POST']) @permission_classes((UnbannedOnly, )) @csrf_protect +@require_dict_data def change_forgotten_password(request, pk, token): """ POST /auth/change-password/user/token/ with CSRF and new password diff --git a/misago/users/api/userendpoints/avatar.py b/misago/users/api/userendpoints/avatar.py index 91bfed64f2..206c963d94 100644 --- a/misago/users/api/userendpoints/avatar.py +++ b/misago/users/api/userendpoints/avatar.py @@ -7,12 +7,14 @@ from django.utils.translation import ugettext as _ from misago.conf import settings +from misago.core.decorators import require_dict_data from misago.core.utils import format_plaintext_for_html from misago.users import avatars from misago.users.models import AvatarGallery from misago.users.serializers import ModerateAvatarSerializer +@require_dict_data def avatar_endpoint(request, pk=None): if request.user.is_avatar_locked: if request.user.avatar_lock_user_message: diff --git a/misago/users/tests/test_auth_api.py b/misago/users/tests/test_auth_api.py index 40d36b560f..b3ececd94e 100644 --- a/misago/users/tests/test_auth_api.py +++ b/misago/users/tests/test_auth_api.py @@ -84,6 +84,15 @@ def test_submit_empty(self): response = self.client.post('/api/auth/') self.assertContains(response, 'empty_data', status_code=400) + def test_submit_invalid(self): + """login api errors for invalid data""" + response = self.client.post( + '/api/auth/', + 'false', + content_type="application/json", + ) + self.assertContains(response, "Invalid data.", status_code=400) + def test_login_banned(self): """login api fails to sign banned user in""" UserModel.objects.create_user('Bob', 'bob@test.com', 'Pass.123') @@ -279,7 +288,16 @@ def test_submit_empty(self): self.assertTrue(not mail.outbox) - def test_submit_invalid(self): + def test_submit_invalid_data(self): + """login api errors for invalid data""" + response = self.client.post( + self.link, + 'false', + content_type="application/json", + ) + self.assertContains(response, "Invalid data.", status_code=400) + + def test_submit_invalid_email(self): """request activation link api errors for invalid email""" response = self.client.post( self.link, @@ -403,6 +421,15 @@ def test_submit_invalid(self): self.assertTrue(not mail.outbox) + def test_submit_invalid_data(self): + """login api errors for invalid data""" + response = self.client.post( + self.link, + 'false', + content_type="application/json", + ) + self.assertContains(response, "Invalid data.", status_code=400) + def test_submit_inactive_user(self): """request change password form link api errors for inactive users""" self.user.requires_activation = 1 @@ -462,6 +489,15 @@ def test_submit_with_whitespaces(self): user = UserModel.objects.get(id=self.user.pk) self.assertTrue(user.check_password(' n3wp4ss! ')) + def test_submit_invalid_data(self): + """login api errors for invalid data""" + response = self.client.post( + self.link % (self.user.pk, make_password_change_token(self.user)), + 'false', + content_type="application/json", + ) + self.assertContains(response, "Invalid data.", status_code=400) + def test_invalid_token_link(self): """api errors on invalid user id link""" response = self.client.post(self.link % (self.user.pk, 'asda7ad89sa7d9s789as')) diff --git a/misago/users/tests/test_user_create_api.py b/misago/users/tests/test_user_create_api.py index b270d816df..48820e1c3c 100644 --- a/misago/users/tests/test_user_create_api.py +++ b/misago/users/tests/test_user_create_api.py @@ -23,6 +23,15 @@ def test_empty_request(self): response = self.client.post(self.api_link) self.assertEqual(response.status_code, 400) + def test_invalid_data(self): + """invalid request data errors with code 400""" + response = self.client.post( + self.api_link, + 'false', + content_type="application/json", + ) + self.assertEqual(response.status_code, 400) + def test_authenticated_request(self): """authentiated user request errors with code 403""" self.login_user(self.get_authenticated_user())