From 57a274da3a77112e6932599fb5788c4f4a78b9c7 Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Thu, 10 Oct 2024 15:47:03 +0530 Subject: [PATCH 1/9] Update get.py --- src/plone/restapi/services/querystring/get.py | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/plone/restapi/services/querystring/get.py b/src/plone/restapi/services/querystring/get.py index 359bf8625c..a18268115a 100644 --- a/src/plone/restapi/services/querystring/get.py +++ b/src/plone/restapi/services/querystring/get.py @@ -4,18 +4,38 @@ from zope.component import getMultiAdapter from zope.component import getUtility - -class QuerystringGet(Service): - """Returns the querystring configuration. - - This basically does the same thing as the '@@querybuilderjsonconfig' - view from p.a.querystring, but exposes the config via the REST API. +class QuerystringEditorGet(Service): + """Returns the complete querystring configuration for editors. + This maintains all existing functionality but requires edit permissions. """ + def reply(self): + registry = getUtility(IRegistry) + reader = getMultiAdapter((registry, self.request), IQuerystringRegistryReader) + reader.vocab_context = self.context + result = reader() + result["@id"] = f"{self.context.absolute_url()}/@querystring" + return result +class QuerystringPublicGet(Service): + """Returns a filtered querystring configuration for public use. + This removes sensitive information like user and group vocabularies. + """ def reply(self): registry = getUtility(IRegistry) reader = getMultiAdapter((registry, self.request), IQuerystringRegistryReader) reader.vocab_context = self.context result = reader() - result["@id"] = "%s/@querystring" % self.context.absolute_url() + + # Filter out sensitive information + sensitive_vocabs = ['plone.app.vocabularies.Users', 'plone.app.vocabularies.Groups'] + indexes_to_remove = [] + + for index_name, index_data in result['indexes'].items(): + if 'vocabulary' in index_data and index_data['vocabulary'] in sensitive_vocabs: + indexes_to_remove.append(index_name) + + for index_name in indexes_to_remove: + del result['indexes'][index_name] + + result["@id"] = f"{self.context.absolute_url()}/@querystring-public" return result From b50ae6782893301bce57d3d1529524110cdd4ea8 Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Thu, 10 Oct 2024 15:47:54 +0530 Subject: [PATCH 2/9] Update configure.zcml --- .../services/querystring/configure.zcml | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/plone/restapi/services/querystring/configure.zcml b/src/plone/restapi/services/querystring/configure.zcml index f7e6f38343..cec5d64195 100644 --- a/src/plone/restapi/services/querystring/configure.zcml +++ b/src/plone/restapi/services/querystring/configure.zcml @@ -4,27 +4,48 @@ xmlns:plone="http://namespaces.plone.org/plone" xmlns:zcml="http://namespaces.zope.org/zcml" > - + + - + + + + + + - From 94011ba84c9e3d8dde268dcc312dcddd8ff4712a Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Thu, 10 Oct 2024 15:48:20 +0530 Subject: [PATCH 3/9] Update configure.zcml --- .../services/querystring/configure.zcml | 37 ++++--------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/src/plone/restapi/services/querystring/configure.zcml b/src/plone/restapi/services/querystring/configure.zcml index cec5d64195..f7e6f38343 100644 --- a/src/plone/restapi/services/querystring/configure.zcml +++ b/src/plone/restapi/services/querystring/configure.zcml @@ -4,48 +4,27 @@ xmlns:plone="http://namespaces.plone.org/plone" xmlns:zcml="http://namespaces.zope.org/zcml" > - - - - - - + + - - + From 1fe3fc719358e4852a95bd1c36cda6b967f38c2e Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Thu, 10 Oct 2024 15:48:42 +0530 Subject: [PATCH 4/9] Update configure.zcml --- .../services/querystring/configure.zcml | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/plone/restapi/services/querystring/configure.zcml b/src/plone/restapi/services/querystring/configure.zcml index f7e6f38343..cec5d64195 100644 --- a/src/plone/restapi/services/querystring/configure.zcml +++ b/src/plone/restapi/services/querystring/configure.zcml @@ -4,27 +4,48 @@ xmlns:plone="http://namespaces.plone.org/plone" xmlns:zcml="http://namespaces.zope.org/zcml" > - + + - + + + + + + - From c11ef26107e37159a077841e19211df463d74e43 Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Wed, 16 Oct 2024 17:32:56 +0530 Subject: [PATCH 5/9] Update get.py --- src/plone/restapi/services/querystring/get.py | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/plone/restapi/services/querystring/get.py b/src/plone/restapi/services/querystring/get.py index a18268115a..f1d6206437 100644 --- a/src/plone/restapi/services/querystring/get.py +++ b/src/plone/restapi/services/querystring/get.py @@ -3,39 +3,29 @@ from plone.restapi.services import Service from zope.component import getMultiAdapter from zope.component import getUtility +from plone.restapi.services.vocabularies.get import VocabulariesGet -class QuerystringEditorGet(Service): - """Returns the complete querystring configuration for editors. - This maintains all existing functionality but requires edit permissions. - """ - def reply(self): - registry = getUtility(IRegistry) - reader = getMultiAdapter((registry, self.request), IQuerystringRegistryReader) - reader.vocab_context = self.context - result = reader() - result["@id"] = f"{self.context.absolute_url()}/@querystring" - return result - -class QuerystringPublicGet(Service): - """Returns a filtered querystring configuration for public use. - This removes sensitive information like user and group vocabularies. - """ +class QuerystringGet(Service): + """Returns the querystring configuration, filtering based on user permissions.""" + def reply(self): registry = getUtility(IRegistry) reader = getMultiAdapter((registry, self.request), IQuerystringRegistryReader) reader.vocab_context = self.context result = reader() - # Filter out sensitive information - sensitive_vocabs = ['plone.app.vocabularies.Users', 'plone.app.vocabularies.Groups'] + # Filter vocabularies based on user permissions + vocabularies_get_service = VocabulariesGet(self.context, self.request) indexes_to_remove = [] for index_name, index_data in result['indexes'].items(): - if 'vocabulary' in index_data and index_data['vocabulary'] in sensitive_vocabs: - indexes_to_remove.append(index_name) - + if 'vocabulary' in index_data: + vocabulary_name = index_data['vocabulary'] + if not vocabularies_get_service._has_permission_to_access_vocabulary(vocabulary_name): + indexes_to_remove.append(index_name) + for index_name in indexes_to_remove: del result['indexes'][index_name] - result["@id"] = f"{self.context.absolute_url()}/@querystring-public" + result["@id"] = f"{self.context.absolute_url()}/@querystring" return result From 7ca1c38674f801db8c8c966affde838430f6cd47 Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Wed, 16 Oct 2024 17:35:03 +0530 Subject: [PATCH 6/9] Create test_querystring_get.py --- .../restapi/tests/test_querystring_get.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/plone/restapi/tests/test_querystring_get.py diff --git a/src/plone/restapi/tests/test_querystring_get.py b/src/plone/restapi/tests/test_querystring_get.py new file mode 100644 index 0000000000..ddcf054a1e --- /dev/null +++ b/src/plone/restapi/tests/test_querystring_get.py @@ -0,0 +1,27 @@ +from plone.app.testing import PLONE_RESTAPI_FUNCTIONAL_TESTING +from plone.restapi.testing import RelativeSession +import unittest + +class TestQuerystringGet(unittest.TestCase): + + layer = PLONE_RESTAPI_FUNCTIONAL_TESTING + + def setUp(self): + self.portal = self.layer["portal"] + self.api_session = RelativeSession(self.portal.absolute_url()) + self.api_session.headers.update({"Accept": "application/json"}) + + def test_vocabularies_permission_filtering(self): + response = self.api_session.get("/@querystring") + self.assertEqual(response.status_code, 200) + result = response.json() + + # Assert that a sensitive vocabulary is filtered out + self.assertNotIn('plone.app.vocabularies.Users', result['indexes']) + self.assertNotIn('plone.app.vocabularies.Groups', result['indexes']) + + # Assert that public vocabularies are still present + self.assertIn('plone.app.vocabularies.Keywords', result['indexes']) + + def tearDown(self): + self.api_session.close() From 6363cfdf0861e50439e613771ae6061506fd4feb Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Wed, 16 Oct 2024 17:41:25 +0530 Subject: [PATCH 7/9] Update querystring.md --- docs/source/endpoints/querystring.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/endpoints/querystring.md b/docs/source/endpoints/querystring.md index b376fa0625..e689638550 100644 --- a/docs/source/endpoints/querystring.md +++ b/docs/source/endpoints/querystring.md @@ -21,6 +21,8 @@ The `operators` property contains additional metadata about each operation. If an index uses a vocabulary, the vocabulary values are included in the `values` property. The vocabulary is resolved in the same context where the `/@querystring` endpoint is called (requires `plone.app.querystring >= 2.1.0`). +**Note:** This endpoint returns the querystring configuration, filtering out vocabularies that the current user does not have permission to access. The filtering is done based on the same permission checks as the `@vocabularies` endpoint, ensuring that sensitive information, such as user or group data, is not exposed to unauthorized users. + ## Get `querystring` configuration To get the metadata about all query operations available in the portal, call the `/@querystring` endpoint with a `GET` request: From 986a18edcac55022b4fe3e9ca45b64a99694c9b3 Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Thu, 17 Oct 2024 12:31:13 +0530 Subject: [PATCH 8/9] Update docs/source/endpoints/querystring.md Co-authored-by: David Glick --- docs/source/endpoints/querystring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/endpoints/querystring.md b/docs/source/endpoints/querystring.md index e689638550..4083e4966c 100644 --- a/docs/source/endpoints/querystring.md +++ b/docs/source/endpoints/querystring.md @@ -21,7 +21,7 @@ The `operators` property contains additional metadata about each operation. If an index uses a vocabulary, the vocabulary values are included in the `values` property. The vocabulary is resolved in the same context where the `/@querystring` endpoint is called (requires `plone.app.querystring >= 2.1.0`). -**Note:** This endpoint returns the querystring configuration, filtering out vocabularies that the current user does not have permission to access. The filtering is done based on the same permission checks as the `@vocabularies` endpoint, ensuring that sensitive information, such as user or group data, is not exposed to unauthorized users. +If an index uses a vocabulary that the current user does not have permission to access, that index will not be included in the results. ## Get `querystring` configuration From 4f30b13d7a79084a761c2f798315b8ec59360409 Mon Sep 17 00:00:00 2001 From: Aditya Pandey Date: Tue, 22 Oct 2024 15:11:28 +0530 Subject: [PATCH 9/9] Update configure.zcml --- .../services/querystring/configure.zcml | 37 ++++--------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/src/plone/restapi/services/querystring/configure.zcml b/src/plone/restapi/services/querystring/configure.zcml index cec5d64195..f7e6f38343 100644 --- a/src/plone/restapi/services/querystring/configure.zcml +++ b/src/plone/restapi/services/querystring/configure.zcml @@ -4,48 +4,27 @@ xmlns:plone="http://namespaces.plone.org/plone" xmlns:zcml="http://namespaces.zope.org/zcml" > - - - - - - + + - - +