From d1b543c25de1bb1b94f7a8205cda483d16bbe4fd Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 2 May 2012 17:11:29 +0200 Subject: [PATCH] wip: UI refactoring * Per object permissions * Faster identities listing * Possibility to filter on alias names * fixes see #204 --- admin/fixtures/initial_data.json | 2 +- admin/forms.py | 72 +++++++++----- admin/migrations/0017_set_permissions.py | 80 +++++++++------- admin/models.py | 97 +++++++++++-------- admin/tests.py | 19 +++- admin/urls.py | 3 - admin/views.py | 114 +++++++---------------- extensions/amavis_quarantine/__init__.py | 11 ++- extensions/limits/controls.py | 3 +- lib/permissions.py | 35 ++----- 10 files changed, 219 insertions(+), 217 deletions(-) diff --git a/admin/fixtures/initial_data.json b/admin/fixtures/initial_data.json index 857a2ea36..55d095bb8 100644 --- a/admin/fixtures/initial_data.json +++ b/admin/fixtures/initial_data.json @@ -1 +1 @@ -[{"pk": 1, "model": "auth.group", "fields": {"name": "DomainAdmins", "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"], ["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"]]}},{"pk": 2, "model": "auth.group", "fields": {"name": "SimpleUsers", "permissions": []}},{"pk": 1, "model": "auth.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-01-26 15:19:25", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26 15:19:25"}}] +[{"pk": 1, "model": "auth.group", "fields": {"name": "DomainAdmins", "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"], ["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"]]}},{"pk": 2, "model": "auth.group", "fields": {"name": "SimpleUsers", "permissions": []}},{"pk": 1, "model": "auth.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-01-26 15:19:25", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26 15:19:25"}},{"pk": 1, "model": "admin.objectaccess", "fields": {"is_owner": false, "user": 1, "content_type": ["admin", "user"], "object_id": 1}}] diff --git a/admin/forms.py b/admin/forms.py index 3bf1d3b22..4086da76d 100644 --- a/admin/forms.py +++ b/admin/forms.py @@ -95,8 +95,6 @@ class DomainForm(forms.ModelForm, DynamicForm): for dalias in d.domainalias_set.all(): if not len(filter(lambda name: self.cleaned_data[name] == dalias.name, self.cleaned_data.keys())): - events.raiseEvent("DomainAliasDeleted", dalias) - ungrant_access_to_object(dalias) dalias.delete() return d @@ -114,7 +112,8 @@ class DlistForm(forms.ModelForm, DynamicForm): model = Alias fields = ("enabled",) - def __init__(self, *args, **kwargs): + def __init__(self, user, *args, **kwargs): + self.user = user super(DlistForm, self).__init__(*args, **kwargs) self.fields.keyOrder = ['email', 'recipients', 'enabled'] @@ -138,12 +137,14 @@ class DlistForm(forms.ModelForm, DynamicForm): def clean_email(self): localpart, domname = split_mailbox(self.cleaned_data["email"]) try: - Domain.objects.get(name=domname) + domain = Domain.objects.get(name=domname) except Domain.DoesNotExist: raise forms.ValidationError(_("Domain does not exist")) + if not self.user.can_access(domain): + raise forms.ValidationError(_("You don't have access to this domain")) return self.cleaned_data["email"] - def set_recipients(self, user): + def set_recipients(self): """Recipients dispatching We make a difference between 'local' recipients (the ones hosted @@ -166,7 +167,7 @@ class DlistForm(forms.ModelForm, DynamicForm): except Domain.DoesNotExist: domain = None if domain: - if not user.can_access(domain): + if not self.user.can_access(domain): raise PermDeniedException(v) try: mb = Mailbox.objects.get(domain=domain, address=local_part) @@ -209,7 +210,8 @@ class ForwardForm(forms.ModelForm): model = Alias fields = ("enabled",) - def __init__(self, *args, **kwargs): + def __init__(self, user, *args, **kwargs): + self.user = user super(ForwardForm, self).__init__(*args, **kwargs) self.fields.keyOrder = ['email', 'recipient', 'enabled'] if kwargs.has_key("instance"): @@ -220,9 +222,11 @@ class ForwardForm(forms.ModelForm): def clean_email(self): localpart, domname = split_mailbox(self.cleaned_data["email"]) try: - Domain.objects.get(name=domname) + domain = Domain.objects.get(name=domname) except Domain.DoesNotExist: raise forms.ValidationError(_("Domain does not exist")) + if not self.user.can_access(domain): + raise forms.ValidationError(_("You don't have access to this domain")) return self.cleaned_data["email"] def clean_recipient(self): @@ -235,7 +239,7 @@ class ForwardForm(forms.ModelForm): raise forms.ValidationError(_("Local addresses are forbidden")) return self.cleaned_data["recipient"] - def set_recipients(self, user): + def set_recipients(self): pass def save(self, commit=True): @@ -286,32 +290,33 @@ class AccountFormGeneral(forms.ModelForm): self.user = user if user.group == "DomainAdmins": del self.fields["role"] - return + else: + self.fields["role"].choices = \ + [('', ugettext_lazy("Choose"))] + get_account_roles(user) - self.fields["role"].choices = \ - [('', ugettext_lazy("Choose"))] + get_account_roles(user) if kwargs.has_key("instance"): if len(args) \ and (args[0].get("password1", "") == "" \ and args[0].get("password2", "") == ""): self.fields["password1"].required = False self.fields["password2"].required = False - - u = kwargs["instance"] - if u.is_superuser: - role = "SuperAdmins" - else: - try: - role = u.groups.all()[0].name - except IndexError: - pass - self.fields["role"].initial = role + if user.group != "DomainAdmins": + u = kwargs["instance"] + if u.is_superuser: + role = "SuperAdmins" + else: + try: + role = u.groups.all()[0].name + except IndexError: + pass + self.fields["role"].initial = role def clean_username(self): from django.core.validators import validate_email - + if not self.cleaned_data.has_key("role"): + return self.cleaned_data["username"] if self.cleaned_data["role"] != "SimpleUsers": - return self.cleaned_data + return self.cleaned_data["username"] validate_email(self.cleaned_data["username"]) return self.cleaned_data["username"] @@ -326,7 +331,7 @@ class AccountFormGeneral(forms.ModelForm): def save(self, commit=True): account = super(AccountFormGeneral, self).save(commit=False) if commit: - if self.cleaned_data.has_key("password1"): + if self.cleaned_data["password1"] != "": account.set_password(self.cleaned_data["password1"]) account.save() role = None @@ -396,6 +401,10 @@ class AccountFormMail(forms.Form, DynamicForm): self.cleaned_data["quota"]) grant_access_to_object(user, self.mb, is_owner=True) events.raiseEvent("CreateMailbox", user, self.mb) + if user.is_superuser: + for admin in self.mb.domain.admins: + grant_access_to_object(admin, self.mb) + else: if self.cleaned_data["email"] != self.mb.full_address: if self.mb.rename_dir(domname, locpart): @@ -426,6 +435,9 @@ class AccountFormMail(forms.Form, DynamicForm): al.save([self.mb], []) grant_access_to_object(user, al, is_owner=True) events.raiseEvent("MailboxAliasCreated", user, al) + if user.is_superuser: + for admin in al.domain.admins: + grant_access_to_object(admin, al) for alias in self.mb.alias_set.all(): if len(alias.get_recipients()) >= 2: @@ -470,11 +482,21 @@ class AccountPermissionsForm(forms.Form, DynamicForm): if not value in current_domains: domain = Domain.objects.get(name=value) grant_access_to_object(self.account, domain) + for mb in domain.mailbox_set.all(): + grant_access_to_object(self.account, mb) + grant_access_to_object(self.account, mb.user) + for al in Alias.objects.filter(domain=domain): + grant_access_to_object(self.account, al) for domain in self.account.get_domains(): if not len(filter(lambda name: self.cleaned_data[name] == domain.name, self.cleaned_data.keys())): ungrant_access_to_object(domain, self.account) + for mb in domain.mailbox_set.all(): + ungrant_access_to_object(mb, self.account) + ungrant_access_to_object(mb.user, self.account) + for al in Alias.objects.filter(domain=domain): + ungrant_access_to_object(al, self.account) class AccountForm(TabForms): diff --git a/admin/migrations/0017_set_permissions.py b/admin/migrations/0017_set_permissions.py index e997cb59f..8ffd98cf7 100644 --- a/admin/migrations/0017_set_permissions.py +++ b/admin/migrations/0017_set_permissions.py @@ -7,20 +7,21 @@ from django.core.exceptions import ObjectDoesNotExist class Migration(DataMigration): + def grant_access(self, orm, user, ct, obj, is_owner=False): + objaccess = orm["admin.ObjectAccess"]( + user=user, content_type=ct, object_id=obj.id + ) + objaccess.is_owner = is_owner + objaccess.save() + def forwards(self, orm): - "Write your forwards methods here." - superusers = orm.User.objects.filter(is_superuser=True) + superusers = orm["auth.User"].objects.filter(is_superuser=True) if not len(superusers): # Empty db, nothing to do return - domadmins = orm.User.objects.filter(groups__name='DomainAdmins') - domains = orm.Domain.objects.all() - deferred_admin = [] - - domct = orm['contenttypes.ContentType'].objects.get( - app_label='admin', model='domain' - ) + # 1: all superadmins must be able to access every defined + # object. (user, domain, domain alias, mailbox, alias) try: uct = orm['contenttypes.ContentType'].objects.get( app_label='admin', model='user' @@ -30,35 +31,48 @@ class Migration(DataMigration): name='user', app_label='admin', model='user' ) uct.save() + for account in orm["admin.User"].objects.all(): + for i, su in enumerate(superusers): + self.grant_access(orm, su, uct, account, i == 0) + domct = orm['contenttypes.ContentType'].objects.get( + app_label='admin', model='domain' + ) + for dom in orm["admin.Domain"].objects.all(): + for i, su in enumerate(superusers): + self.grant_access(orm, su, domct, dom, i == 0) + dalct = orm['contenttypes.ContentType'].objects.get( + app_label='admin', model='domainalias' + ) + for domalias in orm["admin.DomainAlias"].objects.all(): + for i, su in enumerate(superusers): + self.grant_access(orm, su, dalct, domalias, i == 0) + mbct = orm['contenttypes.ContentType'].objects.get( + app_label='admin', model='mailbox' + ) + for mb in orm["admin.Mailbox"].objects.all(): + for i, su in enumerate(superusers): + self.grant_access(orm, su, mbct, mb, i == 0) + alct = orm['contenttypes.ContentType'].objects.get( + app_label='admin', model='alias' + ) + for alias in orm["admin.Alias"].objects.all(): + for i, su in enumerate(superusers): + self.grant_access(orm, su, alct, alias, i == 0) - for da in domadmins: - for i, su in enumerate(superusers): - objaccess = orm.ObjectAccess( - user=su, content_type=uct, object_id=da.id - ) - if i == 0: - objaccess.is_owner = True - objaccess.save() - + # 2: domain admins must have access to their domain's content + for da in orm["admin.User"].objects.filter(groups__name='DomainAdmins'): dom = da.mailbox_set.all()[0].domain - objaccess = orm.ObjectAccess( - user=da, content_type=domct, object_id=dom.id, is_owner=True - ) - objaccess.save() - deferred_admin += [dom] - - for dom in domains: - for i, su in enumerate(superusers): - objaccess = orm.ObjectAccess( - user=su, content_type=domct, object_id=dom.id - ) - if not dom in deferred_admin: - objaccess.is_owner = True - objaccess.save() + self.grant_access(orm, da, domct, dom) + + for mb in dom.mailbox_set.all(): + self.grant_access(orm, da, mbct, mb) + self.grant_access(orm, da, uct, mb.user) + for al in dom.alias_set.all(): + self.grant_access(orm, da, alct, al) def backwards(self, orm): "Write your backwards methods here." - + raise RuntimeError("Cannot revert this migration") models = { 'admin.alias': { diff --git a/admin/models.py b/admin/models.py index c352d38d1..5c0a98c51 100644 --- a/admin/models.py +++ b/admin/models.py @@ -69,24 +69,19 @@ class User(DUser): if fromuser == self: raise AdminError(_("You can't delete your own account")) + if not fromuser.can_access(self): + raise PermDeniedException("You don't have access to this account") + if self.has_mailbox: mb = self.mailbox_set.all()[0] - if not fromuser.can_access(mb.domain): - raise PermDeniedException(_("You don't have access to this domain")) - events.raiseEvent("DeleteMailbox", mb) - ungrant_access_to_object(mb) + if not fromuser.can_access(mb): + raise PermDeniedException(_("You don't have access to this mailbox")) mb.delete(keepdir=keep_mb_dir) - elif not fromuser.can_access(self): - raise PermDeniedException(_("You don't have access to this account")) owner = get_object_owner(self) - for ooentry in self.objectaccess_set.all(): - if ooentry.is_owner: - if ooentry.content_object is not None: - grant_access_to_object(owner, ooentry.content_object, True) - else: - print "ORPHELIN!!!" - ooentry.delete() + for ooentry in self.objectaccess_set.filter(is_owner=True): + if ooentry.content_object is not None: + grant_access_to_object(owner, ooentry.content_object, True) events.raiseEvent("AccountDeleted", self) ungrant_access_to_object(self) @@ -204,24 +199,22 @@ class User(DUser): return False return True + def get_identities(self): + from modoboa.lib.permissions import get_content_type + + userct = get_content_type(self) + alct = get_content_type(Alias) + return self.objectaccess_set.filter(content_type__in=[userct, alct]) + def get_domains(self): """Return the domains belonging to this user The result is a ``QuerySet`` object, so this function can be used to fill ``ModelChoiceField`` objects. - - :param user: a ``User`` object """ if self.is_superuser: return Domain.objects.all() - result = Domain.objects.filter(owners__user=self).distinct() - - userct = self.get_content_type() - for entry in self.objectaccess_set.filter(content_type=userct): - if not entry.content_object.belongs_to_group('DomainAdmins'): - continue - result |= entry.content_object.get_domains().distinct() - return result + return Domain.objects.filter(owners__user=self) def get_domaliases(self): """Return the domain aliases that belong to this user @@ -327,6 +320,9 @@ class ObjectAccess(models.Model): class Meta: unique_together = (("user", "content_type", "object_id"),) + + def __unicode__(self): + return "%s => %s (%s)" % (self.user, self.content_object, self.content_type) class ObjectDates(models.Model): """Dates recording for admin objects @@ -400,6 +396,10 @@ class Domain(DatesAware): def mbalias_count(self): return len(self.alias_set.all()) + @property + def admins(self): + return [oa.user for oa in self.owners.filter(user__is_superuser=False)] + def create_dir(self): if parameters.get_admin("CREATE_DIRECTORIES") == "yes": path = "%s/%s" % (parameters.get_admin("STORAGE_PATH"), self.name) @@ -421,17 +421,21 @@ class Domain(DatesAware): self.name)) return True - def delete(self, *args, **kwargs): - keepdir = False - if kwargs.has_key("keepdir"): - keepdir = kwargs["keepdir"] - del kwargs["keepdir"] - # Not very optimized but currently, this is the simple way - # I've found to delete all related objects (mailboxes, users - # and aliases)! - # for mb in self.mailbox_set.all(): - # mb.delete(keepdir=keepdir) - super(Domain, self).delete(*args, **kwargs) + def delete(self, keepdir=False): + from modoboa.lib.permissions import ungrant_access_to_object, ungrant_access_to_objects + + if len(self.domainalias_set.all()): + events.raiseEvent("DomainAliasDeleted", self.domainalias_set.all()) + ungrant_access_to_objects(self.domainalias_set.all()) + if len(self.mailbox_set.all()): + events.raiseEvent("DeleteMailbox", self.mailbox_set.all()) + ungrant_access_to_objects(self.mailbox_set.all()) + if len(self.alias_set.all()): + events.raiseEvent("MailboxAliasDelete", self.alias_set.all()) + ungrant_access_to_objects(self.alias_set.all()) + events.raiseEvent("DeleteDomain", self) + ungrant_access_to_object(self) + super(Domain, self).delete() if not keepdir: self.delete_dir() @@ -469,6 +473,12 @@ class DomainAlias(DatesAware): def __unicode__(self): return self.name + def delete(self): + from modoboa.lib.permissions import ungrant_access_to_object + events.raiseEvent("DomainAliasDeleted", self) + ungrant_access_to_object(self) + super(DomainAlias, self).delete() + class Mailbox(DatesAware): address = models.CharField( ugettext_lazy('address'), max_length=100, @@ -668,16 +678,16 @@ class Mailbox(DatesAware): self.save(username=line[1].strip(), name=name, password=line[2].strip(), enabled=True) - def delete(self, *args, **kwargs): - keepdir = False - if kwargs.has_key("keepdir"): - keepdir = kwargs["keepdir"] - del kwargs["keepdir"] + def delete(self, keepdir=False): + from modoboa.lib.permissions import ungrant_access_to_object + + events.raiseEvent("DeleteMailbox", self) + ungrant_access_to_object(self) for alias in self.alias_set.all(): alias.mboxes.remove(self) if len(alias.mboxes.all()) == 0: alias.delete() - super(Mailbox, self).delete(*args, **kwargs) + super(Mailbox, self).delete() if not keepdir: self.delete_dir() @@ -731,6 +741,13 @@ class Alias(DatesAware): if not t in int_rcpts: self.mboxes.remove(t) + def delete(self): + from modoboa.lib.permissions import ungrant_access_to_object + + events.raiseEvent("MailboxAliasDeleted", self) + ungrant_access_to_object(self) + super(Alias, self).delete() + def get_recipients(self): """Return the recipients list diff --git a/admin/tests.py b/admin/tests.py index 626db79b6..e70ea044a 100644 --- a/admin/tests.py +++ b/admin/tests.py @@ -183,6 +183,11 @@ class PermissionsTestCase(ModoTestCase): fixtures = ["test_content.json"] def setUp(self): + """ + FIXME: je n'arrive pas à générer des données de test incluant + déjà les permissions (cf. table ObjectAccess). C'est pour + cette raison que j'assigne le domaine test.com ici... + """ from modoboa.lib.permissions import grant_access_to_object super(PermissionsTestCase, self).setUp() @@ -191,9 +196,13 @@ class PermissionsTestCase(ModoTestCase): username=self.user.username, role="DomainAdmins", is_active=self.user.is_active, email="user@test.com" ) - grant_access_to_object(User.objects.get(username="admin@test.com"), - Domain.objects.get(name="test.com")) - + self.admin = User.objects.get(username="admin@test.com") + self.check_ajax_post("/modoboa/admin/accounts/edit/%d/" % self.admin.id, dict( + username=self.admin.username, role="DomainAdmins", + is_active=self.admin.is_active, email="admin@test.com", + password1="", password2="", domains="test.com" + )) + def tearDown(self): self.clt.logout() @@ -224,7 +233,7 @@ class PermissionsTestCase(ModoTestCase): self.assertEqual(self.clt.login(username="admin@test.com", password="toto"), True) admin = User.objects.get(username="admin@test.com") - values = dict(username="admin@test.com", first_name="Admin", + values = dict(username="admin@test.com", first_name="Admin", password1="", password2="", is_active=True, email="admin@test.com") self.check_ajax_post("/modoboa/admin/accounts/edit/%d/" % admin.id, values) self.assertEqual(admin.group, "DomainAdmins") @@ -247,4 +256,6 @@ class PermissionsTestCase(ModoTestCase): HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertNotEqual(response["Content-Type"], "application/json") + + diff --git a/admin/urls.py b/admin/urls.py index d4494d584..55a0e3ae7 100644 --- a/admin/urls.py +++ b/admin/urls.py @@ -10,15 +10,12 @@ urlpatterns = patterns( (r'^domains/delete/$', 'deldomain'), (r'^domains/import/$', 'import_domains'), - (r'^mailboxes/search/$', 'mailboxes_search'), - (r'^settings/$', 'viewparameters'), (r'^settings/parameters/$', 'viewparameters'), (r'^settings/parameters/save/$', 'saveparameters'), (r'^settings/extensions/$', 'viewextensions'), (r'^settings/extensions/save/$', 'saveextensions'), - (r'^permissions/searchaccount/$', 'search_account'), (r'^permissions/remove/$', 'remove_permission'), (r'^identities/$', 'identities'), diff --git a/admin/views.py b/admin/views.py index 7d80d8db0..d79b0e38c 100644 --- a/admin/views.py +++ b/admin/views.py @@ -97,10 +97,9 @@ def editdomain(request, dom_id, tplname="admin/editdomainform.html"): if not request.user.can_access(domain): raise PermDeniedException(_("You can't edit this domain")) - domadmins = map(lambda oaccess: oaccess.user, domain.owners.all()) domadmins = filter( lambda u: request.user.can_access(u) and not u.is_superuser, - domadmins + domain.admins ) if not request.user.is_superuser: domadmins = filter(lambda u: u.group == "DomainAdmins", domadmins) @@ -137,37 +136,12 @@ def deldomain(request): raise PermDeniedException(_("You can't delete this domain")) if mb and mb.domain == dom: raise AdminError(_("You can't delete your own domain")) - - if len(dom.domainalias_set.all()): - events.raiseEvent("DomainAliasDeleted", dom.domainalias_set.all()) - if len(dom.mailbox_set.all()): - events.raiseEvent("DeleteMailbox", dom.mailbox_set.all()) - events.raiseEvent("DeleteDomain", dom) - ungrant_access_to_object(dom) dom.delete(keepdir=keepdir) msg = ungettext("Domain deleted", "Domains deleted", len(selection)) messages.info(request, msg, fail_silently=True) return ajax_response(request) -@login_required -@permission_required("admin.view_mailboxes") -@check_domain_access -def mailboxes_search(request): - if request.method != "POST": - return - local_part, domain = split_mailbox(request.POST["search"]) - query = Q(address__startswith=local_part) - if domain is not None and domain != "": - query &= Q(domain__name__startswith=domain) - if request.GET.has_key("domid"): - query = Q(domain=request.GET["domid"]) & query - elif not request.user.is_superuser: - query = Q(domain__in=request.user.get_domains()) & query - mboxes = Mailbox.objects.filter(query) - result = map(lambda mb: mb.full_address, mboxes) - return ajax_simple_response(result) - def _validate_alias(request, form, successmsg, tplname, commonctx, callback=None): """Alias validation @@ -175,7 +149,7 @@ def _validate_alias(request, form, successmsg, tplname, commonctx, callback=None """ error = None if form.is_valid(): - form.set_recipients(request.user) + form.set_recipients() try: alias = form.save() except IntegrityError: @@ -202,11 +176,14 @@ def _new_alias(request, tplname, formclass, ctx, successmsg): def callback(user, alias): grant_access_to_object(user, alias, is_owner=True) events.raiseEvent("MailboxAliasCreated", user, alias) + if user.is_superuser: + for admin in alias.domain.admins: + grant_access_to_object(admin, alias) - form = formclass(request.POST) + form = formclass(request.user, request.POST) return _validate_alias(request, form, successmsg, tplname, ctx, callback) - form = formclass() + form = formclass(request.user) ctx["form"] = form return _render(request, tplname, ctx) @@ -231,15 +208,15 @@ def newforward(request): ForwardForm, ctx, _("Forward created")) def _edit_alias(request, alias, tplname, formclass, ctx, successmsg): - if not request.user.can_access(alias.domain): - raise PermDeniedException(_("You do not have access to this domain")) + if not request.user.can_access(alias): + raise PermDeniedException(_("You do not have access to this alias")) ctx.update(title=alias.full_address, action_label=_("Update"), action_classes="submit") if request.method == "POST": - form = formclass(request.POST, instance=alias) + form = formclass(request.user, request.POST, instance=alias) return _validate_alias(request, form, successmsg, tplname, ctx) - form = formclass(instance=alias) + form = formclass(request.user, instance=alias) ctx["form"] = form return _render(request, tplname, ctx) @@ -264,10 +241,8 @@ def _del_alias(request, msg, msgs): selection = request.GET["selection"].split(",") for alid in selection: alias = Alias.objects.get(pk=alid) - if not request.user.can_access(alias.domain): - raise PermDeniedException(_("You do not have access to this domain")) - events.raiseEvent("MailboxAliasDeleted", alias) - ungrant_access_to_object(alias) + if not request.user.can_access(alias): + raise PermDeniedException(_("!")) alias.delete() msg = ungettext(msg, msgs, len(selection)) @@ -289,32 +264,29 @@ def delforward(request): @login_required @user_passes_test(lambda u: u.has_perm("auth.add_user") or u.has_perm("admin.add_alias")) def identities(request, tplname='admin/identities.html'): - accounts_list = [] + idents_list = request.user.get_identities() squery = request.GET.get("searchquery", None) - mbalias_q = Q(domain__in=request.user.get_domains()) if squery: - accounts = User.objects.filter(username__contains=squery) + uct = get_content_type(User) + uids = idents_list.filter(content_type=uct).values_list("object_id", flat=True) + objects = [u for u in User.objects.filter(pk__in=uids, username__contains=squery)] + alct = get_content_type(Alias) + alids = idents_list.filter(content_type=alct).values_list("object_id", flat=True) if squery.find('@') != -1: local_part, domname = split_mailbox(squery) mbaliases = Alias.objects.filter(address__contains=local_part, domain__name__contains=domname) + q = Q(address__contains=local_part, domain__name__contains=domname) else: - mbaliases = Alias.objects.filter(mbalias_q & Q(address__contains=squery)) + q = Q(address__contains=squery) + objects += [al for al in Alias.objects.filter(Q(pk__in=alids) & q)] else: - accounts = User.objects.all() - mbaliases = Alias.objects.filter(mbalias_q) - - for account in accounts: - if request.user.can_access(account) or \ - account.has_mailbox and request.user.can_access(account.mailbox_set.all()[0].domain): - accounts_list += [account] - for mbalias in mbaliases: - if len(mbalias.get_recipients()) >= 2 or \ - not len(mbalias.mboxes.all()): - accounts_list += [mbalias] - + objects = [] + for oa in idents_list: + if oa.content_object: + objects.append(oa.content_object) return render_listing(request, "identities", tplname, - objects=accounts_list, squery=squery) + objects=objects, squery=squery) @login_required @permission_required("auth.add_user") @@ -332,7 +304,7 @@ def newaccount(request, tplname='admin/newaccount.html'): def create_account(steps): """Account creation callback - Called when all the creation steps have been validated. + Called when all creation steps have been validated. :param steps: the steps data """ @@ -343,7 +315,7 @@ def newaccount(request, tplname='admin/newaccount.html'): events.raiseEvent("AccountCreated", account) mailform = steps[1]["form"] - mailform.save(request.user, account) + mb = mailform.save(request.user, account) ctx = dict( title=_("New account"), @@ -386,14 +358,11 @@ def newaccount(request, tplname='admin/newaccount.html'): @transaction.commit_on_success def editaccount(request, accountid, tplname="common/tabforms.html"): account = User.objects.get(pk=accountid) + if not request.user.can_access(account): + raise PermDeniedException(_("You can't access this account")) + mb = None if account.has_mailbox: mb = account.mailbox_set.all()[0] - if not request.user.can_access(mb.domain): - raise PermDeniedException(_("You don't have access to this domain")) - else: - if not request.user.can_access(account): - raise PermDeniedException(_("You can't access this account")) - mb = None instances = dict(general=account, mail=mb, perms=account) events.raiseEvent("FillAccountInstances", request.user, account, instances) @@ -440,25 +409,6 @@ def delaccount(request): messages.info(request, msg, fail_silently=True) return ajax_response(request) -@login_required -@permission_required("auth.add_account") -@check_domain_access -def search_account(request): - search = request.POST.get("search", None) - if search is None: - return ajax_simple_response([]) - - query = Q(username__startswith=search) - query &= Q(is_superuser=False) - if not request.user.has_perm("admin.add_domain"): - query &= Q(groups__name="SimpleUsers") - else: - query &= ~Q(groups__name__in=["DomainAdmins"]) - - users = User.objects.filter(query) - result = map(lambda u: u.username, users.all()) - return ajax_simple_response(result) - @login_required @permission_required("admin.add_domain") def remove_permission(request): diff --git a/extensions/amavis_quarantine/__init__.py b/extensions/amavis_quarantine/__init__.py index 38a34d193..8bdda7bda 100644 --- a/extensions/amavis_quarantine/__init__.py +++ b/extensions/amavis_quarantine/__init__.py @@ -113,10 +113,17 @@ def modify_user_record(mailbox, oldmailbox): u.save() @events.observe("DeleteMailbox") -def remove_user_record(mailbox): +def remove_user_record(mailboxes): from models import Users + from modoboa.admin.models import Mailbox - Users.objects.get(email=mailbox.full_address).delete() + if isinstance(mailboxes, Mailbox): + mailboxes = [mailboxes] + for mailbox in mailboxes: + try: + Users.objects.get(email=mailbox.full_address).delete() + except Users.DoesNotExist: + pass @events.observe("GetStaticContent") def extra_static_content(user): diff --git a/extensions/limits/controls.py b/extensions/limits/controls.py index fc956bfd6..a42b07e26 100644 --- a/extensions/limits/controls.py +++ b/extensions/limits/controls.py @@ -32,6 +32,8 @@ def inc_limit(user, lname): pass def dec_limit(user, lname): + if user is None: + return try: user.limitspool.dec_curvalue(lname) except LimitsPool.DoesNotExist: @@ -96,7 +98,6 @@ def dec_nb_mailboxes(mailboxes): for mailbox in mailboxes: owner = get_object_owner(mailbox) dec_limit(owner, 'mailboxes_limit') - # FIXME: Gestion des alias ici @events.observe('MailboxAliasCreated') def inc_nb_mbaliases(user, mailboxalias): diff --git a/lib/permissions.py b/lib/permissions.py index 85114c25d..92f45f324 100644 --- a/lib/permissions.py +++ b/lib/permissions.py @@ -78,6 +78,15 @@ def ungrant_access_to_object(obj, user=None): else: ObjectAccess.objects.filter(content_type=ct, object_id=obj.id).delete() +def ungrant_access_to_objects(objects): + """Cancel all accesses for a given objects list + + :param objects: a list of objects inheriting from ``model.Model`` + """ + for obj in objects: + ct = get_content_type(obj) + ObjectAccess.objects.filter(content_type=ct, object_id=obj.id).delete() + def get_object_owner(obj): """Return the unique owner of this object @@ -90,29 +99,3 @@ def get_object_owner(obj): except ObjectAccess.DoesNotExist: return None return entry.user - -def check_domain_access(f): - """Decorator to check if the current user can access a domain - - This decorator only applies to url that contain a 'domid' or - 'mbid' parameter. (for a direct access) - - Raise a ``PermDeniedError`` if the current user can't view the - requested object. - """ - @wraps(f) - def dec(request, **kwargs): - if not request.user.is_superuser: - domain = None - domid = request.GET.get("domid", None) - if domid: - domain = Domain.objects.get(pk=domid) - else: - mbid = request.GET.get("mbid", None) - if mbid: - domain = Mailbox.objects.get(pk=mbid).domain - if domain and not request.user.can_access(domain): - raise PermDeniedError(_("You can't access this domain")) - - return f(request, **kwargs) - return dec