We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Listing all permissions
**Describe the bug**
When attempting to list all the permissions in order to select which ones to assign to a permission, hangs when gets to 141+

**To Reproduce**
Steps to reproduce the behavior:

1. Go to 'Directory'
2. Click on 'Roles'
3. Select (or create a new) role.
4. Click on 'Assign Permissions'
5. Click on '+' to Select Permissions to Grant
6. Scroll through permissions by clicking on '>' until arrive at 121 - 140

**Expected behavior**
Continue displaying all permissions until reaching end of list (Displays 377 Permissions)

**Screenshots**
![image](https://github.com/goauthentik/authentik/assets/54423468/4cbd1788-1066-49df-84e8-1a9f8f00dfbc)

**Logs**
[logs](https://pastebin.com/v4Fd6PDN)

**Version and Deployment (please complete the following information):**

-   authentik version: 2023.10.1
-   Deployment: docker-compose

**Additional context**
Upgraded from 2023.8 -> 2023.10.0 -> 2023.10.1

Can provide more logs or anything else, if needed.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `authentik/rbac/api/rbac.py`
Content:
```
1 """common RBAC serializers"""
2 from django.apps import apps
3 from django.contrib.auth.models import Permission
4 from django.db.models import QuerySet
5 from django_filters.filters import ModelChoiceFilter
6 from django_filters.filterset import FilterSet
7 from rest_framework.exceptions import ValidationError
8 from rest_framework.fields import (
9     CharField,
10     ChoiceField,
11     ListField,
12     ReadOnlyField,
13     SerializerMethodField,
14 )
15 from rest_framework.serializers import ModelSerializer
16 from rest_framework.viewsets import ReadOnlyModelViewSet
17 
18 from authentik.core.api.utils import PassiveSerializer
19 from authentik.core.models import User
20 from authentik.lib.validators import RequiredTogetherValidator
21 from authentik.policies.event_matcher.models import model_choices
22 from authentik.rbac.models import Role
23 
24 
25 class PermissionSerializer(ModelSerializer):
26     """Global permission"""
27 
28     app_label = ReadOnlyField(source="content_type.app_label")
29     app_label_verbose = SerializerMethodField()
30     model = ReadOnlyField(source="content_type.model")
31     model_verbose = SerializerMethodField()
32 
33     def get_app_label_verbose(self, instance: Permission) -> str:
34         """Human-readable app label"""
35         return apps.get_app_config(instance.content_type.app_label).verbose_name
36 
37     def get_model_verbose(self, instance: Permission) -> str:
38         """Human-readable model name"""
39         return apps.get_model(
40             instance.content_type.app_label, instance.content_type.model
41         )._meta.verbose_name
42 
43     class Meta:
44         model = Permission
45         fields = [
46             "id",
47             "name",
48             "codename",
49             "model",
50             "app_label",
51             "app_label_verbose",
52             "model_verbose",
53         ]
54 
55 
56 class PermissionFilter(FilterSet):
57     """Filter permissions"""
58 
59     role = ModelChoiceFilter(queryset=Role.objects.all(), method="filter_role")
60     user = ModelChoiceFilter(queryset=User.objects.all())
61 
62     def filter_role(self, queryset: QuerySet, name, value: Role) -> QuerySet:
63         """Filter permissions based on role"""
64         return queryset.filter(group__role=value)
65 
66     class Meta:
67         model = Permission
68         fields = [
69             "codename",
70             "content_type__model",
71             "content_type__app_label",
72             "role",
73             "user",
74         ]
75 
76 
77 class RBACPermissionViewSet(ReadOnlyModelViewSet):
78     """Read-only list of all permissions, filterable by model and app"""
79 
80     queryset = Permission.objects.none()
81     serializer_class = PermissionSerializer
82     ordering = ["name"]
83     filterset_class = PermissionFilter
84     search_fields = [
85         "codename",
86         "content_type__model",
87         "content_type__app_label",
88     ]
89 
90     def get_queryset(self) -> QuerySet:
91         return (
92             Permission.objects.all()
93             .select_related("content_type")
94             .filter(
95                 content_type__app_label__startswith="authentik",
96             )
97         )
98 
99 
100 class PermissionAssignSerializer(PassiveSerializer):
101     """Request to assign a new permission"""
102 
103     permissions = ListField(child=CharField())
104     model = ChoiceField(choices=model_choices(), required=False)
105     object_pk = CharField(required=False)
106 
107     validators = [RequiredTogetherValidator(fields=["model", "object_pk"])]
108 
109     def validate(self, attrs: dict) -> dict:
110         model_instance = None
111         # Check if we're setting an object-level perm or global
112         model = attrs.get("model")
113         object_pk = attrs.get("object_pk")
114         if model and object_pk:
115             model = apps.get_model(attrs["model"])
116             model_instance = model.objects.filter(pk=attrs["object_pk"]).first()
117         attrs["model_instance"] = model_instance
118         if attrs.get("model"):
119             return attrs
120         permissions = attrs.get("permissions", [])
121         if not all("." in perm for perm in permissions):
122             raise ValidationError(
123                 {
124                     "permissions": (
125                         "When assigning global permissions, codename must be given as "
126                         "app_label.codename"
127                     )
128                 }
129             )
130         return attrs
131
```

Path: `authentik/rbac/api/rbac_roles.py`
Content:
```
1 """common RBAC serializers"""
2 from typing import Optional
3 
4 from django.apps import apps
5 from django_filters.filters import UUIDFilter
6 from django_filters.filterset import FilterSet
7 from guardian.models import GroupObjectPermission
8 from guardian.shortcuts import get_objects_for_group
9 from rest_framework.fields import SerializerMethodField
10 from rest_framework.mixins import ListModelMixin
11 from rest_framework.viewsets import GenericViewSet
12 
13 from authentik.api.pagination import SmallerPagination
14 from authentik.rbac.api.rbac_assigned_by_roles import RoleObjectPermissionSerializer
15 
16 
17 class ExtraRoleObjectPermissionSerializer(RoleObjectPermissionSerializer):
18     """User permission with additional object-related data"""
19 
20     app_label_verbose = SerializerMethodField()
21     model_verbose = SerializerMethodField()
22 
23     object_description = SerializerMethodField()
24 
25     def get_app_label_verbose(self, instance: GroupObjectPermission) -> str:
26         """Get app label from permission's model"""
27         return apps.get_app_config(instance.content_type.app_label).verbose_name
28 
29     def get_model_verbose(self, instance: GroupObjectPermission) -> str:
30         """Get model label from permission's model"""
31         return apps.get_model(
32             instance.content_type.app_label, instance.content_type.model
33         )._meta.verbose_name
34 
35     def get_object_description(self, instance: GroupObjectPermission) -> Optional[str]:
36         """Get model description from attached model. This operation takes at least
37         one additional query, and the description is only shown if the user/role has the
38         view_ permission on the object"""
39         app_label = instance.content_type.app_label
40         model = instance.content_type.model
41         model_class = apps.get_model(app_label, model)
42         objects = get_objects_for_group(instance.group, f"{app_label}.view_{model}", model_class)
43         obj = objects.first()
44         if not obj:
45             return None
46         return str(obj)
47 
48     class Meta(RoleObjectPermissionSerializer.Meta):
49         fields = RoleObjectPermissionSerializer.Meta.fields + [
50             "app_label_verbose",
51             "model_verbose",
52             "object_description",
53         ]
54 
55 
56 class RolePermissionFilter(FilterSet):
57     """Role permission filter"""
58 
59     uuid = UUIDFilter("group__role__uuid", required=True)
60 
61 
62 class RolePermissionViewSet(ListModelMixin, GenericViewSet):
63     """Get a role's assigned object permissions"""
64 
65     serializer_class = ExtraRoleObjectPermissionSerializer
66     ordering = ["group__role__name"]
67     pagination_class = SmallerPagination
68     # The filtering is done in the filterset,
69     # which has a required filter that does the heavy lifting
70     queryset = GroupObjectPermission.objects.select_related("content_type", "group__role").all()
71     filterset_class = RolePermissionFilter
72
```

Path: `authentik/rbac/api/rbac_users.py`
Content:
```
1 """common RBAC serializers"""
2 from typing import Optional
3 
4 from django.apps import apps
5 from django_filters.filters import NumberFilter
6 from django_filters.filterset import FilterSet
7 from guardian.models import UserObjectPermission
8 from guardian.shortcuts import get_objects_for_user
9 from rest_framework.fields import SerializerMethodField
10 from rest_framework.mixins import ListModelMixin
11 from rest_framework.viewsets import GenericViewSet
12 
13 from authentik.api.pagination import SmallerPagination
14 from authentik.rbac.api.rbac_assigned_by_users import UserObjectPermissionSerializer
15 
16 
17 class ExtraUserObjectPermissionSerializer(UserObjectPermissionSerializer):
18     """User permission with additional object-related data"""
19 
20     app_label_verbose = SerializerMethodField()
21     model_verbose = SerializerMethodField()
22 
23     object_description = SerializerMethodField()
24 
25     def get_app_label_verbose(self, instance: UserObjectPermission) -> str:
26         """Get app label from permission's model"""
27         return apps.get_app_config(instance.content_type.app_label).verbose_name
28 
29     def get_model_verbose(self, instance: UserObjectPermission) -> str:
30         """Get model label from permission's model"""
31         return apps.get_model(
32             instance.content_type.app_label, instance.content_type.model
33         )._meta.verbose_name
34 
35     def get_object_description(self, instance: UserObjectPermission) -> Optional[str]:
36         """Get model description from attached model. This operation takes at least
37         one additional query, and the description is only shown if the user/role has the
38         view_ permission on the object"""
39         app_label = instance.content_type.app_label
40         model = instance.content_type.model
41         model_class = apps.get_model(app_label, model)
42         objects = get_objects_for_user(instance.user, f"{app_label}.view_{model}", model_class)
43         obj = objects.first()
44         if not obj:
45             return None
46         return str(obj)
47 
48     class Meta(UserObjectPermissionSerializer.Meta):
49         fields = UserObjectPermissionSerializer.Meta.fields + [
50             "app_label_verbose",
51             "model_verbose",
52             "object_description",
53         ]
54 
55 
56 class UserPermissionFilter(FilterSet):
57     """User-assigned permission filter"""
58 
59     user_id = NumberFilter("user__id", required=True)
60 
61 
62 class UserPermissionViewSet(ListModelMixin, GenericViewSet):
63     """Get a users's assigned object permissions"""
64 
65     serializer_class = ExtraUserObjectPermissionSerializer
66     ordering = ["user__username"]
67     pagination_class = SmallerPagination
68     # The filtering is done in the filterset,
69     # which has a required filter that does the heavy lifting
70     queryset = UserObjectPermission.objects.select_related("content_type", "user").all()
71     filterset_class = UserPermissionFilter
72
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/authentik/rbac/api/rbac.py b/authentik/rbac/api/rbac.py
--- a/authentik/rbac/api/rbac.py
+++ b/authentik/rbac/api/rbac.py
@@ -32,13 +32,19 @@
 
     def get_app_label_verbose(self, instance: Permission) -> str:
         """Human-readable app label"""
-        return apps.get_app_config(instance.content_type.app_label).verbose_name
+        try:
+            return apps.get_app_config(instance.content_type.app_label).verbose_name
+        except LookupError:
+            return f"{instance.content_type.app_label}.{instance.content_type.model}"
 
     def get_model_verbose(self, instance: Permission) -> str:
         """Human-readable model name"""
-        return apps.get_model(
-            instance.content_type.app_label, instance.content_type.model
-        )._meta.verbose_name
+        try:
+            return apps.get_model(
+                instance.content_type.app_label, instance.content_type.model
+            )._meta.verbose_name
+        except LookupError:
+            return f"{instance.content_type.app_label}.{instance.content_type.model}"
 
     class Meta:
         model = Permission
diff --git a/authentik/rbac/api/rbac_roles.py b/authentik/rbac/api/rbac_roles.py
--- a/authentik/rbac/api/rbac_roles.py
+++ b/authentik/rbac/api/rbac_roles.py
@@ -28,9 +28,12 @@
 
     def get_model_verbose(self, instance: GroupObjectPermission) -> str:
         """Get model label from permission's model"""
-        return apps.get_model(
-            instance.content_type.app_label, instance.content_type.model
-        )._meta.verbose_name
+        try:
+            return apps.get_model(
+                instance.content_type.app_label, instance.content_type.model
+            )._meta.verbose_name
+        except LookupError:
+            return f"{instance.content_type.app_label}.{instance.content_type.model}"
 
     def get_object_description(self, instance: GroupObjectPermission) -> Optional[str]:
         """Get model description from attached model. This operation takes at least
@@ -38,7 +41,10 @@
         view_ permission on the object"""
         app_label = instance.content_type.app_label
         model = instance.content_type.model
-        model_class = apps.get_model(app_label, model)
+        try:
+            model_class = apps.get_model(app_label, model)
+        except LookupError:
+            return None
         objects = get_objects_for_group(instance.group, f"{app_label}.view_{model}", model_class)
         obj = objects.first()
         if not obj:
diff --git a/authentik/rbac/api/rbac_users.py b/authentik/rbac/api/rbac_users.py
--- a/authentik/rbac/api/rbac_users.py
+++ b/authentik/rbac/api/rbac_users.py
@@ -28,9 +28,12 @@
 
     def get_model_verbose(self, instance: UserObjectPermission) -> str:
         """Get model label from permission's model"""
-        return apps.get_model(
-            instance.content_type.app_label, instance.content_type.model
-        )._meta.verbose_name
+        try:
+            return apps.get_model(
+                instance.content_type.app_label, instance.content_type.model
+            )._meta.verbose_name
+        except LookupError:
+            return f"{instance.content_type.app_label}.{instance.content_type.model}"
 
     def get_object_description(self, instance: UserObjectPermission) -> Optional[str]:
         """Get model description from attached model. This operation takes at least
@@ -38,7 +41,10 @@
         view_ permission on the object"""
         app_label = instance.content_type.app_label
         model = instance.content_type.model
-        model_class = apps.get_model(app_label, model)
+        try:
+            model_class = apps.get_model(app_label, model)
+        except LookupError:
+            return None
         objects = get_objects_for_user(instance.user, f"{app_label}.view_{model}", model_class)
         obj = objects.first()
         if not obj:

{"golden_diff": "diff --git a/authentik/rbac/api/rbac.py b/authentik/rbac/api/rbac.py\n--- a/authentik/rbac/api/rbac.py\n+++ b/authentik/rbac/api/rbac.py\n@@ -32,13 +32,19 @@\n \n     def get_app_label_verbose(self, instance: Permission) -> str:\n         \"\"\"Human-readable app label\"\"\"\n-        return apps.get_app_config(instance.content_type.app_label).verbose_name\n+        try:\n+            return apps.get_app_config(instance.content_type.app_label).verbose_name\n+        except LookupError:\n+            return f\"{instance.content_type.app_label}.{instance.content_type.model}\"\n \n     def get_model_verbose(self, instance: Permission) -> str:\n         \"\"\"Human-readable model name\"\"\"\n-        return apps.get_model(\n-            instance.content_type.app_label, instance.content_type.model\n-        )._meta.verbose_name\n+        try:\n+            return apps.get_model(\n+                instance.content_type.app_label, instance.content_type.model\n+            )._meta.verbose_name\n+        except LookupError:\n+            return f\"{instance.content_type.app_label}.{instance.content_type.model}\"\n \n     class Meta:\n         model = Permission\ndiff --git a/authentik/rbac/api/rbac_roles.py b/authentik/rbac/api/rbac_roles.py\n--- a/authentik/rbac/api/rbac_roles.py\n+++ b/authentik/rbac/api/rbac_roles.py\n@@ -28,9 +28,12 @@\n \n     def get_model_verbose(self, instance: GroupObjectPermission) -> str:\n         \"\"\"Get model label from permission's model\"\"\"\n-        return apps.get_model(\n-            instance.content_type.app_label, instance.content_type.model\n-        )._meta.verbose_name\n+        try:\n+            return apps.get_model(\n+                instance.content_type.app_label, instance.content_type.model\n+            )._meta.verbose_name\n+        except LookupError:\n+            return f\"{instance.content_type.app_label}.{instance.content_type.model}\"\n \n     def get_object_description(self, instance: GroupObjectPermission) -> Optional[str]:\n         \"\"\"Get model description from attached model. This operation takes at least\n@@ -38,7 +41,10 @@\n         view_ permission on the object\"\"\"\n         app_label = instance.content_type.app_label\n         model = instance.content_type.model\n-        model_class = apps.get_model(app_label, model)\n+        try:\n+            model_class = apps.get_model(app_label, model)\n+        except LookupError:\n+            return None\n         objects = get_objects_for_group(instance.group, f\"{app_label}.view_{model}\", model_class)\n         obj = objects.first()\n         if not obj:\ndiff --git a/authentik/rbac/api/rbac_users.py b/authentik/rbac/api/rbac_users.py\n--- a/authentik/rbac/api/rbac_users.py\n+++ b/authentik/rbac/api/rbac_users.py\n@@ -28,9 +28,12 @@\n \n     def get_model_verbose(self, instance: UserObjectPermission) -> str:\n         \"\"\"Get model label from permission's model\"\"\"\n-        return apps.get_model(\n-            instance.content_type.app_label, instance.content_type.model\n-        )._meta.verbose_name\n+        try:\n+            return apps.get_model(\n+                instance.content_type.app_label, instance.content_type.model\n+            )._meta.verbose_name\n+        except LookupError:\n+            return f\"{instance.content_type.app_label}.{instance.content_type.model}\"\n \n     def get_object_description(self, instance: UserObjectPermission) -> Optional[str]:\n         \"\"\"Get model description from attached model. This operation takes at least\n@@ -38,7 +41,10 @@\n         view_ permission on the object\"\"\"\n         app_label = instance.content_type.app_label\n         model = instance.content_type.model\n-        model_class = apps.get_model(app_label, model)\n+        try:\n+            model_class = apps.get_model(app_label, model)\n+        except LookupError:\n+            return None\n         objects = get_objects_for_user(instance.user, f\"{app_label}.view_{model}\", model_class)\n         obj = objects.first()\n         if not obj:\n", "issue": "Listing all permissions\n**Describe the bug**\r\nWhen attempting to list all the permissions in order to select which ones to assign to a permission, hangs when gets to 141+\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n\r\n1. Go to 'Directory'\r\n2. Click on 'Roles'\r\n3. Select (or create a new) role.\r\n4. Click on 'Assign Permissions'\r\n5. Click on '+' to Select Permissions to Grant\r\n6. Scroll through permissions by clicking on '>' until arrive at 121 - 140\r\n\r\n**Expected behavior**\r\nContinue displaying all permissions until reaching end of list (Displays 377 Permissions)\r\n\r\n**Screenshots**\r\n![image](https://github.com/goauthentik/authentik/assets/54423468/4cbd1788-1066-49df-84e8-1a9f8f00dfbc)\r\n\r\n**Logs**\r\n[logs](https://pastebin.com/v4Fd6PDN)\r\n\r\n**Version and Deployment (please complete the following information):**\r\n\r\n-   authentik version: 2023.10.1\r\n-   Deployment: docker-compose\r\n\r\n**Additional context**\r\nUpgraded from 2023.8 -> 2023.10.0 -> 2023.10.1\r\n\r\nCan provide more logs or anything else, if needed.\n", "before_files": [{"content": "\"\"\"common RBAC serializers\"\"\"\nfrom django.apps import apps\nfrom django.contrib.auth.models import Permission\nfrom django.db.models import QuerySet\nfrom django_filters.filters import ModelChoiceFilter\nfrom django_filters.filterset import FilterSet\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.fields import (\n    CharField,\n    ChoiceField,\n    ListField,\n    ReadOnlyField,\n    SerializerMethodField,\n)\nfrom rest_framework.serializers import ModelSerializer\nfrom rest_framework.viewsets import ReadOnlyModelViewSet\n\nfrom authentik.core.api.utils import PassiveSerializer\nfrom authentik.core.models import User\nfrom authentik.lib.validators import RequiredTogetherValidator\nfrom authentik.policies.event_matcher.models import model_choices\nfrom authentik.rbac.models import Role\n\n\nclass PermissionSerializer(ModelSerializer):\n    \"\"\"Global permission\"\"\"\n\n    app_label = ReadOnlyField(source=\"content_type.app_label\")\n    app_label_verbose = SerializerMethodField()\n    model = ReadOnlyField(source=\"content_type.model\")\n    model_verbose = SerializerMethodField()\n\n    def get_app_label_verbose(self, instance: Permission) -> str:\n        \"\"\"Human-readable app label\"\"\"\n        return apps.get_app_config(instance.content_type.app_label).verbose_name\n\n    def get_model_verbose(self, instance: Permission) -> str:\n        \"\"\"Human-readable model name\"\"\"\n        return apps.get_model(\n            instance.content_type.app_label, instance.content_type.model\n        )._meta.verbose_name\n\n    class Meta:\n        model = Permission\n        fields = [\n            \"id\",\n            \"name\",\n            \"codename\",\n            \"model\",\n            \"app_label\",\n            \"app_label_verbose\",\n            \"model_verbose\",\n        ]\n\n\nclass PermissionFilter(FilterSet):\n    \"\"\"Filter permissions\"\"\"\n\n    role = ModelChoiceFilter(queryset=Role.objects.all(), method=\"filter_role\")\n    user = ModelChoiceFilter(queryset=User.objects.all())\n\n    def filter_role(self, queryset: QuerySet, name, value: Role) -> QuerySet:\n        \"\"\"Filter permissions based on role\"\"\"\n        return queryset.filter(group__role=value)\n\n    class Meta:\n        model = Permission\n        fields = [\n            \"codename\",\n            \"content_type__model\",\n            \"content_type__app_label\",\n            \"role\",\n            \"user\",\n        ]\n\n\nclass RBACPermissionViewSet(ReadOnlyModelViewSet):\n    \"\"\"Read-only list of all permissions, filterable by model and app\"\"\"\n\n    queryset = Permission.objects.none()\n    serializer_class = PermissionSerializer\n    ordering = [\"name\"]\n    filterset_class = PermissionFilter\n    search_fields = [\n        \"codename\",\n        \"content_type__model\",\n        \"content_type__app_label\",\n    ]\n\n    def get_queryset(self) -> QuerySet:\n        return (\n            Permission.objects.all()\n            .select_related(\"content_type\")\n            .filter(\n                content_type__app_label__startswith=\"authentik\",\n            )\n        )\n\n\nclass PermissionAssignSerializer(PassiveSerializer):\n    \"\"\"Request to assign a new permission\"\"\"\n\n    permissions = ListField(child=CharField())\n    model = ChoiceField(choices=model_choices(), required=False)\n    object_pk = CharField(required=False)\n\n    validators = [RequiredTogetherValidator(fields=[\"model\", \"object_pk\"])]\n\n    def validate(self, attrs: dict) -> dict:\n        model_instance = None\n        # Check if we're setting an object-level perm or global\n        model = attrs.get(\"model\")\n        object_pk = attrs.get(\"object_pk\")\n        if model and object_pk:\n            model = apps.get_model(attrs[\"model\"])\n            model_instance = model.objects.filter(pk=attrs[\"object_pk\"]).first()\n        attrs[\"model_instance\"] = model_instance\n        if attrs.get(\"model\"):\n            return attrs\n        permissions = attrs.get(\"permissions\", [])\n        if not all(\".\" in perm for perm in permissions):\n            raise ValidationError(\n                {\n                    \"permissions\": (\n                        \"When assigning global permissions, codename must be given as \"\n                        \"app_label.codename\"\n                    )\n                }\n            )\n        return attrs\n", "path": "authentik/rbac/api/rbac.py"}, {"content": "\"\"\"common RBAC serializers\"\"\"\nfrom typing import Optional\n\nfrom django.apps import apps\nfrom django_filters.filters import UUIDFilter\nfrom django_filters.filterset import FilterSet\nfrom guardian.models import GroupObjectPermission\nfrom guardian.shortcuts import get_objects_for_group\nfrom rest_framework.fields import SerializerMethodField\nfrom rest_framework.mixins import ListModelMixin\nfrom rest_framework.viewsets import GenericViewSet\n\nfrom authentik.api.pagination import SmallerPagination\nfrom authentik.rbac.api.rbac_assigned_by_roles import RoleObjectPermissionSerializer\n\n\nclass ExtraRoleObjectPermissionSerializer(RoleObjectPermissionSerializer):\n    \"\"\"User permission with additional object-related data\"\"\"\n\n    app_label_verbose = SerializerMethodField()\n    model_verbose = SerializerMethodField()\n\n    object_description = SerializerMethodField()\n\n    def get_app_label_verbose(self, instance: GroupObjectPermission) -> str:\n        \"\"\"Get app label from permission's model\"\"\"\n        return apps.get_app_config(instance.content_type.app_label).verbose_name\n\n    def get_model_verbose(self, instance: GroupObjectPermission) -> str:\n        \"\"\"Get model label from permission's model\"\"\"\n        return apps.get_model(\n            instance.content_type.app_label, instance.content_type.model\n        )._meta.verbose_name\n\n    def get_object_description(self, instance: GroupObjectPermission) -> Optional[str]:\n        \"\"\"Get model description from attached model. This operation takes at least\n        one additional query, and the description is only shown if the user/role has the\n        view_ permission on the object\"\"\"\n        app_label = instance.content_type.app_label\n        model = instance.content_type.model\n        model_class = apps.get_model(app_label, model)\n        objects = get_objects_for_group(instance.group, f\"{app_label}.view_{model}\", model_class)\n        obj = objects.first()\n        if not obj:\n            return None\n        return str(obj)\n\n    class Meta(RoleObjectPermissionSerializer.Meta):\n        fields = RoleObjectPermissionSerializer.Meta.fields + [\n            \"app_label_verbose\",\n            \"model_verbose\",\n            \"object_description\",\n        ]\n\n\nclass RolePermissionFilter(FilterSet):\n    \"\"\"Role permission filter\"\"\"\n\n    uuid = UUIDFilter(\"group__role__uuid\", required=True)\n\n\nclass RolePermissionViewSet(ListModelMixin, GenericViewSet):\n    \"\"\"Get a role's assigned object permissions\"\"\"\n\n    serializer_class = ExtraRoleObjectPermissionSerializer\n    ordering = [\"group__role__name\"]\n    pagination_class = SmallerPagination\n    # The filtering is done in the filterset,\n    # which has a required filter that does the heavy lifting\n    queryset = GroupObjectPermission.objects.select_related(\"content_type\", \"group__role\").all()\n    filterset_class = RolePermissionFilter\n", "path": "authentik/rbac/api/rbac_roles.py"}, {"content": "\"\"\"common RBAC serializers\"\"\"\nfrom typing import Optional\n\nfrom django.apps import apps\nfrom django_filters.filters import NumberFilter\nfrom django_filters.filterset import FilterSet\nfrom guardian.models import UserObjectPermission\nfrom guardian.shortcuts import get_objects_for_user\nfrom rest_framework.fields import SerializerMethodField\nfrom rest_framework.mixins import ListModelMixin\nfrom rest_framework.viewsets import GenericViewSet\n\nfrom authentik.api.pagination import SmallerPagination\nfrom authentik.rbac.api.rbac_assigned_by_users import UserObjectPermissionSerializer\n\n\nclass ExtraUserObjectPermissionSerializer(UserObjectPermissionSerializer):\n    \"\"\"User permission with additional object-related data\"\"\"\n\n    app_label_verbose = SerializerMethodField()\n    model_verbose = SerializerMethodField()\n\n    object_description = SerializerMethodField()\n\n    def get_app_label_verbose(self, instance: UserObjectPermission) -> str:\n        \"\"\"Get app label from permission's model\"\"\"\n        return apps.get_app_config(instance.content_type.app_label).verbose_name\n\n    def get_model_verbose(self, instance: UserObjectPermission) -> str:\n        \"\"\"Get model label from permission's model\"\"\"\n        return apps.get_model(\n            instance.content_type.app_label, instance.content_type.model\n        )._meta.verbose_name\n\n    def get_object_description(self, instance: UserObjectPermission) -> Optional[str]:\n        \"\"\"Get model description from attached model. This operation takes at least\n        one additional query, and the description is only shown if the user/role has the\n        view_ permission on the object\"\"\"\n        app_label = instance.content_type.app_label\n        model = instance.content_type.model\n        model_class = apps.get_model(app_label, model)\n        objects = get_objects_for_user(instance.user, f\"{app_label}.view_{model}\", model_class)\n        obj = objects.first()\n        if not obj:\n            return None\n        return str(obj)\n\n    class Meta(UserObjectPermissionSerializer.Meta):\n        fields = UserObjectPermissionSerializer.Meta.fields + [\n            \"app_label_verbose\",\n            \"model_verbose\",\n            \"object_description\",\n        ]\n\n\nclass UserPermissionFilter(FilterSet):\n    \"\"\"User-assigned permission filter\"\"\"\n\n    user_id = NumberFilter(\"user__id\", required=True)\n\n\nclass UserPermissionViewSet(ListModelMixin, GenericViewSet):\n    \"\"\"Get a users's assigned object permissions\"\"\"\n\n    serializer_class = ExtraUserObjectPermissionSerializer\n    ordering = [\"user__username\"]\n    pagination_class = SmallerPagination\n    # The filtering is done in the filterset,\n    # which has a required filter that does the heavy lifting\n    queryset = UserObjectPermission.objects.select_related(\"content_type\", \"user\").all()\n    filterset_class = UserPermissionFilter\n", "path": "authentik/rbac/api/rbac_users.py"}], "after_files": [{"content": "\"\"\"common RBAC serializers\"\"\"\nfrom django.apps import apps\nfrom django.contrib.auth.models import Permission\nfrom django.db.models import QuerySet\nfrom django_filters.filters import ModelChoiceFilter\nfrom django_filters.filterset import FilterSet\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.fields import (\n    CharField,\n    ChoiceField,\n    ListField,\n    ReadOnlyField,\n    SerializerMethodField,\n)\nfrom rest_framework.serializers import ModelSerializer\nfrom rest_framework.viewsets import ReadOnlyModelViewSet\n\nfrom authentik.core.api.utils import PassiveSerializer\nfrom authentik.core.models import User\nfrom authentik.lib.validators import RequiredTogetherValidator\nfrom authentik.policies.event_matcher.models import model_choices\nfrom authentik.rbac.models import Role\n\n\nclass PermissionSerializer(ModelSerializer):\n    \"\"\"Global permission\"\"\"\n\n    app_label = ReadOnlyField(source=\"content_type.app_label\")\n    app_label_verbose = SerializerMethodField()\n    model = ReadOnlyField(source=\"content_type.model\")\n    model_verbose = SerializerMethodField()\n\n    def get_app_label_verbose(self, instance: Permission) -> str:\n        \"\"\"Human-readable app label\"\"\"\n        try:\n            return apps.get_app_config(instance.content_type.app_label).verbose_name\n        except LookupError:\n            return f\"{instance.content_type.app_label}.{instance.content_type.model}\"\n\n    def get_model_verbose(self, instance: Permission) -> str:\n        \"\"\"Human-readable model name\"\"\"\n        try:\n            return apps.get_model(\n                instance.content_type.app_label, instance.content_type.model\n            )._meta.verbose_name\n        except LookupError:\n            return f\"{instance.content_type.app_label}.{instance.content_type.model}\"\n\n    class Meta:\n        model = Permission\n        fields = [\n            \"id\",\n            \"name\",\n            \"codename\",\n            \"model\",\n            \"app_label\",\n            \"app_label_verbose\",\n            \"model_verbose\",\n        ]\n\n\nclass PermissionFilter(FilterSet):\n    \"\"\"Filter permissions\"\"\"\n\n    role = ModelChoiceFilter(queryset=Role.objects.all(), method=\"filter_role\")\n    user = ModelChoiceFilter(queryset=User.objects.all())\n\n    def filter_role(self, queryset: QuerySet, name, value: Role) -> QuerySet:\n        \"\"\"Filter permissions based on role\"\"\"\n        return queryset.filter(group__role=value)\n\n    class Meta:\n        model = Permission\n        fields = [\n            \"codename\",\n            \"content_type__model\",\n            \"content_type__app_label\",\n            \"role\",\n            \"user\",\n        ]\n\n\nclass RBACPermissionViewSet(ReadOnlyModelViewSet):\n    \"\"\"Read-only list of all permissions, filterable by model and app\"\"\"\n\n    queryset = Permission.objects.none()\n    serializer_class = PermissionSerializer\n    ordering = [\"name\"]\n    filterset_class = PermissionFilter\n    search_fields = [\n        \"codename\",\n        \"content_type__model\",\n        \"content_type__app_label\",\n    ]\n\n    def get_queryset(self) -> QuerySet:\n        return (\n            Permission.objects.all()\n            .select_related(\"content_type\")\n            .filter(\n                content_type__app_label__startswith=\"authentik\",\n            )\n        )\n\n\nclass PermissionAssignSerializer(PassiveSerializer):\n    \"\"\"Request to assign a new permission\"\"\"\n\n    permissions = ListField(child=CharField())\n    model = ChoiceField(choices=model_choices(), required=False)\n    object_pk = CharField(required=False)\n\n    validators = [RequiredTogetherValidator(fields=[\"model\", \"object_pk\"])]\n\n    def validate(self, attrs: dict) -> dict:\n        model_instance = None\n        # Check if we're setting an object-level perm or global\n        model = attrs.get(\"model\")\n        object_pk = attrs.get(\"object_pk\")\n        if model and object_pk:\n            model = apps.get_model(attrs[\"model\"])\n            model_instance = model.objects.filter(pk=attrs[\"object_pk\"]).first()\n        attrs[\"model_instance\"] = model_instance\n        if attrs.get(\"model\"):\n            return attrs\n        permissions = attrs.get(\"permissions\", [])\n        if not all(\".\" in perm for perm in permissions):\n            raise ValidationError(\n                {\n                    \"permissions\": (\n                        \"When assigning global permissions, codename must be given as \"\n                        \"app_label.codename\"\n                    )\n                }\n            )\n        return attrs\n", "path": "authentik/rbac/api/rbac.py"}, {"content": "\"\"\"common RBAC serializers\"\"\"\nfrom typing import Optional\n\nfrom django.apps import apps\nfrom django_filters.filters import UUIDFilter\nfrom django_filters.filterset import FilterSet\nfrom guardian.models import GroupObjectPermission\nfrom guardian.shortcuts import get_objects_for_group\nfrom rest_framework.fields import SerializerMethodField\nfrom rest_framework.mixins import ListModelMixin\nfrom rest_framework.viewsets import GenericViewSet\n\nfrom authentik.api.pagination import SmallerPagination\nfrom authentik.rbac.api.rbac_assigned_by_roles import RoleObjectPermissionSerializer\n\n\nclass ExtraRoleObjectPermissionSerializer(RoleObjectPermissionSerializer):\n    \"\"\"User permission with additional object-related data\"\"\"\n\n    app_label_verbose = SerializerMethodField()\n    model_verbose = SerializerMethodField()\n\n    object_description = SerializerMethodField()\n\n    def get_app_label_verbose(self, instance: GroupObjectPermission) -> str:\n        \"\"\"Get app label from permission's model\"\"\"\n        return apps.get_app_config(instance.content_type.app_label).verbose_name\n\n    def get_model_verbose(self, instance: GroupObjectPermission) -> str:\n        \"\"\"Get model label from permission's model\"\"\"\n        try:\n            return apps.get_model(\n                instance.content_type.app_label, instance.content_type.model\n            )._meta.verbose_name\n        except LookupError:\n            return f\"{instance.content_type.app_label}.{instance.content_type.model}\"\n\n    def get_object_description(self, instance: GroupObjectPermission) -> Optional[str]:\n        \"\"\"Get model description from attached model. This operation takes at least\n        one additional query, and the description is only shown if the user/role has the\n        view_ permission on the object\"\"\"\n        app_label = instance.content_type.app_label\n        model = instance.content_type.model\n        try:\n            model_class = apps.get_model(app_label, model)\n        except LookupError:\n            return None\n        objects = get_objects_for_group(instance.group, f\"{app_label}.view_{model}\", model_class)\n        obj = objects.first()\n        if not obj:\n            return None\n        return str(obj)\n\n    class Meta(RoleObjectPermissionSerializer.Meta):\n        fields = RoleObjectPermissionSerializer.Meta.fields + [\n            \"app_label_verbose\",\n            \"model_verbose\",\n            \"object_description\",\n        ]\n\n\nclass RolePermissionFilter(FilterSet):\n    \"\"\"Role permission filter\"\"\"\n\n    uuid = UUIDFilter(\"group__role__uuid\", required=True)\n\n\nclass RolePermissionViewSet(ListModelMixin, GenericViewSet):\n    \"\"\"Get a role's assigned object permissions\"\"\"\n\n    serializer_class = ExtraRoleObjectPermissionSerializer\n    ordering = [\"group__role__name\"]\n    pagination_class = SmallerPagination\n    # The filtering is done in the filterset,\n    # which has a required filter that does the heavy lifting\n    queryset = GroupObjectPermission.objects.select_related(\"content_type\", \"group__role\").all()\n    filterset_class = RolePermissionFilter\n", "path": "authentik/rbac/api/rbac_roles.py"}, {"content": "\"\"\"common RBAC serializers\"\"\"\nfrom typing import Optional\n\nfrom django.apps import apps\nfrom django_filters.filters import NumberFilter\nfrom django_filters.filterset import FilterSet\nfrom guardian.models import UserObjectPermission\nfrom guardian.shortcuts import get_objects_for_user\nfrom rest_framework.fields import SerializerMethodField\nfrom rest_framework.mixins import ListModelMixin\nfrom rest_framework.viewsets import GenericViewSet\n\nfrom authentik.api.pagination import SmallerPagination\nfrom authentik.rbac.api.rbac_assigned_by_users import UserObjectPermissionSerializer\n\n\nclass ExtraUserObjectPermissionSerializer(UserObjectPermissionSerializer):\n    \"\"\"User permission with additional object-related data\"\"\"\n\n    app_label_verbose = SerializerMethodField()\n    model_verbose = SerializerMethodField()\n\n    object_description = SerializerMethodField()\n\n    def get_app_label_verbose(self, instance: UserObjectPermission) -> str:\n        \"\"\"Get app label from permission's model\"\"\"\n        return apps.get_app_config(instance.content_type.app_label).verbose_name\n\n    def get_model_verbose(self, instance: UserObjectPermission) -> str:\n        \"\"\"Get model label from permission's model\"\"\"\n        try:\n            return apps.get_model(\n                instance.content_type.app_label, instance.content_type.model\n            )._meta.verbose_name\n        except LookupError:\n            return f\"{instance.content_type.app_label}.{instance.content_type.model}\"\n\n    def get_object_description(self, instance: UserObjectPermission) -> Optional[str]:\n        \"\"\"Get model description from attached model. This operation takes at least\n        one additional query, and the description is only shown if the user/role has the\n        view_ permission on the object\"\"\"\n        app_label = instance.content_type.app_label\n        model = instance.content_type.model\n        try:\n            model_class = apps.get_model(app_label, model)\n        except LookupError:\n            return None\n        objects = get_objects_for_user(instance.user, f\"{app_label}.view_{model}\", model_class)\n        obj = objects.first()\n        if not obj:\n            return None\n        return str(obj)\n\n    class Meta(UserObjectPermissionSerializer.Meta):\n        fields = UserObjectPermissionSerializer.Meta.fields + [\n            \"app_label_verbose\",\n            \"model_verbose\",\n            \"object_description\",\n        ]\n\n\nclass UserPermissionFilter(FilterSet):\n    \"\"\"User-assigned permission filter\"\"\"\n\n    user_id = NumberFilter(\"user__id\", required=True)\n\n\nclass UserPermissionViewSet(ListModelMixin, GenericViewSet):\n    \"\"\"Get a users's assigned object permissions\"\"\"\n\n    serializer_class = ExtraUserObjectPermissionSerializer\n    ordering = [\"user__username\"]\n    pagination_class = SmallerPagination\n    # The filtering is done in the filterset,\n    # which has a required filter that does the heavy lifting\n    queryset = UserObjectPermission.objects.select_related(\"content_type\", \"user\").all()\n    filterset_class = UserPermissionFilter\n", "path": "authentik/rbac/api/rbac_users.py"}]}