Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not expose private metadata via relationfield serializer. #1635

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions news/1634.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Do not expose private metadata via relationfield serializer.
[maethu]
15 changes: 13 additions & 2 deletions src/plone/restapi/serializer/relationfield.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from plone import api
from plone.dexterity.interfaces import IDexterityContent
from plone.restapi.interfaces import IFieldSerializer
from plone.restapi.interfaces import IJsonCompatible
Expand All @@ -17,7 +18,12 @@
@adapter(IRelationValue)
@implementer(IJsonCompatible)
def relationvalue_converter(value):
if value.to_object:
has_permission = api.user.has_permission(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@plone/restapi-team In a comment above @erral wrote plone.api usage is allowed in this package in tests only and asked if this is right. May one you confirm this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We agreed that we can use plone.api in tests. Though, if I am not mistaken we also decided lately that plone.restapi can use plone.api everywhere, correct? Sorry for the late reply here. Just want to get this straight.

"Access Contents Information",
obj=value.__parent__
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not get why we now check on parent? This is is probably a misunderstanding and wrong.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jensens

Probably another miss-understanding on my end.

I used the parent, since @davisagli wrote in the issue #1634
@maethu In general, permission to view an object's metadata in listings is controlled by the "Access contents information" permission on its container, not the View permission.

I'm happy to change this!

)

if value.to_object and has_permission:
request = getRequest()
request.form["metadata_fields"] = ["UID"]
summary = getMultiAdapter((value.to_object, request), ISerializeToJsonSummary)()
Expand All @@ -33,4 +39,9 @@ class RelationChoiceFieldSerializer(DefaultFieldSerializer):
@adapter(IRelationList, IDexterityContent, Interface)
@implementer(IFieldSerializer)
class RelationListFieldSerializer(DefaultFieldSerializer):
pass
def __call__(self):
value = self.get_value()
if value:
return [item for item in json_compatible(value) if item]
else:
return super().__call__()
Comment on lines +44 to +47
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if value:
return [item for item in json_compatible(value) if item]
else:
return super().__call__()
if value:
return [item for item in json_compatible(value) if item]
return super().__call__()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sake of readability: No else needed when the if returns.

26 changes: 25 additions & 1 deletion src/plone/restapi/tests/test_content_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from plone.restapi.testing import PLONE_RESTAPI_DX_FUNCTIONAL_TESTING
from Products.CMFCore.utils import getToolByName
from z3c.relationfield import RelationValue
from z3c.relationfield.event import updateRelations
from zope.component import getUtility
from zope.intid.interfaces import IIntIds

Expand All @@ -18,7 +19,6 @@


class TestContentGet(unittest.TestCase):

layer = PLONE_RESTAPI_DX_FUNCTIONAL_TESTING

def setUp(self):
Expand Down Expand Up @@ -137,6 +137,30 @@ def test_get_content_includes_related_items(self):
response.json()["relatedItems"],
)

def test_get_content_includes_related_items_filtered_by_view_permission(self):
intids = getUtility(IIntIds)
self.portal.folder1.doc1.relatedItems = [
RelationValue(intids.getId(self.portal.folder1.folder2.doc2)),
]
updateRelations(self.portal.folder1.doc1, None)
# Remove view permission
self.portal.folder1.folder1.doc1.manage_permission(
"Access contents information", roles=[], acquire=False
)
transaction.commit()

response = requests.get(
self.portal.folder1.doc1.absolute_url(),
headers={"Accept": "application/json"},
auth=(SITE_OWNER_NAME, SITE_OWNER_PASSWORD),
)
self.assertEqual(response.status_code, 200)
self.assertEqual(0, len(response.json()["relatedItems"]))
self.assertEqual(
[],
response.json()["relatedItems"],
)

def test_get_content_related_items_without_workflow(self):
intids = getUtility(IIntIds)

Expand Down