authentik.rbac.api.rbac_assigned_by_roles
common RBAC serializers
1"""common RBAC serializers""" 2 3from django.contrib.auth.models import Permission 4from django.db.models import Q, QuerySet 5from django.db.transaction import atomic 6from django_filters.filters import CharFilter, ChoiceFilter 7from django_filters.filterset import FilterSet 8from drf_spectacular.utils import OpenApiResponse, extend_schema 9from guardian.models import RoleModelPermission, RoleObjectPermission 10from guardian.shortcuts import assign_perm, remove_perm 11from rest_framework.decorators import action 12from rest_framework.fields import CharField, ReadOnlyField 13from rest_framework.mixins import ListModelMixin 14from rest_framework.request import Request 15from rest_framework.response import Response 16from rest_framework.viewsets import GenericViewSet 17 18from authentik.core.api.utils import ModelSerializer, PassiveSerializer 19from authentik.lib.api import model_choices 20from authentik.rbac.api.rbac import PermissionAssignResultSerializer, PermissionAssignSerializer 21from authentik.rbac.decorators import permission_required 22from authentik.rbac.models import Role 23 24 25class RoleObjectPermissionSerializer(ModelSerializer): 26 """Role-bound object level permission""" 27 28 app_label = ReadOnlyField(source="content_type.app_label") 29 model = ReadOnlyField(source="content_type.model") 30 codename = ReadOnlyField(source="permission.codename") 31 name = ReadOnlyField(source="permission.name") 32 object_pk = CharField() 33 34 class Meta: 35 model = RoleObjectPermission 36 fields = ["id", "codename", "model", "app_label", "object_pk", "name"] 37 38 39class RoleModelPermissionSerializer(ModelSerializer): 40 """Role-bound object level permission""" 41 42 app_label = ReadOnlyField(source="content_type.app_label") 43 model = ReadOnlyField(source="content_type.model") 44 codename = ReadOnlyField(source="permission.codename") 45 name = ReadOnlyField(source="permission.name") 46 47 class Meta: 48 model = RoleModelPermission 49 fields = ["id", "codename", "model", "app_label", "name"] 50 51 52class RoleAssignedObjectPermissionSerializer(PassiveSerializer): 53 """Roles assigned object permission serializer""" 54 55 role_pk = CharField(source="pk", read_only=True) 56 name = CharField(read_only=True) 57 object_permissions = RoleObjectPermissionSerializer( 58 many=True, source="roleobjectpermission_set" 59 ) 60 model_permissions = RoleModelPermissionSerializer(many=True, source="rolemodelpermission_set") 61 62 class Meta: 63 model = Role 64 fields = ["role_pk", "name", "object_permissions", "model_permissions"] 65 66 67class RoleAssignedPermissionFilter(FilterSet): 68 """Assigned permission filter""" 69 70 model = ChoiceFilter(choices=model_choices(), method="filter_model", required=True) 71 object_pk = CharFilter(method="filter_object_pk") 72 73 def filter_queryset(self, queryset): 74 queryset = super().filter_queryset(queryset) 75 data = self.form.cleaned_data 76 model: str = data["model"] 77 object_pk: str | None = data.get("object_pk", None) 78 app, _, model = model.partition(".") 79 80 permissions = Permission.objects.filter( 81 content_type__app_label=app, 82 content_type__model=model, 83 ) 84 85 role_pks_with_model_permission = ( 86 permissions.order_by().values_list("rolemodelpermission__role", flat=True).distinct() 87 ) 88 role_pks_with_object_permission = [] 89 if object_pk: 90 role_pks_with_object_permission = ( 91 RoleObjectPermission.objects.filter( 92 permission__in=permissions, 93 object_pk=object_pk, 94 ) 95 .order_by() 96 .values_list("role", flat=True) 97 .distinct() 98 ) 99 100 return queryset.filter( 101 Q(pk__in=role_pks_with_model_permission) | Q(pk__in=role_pks_with_object_permission) 102 ) 103 104 def filter_model(self, queryset: QuerySet, name, value: str) -> QuerySet: 105 """Filter by object type""" 106 # Actual filtering is handled by the above method where both `model` and `object_pk` are 107 # available. Don't do anything here, this method is only left here to avoid overriding too 108 # much of filter_queryset. 109 return queryset 110 111 def filter_object_pk(self, queryset: QuerySet, name, value: str) -> QuerySet: 112 """Filter by object primary key""" 113 # Actual filtering is handled by the above method where both `model` and `object_pk` are 114 # available. Don't do anything here, this method is only left here to avoid overriding too 115 # much of filter_queryset. 116 return queryset 117 118 119class RoleAssignedPermissionViewSet(ListModelMixin, GenericViewSet): 120 """Get assigned object permissions for a single object""" 121 122 serializer_class = RoleAssignedObjectPermissionSerializer 123 ordering = ["name"] 124 # The filtering is done in the filterset, 125 # which has a required filter that does the heavy lifting 126 queryset = Role.objects.all() 127 filterset_class = RoleAssignedPermissionFilter 128 search_fields = ["name"] 129 130 @permission_required("authentik_rbac.assign_role_permissions") 131 @extend_schema( 132 request=PermissionAssignSerializer(), 133 responses={ 134 200: PermissionAssignResultSerializer(many=True), 135 }, 136 operation_id="rbac_permissions_assigned_by_roles_assign", 137 ) 138 @action(methods=["POST"], detail=True, pagination_class=None, filter_backends=[]) 139 def assign(self, request: Request, *args, **kwargs) -> Response: 140 """Assign permission(s) to role. When `object_pk` is set, the permissions 141 are only assigned to the specific object, otherwise they are assigned globally.""" 142 role: Role = self.get_object() 143 data = PermissionAssignSerializer(data=request.data) 144 data.is_valid(raise_exception=True) 145 ids = [] 146 with atomic(): 147 for perm in data.validated_data["permissions"]: 148 assigned_perm = assign_perm(perm, role, data.validated_data["model_instance"]) 149 ids.append(PermissionAssignResultSerializer(instance={"id": assigned_perm.pk}).data) 150 return Response(ids, status=200) 151 152 @permission_required("authentik_rbac.unassign_role_permissions") 153 @extend_schema( 154 request=PermissionAssignSerializer(), 155 responses={ 156 204: OpenApiResponse(description="Successfully unassigned"), 157 }, 158 ) 159 @action(methods=["PATCH"], detail=True, pagination_class=None, filter_backends=[]) 160 def unassign(self, request: Request, *args, **kwargs) -> Response: 161 """Unassign permission(s) to role. When `object_pk` is set, the permissions 162 are only assigned to the specific object, otherwise they are assigned globally.""" 163 role: Role = self.get_object() 164 data = PermissionAssignSerializer(data=request.data) 165 data.is_valid(raise_exception=True) 166 with atomic(): 167 for perm in data.validated_data["permissions"]: 168 remove_perm(perm, role, data.validated_data["model_instance"]) 169 return Response(status=204)
26class RoleObjectPermissionSerializer(ModelSerializer): 27 """Role-bound object level permission""" 28 29 app_label = ReadOnlyField(source="content_type.app_label") 30 model = ReadOnlyField(source="content_type.model") 31 codename = ReadOnlyField(source="permission.codename") 32 name = ReadOnlyField(source="permission.name") 33 object_pk = CharField() 34 35 class Meta: 36 model = RoleObjectPermission 37 fields = ["id", "codename", "model", "app_label", "object_pk", "name"]
Role-bound object level permission
Inherited Members
40class RoleModelPermissionSerializer(ModelSerializer): 41 """Role-bound object level permission""" 42 43 app_label = ReadOnlyField(source="content_type.app_label") 44 model = ReadOnlyField(source="content_type.model") 45 codename = ReadOnlyField(source="permission.codename") 46 name = ReadOnlyField(source="permission.name") 47 48 class Meta: 49 model = RoleModelPermission 50 fields = ["id", "codename", "model", "app_label", "name"]
Role-bound object level permission
Inherited Members
53class RoleAssignedObjectPermissionSerializer(PassiveSerializer): 54 """Roles assigned object permission serializer""" 55 56 role_pk = CharField(source="pk", read_only=True) 57 name = CharField(read_only=True) 58 object_permissions = RoleObjectPermissionSerializer( 59 many=True, source="roleobjectpermission_set" 60 ) 61 model_permissions = RoleModelPermissionSerializer(many=True, source="rolemodelpermission_set") 62 63 class Meta: 64 model = Role 65 fields = ["role_pk", "name", "object_permissions", "model_permissions"]
Roles assigned object permission serializer
Inherited Members
63 class Meta: 64 model = Role 65 fields = ["role_pk", "name", "object_permissions", "model_permissions"]
68class RoleAssignedPermissionFilter(FilterSet): 69 """Assigned permission filter""" 70 71 model = ChoiceFilter(choices=model_choices(), method="filter_model", required=True) 72 object_pk = CharFilter(method="filter_object_pk") 73 74 def filter_queryset(self, queryset): 75 queryset = super().filter_queryset(queryset) 76 data = self.form.cleaned_data 77 model: str = data["model"] 78 object_pk: str | None = data.get("object_pk", None) 79 app, _, model = model.partition(".") 80 81 permissions = Permission.objects.filter( 82 content_type__app_label=app, 83 content_type__model=model, 84 ) 85 86 role_pks_with_model_permission = ( 87 permissions.order_by().values_list("rolemodelpermission__role", flat=True).distinct() 88 ) 89 role_pks_with_object_permission = [] 90 if object_pk: 91 role_pks_with_object_permission = ( 92 RoleObjectPermission.objects.filter( 93 permission__in=permissions, 94 object_pk=object_pk, 95 ) 96 .order_by() 97 .values_list("role", flat=True) 98 .distinct() 99 ) 100 101 return queryset.filter( 102 Q(pk__in=role_pks_with_model_permission) | Q(pk__in=role_pks_with_object_permission) 103 ) 104 105 def filter_model(self, queryset: QuerySet, name, value: str) -> QuerySet: 106 """Filter by object type""" 107 # Actual filtering is handled by the above method where both `model` and `object_pk` are 108 # available. Don't do anything here, this method is only left here to avoid overriding too 109 # much of filter_queryset. 110 return queryset 111 112 def filter_object_pk(self, queryset: QuerySet, name, value: str) -> QuerySet: 113 """Filter by object primary key""" 114 # Actual filtering is handled by the above method where both `model` and `object_pk` are 115 # available. Don't do anything here, this method is only left here to avoid overriding too 116 # much of filter_queryset. 117 return queryset
Assigned permission filter
74 def filter_queryset(self, queryset): 75 queryset = super().filter_queryset(queryset) 76 data = self.form.cleaned_data 77 model: str = data["model"] 78 object_pk: str | None = data.get("object_pk", None) 79 app, _, model = model.partition(".") 80 81 permissions = Permission.objects.filter( 82 content_type__app_label=app, 83 content_type__model=model, 84 ) 85 86 role_pks_with_model_permission = ( 87 permissions.order_by().values_list("rolemodelpermission__role", flat=True).distinct() 88 ) 89 role_pks_with_object_permission = [] 90 if object_pk: 91 role_pks_with_object_permission = ( 92 RoleObjectPermission.objects.filter( 93 permission__in=permissions, 94 object_pk=object_pk, 95 ) 96 .order_by() 97 .values_list("role", flat=True) 98 .distinct() 99 ) 100 101 return queryset.filter( 102 Q(pk__in=role_pks_with_model_permission) | Q(pk__in=role_pks_with_object_permission) 103 )
Filter the queryset with the underlying form's cleaned_data. You must
call is_valid() or errors before calling this method.
This method should be overridden if additional filtering needs to be applied to the queryset before it is cached.
105 def filter_model(self, queryset: QuerySet, name, value: str) -> QuerySet: 106 """Filter by object type""" 107 # Actual filtering is handled by the above method where both `model` and `object_pk` are 108 # available. Don't do anything here, this method is only left here to avoid overriding too 109 # much of filter_queryset. 110 return queryset
Filter by object type
112 def filter_object_pk(self, queryset: QuerySet, name, value: str) -> QuerySet: 113 """Filter by object primary key""" 114 # Actual filtering is handled by the above method where both `model` and `object_pk` are 115 # available. Don't do anything here, this method is only left here to avoid overriding too 116 # much of filter_queryset. 117 return queryset
Filter by object primary key
120class RoleAssignedPermissionViewSet(ListModelMixin, GenericViewSet): 121 """Get assigned object permissions for a single object""" 122 123 serializer_class = RoleAssignedObjectPermissionSerializer 124 ordering = ["name"] 125 # The filtering is done in the filterset, 126 # which has a required filter that does the heavy lifting 127 queryset = Role.objects.all() 128 filterset_class = RoleAssignedPermissionFilter 129 search_fields = ["name"] 130 131 @permission_required("authentik_rbac.assign_role_permissions") 132 @extend_schema( 133 request=PermissionAssignSerializer(), 134 responses={ 135 200: PermissionAssignResultSerializer(many=True), 136 }, 137 operation_id="rbac_permissions_assigned_by_roles_assign", 138 ) 139 @action(methods=["POST"], detail=True, pagination_class=None, filter_backends=[]) 140 def assign(self, request: Request, *args, **kwargs) -> Response: 141 """Assign permission(s) to role. When `object_pk` is set, the permissions 142 are only assigned to the specific object, otherwise they are assigned globally.""" 143 role: Role = self.get_object() 144 data = PermissionAssignSerializer(data=request.data) 145 data.is_valid(raise_exception=True) 146 ids = [] 147 with atomic(): 148 for perm in data.validated_data["permissions"]: 149 assigned_perm = assign_perm(perm, role, data.validated_data["model_instance"]) 150 ids.append(PermissionAssignResultSerializer(instance={"id": assigned_perm.pk}).data) 151 return Response(ids, status=200) 152 153 @permission_required("authentik_rbac.unassign_role_permissions") 154 @extend_schema( 155 request=PermissionAssignSerializer(), 156 responses={ 157 204: OpenApiResponse(description="Successfully unassigned"), 158 }, 159 ) 160 @action(methods=["PATCH"], detail=True, pagination_class=None, filter_backends=[]) 161 def unassign(self, request: Request, *args, **kwargs) -> Response: 162 """Unassign permission(s) to role. When `object_pk` is set, the permissions 163 are only assigned to the specific object, otherwise they are assigned globally.""" 164 role: Role = self.get_object() 165 data = PermissionAssignSerializer(data=request.data) 166 data.is_valid(raise_exception=True) 167 with atomic(): 168 for perm in data.validated_data["permissions"]: 169 remove_perm(perm, role, data.validated_data["model_instance"]) 170 return Response(status=204)
Get assigned object permissions for a single object
131 @permission_required("authentik_rbac.assign_role_permissions") 132 @extend_schema( 133 request=PermissionAssignSerializer(), 134 responses={ 135 200: PermissionAssignResultSerializer(many=True), 136 }, 137 operation_id="rbac_permissions_assigned_by_roles_assign", 138 ) 139 @action(methods=["POST"], detail=True, pagination_class=None, filter_backends=[]) 140 def assign(self, request: Request, *args, **kwargs) -> Response: 141 """Assign permission(s) to role. When `object_pk` is set, the permissions 142 are only assigned to the specific object, otherwise they are assigned globally.""" 143 role: Role = self.get_object() 144 data = PermissionAssignSerializer(data=request.data) 145 data.is_valid(raise_exception=True) 146 ids = [] 147 with atomic(): 148 for perm in data.validated_data["permissions"]: 149 assigned_perm = assign_perm(perm, role, data.validated_data["model_instance"]) 150 ids.append(PermissionAssignResultSerializer(instance={"id": assigned_perm.pk}).data) 151 return Response(ids, status=200)
Assign permission(s) to role. When object_pk is set, the permissions
are only assigned to the specific object, otherwise they are assigned globally.
153 @permission_required("authentik_rbac.unassign_role_permissions") 154 @extend_schema( 155 request=PermissionAssignSerializer(), 156 responses={ 157 204: OpenApiResponse(description="Successfully unassigned"), 158 }, 159 ) 160 @action(methods=["PATCH"], detail=True, pagination_class=None, filter_backends=[]) 161 def unassign(self, request: Request, *args, **kwargs) -> Response: 162 """Unassign permission(s) to role. When `object_pk` is set, the permissions 163 are only assigned to the specific object, otherwise they are assigned globally.""" 164 role: Role = self.get_object() 165 data = PermissionAssignSerializer(data=request.data) 166 data.is_valid(raise_exception=True) 167 with atomic(): 168 for perm in data.validated_data["permissions"]: 169 remove_perm(perm, role, data.validated_data["model_instance"]) 170 return Response(status=204)
Unassign permission(s) to role. When object_pk is set, the permissions
are only assigned to the specific object, otherwise they are assigned globally.