Repository: plone/plone.restapi (language: python)

Issue:
Adding a behavior with permissions to a PloneSite breaks the serialization
Traceback
```
2024-10-31 17:43:51,233 ERROR   [Zope.SiteErrorLog:36][waitress-0] AttributeError: http://localhost:8080/Plone/++api++/GET_application_json_
Traceback (innermost last):
  Module ZPublisher.WSGIPublisher, line 181, in transaction_pubevents
  Module ZPublisher.WSGIPublisher, line 391, in publish_module
  Module ZPublisher.WSGIPublisher, line 285, in publish
  Module ZPublisher.mapply, line 98, in mapply
  Module ZPublisher.WSGIPublisher, line 68, in call_object
  Module plone.rest.service, line 21, in __call__
  Module plone.restapi.services, line 19, in render
  Module plone.restapi.services.content.get, line 16, in reply
  Module plone.restapi.serializer.site, line 88, in __call__
  Module plone.restapi.serializer.site, line 133, in check_permission
AttributeError: 'SerializeSiteRootToJson' object has no attribute 'permission_cache'
```


Discussion / hints:

Fix patch:
diff --git a/news/1830.bugfix b/news/1830.bugfix
new file mode 100644
index 0000000000..df8dfb81c3
--- /dev/null
+++ b/news/1830.bugfix
@@ -0,0 +1,1 @@
+Fixed Plone Site serialization when there is a field with read_permission set. @ericof
\ No newline at end of file
diff --git a/src/plone/restapi/serializer/site.py b/src/plone/restapi/serializer/site.py
index 11c5a0a2a3..65081fd2ea 100644
--- a/src/plone/restapi/serializer/site.py
+++ b/src/plone/restapi/serializer/site.py
@@ -4,15 +4,17 @@
 from plone.dexterity.utils import iterSchemata
 from plone.restapi.batching import HypermediaBatch
 from plone.restapi.bbb import IPloneSiteRoot
-from plone.restapi.blocks import visit_blocks, iter_block_transform_handlers
+from plone.restapi.blocks import iter_block_transform_handlers
+from plone.restapi.blocks import visit_blocks
+from plone.restapi.interfaces import IBlockFieldSerializationTransformer
 from plone.restapi.interfaces import IFieldSerializer
 from plone.restapi.interfaces import ISerializeToJson
 from plone.restapi.interfaces import ISerializeToJsonSummary
-from plone.restapi.interfaces import IBlockFieldSerializationTransformer
 from plone.restapi.serializer.converters import json_compatible
+from plone.restapi.serializer.dxcontent import get_allow_discussion_value
 from plone.restapi.serializer.expansion import expandable_elements
-from plone.restapi.services.locking import lock_info
 from plone.restapi.serializer.utils import get_portal_type_title
+from plone.restapi.services.locking import lock_info
 from plone.supermodel.utils import mergedTaggedValueDict
 from Products.CMFCore.utils import getToolByName
 from zope.component import adapter
@@ -23,7 +25,6 @@
 from zope.interface import Interface
 from zope.schema import getFields
 from zope.security.interfaces import IPermission
-from plone.restapi.serializer.dxcontent import get_allow_discussion_value
 
 import json
 
@@ -39,6 +40,7 @@ class SerializeSiteRootToJson:
     def __init__(self, context, request):
         self.context = context
         self.request = request
+        self.permission_cache = {}
 
     def _build_query(self):
         path = "/".join(self.context.getPhysicalPath())


Test patch:
diff --git a/src/plone/restapi/tests/dxtypes.py b/src/plone/restapi/tests/dxtypes.py
index b3632d41d9..c7f8ef148f 100644
--- a/src/plone/restapi/tests/dxtypes.py
+++ b/src/plone/restapi/tests/dxtypes.py
@@ -312,6 +312,9 @@ class ITestBehavior(model.Schema):
     test_behavior_field = schema.TextLine(required=False)
     # Add nav_title to test if it gets substituted in Navigation service
     nav_title = schema.TextLine(required=False)
+    # Add a field with read permission set
+    test_secure_field = schema.TextLine(required=False)
+    read_permission(test_secure_field="cmf.ManagePortal")
 
 
 @provider(IFormFieldProvider)
diff --git a/src/plone/restapi/tests/test_site_serializer.py b/src/plone/restapi/tests/test_site_serializer.py
index 34174a7629..60de5ffffd 100644
--- a/src/plone/restapi/tests/test_site_serializer.py
+++ b/src/plone/restapi/tests/test_site_serializer.py
@@ -38,6 +38,7 @@ def setUp(self):
         if fti is not None:
             behavior_list = [a for a in fti.behaviors]
             behavior_list.append("volto.blocks")
+            behavior_list.append("tests.restapi.test_behavior")
             fti.behaviors = tuple(behavior_list)
             # Invalidating the cache is required for the FTI to be applied
             # on the existing object