diff --git a/authentik/core/api/groups.py b/authentik/core/api/groups.py index bb7089107..94d932e1f 100644 --- a/authentik/core/api/groups.py +++ b/authentik/core/api/groups.py @@ -1,5 +1,6 @@ """Groups API Viewset""" from json import loads +from typing import Optional from django.db.models.query import QuerySet from django.http import Http404 @@ -52,6 +53,14 @@ class GroupSerializer(ModelSerializer): num_pk = IntegerField(read_only=True) + def validate_parent(self, parent: Optional[Group]): + """Validate group parent (if set), ensuring the parent isn't itself""" + if not self.instance or not parent: + return parent + if str(parent.group_uuid) == str(self.instance.group_uuid): + raise ValidationError("Cannot set group as parent of itself.") + return parent + class Meta: model = Group fields = [ diff --git a/authentik/core/tests/test_groups_api.py b/authentik/core/tests/test_groups_api.py index b4479e451..cff643f5a 100644 --- a/authentik/core/tests/test_groups_api.py +++ b/authentik/core/tests/test_groups_api.py @@ -67,3 +67,16 @@ class TestGroupsAPI(APITestCase): }, ) self.assertEqual(res.status_code, 404) + + def test_parent_self(self): + """Test parent""" + group = Group.objects.create(name=generate_id()) + self.client.force_login(self.admin) + res = self.client.patch( + reverse("authentik_api:group-detail", kwargs={"pk": group.pk}), + data={ + "pk": self.user.pk + 3, + "parent": group.pk, + }, + ) + self.assertEqual(res.status_code, 400) diff --git a/web/src/admin/groups/GroupForm.ts b/web/src/admin/groups/GroupForm.ts index 35efe6bda..d849d67f8 100644 --- a/web/src/admin/groups/GroupForm.ts +++ b/web/src/admin/groups/GroupForm.ts @@ -95,6 +95,9 @@ export class GroupForm extends ModelForm { args.search = query; } const groups = await new CoreApi(DEFAULT_CONFIG).coreGroupsList(args); + if (this.instance) { + return groups.results.filter((g) => g.pk !== this.instance?.pk); + } return groups.results; }} .renderElement=${(group: Group): string => {