diff --git a/modoboa/admin/api/v1/viewsets.py b/modoboa/admin/api/v1/viewsets.py index 9fa0d4dd6..b1dc5aa84 100644 --- a/modoboa/admin/api/v1/viewsets.py +++ b/modoboa/admin/api/v1/viewsets.py @@ -192,6 +192,18 @@ class AccountViewSet( return Response(body) +class AliasFilter(dj_filters.FilterSet): + + domain = dj_filters.ModelChoiceFilter( + queryset=lambda request: models.Domain.objects.get_for_admin(request.user), + field_name="mailbox__domain", + ) + + class Meta: + model = models.Alias + fields = ["domain"] + + class AliasViewSet( GetThrottleViewsetMixin, lib_viewsets.RevisionModelMixin, viewsets.ModelViewSet ): @@ -200,6 +212,10 @@ class AliasViewSet( Create a new alias instance. """ + filter_backends = [ + dj_filters.DjangoFilterBackend, + ] + filterset_class = AliasFilter permission_classes = [ IsAuthenticated, DjangoModelPermissions, @@ -209,14 +225,12 @@ class AliasViewSet( def get_queryset(self): """Filter queryset based on current user.""" user = self.request.user + if user is None or user.is_anonymous: + return models.Alias.objects.none() ids = user.objectaccess_set.filter( content_type=ContentType.objects.get_for_model(models.Alias) ).values_list("object_id", flat=True) - queryset = models.Alias.objects.filter(pk__in=ids) - domain = self.request.query_params.get("domain") - if domain: - queryset = queryset.filter(domain__name=domain) - return queryset + return models.Alias.objects.filter(pk__in=ids, internal=False) class SenderAddressFilterSet(dj_filters.FilterSet): diff --git a/modoboa/admin/api/v2/serializers.py b/modoboa/admin/api/v2/serializers.py index 49dbb27ff..532093049 100644 --- a/modoboa/admin/api/v2/serializers.py +++ b/modoboa/admin/api/v2/serializers.py @@ -521,7 +521,8 @@ class WritableAccountSerializer(v1_serializers.WritableAccountSerializer): if not param_tools.get_global_parameter("enable_admin_limits", app="limits"): return del self.fields["domains"] - if self.context["request"].user.role in ["Resellers", "SuperAdmins"]: + user = self.context["request"].user + if not user.is_anonymous and user.role in ["Resellers", "SuperAdmins"]: self.fields["resources"] = WritableResourceSerializer( many=True, required=False ) diff --git a/modoboa/admin/api/v2/viewsets.py b/modoboa/admin/api/v2/viewsets.py index 16e7f2653..306f0bcb3 100644 --- a/modoboa/admin/api/v2/viewsets.py +++ b/modoboa/admin/api/v2/viewsets.py @@ -74,7 +74,9 @@ class DomainViewSet( def get_queryset(self): """Filter queryset based on current user.""" - return models.Domain.objects.get_for_admin(self.request.user) + if self.request.user and not self.request.user.is_anonymous: + return models.Domain.objects.get_for_admin(self.request.user) + return models.Domain.objects.none() def get_serializer_class(self, *args, **kwargs): if self.action == "delete": @@ -198,6 +200,8 @@ class AccountViewSet(v1_viewsets.AccountViewSet): def get_queryset(self): """Filter queryset based on current user.""" user = self.request.user + if not user or user.is_anonymous: + return core_models.User.objects.none() ids = user.objectaccess_set.filter( content_type=ContentType.objects.get_for_model(user) ).values_list("object_id", flat=True) @@ -271,14 +275,27 @@ class IdentityViewSet(GetThrottleViewsetMixin, viewsets.ViewSet): return response.Response({"status": status, "message": msg}) +class AliasFilter(dj_filters.FilterSet): + + domain = dj_filters.ModelChoiceFilter( + queryset=lambda request: models.Domain.objects.get_for_admin(request.user), + field_name="domain__name", + ) + + class Meta: + model = models.Alias + fields = ["domain"] + + class AliasViewSet(v1_viewsets.AliasViewSet): """Viewset for Alias.""" - filter_backends = ( + filter_backends = [ + dj_filters.DjangoFilterBackend, filters.OrderingFilter, filters.SearchFilter, - dj_filters.DjangoFilterBackend, - ) + ] + filterset_class = AliasFilter pagination_class = pagination.CustomPageNumberPagination search_fields = ["address"] serializer_class = serializers.AliasSerializer @@ -391,6 +408,8 @@ class AlarmViewSet( serializer_class = serializers.AlarmSerializer def get_queryset(self): + if self.request.user.is_anonymous: + return models.Alarm.objects.none() return ( models.Alarm.objects.select_related("domain") .filter(domain__in=models.Domain.objects.get_for_admin(self.request.user)) diff --git a/modoboa/admin/handlers.py b/modoboa/admin/handlers.py index 4634d0bce..35aa42561 100644 --- a/modoboa/admin/handlers.py +++ b/modoboa/admin/handlers.py @@ -236,11 +236,6 @@ def import_account_mailbox(sender, user, account, row, **kwargs): sender="import", context=domain, object_type="mailboxes" ) account.save() - qset = models.Mailbox.objects.filter(address=mailbox, domain=domain) - if qset.exists(): - raise exceptions.Conflict( - _("Mailbox {} already exists").format(account.email) - ) if len(row) == 1: quota = None else: @@ -274,16 +269,16 @@ def grant_access_to_all_objects(sender, account, role, **kwargs): """Grant all permissions if new role is SuperAdmin.""" if role != "SuperAdmins": return - perm_models = [ - core_models.User, - models.Domain, - models.DomainAlias, - models.Mailbox, - models.Alias, + querysets = [ + core_models.User.objects.all(), + models.Domain.objects.all(), + models.DomainAlias.objects.all(), + models.Mailbox.objects.all(), + models.Alias.objects.filter(internal=False), ] - for model in perm_models: + for queryset in querysets: permissions.grant_access_to_objects( - account, model.objects.all(), ContentType.objects.get_for_model(model) + account, queryset, ContentType.objects.get_for_model(queryset.model) ) diff --git a/modoboa/admin/management/commands/subcommands/_repair.py b/modoboa/admin/management/commands/subcommands/_repair.py index f17a54a50..3774e400a 100644 --- a/modoboa/admin/management/commands/subcommands/_repair.py +++ b/modoboa/admin/management/commands/subcommands/_repair.py @@ -56,7 +56,7 @@ def sometimes_objects_have_no_owner(**options): User.objects.all(), models.Domain.objects.all(), models.DomainAlias.objects.all(), - models.Alias.objects.filter(domain__isnull=False), + models.Alias.objects.filter(internal=False, domain__isnull=False), models.Mailbox.objects.filter(domain__isnull=False), ) for qs in owned_models: diff --git a/modoboa/admin/models/domain.py b/modoboa/admin/models/domain.py index ad5bef7dd..24da5bba0 100644 --- a/modoboa/admin/models/domain.py +++ b/modoboa/admin/models/domain.py @@ -257,7 +257,7 @@ class Domain(mixins.MessageLimitMixin, AdminObject): continue grant_access_to_object(account, mb) grant_access_to_object(account, mb.user) - for al in self.alias_set.all(): + for al in self.alias_set.filter(internal=False): grant_access_to_object(account, al) def remove_admin(self, account): diff --git a/modoboa/core/commands/templates/settings.py.tpl b/modoboa/core/commands/templates/settings.py.tpl index 8d5ced9e5..ad9246b76 100644 --- a/modoboa/core/commands/templates/settings.py.tpl +++ b/modoboa/core/commands/templates/settings.py.tpl @@ -71,6 +71,7 @@ INSTALLED_APPS = ( 'corsheaders', 'rest_framework', 'rest_framework.authtoken', + 'django_filters', 'drf_spectacular', 'phonenumber_field', 'django_otp', @@ -246,6 +247,9 @@ SPECTACULAR_SETTINGS = { 'TITLE': 'Modoboa API', 'VERSION': None, 'SERVE_AUTHENTICATION': [], + 'DEFAULT_FILTER_INSPECTORS': [ + 'drf_spectacular.contrib.django_filters.DjangoFilterBackendInspector', + ], } # Modoboa settings diff --git a/test_project/test_project/settings.py b/test_project/test_project/settings.py index e6871d18c..3a55d6b6a 100644 --- a/test_project/test_project/settings.py +++ b/test_project/test_project/settings.py @@ -79,6 +79,7 @@ INSTALLED_APPS = ( "corsheaders", "rest_framework", "rest_framework.authtoken", + "django_filters", "drf_spectacular", "django_otp", "django_otp.plugins.otp_totp", @@ -262,6 +263,9 @@ SPECTACULAR_SETTINGS = { "TITLE": "Modoboa API", "VERSION": None, "SERVE_AUTHENTICATION": [], + "DEFAULT_FILTER_INSPECTORS": [ + "drf_spectacular.contrib.django_filters.DjangoFilterBackendInspector", + ], } # Modoboa settings