From b6f1282c7edbd9d4965577df2572d524fc15d923 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 2 Mar 2016 15:57:09 +0100 Subject: [PATCH] WIP see #832 --- modoboa/admin/forms/account.py | 30 +++-------------- modoboa/admin/models/mailbox.py | 38 +++++++++++++++++++--- modoboa/admin/serializers.py | 45 +++++++++++++++++++------- modoboa/admin/static/admin/js/admin.js | 5 +++ modoboa/admin/tests/account.py | 5 +-- modoboa/admin/tests/api.py | 21 +++++++++++- 6 files changed, 99 insertions(+), 45 deletions(-) diff --git a/modoboa/admin/forms/account.py b/modoboa/admin/forms/account.py index 9bdd251c3..22729bf5f 100644 --- a/modoboa/admin/forms/account.py +++ b/modoboa/admin/forms/account.py @@ -124,6 +124,7 @@ class AccountFormGeneral(forms.ModelForm): return self.cleaned_data["role"] def clean_username(self): + """username must be a valid email address for simple users.""" if "role" not in self.cleaned_data: return self.cleaned_data["username"] if self.cleaned_data["role"] != "SimpleUsers": @@ -261,31 +262,6 @@ class AccountFormMail(forms.Form, DynamicForm): user.has_perm("admin.add_domain")) self.mb.save(creator=user) - def update_mailbox(self, user, account): - newaddress = None - if self.cleaned_data["email"] != self.mb.full_address: - newaddress = self.cleaned_data["email"] - elif (account.group == "SimpleUsers" and - account.username != self.mb.full_address): - newaddress = account.username - if newaddress is not None: - local_part, domname = split_mailbox(newaddress) - try: - domain = Domain.objects.get(name=domname) - except Domain.DoesNotExist: - raise NotFound(_("Domain does not exist")) - if not user.can_access(domain): - raise PermDeniedException - self.mb.use_domain_quota = self.cleaned_data["quota_act"] - override_rules = True \ - if not self.mb.quota or user.has_perm("admin.add_domain") \ - else False - self.mb.set_quota(self.cleaned_data["quota"], override_rules) - self.mb.save() - if newaddress: - self.mb.rename(local_part, domain) - events.raiseEvent("MailboxModified", self.mb) - def _update_aliases(self, user, account): """Update mailbox aliases.""" aliases = [] @@ -334,7 +310,9 @@ class AccountFormMail(forms.Form, DynamicForm): if not hasattr(self, "mb") or self.mb is None: self.create_mailbox(user, account) else: - self.update_mailbox(user, account) + self.cleaned_data["use_domain_quota"] = ( + self.cleaned_data["quota_act"]) + self.mb.update_from_dict(user, self.cleaned_data) events.raiseEvent( 'SaveExtraFormFields', 'mailform', self.mb, self.cleaned_data ) diff --git a/modoboa/admin/models/mailbox.py b/modoboa/admin/models/mailbox.py index 3c214691f..a061042e1 100644 --- a/modoboa/admin/models/mailbox.py +++ b/modoboa/admin/models/mailbox.py @@ -16,7 +16,8 @@ from .base import AdminObject from .domain import Domain from modoboa.core.models import User from modoboa.lib import events, parameters -from modoboa.lib.exceptions import BadRequest, Conflict, InternalError +from modoboa.lib import exceptions as lib_exceptions +from modoboa.lib.email_utils import split_mailbox from modoboa.lib.sysutils import exec_cmd @@ -144,7 +145,7 @@ class Mailbox(AdminObject): "doveadm user %s -f home" % self.full_address, **options ) if code: - raise InternalError( + raise lib_exceptions.InternalError( _("Failed to retrieve mailbox location (%s)" % output)) self.__mail_home = output.strip() return self.__mail_home @@ -233,14 +234,14 @@ class Mailbox(AdminObject): else: self.quota = 0 elif int(value) > self.domain.quota and not override_rules: - raise BadRequest( + raise lib_exceptions.BadRequest( _("Quota is greater than the allowed domain's limit (%dM)" % self.domain.quota) ) else: self.quota = value if not self.quota and self.domain.quota and not override_rules: - raise BadRequest(_("A quota is required")) + raise lib_exceptions.BadRequest(_("A quota is required")) def get_quota(self): """Get quota limit. @@ -279,6 +280,33 @@ class Mailbox(AdminObject): grant_access_to_object(admin, self) grant_access_to_object(admin, self.user) + def update_from_dict(self, user, values): + """Update mailbox from a dictionary.""" + newaddress = None + if values["email"] != self.full_address: + newaddress = values["email"] + elif (self.user.group == "SimpleUsers" and + self.user.username != self.full_address): + newaddress = self.user.username + if newaddress is not None: + local_part, domname = split_mailbox(newaddress) + try: + domain = Domain.objects.get(name=domname) + except Domain.DoesNotExist: + raise lib_exceptions.NotFound(_("Domain does not exist")) + if not user.can_access(domain): + raise lib_exceptions.PermDeniedException + if "use_domain_quota" in values: + self.use_domain_quota = values["use_domain_quota"] + override_rules = True \ + if not self.quota or user.has_perm("admin.add_domain") \ + else False + self.set_quota(values["quota"], override_rules) + if newaddress: + self.rename(local_part, domain) + self.save() + events.raiseEvent("MailboxModified", self) + def save(self, *args, **kwargs): """Custom save. @@ -290,7 +318,7 @@ class Mailbox(AdminObject): if self.pk: qset = qset.exclude(pk=self.pk) if qset.exists(): - raise Conflict( + raise lib_exceptions.Conflict( _("Mailbox {} already exists").format(self)) if self.quota_value is None: self.quota_value, created = Quota.objects.get_or_create( diff --git a/modoboa/admin/serializers.py b/modoboa/admin/serializers.py index 2b79f2e5e..ed66ffb24 100644 --- a/modoboa/admin/serializers.py +++ b/modoboa/admin/serializers.py @@ -107,6 +107,21 @@ class CreateAccountSerializer(AccountSerializer): }) return data + def _create_mailbox(self, creator, account, data): + """Create a new Mailbox instance.""" + full_address = data.pop("full_address") + address, domain_name = email_utils.split_mailbox(full_address) + domain = get_object_or_404( + admin_models.Domain, name=domain_name) + mb = admin_models.Mailbox( + user=account, address=address, domain=domain, **data) + mb.set_quota( + data.get("quota"), creator.has_perm("admin.add_domain") + ) + mb.save(creator=creator) + account.email = full_address + return mb + def create(self, validated_data): """Create appropriate objects.""" creator = self.context["request"].user @@ -116,16 +131,24 @@ class CreateAccountSerializer(AccountSerializer): user.set_password(validated_data["password"]) user.save(creator=creator) if mailbox_data: - full_address = mailbox_data.pop("full_address") - address, domain_name = email_utils.split_mailbox(full_address) - domain = get_object_or_404( - admin_models.Domain, name=domain_name) - mb = admin_models.Mailbox( - user=user, address=address, domain=domain, **mailbox_data) - mb.set_quota( - mailbox_data.get("quota"), creator.has_perm("admin.add_domain") - ) - mb.save(creator=creator) - user.email = full_address + self._create_mailbox(creator, user, mailbox_data) user.role = role return user + + def update(self, instance, validated_data): + """Update account and associated objects.""" + mailbox_data = validated_data.pop("mailbox") + password = validated_data.pop("password") + for key, value in validated_data.items(): + setattr(instance, key, value) + instance.set_password(password) + instance.save() + if mailbox_data: + creator = self.context["request"].user + if instance.mailbox: + # FIXME: compat, to remove. + mailbox_data["email"] = mailbox_data["full_address"] + instance.mailbox.update_from_dict(creator, mailbox_data) + else: + self._create_mailbox(creator, instance, mailbox_data) + return instance diff --git a/modoboa/admin/static/admin/js/admin.js b/modoboa/admin/static/admin/js/admin.js index b35e5caa8..0e91c09e2 100644 --- a/modoboa/admin/static/admin/js/admin.js +++ b/modoboa/admin/static/admin/js/admin.js @@ -6,6 +6,11 @@ * @classdesc This class contains utility methods used by the admin * application. */ + +/*global + $ Listing +*/ + var Admin = function(options) { Listing.call(this, options); }; diff --git a/modoboa/admin/tests/account.py b/modoboa/admin/tests/account.py index 83a682f6e..1ea5457e8 100644 --- a/modoboa/admin/tests/account.py +++ b/modoboa/admin/tests/account.py @@ -44,11 +44,12 @@ class AccountTestCase(ModoTestCase): alias__internal=True).exists() ) - values["username"] = "pouet@test.com" + values.update({ + "username": "pouet@test.com", "email": "pouet@test.com"}) self.ajax_post( reverse("admin:account_change", args=[account.id]), values ) - mb = models.Mailbox.objects.get(pk=mb.id) + mb = models.Mailbox.objects.get(pk=mb.pk) self.assertEqual(mb.full_address, "pouet@test.com") self.assertEqual(mb.quota_value.username, "pouet@test.com") # Check if self alias has been updated diff --git a/modoboa/admin/tests/api.py b/modoboa/admin/tests/api.py index 27adabf96..358d2c972 100644 --- a/modoboa/admin/tests/api.py +++ b/modoboa/admin/tests/api.py @@ -124,6 +124,8 @@ class AccountAPITestCase(ModoAPITestCase): user = core_models.User.objects.filter(pk=account["pk"]).first() self.assertIsNot(user, None) self.assertIsNot(user.mailbox, None) + domadmin = core_models.User.objects.get(username="admin@test.com") + self.assertTrue(domadmin.can_access(user)) data = copy.deepcopy(self.ACCOUNT_DATA) data["username"] = "fromapi_ééé@test.com" @@ -193,13 +195,30 @@ class AccountAPITestCase(ModoAPITestCase): def test_update_account(self): """Try to update an account.""" - pass + account = core_models.User.objects.get(username="user@test.com") + url = reverse("external_api:account-detail", args=[account.pk]) + data = { + "username": "fromapi@test.com", + "role": account.role, + "password": "Toto1234", + "mailbox": { + "full_address": "fromapi@test.com", + "quota": account.mailbox.quota + } + } + response = self.client.put(url, data, format="json") + self.assertEqual(response.status_code, 200) + account.refresh_from_db() + self.assertEqual(account.email, account.mailbox.full_address) def test_delete_account(self): """Try to delete an account.""" account = core_models.User.objects.get(username="user@test.com") + domadmin = core_models.User.objects.get(username="admin@test.com") + self.assertTrue(domadmin.can_access(account)) url = reverse("external_api:account-detail", args=[account.pk]) response = self.client.delete(url) self.assertEqual(response.status_code, 204) self.assertFalse( core_models.User.objects.filter(pk=account.pk).exists()) + self.assertFalse(domadmin.can_access(account))