Compare commits
	
		
			2 Commits
		
	
	
		
			npm-worksp
			...
			core/fix-g
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| af9ce90b8b | |||
| 05e5c6309c | 
| @ -16,10 +16,12 @@ from drf_spectacular.utils import ( | |||||||
| from guardian.shortcuts import get_objects_for_user | from guardian.shortcuts import get_objects_for_user | ||||||
| from rest_framework.decorators import action | from rest_framework.decorators import action | ||||||
| from rest_framework.fields import CharField, IntegerField, SerializerMethodField | from rest_framework.fields import CharField, IntegerField, SerializerMethodField | ||||||
|  | from rest_framework.permissions import SAFE_METHODS, BasePermission | ||||||
| from rest_framework.request import Request | from rest_framework.request import Request | ||||||
| from rest_framework.response import Response | from rest_framework.response import Response | ||||||
| from rest_framework.serializers import ListSerializer, ValidationError | from rest_framework.serializers import ListSerializer, ValidationError | ||||||
| from rest_framework.validators import UniqueValidator | from rest_framework.validators import UniqueValidator | ||||||
|  | from rest_framework.views import View | ||||||
| from rest_framework.viewsets import ModelViewSet | from rest_framework.viewsets import ModelViewSet | ||||||
|  |  | ||||||
| from authentik.core.api.used_by import UsedByMixin | from authentik.core.api.used_by import UsedByMixin | ||||||
| @ -85,34 +87,6 @@ class GroupSerializer(ModelSerializer): | |||||||
|             raise ValidationError(_("Cannot set group as parent of itself.")) |             raise ValidationError(_("Cannot set group as parent of itself.")) | ||||||
|         return parent |         return parent | ||||||
|  |  | ||||||
|     def validate_is_superuser(self, superuser: bool): |  | ||||||
|         """Ensure that the user creating this group has permissions to set the superuser flag""" |  | ||||||
|         request: Request = self.context.get("request", None) |  | ||||||
|         if not request: |  | ||||||
|             return superuser |  | ||||||
|         # If we're updating an instance, and the state hasn't changed, we don't need to check perms |  | ||||||
|         if self.instance and superuser == self.instance.is_superuser: |  | ||||||
|             return superuser |  | ||||||
|         user: User = request.user |  | ||||||
|         perm = ( |  | ||||||
|             "authentik_core.enable_group_superuser" |  | ||||||
|             if superuser |  | ||||||
|             else "authentik_core.disable_group_superuser" |  | ||||||
|         ) |  | ||||||
|         has_perm = user.has_perm(perm) |  | ||||||
|         if self.instance and not has_perm: |  | ||||||
|             has_perm = user.has_perm(perm, self.instance) |  | ||||||
|         if not has_perm: |  | ||||||
|             raise ValidationError( |  | ||||||
|                 _( |  | ||||||
|                     ( |  | ||||||
|                         "User does not have permission to set " |  | ||||||
|                         "superuser status to {superuser_status}." |  | ||||||
|                     ).format_map({"superuser_status": superuser}) |  | ||||||
|                 ) |  | ||||||
|             ) |  | ||||||
|         return superuser |  | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = Group |         model = Group | ||||||
|         fields = [ |         fields = [ | ||||||
| @ -180,6 +154,36 @@ class GroupFilter(FilterSet): | |||||||
|         fields = ["name", "is_superuser", "members_by_pk", "attributes", "members_by_username"] |         fields = ["name", "is_superuser", "members_by_pk", "attributes", "members_by_username"] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class SuperuserSetter(BasePermission): | ||||||
|  |     """Check for enable_group_superuser or disable_group_superuser permissions""" | ||||||
|  |  | ||||||
|  |     message = _("User does not have permission to set the given superuser status.") | ||||||
|  |     enable_perm = "authentik_core.enable_group_superuser" | ||||||
|  |     disable_perm = "authentik_core.disable_group_superuser" | ||||||
|  |  | ||||||
|  |     def has_permission(self, request: Request, view: View): | ||||||
|  |         if request.method != "POST": | ||||||
|  |             return True | ||||||
|  |  | ||||||
|  |         is_superuser = request.data.get("is_superuser", False) | ||||||
|  |         if not is_superuser: | ||||||
|  |             return True | ||||||
|  |  | ||||||
|  |         return request.user.has_perm(self.enable_perm) | ||||||
|  |  | ||||||
|  |     def has_object_permission(self, request: Request, view: View, object: Group): | ||||||
|  |         if request.method in SAFE_METHODS: | ||||||
|  |             return True | ||||||
|  |  | ||||||
|  |         new_value = request.data.get("is_superuser") | ||||||
|  |         old_value = object.is_superuser | ||||||
|  |         if new_value is None or new_value == old_value: | ||||||
|  |             return True | ||||||
|  |  | ||||||
|  |         perm = self.enable_perm if new_value else self.disable_perm | ||||||
|  |         return request.user.has_perm(perm) or request.user.has_perm(perm, object) | ||||||
|  |  | ||||||
|  |  | ||||||
| class GroupViewSet(UsedByMixin, ModelViewSet): | class GroupViewSet(UsedByMixin, ModelViewSet): | ||||||
|     """Group Viewset""" |     """Group Viewset""" | ||||||
|  |  | ||||||
| @ -192,6 +196,7 @@ class GroupViewSet(UsedByMixin, ModelViewSet): | |||||||
|     serializer_class = GroupSerializer |     serializer_class = GroupSerializer | ||||||
|     search_fields = ["name", "is_superuser"] |     search_fields = ["name", "is_superuser"] | ||||||
|     filterset_class = GroupFilter |     filterset_class = GroupFilter | ||||||
|  |     permission_classes = [SuperuserSetter] | ||||||
|     ordering = ["name"] |     ordering = ["name"] | ||||||
|  |  | ||||||
|     def get_queryset(self): |     def get_queryset(self): | ||||||
|  | |||||||
| @ -118,12 +118,25 @@ class TestGroupsAPI(APITestCase): | |||||||
|             reverse("authentik_api:group-list"), |             reverse("authentik_api:group-list"), | ||||||
|             data={"name": generate_id(), "is_superuser": True}, |             data={"name": generate_id(), "is_superuser": True}, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(res.status_code, 400) |         self.assertEqual(res.status_code, 403) | ||||||
|         self.assertJSONEqual( |         self.assertJSONEqual( | ||||||
|             res.content, |             res.content, | ||||||
|             {"is_superuser": ["User does not have permission to set superuser status to True."]}, |             {"detail": "User does not have permission to set the given superuser status."}, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     def test_superuser_update_object_perm(self): | ||||||
|  |         """Test updating a superuser group with object permission""" | ||||||
|  |         group = Group.objects.create(name=generate_id(), is_superuser=False) | ||||||
|  |         assign_perm("view_group", self.login_user, group) | ||||||
|  |         assign_perm("change_group", self.login_user, group) | ||||||
|  |         assign_perm("enable_group_superuser", self.login_user, group) | ||||||
|  |         self.client.force_login(self.login_user) | ||||||
|  |         res = self.client.patch( | ||||||
|  |             reverse("authentik_api:group-detail", kwargs={"pk": group.pk}), | ||||||
|  |             data={"is_superuser": True}, | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(res.status_code, 200) | ||||||
|  |  | ||||||
|     def test_superuser_update_no_perm(self): |     def test_superuser_update_no_perm(self): | ||||||
|         """Test updating a superuser group without permission""" |         """Test updating a superuser group without permission""" | ||||||
|         group = Group.objects.create(name=generate_id(), is_superuser=True) |         group = Group.objects.create(name=generate_id(), is_superuser=True) | ||||||
| @ -134,10 +147,10 @@ class TestGroupsAPI(APITestCase): | |||||||
|             reverse("authentik_api:group-detail", kwargs={"pk": group.pk}), |             reverse("authentik_api:group-detail", kwargs={"pk": group.pk}), | ||||||
|             data={"is_superuser": False}, |             data={"is_superuser": False}, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(res.status_code, 400) |         self.assertEqual(res.status_code, 403) | ||||||
|         self.assertJSONEqual( |         self.assertJSONEqual( | ||||||
|             res.content, |             res.content, | ||||||
|             {"is_superuser": ["User does not have permission to set superuser status to False."]}, |             {"detail": "User does not have permission to set the given superuser status."}, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def test_superuser_update_no_change(self): |     def test_superuser_update_no_change(self): | ||||||
| @ -163,3 +176,27 @@ class TestGroupsAPI(APITestCase): | |||||||
|             data={"name": generate_id(), "is_superuser": True}, |             data={"name": generate_id(), "is_superuser": True}, | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(res.status_code, 201) |         self.assertEqual(res.status_code, 201) | ||||||
|  |  | ||||||
|  |     def test_superuser_create_no_perm(self): | ||||||
|  |         """Test creating a superuser group with no permission""" | ||||||
|  |         assign_perm("authentik_core.add_group", self.login_user) | ||||||
|  |         self.client.force_login(self.login_user) | ||||||
|  |         res = self.client.post( | ||||||
|  |             reverse("authentik_api:group-list"), | ||||||
|  |             data={"name": generate_id(), "is_superuser": True}, | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(res.status_code, 403) | ||||||
|  |         self.assertJSONEqual( | ||||||
|  |             res.content, | ||||||
|  |             {"detail": "User does not have permission to set the given superuser status."}, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     def test_no_superuser_create_no_perm(self): | ||||||
|  |         """Test creating a non-superuser group with no permission""" | ||||||
|  |         assign_perm("authentik_core.add_group", self.login_user) | ||||||
|  |         self.client.force_login(self.login_user) | ||||||
|  |         res = self.client.post( | ||||||
|  |             reverse("authentik_api:group-list"), | ||||||
|  |             data={"name": generate_id()}, | ||||||
|  |         ) | ||||||
|  |         self.assertEqual(res.status_code, 201) | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	