diff --git a/geonode/people/tests.py b/geonode/people/tests.py index 7bc783864fc..3f271817a2d 100644 --- a/geonode/people/tests.py +++ b/geonode/people/tests.py @@ -62,6 +62,7 @@ def setUp(self): self.groups = Group.objects.all()[:3] self.group_ids = ",".join(str(element.pk) for element in self.groups) self.bar = GroupProfile.objects.get(slug="bar") + self.group_profiles = [GroupProfile.objects.create(title=f"group_{c}", slug=f"slug_{c}") for c in range(5)] def test_redirect_on_get_request(self): """ @@ -913,6 +914,226 @@ def test_delete_a_user_with_resource(self): self.assertNotEqual(get_user_model().objects.filter(username="bobby").first(), None) self.assertTrue("user_has_resources" in response.json()["errors"][0]) + + def test_remove_self_from_group_manager_all(self): + """ + user is manager of some groups + and is quitting all + """ + bobby = get_user_model().objects.get(username="bobby") + for group in self.group_profiles: + group.join(bobby) + group.promote(bobby) + + self.assertTrue(self.client.login(username="bobby", password="bob")) + self.assertTrue(bobby.is_authenticated) + # assert that bobby is manager of these groups + for group in self.group_profiles: + self.assertTrue(bobby in group.get_managers()) + # call the api that removes him from all assigned groups as manager + response = self.client.post( + path=f"{reverse('users-list')}/{bobby.pk}/remove_from_group_manager", + data={"groups": "ALL"}, + content_type="application/json", + ) + self.assertTrue(response.status_code == 200) + # check that bobby is manager no more + for group in self.group_profiles: + self.assertFalse(bobby in group.get_managers()) + self.assertTrue(group.title in response.json()["success"]) + + def test_remove_self_as_group_manager_list(self): + """ + user is manager of some groups + and is quitting some of them + """ + bobby = get_user_model().objects.get(username="bobby") + + for group in self.group_profiles: + group.join(bobby) + group.promote(bobby) + + self.assertTrue(self.client.login(username="bobby", password="bob")) + self.assertTrue(bobby.is_authenticated) + # assert that bobby is manager of these groups + for group in self.group_profiles: + self.assertTrue(bobby in group.get_managers()) + # call the api that removes him from all assigned groups as manager + response = self.client.post( + path=f"{reverse('users-list')}/{bobby.pk}/remove_from_group_manager", + data={"groups": [group.group_id for group in self.group_profiles[:3]]}, + content_type="application/json", + ) + self.assertTrue(response.status_code == 200) + # check that bobby is no more manager in the first groups + for group in self.group_profiles[:3]: + self.assertFalse(bobby in group.get_managers()) + self.assertTrue(group.title in response.json()["success"]) + # but still manager in the others + for group in self.group_profiles[4:]: + self.assertTrue(bobby in group.get_managers()) + + def test_remove_user_as_group_manager_empty(self): + """ + user is manager of some groups + and is sending empty payload + """ + bobby = get_user_model().objects.get(username="bobby") + + for group in self.group_profiles: + group.join(bobby) + group.promote(bobby) + + self.assertTrue(self.client.login(username="bobby", password="bob")) + self.assertTrue(bobby.is_authenticated) + # assert that bobby is manager of these groups + for group in self.group_profiles: + self.assertTrue(bobby in group.get_managers()) + # call the api that removes him from all assigned groups as manager + response = self.client.post( + path=f"{reverse('users-list')}/{bobby.pk}/remove_from_group_manager", + data={"groups": ""}, + content_type="application/json", + ) + self.assertTrue(response.status_code == 400) + self.assertTrue("No groups IDs were provided" in response.json()["error"]) + # check that bobby is still manager at all groups + for group in self.group_profiles: + self.assertTrue(bobby in group.get_managers()) + + def test_remove_user_as_group_manager_of_invalid_groups(self): + """ + user is manager of some groups + and is trying to remove himself as a manger of a group hes not part of + """ + bobby = get_user_model().objects.get(username="bobby") + for group in self.group_profiles: + group.join(bobby) + group.promote(bobby) + newgroup = GroupProfile.objects.create(title="newgroup") + self.assertTrue(self.client.login(username="bobby", password="bob")) + self.assertTrue(bobby.is_authenticated) + # assert that bobby is manager of these groups + for group in self.group_profiles: + self.assertTrue(bobby in group.get_managers()) + # and not for the new group + self.assertFalse(bobby in newgroup.get_managers()) + # call the api that removes him from all assigned groups as manager" + response = self.client.post( + path=f"{reverse('users-list')}/{bobby.pk}/remove_from_group_manager", + data={"groups": [newgroup.group_id]}, + content_type="application/json", + ) + self.assertTrue(response.status_code == 400) + # check that bobby is still manager at all groups + for group in self.group_profiles: + self.assertTrue(bobby in group.get_managers()) + # assert the invalid group is in the payload + self.assertTrue("User is not manager of the following groups" in response.json()["error"]) + self.assertTrue(f"{newgroup.group_id}" in response.json()["error"]) + + def test_non_admin_remove_user_as_group_manager(self): + """ + a non admin user tries to remove another user as the group manager + """ + bobby = get_user_model().objects.get(username="bobby") + norman = get_user_model().objects.get(username="norman") + for group in self.group_profiles: + group.join(bobby) + group.promote(bobby) + + # assert that bobby is manager of these groups + for group in self.group_profiles: + self.assertTrue(bobby in group.get_managers()) + # login as norman + self.client.force_login(norman) + self.assertTrue(norman.is_authenticated) + # assert that norman is neither a super user or staff + self.assertFalse(norman.is_superuser) + self.assertFalse(norman.is_staff) + + response = self.client.post( + path=f"{reverse('users-list')}/{bobby.pk}/remove_from_group_manager", + data={"groups": "ALL"}, + content_type="application/json", + ) + self.assertTrue(response.status_code == 403) + # check that bobby is still manager at all groups + for group in self.group_profiles: + self.assertTrue(bobby in group.get_managers()) + # assert that norman has no permission for this action + self.assertEqual("permission_denied", response.json()["code"]) + + def test_admin_remove_user_as_group_manager(self): + """ + an admin user tries to remove another user as the group manager + """ + bobby = get_user_model().objects.get(username="bobby") + admin = get_user_model().objects.get(username="admin") + for group in self.group_profiles: + group.join(bobby) + group.promote(bobby) + + # assert that bobby is manager of these groups + for group in self.group_profiles: + self.assertTrue(bobby in group.get_managers()) + # login as admin + self.client.force_login(admin) + self.assertTrue(admin.is_authenticated) + # assert that admin is a super user , staff + self.assertTrue(admin.is_superuser) + self.assertTrue(admin.is_staff) + + response = self.client.post( + path=f"{reverse('users-list')}/{bobby.pk}/remove_from_group_manager", + data={"groups": "ALL"}, + content_type="application/json", + ) + # assert that admin has permission for this action + self.assertTrue(response.status_code == 200) + # check that bobby is not manager in all his groups + for group in self.group_profiles: + self.assertFalse(bobby in group.get_managers()) + self.assertTrue("User removed as a group manager" in response.json()["success"]) + for group in self.group_profiles: + self.assertTrue(f"{group.title}" in response.json()["success"]) + + def test_staff_remove_user_as_group_manager(self): + """ + an staff user tries to remove another user as the group manager + """ + bobby = get_user_model().objects.get(username="bobby") + admin = get_user_model().objects.get(username="admin") + admin.is_superuser = False + admin.save() + for group in self.group_profiles: + group.join(bobby) + group.promote(bobby) + + # assert that bobby is manager of these groups + for group in self.group_profiles: + self.assertTrue(bobby in group.get_managers()) + # login as admin + self.client.force_login(admin) + self.assertTrue(admin.is_authenticated) + # assert that admin is only a staff user + self.assertFalse(admin.is_superuser) + self.assertTrue(admin.is_staff) + + response = self.client.post( + path=f"{reverse('users-list')}/{bobby.pk}/remove_from_group_manager", + data={"groups": "ALL"}, + content_type="application/json", + ) + # assert that admin has permission for this action + self.assertTrue(response.status_code == 200) + # check that bobby is not manager in all his groups + for group in self.group_profiles: + self.assertFalse(bobby in group.get_managers()) + self.assertTrue("User removed as a group manager" in response.json()["success"]) + for group in self.group_profiles: + self.assertTrue(f"{group.title}" in response.json()["success"]) + def test_transfer_resources_all(self): """ user wants to transfer resources to target @@ -1065,3 +1286,4 @@ def test_transfer_resources_nopayload(self): self.assertTrue(bobby_resources.exists()) later_bobby_resources = ResourceBase.objects.filter(owner=bobby).all() self.assertTrue(set(prior_bobby_resources) == set(later_bobby_resources)) + diff --git a/geonode/people/views.py b/geonode/people/views.py index 7da510cf711..7af034ac895 100644 --- a/geonode/people/views.py +++ b/geonode/people/views.py @@ -252,6 +252,35 @@ def groups(self, request, pk=None): return Response(GroupProfileSerializer(embed=True, many=True).to_representation(groups)) @action(detail=True, methods=["post"]) + + def remove_from_group_manager(self, request, pk=None): + user = self.get_object() + target_ids = request.data.get("groups", []) + user_groups = [] + invalid_groups = [] + + if not target_ids: + return Response({"error": "No groups IDs were provided"}, status=400) + + if target_ids == "ALL": + user_groups = GroupProfile.groups_for_user(user) + else: + target_ids = set(target_ids) + user_groups = GroupProfile.groups_for_user(user).filter(group_id__in=target_ids) + # check for groups that user is not part of: + invalid_groups.extend(target_ids - set(ug.group_id for ug in user_groups)) + + for group in user_groups: + group.demote(user) + group_names = [group.title for group in user_groups] + + payload = {"success": f"User removed as a group manager from : {', '.join(group_names)}"} + + if invalid_groups: + payload["error"] = f"User is not manager of the following groups: : {invalid_groups}" + return Response(payload, status=400) + return Response(payload, status=200) + def transfer_resources(self, request, pk=None): user = self.get_object() admin = get_user_model().objects.filter(is_superuser=True, is_staff=True).first() @@ -274,6 +303,7 @@ def transfer_resources(self, request, pk=None): return Response("Resources transfered successfully", status=200) + class ProfileAutocomplete(autocomplete.Select2QuerySetView): def get_queryset(self): if self.request and self.request.user: