Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into translate_errors
Browse files Browse the repository at this point in the history
  • Loading branch information
davisagli committed Jan 14, 2024
2 parents 0ab90f8 + 65f0d54 commit 4d35524
Show file tree
Hide file tree
Showing 32 changed files with 604 additions and 47 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ jobs:
plone-version: "6.0"
- python-version: "3.11"
plone-version: "6.0"
- python-version: "3.12"
# FIXME: Change to 3.12 when https://github.com/python/cpython/issues/113267 is fixed
- python-version: "3.12.0"
plone-version: "6.0"
steps:
# git checkout
Expand Down
15 changes: 15 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ Changelog
.. towncrier release notes start
9.3.0 (2024-01-10)
------------------

New features:


- Give Site Administrator permission to manage users. To make this possible, we now check the "plone.app.controlpanel.UsersAndGroups" permission instead of "cmf.ManagePortal" in a lot of operations in the users and groups endpoints. @wesleybl (#1712)


Internal:


- Use Python 3.12.0 in tests to work around https://github.com/python/cpython/issues/113267. @wesleybl (#1740)


9.2.1 (2023-12-14)
------------------

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import sys


version = "9.2.2.dev0"
version = "9.3.1.dev0"

if sys.version_info.major == 2:
raise ValueError(
Expand Down
12 changes: 10 additions & 2 deletions src/plone/restapi/locales/de/LC_MESSAGES/plone.restapi.po
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ msgstr ""
msgid "Volto Blocks"
msgstr ""

#: plone/restapi/services/users/update.py:119
#: plone/restapi/services/users/update.py:151
msgid "You are not authorized to perform this action"
msgstr ""

Expand All @@ -167,10 +167,18 @@ msgstr ""
msgid "You can't set a password without a password reset token."
msgstr ""

#: plone/restapi/services/users/update.py:125
#: plone/restapi/services/users/update.py:120
msgid "You can't update roles of this user"
msgstr ""

#: plone/restapi/services/users/update.py:157
msgid "You can't update the properties of this user"
msgstr ""

#: plone/restapi/services/users/update.py:94
msgid "You can't update this user"
msgstr ""

#: plone/restapi/services/users/add.py:284
msgid "You can't use 'reset_token' and 'old_password' together."
msgstr ""
Expand Down
12 changes: 10 additions & 2 deletions src/plone/restapi/locales/es/LC_MESSAGES/plone.restapi.po
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ msgstr "El reset_token es desconocido/no válido."
msgid "Volto Blocks"
msgstr "Bloques Volto"

#: plone/restapi/services/users/update.py:119
#: plone/restapi/services/users/update.py:151
msgid "You are not authorized to perform this action"
msgstr "No está autorizado a realizar esta acción."

Expand All @@ -173,10 +173,18 @@ msgstr "No puede enviar la contraseña y 'Enviar un correo electrónico de confi
msgid "You can't set a password without a password reset token."
msgstr "No puede establecer una contraseña sin un token de restablecimiento de contraseña."

#: plone/restapi/services/users/update.py:125
#: plone/restapi/services/users/update.py:120
msgid "You can't update roles of this user"
msgstr ""

#: plone/restapi/services/users/update.py:157
msgid "You can't update the properties of this user"
msgstr "No puede actualizar las propiedades de este usuario."

#: plone/restapi/services/users/update.py:94
msgid "You can't update this user"
msgstr ""

#: plone/restapi/services/users/add.py:284
msgid "You can't use 'reset_token' and 'old_password' together."
msgstr "No puede utilizar 'Restablecer token' y 'Contraseña anterior' juntas."
Expand Down
12 changes: 10 additions & 2 deletions src/plone/restapi/locales/fr/LC_MESSAGES/plone.restapi.po
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ msgstr "Le reset_token est inconnu/non valide."
msgid "Volto Blocks"
msgstr "Blocs Volto"

#: plone/restapi/services/users/update.py:119
#: plone/restapi/services/users/update.py:151
msgid "You are not authorized to perform this action"
msgstr "Vous n'êtes pas autorisé à effectuer cette action"

Expand All @@ -167,10 +167,18 @@ msgstr "Vous ne pouvez pas envoyer à la fois le password et sendPasswordReset"
msgid "You can't set a password without a password reset token."
msgstr "Vous ne pouvez pas définir de mot de passe sans un jeton de réinitialisation de mot de passe."

#: plone/restapi/services/users/update.py:125
#: plone/restapi/services/users/update.py:120
msgid "You can't update roles of this user"
msgstr ""

#: plone/restapi/services/users/update.py:157
msgid "You can't update the properties of this user"
msgstr "Vous ne pouvez pas mettre à jour les propriétés de cet utilisateur"

#: plone/restapi/services/users/update.py:94
msgid "You can't update this user"
msgstr ""

#: plone/restapi/services/users/add.py:284
msgid "You can't use 'reset_token' and 'old_password' together."
msgstr "Vous ne pouvez pas utiliser 'reset_token' et 'old_password' ensemble."
Expand Down
12 changes: 10 additions & 2 deletions src/plone/restapi/locales/plone.restapi.pot
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ msgstr ""
msgid "Volto Blocks"
msgstr ""

#: plone/restapi/services/users/update.py:119
#: plone/restapi/services/users/update.py:151
msgid "You are not authorized to perform this action"
msgstr ""

Expand All @@ -170,10 +170,18 @@ msgstr ""
msgid "You can't set a password without a password reset token."
msgstr ""

#: plone/restapi/services/users/update.py:125
#: plone/restapi/services/users/update.py:120
msgid "You can't update roles of this user"
msgstr ""

#: plone/restapi/services/users/update.py:157
msgid "You can't update the properties of this user"
msgstr ""

#: plone/restapi/services/users/update.py:94
msgid "You can't update this user"
msgstr ""

#: plone/restapi/services/users/add.py:284
msgid "You can't use 'reset_token' and 'old_password' together."
msgstr ""
Expand Down
12 changes: 10 additions & 2 deletions src/plone/restapi/locales/pt_BR/LC_MESSAGES/plone.restapi.po
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ msgstr "O reset_token é desconhecido/inválido."
msgid "Volto Blocks"
msgstr "Blocos Volto"

#: plone/restapi/services/users/update.py:119
#: plone/restapi/services/users/update.py:151
msgid "You are not authorized to perform this action"
msgstr "Você não está autorizado a realizar esta ação"

Expand All @@ -167,10 +167,18 @@ msgstr "Você não pode informar Senha e 'Envie um e-mail de confirmação com u
msgid "You can't set a password without a password reset token."
msgstr "Você não pode definir uma senha sem um token de redefinição de senha."

#: plone/restapi/services/users/update.py:125
#: plone/restapi/services/users/update.py:120
msgid "You can't update roles of this user"
msgstr "Você não pode atualizar os papéis deste usuário"

#: plone/restapi/services/users/update.py:157
msgid "You can't update the properties of this user"
msgstr "Você não pode atualizar as propriedades deste usuário"

#: plone/restapi/services/users/update.py:94
msgid "You can't update this user"
msgstr "Você não pode atualizar este usuário"

#: plone/restapi/services/users/add.py:284
msgid "You can't use 'reset_token' and 'old_password' together."
msgstr "Você não pode usar 'Redefinir Token' e 'Senha Antiga' juntos."
Expand Down
2 changes: 2 additions & 0 deletions src/plone/restapi/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
# permissions. Granted to Anonymous (i.e. everyone) by default via rolemap.xml

UseRESTAPI = "plone.restapi: Use REST API"

PloneManageUsers = "Plone Site Setup: Users and Groups"
2 changes: 1 addition & 1 deletion src/plone/restapi/profiles/default/metadata.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<metadata>
<version>0006</version>
<version>0007</version>
</metadata>
1 change: 1 addition & 0 deletions src/plone/restapi/profiles/default/rolemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</permission>
<permission name="plone.restapi: Access Plone user information" acquire="True">
<role name="Manager"/>
<role name="Site Administrator"/>
</permission>
</permissions>
</rolemap>
6 changes: 3 additions & 3 deletions src/plone/restapi/serializer/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from plone.dexterity.schema import lookup_fti
from plone.app.uuid.utils import uuidToCatalogBrain
from plone.dexterity.schema import lookup_fti
from plone.restapi.interfaces import IObjectPrimaryFieldTarget
from zope.component import queryMultiAdapter
from zope.i18n import translate
from zope.globalrequest import getRequest
from zope.i18n import translate

import re

Expand Down Expand Up @@ -43,7 +43,7 @@ def resolve_uid(path):


def uid_to_url(path):
path, brain = resolve_uid(path)
path, _brain = resolve_uid(path)
return path


Expand Down
14 changes: 13 additions & 1 deletion src/plone/restapi/services/groups/add.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from AccessControl import getSecurityManager
from plone.restapi.deserializer import json_body
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 zExceptions import BadRequest
from zope.component import queryMultiAdapter
Expand All @@ -13,6 +15,10 @@
class GroupsPost(Service):
"""Creates a new group."""

@property
def is_zope_manager(self):
return getSecurityManager().checkPermission(ManagePortal, self.context)

def reply(self):
portal = getSite()
data = json_body(self.request)
Expand All @@ -22,10 +28,16 @@ def reply(self):
if not groupname:
raise BadRequest("Property 'groupname' is required")

roles = data.get("roles", [])

if not self.is_zope_manager and "Manager" in roles:
raise BadRequest(
"You don't have permission to create a group with the 'Manager' role"
)

email = data.get("email", None)
title = data.get("title", None)
description = data.get("description", None)
roles = data.get("roles", None)
groups = data.get("groups", None)
users = data.get("users", [])

Expand Down
8 changes: 4 additions & 4 deletions src/plone/restapi/services/groups/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,31 @@
method="GET"
factory=".get.GroupsGet"
for="Products.CMFCore.interfaces.ISiteRoot"
permission="cmf.ManagePortal"
permission="plone.app.controlpanel.UsersAndGroups"
name="@groups"
/>

<plone:service
method="PATCH"
factory=".update.GroupsPatch"
for="Products.CMFCore.interfaces.ISiteRoot"
permission="cmf.ManagePortal"
permission="plone.app.controlpanel.UsersAndGroups"
name="@groups"
/>

<plone:service
method="POST"
factory=".add.GroupsPost"
for="Products.CMFCore.interfaces.ISiteRoot"
permission="cmf.ManagePortal"
permission="plone.app.controlpanel.UsersAndGroups"
name="@groups"
/>

<plone:service
method="DELETE"
factory=".delete.GroupsDelete"
for="Products.CMFCore.interfaces.ISiteRoot"
permission="cmf.ManagePortal"
permission="plone.app.controlpanel.UsersAndGroups"
name="@groups"
/>

Expand Down
12 changes: 12 additions & 0 deletions src/plone/restapi/services/groups/delete.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from AccessControl import getSecurityManager
from plone.restapi.services import Service
from Products.CMFCore.permissions import ManagePortal
from Products.CMFCore.utils import getToolByName
from zExceptions import BadRequest
from zExceptions import NotFound
from zope.component.hooks import getSite
from zope.interface import implementer
Expand All @@ -19,6 +22,10 @@ def publishTraverse(self, request, name):
self.params.append(name)
return self

@property
def is_zope_manager(self):
return getSecurityManager().checkPermission(ManagePortal, self.context)

@property
def _get_group_id(self):
if len(self.params) != 1:
Expand All @@ -38,6 +45,11 @@ def reply(self):
if not group:
raise NotFound("Trying to delete a non-existing group.")

if not self.is_zope_manager and "Manager" in group.getRoles():
raise BadRequest(
"You don't have permission to delete a group with the Manager role"
)

delete_successful = portal_groups.removeGroup(self._get_group_id)
if delete_successful:
return self.reply_no_content()
Expand Down
39 changes: 38 additions & 1 deletion src/plone/restapi/services/groups/update.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from AccessControl import getSecurityManager
from plone.restapi.deserializer import json_body
from plone.restapi.services import Service
from Products.CMFCore.permissions import ManagePortal
from Products.CMFCore.utils import getToolByName
from zExceptions import BadRequest
from zope.component.hooks import getSite
Expand Down Expand Up @@ -34,6 +36,36 @@ def __init__(self, context, request):
super().__init__(context, request)
self.params = []

@property
def is_zope_manager(self):
return getSecurityManager().checkPermission(ManagePortal, self.context)

def can_update(self, group, users, roles, groups):
# Manager can update
if self.is_zope_manager:
return True
# Does not allow an Site Administrator to add users to groups
# with the Manager role
current_group_roles = group.getRoles()
if "Manager" in current_group_roles and users:
return False
# Does not allow an Site Administrator set Manager to group
result_roles = True
if roles:
if "Manager" in roles:
result_roles = "Manager" in current_group_roles
else:
result_roles = "Manager" not in current_group_roles
# Does not allow an Site Administrator add group to group with Manager role
result_groups = True
if groups:
for assign_group_id in groups:
assign_group = self._get_group(assign_group_id)
if "Manager" in assign_group.getRoles():
result_groups = False
break
return result_roles and result_groups

def publishTraverse(self, request, name):
# Consume any path segments after /@groups as parameters
self.params.append(name)
Expand All @@ -57,9 +89,14 @@ def reply(self):
if not group:
raise BadRequest("Trying to update a non-existing group.")

users = data.get("users", {})
roles = data.get("roles", None)
groups = data.get("groups", None)
users = data.get("users", {})

if not self.can_update(group, users, roles, groups):
raise BadRequest(
"You don't have permission to assign a 'Manager' role to a group."
)

# Disable CSRF protection
if "IDisableCSRFProtection" in dir(plone.protect.interfaces):
Expand Down
Loading

0 comments on commit 4d35524

Please sign in to comment.