diff --git a/src/plone/restapi/services/users/get.py b/src/plone/restapi/services/users/get.py index 75fdf453be..38ad2fcade 100644 --- a/src/plone/restapi/services/users/get.py +++ b/src/plone/restapi/services/users/get.py @@ -8,6 +8,7 @@ from plone.namedfile.utils import stream_data from plone.restapi.interfaces import ISerializeToJson from plone.restapi.services import Service +from Products.CMFCore.permissions import ManagePortal from Products.CMFCore.utils import getToolByName from Products.CMFPlone.utils import normalizeString from Products.PlonePAS.tools.memberdata import MemberData @@ -60,6 +61,10 @@ def __init__(self, context, request): self.query = parse_qs(self.request["QUERY_STRING"]) self.search_term = self.query.get("search", [""])[0] + @property + def is_zope_manager(self): + return getSecurityManager().checkPermission(ManagePortal, self.context) + def publishTraverse(self, request, name): # Consume any path segments after /@users as parameters self.params.append(name) @@ -165,7 +170,13 @@ def has_permission_to_access_user_info(self): "plone.restapi: Access Plone user information", self.context ) + def can_delete(self, is_zope_manager, roles): + if is_zope_manager: + return True + return "Manager" not in roles + def reply(self): + is_zope_manager = self.is_zope_manager if len(self.query) > 0 and len(self.params) == 0: query = self.query.get("query", "") groups_filter = self.query.get("groups-filter:list", []) @@ -180,7 +191,11 @@ def reply(self): serializer = queryMultiAdapter( (user, self.request), ISerializeToJson ) - result.append(serializer()) + user_serializer = serializer() + user_serializer["can_delete"] = self.can_delete( + is_zope_manager, user_serializer["roles"] + ) + result.append(user_serializer) return result else: self.request.response.setStatus(401) @@ -196,7 +211,11 @@ def reply(self): serializer = queryMultiAdapter( (user, self.request), ISerializeToJson ) - result.append(serializer()) + user_serializer = serializer() + user_serializer["can_delete"] = self.can_delete( + is_zope_manager, user_serializer["roles"] + ) + result.append(user_serializer) return result else: self.request.response.setStatus(401) @@ -216,7 +235,11 @@ def reply(self): self.request.response.setStatus(404) return serializer = queryMultiAdapter((user, self.request), ISerializeToJson) - return serializer() + user_serializer = serializer() + user_serializer["can_delete"] = self.can_delete( + is_zope_manager, user_serializer["roles"] + ) + return user_serializer else: self.request.response.setStatus(401) return diff --git a/src/plone/restapi/tests/test_services_users.py b/src/plone/restapi/tests/test_services_users.py index 2db6701e3e..626f85f2e9 100644 --- a/src/plone/restapi/tests/test_services_users.py +++ b/src/plone/restapi/tests/test_services_users.py @@ -122,6 +122,22 @@ def test_list_users(self): self.assertEqual("web.mit.edu/chomsky", noam.get("home_page")) # noqa self.assertEqual("Professor of Linguistics", noam.get("description")) # noqa self.assertEqual("Cambridge, MA", noam.get("location")) + self.assertTrue(noam.get("can_delete")) + + def test_siteadm_can_delete(self): + self.set_siteadm() + api.user.create( + email="manager@example.com", + roles=["Manager"], + username="manager", + password="managerpassword", + ) + transaction.commit() + + response = self.api_session.get("/@users") + + manager = [x for x in response.json() if x.get("username") == "manager"][0] + self.assertFalse(manager.get("can_delete")) def test_list_users_without_being_manager(self): noam_api_session = RelativeSession(self.portal_url, test=self)