Repository: paperless-ngx/paperless-ngx

Issue:
[BUG] Permissions to delete Email-Rules/Accounts: disabled for a user, but user can still delete them
### Description

First the issue description: 
A user without "delete"-permissions for email-rules can still delete them also when the rule was created by another user.

Second a question:
Is it possible for a user to fully manage his own email-rules/accounts without seeing the rules/accounts from other users?

### Steps to reproduce

1. Create a user (e.g. test-user) with mail-rule/mail-account add/edit/read permissions (no delete permission).
2. Create a mail-rule with other user (e.g. admin user)
3. Login with the "test-user"
4. Go to settings -> email 
5. Delete the mail-rule created by admin-user

Also the mail-account can be deleted without delete-permissions.

### Webserver logs

```bash
No logs are visible
```


### Browser logs

_No response_

### Paperless-ngx version

1.16.5

### Host OS

Debian x86_64

### Installation method

Docker - official image

### Browser

_No response_

### Configuration changes

_No response_

### Other

_No response_


Discussion:

Resolving patch:
diff --git a/src/paperless_mail/admin.py b/src/paperless_mail/admin.py
--- a/src/paperless_mail/admin.py
+++ b/src/paperless_mail/admin.py
@@ -1,6 +1,7 @@
 from django import forms
 from django.contrib import admin
 from django.utils.translation import gettext_lazy as _
+from guardian.admin import GuardedModelAdmin
 
 from paperless_mail.models import MailAccount
 from paperless_mail.models import MailRule
@@ -31,7 +32,7 @@ class Meta:
         ]
 
 
-class MailAccountAdmin(admin.ModelAdmin):
+class MailAccountAdmin(GuardedModelAdmin):
     list_display = ("name", "imap_server", "username")
 
     fieldsets = [
@@ -45,7 +46,7 @@ class MailAccountAdmin(admin.ModelAdmin):
     form = MailAccountAdminForm
 
 
-class MailRuleAdmin(admin.ModelAdmin):
+class MailRuleAdmin(GuardedModelAdmin):
     radio_fields = {
         "attachment_type": admin.VERTICAL,
         "action": admin.VERTICAL,
diff --git a/src/paperless_mail/serialisers.py b/src/paperless_mail/serialisers.py
--- a/src/paperless_mail/serialisers.py
+++ b/src/paperless_mail/serialisers.py
@@ -25,7 +25,6 @@ class MailAccountSerializer(OwnedObjectSerializer):
 
     class Meta:
         model = MailAccount
-        depth = 1
         fields = [
             "id",
             "name",
@@ -36,6 +35,10 @@ class Meta:
             "password",
             "character_set",
             "is_token",
+            "owner",
+            "user_can_change",
+            "permissions",
+            "set_permissions",
         ]
 
     def update(self, instance, validated_data):
@@ -67,7 +70,6 @@ class MailRuleSerializer(OwnedObjectSerializer):
 
     class Meta:
         model = MailRule
-        depth = 1
         fields = [
             "id",
             "name",
@@ -89,6 +91,10 @@ class Meta:
             "order",
             "attachment_type",
             "consumption_scope",
+            "owner",
+            "user_can_change",
+            "permissions",
+            "set_permissions",
         ]
 
     def update(self, instance, validated_data):
diff --git a/src/paperless_mail/views.py b/src/paperless_mail/views.py
--- a/src/paperless_mail/views.py
+++ b/src/paperless_mail/views.py
@@ -7,6 +7,8 @@
 from rest_framework.response import Response
 from rest_framework.viewsets import ModelViewSet
 
+from documents.filters import ObjectOwnedOrGrantedPermissionsFilter
+from documents.permissions import PaperlessObjectPermissions
 from documents.views import PassUserMixin
 from paperless.views import StandardPagination
 from paperless_mail.mail import MailError
@@ -24,7 +26,8 @@ class MailAccountViewSet(ModelViewSet, PassUserMixin):
     queryset = MailAccount.objects.all().order_by("pk")
     serializer_class = MailAccountSerializer
     pagination_class = StandardPagination
-    permission_classes = (IsAuthenticated,)
+    permission_classes = (IsAuthenticated, PaperlessObjectPermissions)
+    filter_backends = (ObjectOwnedOrGrantedPermissionsFilter,)
 
 
 class MailRuleViewSet(ModelViewSet, PassUserMixin):
@@ -33,7 +36,8 @@ class MailRuleViewSet(ModelViewSet, PassUserMixin):
     queryset = MailRule.objects.all().order_by("order")
     serializer_class = MailRuleSerializer
     pagination_class = StandardPagination
-    permission_classes = (IsAuthenticated,)
+    permission_classes = (IsAuthenticated, PaperlessObjectPermissions)
+    filter_backends = (ObjectOwnedOrGrantedPermissionsFilter,)
 
 
 class MailAccountTestView(GenericAPIView):


Test patch:
diff --git a/src/paperless_mail/tests/test_api.py b/src/paperless_mail/tests/test_api.py
--- a/src/paperless_mail/tests/test_api.py
+++ b/src/paperless_mail/tests/test_api.py
@@ -1,7 +1,9 @@
 import json
 from unittest import mock
 
+from django.contrib.auth.models import Permission
 from django.contrib.auth.models import User
+from guardian.shortcuts import assign_perm
 from rest_framework import status
 from rest_framework.test import APITestCase
 
@@ -27,7 +29,9 @@ def setUp(self):
 
         super().setUp()
 
-        self.user = User.objects.create_superuser(username="temp_admin")
+        self.user = User.objects.create_user(username="temp_admin")
+        self.user.user_permissions.add(*Permission.objects.all())
+        self.user.save()
         self.client.force_authenticate(user=self.user)
 
     def test_get_mail_accounts(self):
@@ -266,6 +270,73 @@ def test_mail_account_test_existing(self):
         self.assertEqual(response.status_code, status.HTTP_200_OK)
         self.assertEqual(response.data["success"], True)
 
+    def test_get_mail_accounts_owner_aware(self):
+        """
+        GIVEN:
+            - Configured accounts with different users
+        WHEN:
+            - API call is made to get mail accounts
+        THEN:
+            - Only unowned, owned by user or granted accounts are provided
+        """
+
+        user2 = User.objects.create_user(username="temp_admin2")
+
+        account1 = MailAccount.objects.create(
+            name="Email1",
+            username="username1",
+            password="password1",
+            imap_server="server.example.com",
+            imap_port=443,
+            imap_security=MailAccount.ImapSecurity.SSL,
+            character_set="UTF-8",
+        )
+
+        account2 = MailAccount.objects.create(
+            name="Email2",
+            username="username2",
+            password="password2",
+            imap_server="server.example.com",
+            imap_port=443,
+            imap_security=MailAccount.ImapSecurity.SSL,
+            character_set="UTF-8",
+        )
+        account2.owner = self.user
+        account2.save()
+
+        account3 = MailAccount.objects.create(
+            name="Email3",
+            username="username3",
+            password="password3",
+            imap_server="server.example.com",
+            imap_port=443,
+            imap_security=MailAccount.ImapSecurity.SSL,
+            character_set="UTF-8",
+        )
+        account3.owner = user2
+        account3.save()
+
+        account4 = MailAccount.objects.create(
+            name="Email4",
+            username="username4",
+            password="password4",
+            imap_server="server.example.com",
+            imap_port=443,
+            imap_security=MailAccount.ImapSecurity.SSL,
+            character_set="UTF-8",
+        )
+        account4.owner = user2
+        account4.save()
+        assign_perm("view_mailaccount", self.user, account4)
+
+        response = self.client.get(self.ENDPOINT)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(response.data["count"], 3)
+        self.assertEqual(response.data["results"][0]["name"], account1.name)
+        self.assertEqual(response.data["results"][1]["name"], account2.name)
+        self.assertEqual(response.data["results"][2]["name"], account4.name)
+
 
 class TestAPIMailRules(DirectoriesMixin, APITestCase):
     ENDPOINT = "/api/mail_rules/"
@@ -273,7 +344,9 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
     def setUp(self):
         super().setUp()
 
-        self.user = User.objects.create_superuser(username="temp_admin")
+        self.user = User.objects.create_user(username="temp_admin")
+        self.user.user_permissions.add(*Permission.objects.all())
+        self.user.save()
         self.client.force_authenticate(user=self.user)
 
     def test_get_mail_rules(self):
@@ -533,3 +606,72 @@ def test_update_mail_rule(self):
         returned_rule1 = MailRule.objects.get(pk=rule1.pk)
         self.assertEqual(returned_rule1.name, "Updated Name 1")
         self.assertEqual(returned_rule1.action, MailRule.MailAction.DELETE)
+
+    def test_get_mail_rules_owner_aware(self):
+        """
+        GIVEN:
+            - Configured rules with different users
+        WHEN:
+            - API call is made to get mail rules
+        THEN:
+            - Only unowned, owned by user or granted mail rules are provided
+        """
+
+        user2 = User.objects.create_user(username="temp_admin2")
+
+        account1 = MailAccount.objects.create(
+            name="Email1",
+            username="username1",
+            password="password1",
+            imap_server="server.example.com",
+            imap_port=443,
+            imap_security=MailAccount.ImapSecurity.SSL,
+            character_set="UTF-8",
+        )
+
+        rule1 = MailRule.objects.create(
+            name="Rule1",
+            account=account1,
+            folder="INBOX",
+            filter_from="from@example1.com",
+            order=0,
+        )
+
+        rule2 = MailRule.objects.create(
+            name="Rule2",
+            account=account1,
+            folder="INBOX",
+            filter_from="from@example2.com",
+            order=1,
+        )
+        rule2.owner = self.user
+        rule2.save()
+
+        rule3 = MailRule.objects.create(
+            name="Rule3",
+            account=account1,
+            folder="INBOX",
+            filter_from="from@example3.com",
+            order=2,
+        )
+        rule3.owner = user2
+        rule3.save()
+
+        rule4 = MailRule.objects.create(
+            name="Rule4",
+            account=account1,
+            folder="INBOX",
+            filter_from="from@example4.com",
+            order=3,
+        )
+        rule4.owner = user2
+        rule4.save()
+        assign_perm("view_mailrule", self.user, rule4)
+
+        response = self.client.get(self.ENDPOINT)
+
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        self.assertEqual(response.data["count"], 3)
+        self.assertEqual(response.data["results"][0]["name"], rule1.name)
+        self.assertEqual(response.data["results"][1]["name"], rule2.name)
+        self.assertEqual(response.data["results"][2]["name"], rule4.name)