From 3725c8b77e7b2222e16187f347d224c11f6c0cb8 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Thu, 7 Jul 2016 18:28:07 +0200 Subject: [PATCH] Disallow PATCH method and allow account update with no password. see #884 --- modoboa/admin/api.py | 4 ++++ modoboa/admin/serializers.py | 7 +++++-- modoboa/admin/tests/test_api.py | 20 ++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/modoboa/admin/api.py b/modoboa/admin/api.py index f521b9590..c8c39fa92 100644 --- a/modoboa/admin/api.py +++ b/modoboa/admin/api.py @@ -21,6 +21,7 @@ class DomainViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated, DjangoModelPermissions, ] serializer_class = serializers.DomainSerializer + http_method_names = ["get", "post", "put", "delete"] def get_queryset(self): """Filter queryset based on current user.""" @@ -36,6 +37,7 @@ class DomainAliasViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated, DjangoModelPermissions, ] serializer_class = serializers.DomainAliasSerializer + http_method_names = ["get", "post", "put", "delete"] def get_queryset(self): """Filter queryset based on current user.""" @@ -50,6 +52,7 @@ class AccountViewSet(viewsets.ModelViewSet): """ViewSet for User/Mailbox.""" permission_classes = [IsAuthenticated, DjangoModelPermissions, ] + http_method_names = ["get", "post", "put", "delete"] def get_serializer_class(self): """Return a serializer.""" @@ -142,6 +145,7 @@ class AliasViewSet(viewsets.ModelViewSet): permission_classes = [IsAuthenticated, DjangoModelPermissions, ] serializer_class = serializers.AliasSerializer + http_method_names = ["get", "post", "put", "delete"] def get_queryset(self): """Filter queryset based on current user.""" diff --git a/modoboa/admin/serializers.py b/modoboa/admin/serializers.py index f7713e2cc..47de388b0 100644 --- a/modoboa/admin/serializers.py +++ b/modoboa/admin/serializers.py @@ -169,6 +169,8 @@ class WritableAccountSerializer(AccountSerializer): choices=permissions.get_account_roles(user)) self.fields["domains"] = serializers.ListField( child=serializers.CharField(), allow_empty=False, required=False) + if request.method == "PUT": + self.fields["password"].required = False def validate_password(self, value): """Check password constraints.""" @@ -270,11 +272,12 @@ class WritableAccountSerializer(AccountSerializer): def update(self, instance, validated_data): """Update account and associated objects.""" mailbox_data = validated_data.pop("mailbox") - password = validated_data.pop("password") + password = validated_data.pop("password", None) domains = validated_data.pop("domains", []) for key, value in validated_data.items(): setattr(instance, key, value) - instance.set_password(password) + if password: + instance.set_password(password) instance.save() if mailbox_data: creator = self.context["request"].user diff --git a/modoboa/admin/tests/test_api.py b/modoboa/admin/tests/test_api.py index 8788d01d0..157b92e2d 100644 --- a/modoboa/admin/tests/test_api.py +++ b/modoboa/admin/tests/test_api.py @@ -336,6 +336,26 @@ class AccountAPITestCase(ModoAPITestCase): self.assertEqual(response.status_code, 200) account.refresh_from_db() self.assertEqual(account.email, account.mailbox.full_address) + self.assertTrue(account.check_password("Toto1234")) + + del data["password"] + response = self.client.put(url, data, format="json") + self.assertEqual(response.status_code, 200) + account.refresh_from_db() + self.assertTrue(account.check_password("Toto1234")) + + def test_patch_account(self): + """Try to patch an account.""" + account = core_models.User.objects.get(username="user@test.com") + url = reverse("external_api:account-detail", args=[account.pk]) + data = { + "username": "fromapi@test.com", + "mailbox": { + "full_address": "fromapi@test.com", + } + } + response = self.client.patch(url, data, format="json") + self.assertEqual(response.status_code, 405) def test_update_domain_admin_account(self): """Try to change administered domains."""