From 9030f694d6fe4f7963f2504da54d2481f0346ff0 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 9 Mar 2016 15:42:53 +0100 Subject: [PATCH] Alias API. see #844 --- modoboa/admin/api.py | 17 ++++++ modoboa/admin/models/alias.py | 2 +- modoboa/admin/serializers.py | 38 ++++++++++++ modoboa/admin/tests/__init__.py | 3 +- modoboa/admin/tests/api.py | 101 ++++++++++++++++++++++++++++++++ modoboa/admin/urls_api.py | 1 + 6 files changed, 160 insertions(+), 2 deletions(-) diff --git a/modoboa/admin/api.py b/modoboa/admin/api.py index bfe452cf7..d72e082b9 100644 --- a/modoboa/admin/api.py +++ b/modoboa/admin/api.py @@ -64,3 +64,20 @@ class AccountViewSet(viewsets.ModelViewSet): data = {"exists": True} serializer = serializers.AccountExistsSerializer(data) return Response(serializer.data) + + +class AliasViewSet(viewsets.ModelViewSet): + """ViewSet for Alias.""" + + permission_classes = [DjangoModelPermissions, ] + serializer_class = serializers.AliasSerializer + + def get_queryset(self): + """Filter queryset based on current user.""" + user = self.request.user + ids = ( + user.objectaccess_set.filter( + content_type=ContentType.objects.get_for_model(models.Alias)) + .values_list('object_id', flat=True) + ) + return models.Alias.objects.filter(pk__in=ids) diff --git a/modoboa/admin/models/alias.py b/modoboa/admin/models/alias.py index ac446b8c8..3948966b7 100644 --- a/modoboa/admin/models/alias.py +++ b/modoboa/admin/models/alias.py @@ -213,5 +213,5 @@ class AliasRecipient(models.Model): def __str__(self): """Return alias and recipient.""" return smart_text( - "{} -> {}".format(self.alias.address, self.alias) + "{} -> {}".format(self.alias.address, self.address) ) diff --git a/modoboa/admin/serializers.py b/modoboa/admin/serializers.py index 473395656..07d092ecf 100644 --- a/modoboa/admin/serializers.py +++ b/modoboa/admin/serializers.py @@ -161,3 +161,41 @@ class WritableAccountSerializer(AccountSerializer): else: self._create_mailbox(creator, instance, mailbox_data) return instance + + +class AliasSerializer(serializers.ModelSerializer): + """Base Alias serializer.""" + + address = lib_fields.DRFEmailFieldUTF8() + recipients = serializers.ListField( + child=lib_fields.DRFEmailFieldUTF8(), allow_empty=False) + + class Meta: + model = admin_models.Alias + fields = ("pk", "address", "enabled", "internal", "recipients") + + def validate_address(self, value): + """Check domain.""" + local_part, domain = email_utils.split_mailbox(value) + self.domain = admin_models.Domain.objects.filter(name=domain).first() + if self.domain is None: + raise serializers.ValidationError("Domain not found.") + return value + + def create(self, validated_data): + """Create appropriate objects.""" + creator = self.context["request"].user + recipients = validated_data.pop("recipients", None) + alias = admin_models.Alias(domain=self.domain, **validated_data) + alias.save(creator=creator) + alias.set_recipients(recipients) + return alias + + def update(self, instance, validated_data): + """Update objects.""" + recipients = validated_data.pop("recipients", None) + for key, value in validated_data.items(): + setattr(instance, key, value) + instance.save() + instance.set_recipients(recipients) + return instance diff --git a/modoboa/admin/tests/__init__.py b/modoboa/admin/tests/__init__.py index de79ff43c..d25bbe957 100644 --- a/modoboa/admin/tests/__init__.py +++ b/modoboa/admin/tests/__init__.py @@ -2,7 +2,7 @@ from .account import AccountTestCase, PermissionsTestCase from .alias import AliasTestCase -from .api import DomainAPITestCase, AccountAPITestCase +from .api import DomainAPITestCase, AccountAPITestCase, AliasAPITestCase from .domain import DomainTestCase from .domain_alias import DomainAliasTestCase from .export import ExportTestCase @@ -14,6 +14,7 @@ from .user import ForwardTestCase __all__ = [ 'AccountAPITestCase', 'AccountTestCase', + 'AliasAPITestCase', 'AliasTestCase', 'DomainAPITestCase', 'DomainTestCase', diff --git a/modoboa/admin/tests/api.py b/modoboa/admin/tests/api.py index 7c610f774..2681e86f1 100644 --- a/modoboa/admin/tests/api.py +++ b/modoboa/admin/tests/api.py @@ -272,3 +272,104 @@ class AccountAPITestCase(ModoAPITestCase): self.assertFalse(content["exists"]) response = self.client.get(url) self.assertEqual(response.status_code, 400) + + +class AliasAPITestCase(ModoAPITestCase): + """Check Alias API.""" + + ALIAS_DATA = { + "address": "alias_fromapi@test.com", + "recipients": [ + "user@test.com", "postmaster@test.com", "user_éé@nonlocal.com" + ] + } + + @classmethod + def setUpTestData(cls): + """Create test data.""" + super(AliasAPITestCase, cls).setUpTestData() + factories.populate_database() + cls.da_token = Token.objects.create( + user=core_models.User.objects.get(username="admin@test.com")) + + def test_get_aliases(self): + """Retrieve a list of aliases.""" + url = reverse("external_api:alias-list") + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + response = json.loads(response.content) + self.assertEqual(len(response), 3) + + def test_get_alias(self): + """Retrieve an alias.""" + al = models.Alias.objects.get(address="alias@test.com") + url = reverse("external_api:alias-detail", args=[al.pk]) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + response = json.loads(response.content) + self.assertEqual(response["recipients"], ["user@test.com"]) + + def test_create_alias(self): + """Try to create a new alias.""" + url = reverse("external_api:alias-list") + response = self.client.post(url, self.ALIAS_DATA, format="json") + self.assertEqual(response.status_code, 201) + + alias = json.loads(response.content) + alias = models.Alias.objects.filter(pk=alias["pk"]).first() + domadmin = core_models.User.objects.get(username="admin@test.com") + self.assertTrue(domadmin.can_access(alias)) + self.assertEqual(alias.aliasrecipient_set.count(), 3) + self.assertTrue(alias.aliasrecipient_set.filter( + address="user@test.com", r_mailbox__isnull=False).exists()) + self.assertTrue(alias.aliasrecipient_set.filter( + address="postmaster@test.com", r_alias__isnull=False).exists()) + self.assertTrue(alias.aliasrecipient_set.filter( + address="user_éé@nonlocal.com", + r_mailbox__isnull=True, r_alias__isnull=True).exists()) + + def test_update_alias(self): + """Try to update an alias.""" + alias = models.Alias.objects.get(address="alias@test.com") + url = reverse("external_api:alias-detail", args=[alias.pk]) + data = { + "address": "alias@test.com", + "recipients": ["user@test.com", "user@nonlocal.com"] + } + response = self.client.put(url, data, format="json") + self.assertEqual(response.status_code, 200) + + alias.refresh_from_db() + self.assertEqual(alias.aliasrecipient_set.count(), 2) + data = { + "address": "alias@test.com", + "recipients": ["user@nonlocal.com"] + } + response = self.client.put(url, data, format="json") + self.assertEqual(response.status_code, 200) + alias.refresh_from_db() + self.assertEqual(alias.aliasrecipient_set.count(), 1) + + data = { + "address": "alias@test.com", + "recipients": [] + } + response = self.client.put(url, data, format="json") + self.assertEqual(response.status_code, 400) + + def test_delete_alias(self): + """Try to delete an existing alias.""" + alias = models.Alias.objects.get(address="alias@test.com") + domadmin = core_models.User.objects.get(username="admin@test.com") + self.assertTrue(domadmin.can_access(alias)) + url = reverse("external_api:alias-detail", args=[alias.pk]) + response = self.client.delete(url) + self.assertEqual(response.status_code, 204) + self.assertFalse( + models.Alias.objects.filter(pk=alias.pk).exists()) + self.assertFalse(domadmin.can_access(alias)) + self.assertFalse( + models.AliasRecipient.objects.filter( + address="user@test.com", alias__address="alias@test.com") + .exists() + ) diff --git a/modoboa/admin/urls_api.py b/modoboa/admin/urls_api.py index f83d0d378..c5e0df79a 100644 --- a/modoboa/admin/urls_api.py +++ b/modoboa/admin/urls_api.py @@ -8,5 +8,6 @@ from . import api router = routers.SimpleRouter() router.register(r"domains", api.DomainViewSet, base_name="domain") router.register(r"accounts", api.AccountViewSet, base_name="account") +router.register(r"aliases", api.AliasViewSet, base_name="alias") urlpatterns = router.urls