From 6bc0eb6a098f13476d052a5f26bb83f1676dbfd4 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 27 Apr 2016 15:51:06 +0200 Subject: [PATCH] Add possibility to set domain permissions using the API. --- modoboa/admin/serializers.py | 45 ++++++++++++++++++++++++++++++++- modoboa/admin/tests/test_api.py | 39 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/modoboa/admin/serializers.py b/modoboa/admin/serializers.py index d7f12c966..f7713e2cc 100644 --- a/modoboa/admin/serializers.py +++ b/modoboa/admin/serializers.py @@ -77,13 +77,17 @@ class AccountSerializer(serializers.ModelSerializer): role = serializers.SerializerMethodField() mailbox = MailboxSerializer(required=False) + domains = serializers.SerializerMethodField( + help_text=_( + "List of administered domains (resellers and domain " + "administrators only).")) class Meta: model = core_models.User fields = ( "pk", "username", "first_name", "last_name", "is_active", "master_user", "mailbox", "role", "language", "phone_number", - "secondary_email", + "secondary_email", "domains", ) def __init__(self, *args, **kwargs): @@ -100,6 +104,13 @@ class AccountSerializer(serializers.ModelSerializer): """Return role.""" return account.group + def get_domains(self, account): + """Return domains administered by this account.""" + if account.role not in ["DomainAdmins", "Resellers"]: + return [] + return admin_models.Domain.objects.get_for_admin(account).values_list( + "name", flat=True) + class AccountExistsSerializer(serializers.Serializer): """Simple serializer used with existence checks.""" @@ -156,6 +167,8 @@ class WritableAccountSerializer(AccountSerializer): user = self.context["request"].user self.fields["role"] = serializers.ChoiceField( choices=permissions.get_account_roles(user)) + self.fields["domains"] = serializers.ListField( + child=serializers.CharField(), allow_empty=False, required=False) def validate_password(self, value): """Check password constraints.""" @@ -183,6 +196,19 @@ class WritableAccountSerializer(AccountSerializer): raise serializers.ValidationError({ "username": _("Must be equal to mailbox full_address") }) + domain_names = data.get("domains") + if not domain_names: + return data + domains = [] + for name in domain_names: + domain = admin_models.Domain.objects.filter(name=name).first() + if domain: + domains.append(domain) + continue + raise serializers.ValidationError({ + "domains": _("Local domain {} does not exist").format(name) + }) + data["domains"] = domains return data def _create_mailbox(self, creator, account, data): @@ -213,23 +239,39 @@ class WritableAccountSerializer(AccountSerializer): account.email = full_address return mb + def set_permissions(self, account, domains): + """Assign permissions on domain(s).""" + if account.role not in ["DomainAdmins", "Resellers"]: + return + current_domains = admin_models.Domain.objects.get_for_admin(account) + for domain in current_domains: + if domain not in domains: + domain.remove_admin(account) + else: + domains.remove(domain) + for domain in domains: + domain.add_admin(account) + def create(self, validated_data): """Create appropriate objects.""" creator = self.context["request"].user mailbox_data = validated_data.pop("mailbox", None) role = validated_data.pop("role") + domains = validated_data.pop("domains", []) user = core_models.User(**validated_data) user.set_password(validated_data["password"]) user.save(creator=creator) if mailbox_data: self._create_mailbox(creator, user, mailbox_data) user.role = role + self.set_permissions(user, domains) return user def update(self, instance, validated_data): """Update account and associated objects.""" mailbox_data = validated_data.pop("mailbox") password = validated_data.pop("password") + domains = validated_data.pop("domains", []) for key, value in validated_data.items(): setattr(instance, key, value) instance.set_password(password) @@ -242,6 +284,7 @@ class WritableAccountSerializer(AccountSerializer): instance.mailbox.update_from_dict(creator, mailbox_data) else: self._create_mailbox(creator, instance, mailbox_data) + self.set_permissions(instance, domains) return instance diff --git a/modoboa/admin/tests/test_api.py b/modoboa/admin/tests/test_api.py index 58204920e..8788d01d0 100644 --- a/modoboa/admin/tests/test_api.py +++ b/modoboa/admin/tests/test_api.py @@ -8,6 +8,7 @@ from django.core.urlresolvers import reverse from rest_framework.authtoken.models import Token +from modoboa.admin import models as admin_models from modoboa.core import models as core_models from modoboa.lib import parameters from modoboa.lib.tests import ModoAPITestCase @@ -192,6 +193,12 @@ class AccountAPITestCase(ModoAPITestCase): cls.da_token = Token.objects.create( user=core_models.User.objects.get(username="admin@test.com")) + def setUp(self): + """Test setup.""" + super(AccountAPITestCase, self).setUp() + parameters.save_admin("ENABLE_ADMIN_LIMITS", "no", app="limits") + parameters.save_admin("ENABLE_DOMAIN_LIMITS", "no", app="limits") + def test_get_accounts(self): """Retrieve a list of accounts.""" url = reverse("external_api:account-list") @@ -233,10 +240,15 @@ class AccountAPITestCase(ModoAPITestCase): def test_create_domainadmin_account(self): """Try to create a domain admin.""" data = copy.deepcopy(self.ACCOUNT_DATA) + data["domains"] = ["test.com"] data["role"] = "DomainAdmins" url = reverse("external_api:account-list") response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, 201) + domain = admin_models.Domain.objects.get(name="test.com") + admin = core_models.User.objects.get( + pk=json.loads(response.content)["pk"]) + self.assertIn(admin, domain.admins) data["username"] = "domain_admin" del data["mailbox"] @@ -325,6 +337,33 @@ class AccountAPITestCase(ModoAPITestCase): account.refresh_from_db() self.assertEqual(account.email, account.mailbox.full_address) + def test_update_domain_admin_account(self): + """Try to change administered domains.""" + account = core_models.User.objects.get(username="admin@test.com") + url = reverse("external_api:account-detail", args=[account.pk]) + data = { + "username": account.username, + "role": account.role, + "password": "Toto1234", + "mailbox": { + "full_address": account.mailbox.full_address, + "quota": account.mailbox.quota + }, + "domains": ["test.com", "test2.com"] + } + response = self.client.put(url, data, format="json") + self.assertEqual(response.status_code, 200) + domains = admin_models.Domain.objects.get_for_admin(account) + self.assertEqual(domains.count(), 2) + self.assertTrue(domains.filter(name="test2.com").exists()) + + data["domains"] = ["test2.com"] + response = self.client.put(url, data, format="json") + self.assertEqual(response.status_code, 200) + domains = admin_models.Domain.objects.get_for_admin(account) + self.assertEqual(domains.count(), 1) + self.assertTrue(domains.filter(name="test2.com").exists()) + def test_update_account_wrong_address(self): """Try to update an account.""" account = core_models.User.objects.get(username="user@test.com")