Commit 30da809a authored by Jérome Perrin's avatar Jérome Perrin

ContributionTool: new isURLIngestionPermitted hook

This allows configuring what URLs can be ingested, by default, no URL
can be ingested.
parent 973ab86d
Pipeline #32719 failed with stage
in 0 seconds
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
import unittest import unittest
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.utils import createZODBPythonScript, removeZODBPythonScript
import urlnorm # pylint: disable=unused-import import urlnorm # pylint: disable=unused-import
# This library is imported to detect lack of # This library is imported to detect lack of
# urlnorm availibility in python environment # urlnorm availibility in python environment
...@@ -71,10 +72,20 @@ class TestWebCrawler(ERP5TypeTestCase): ...@@ -71,10 +72,20 @@ class TestWebCrawler(ERP5TypeTestCase):
self.portal = self.getPortal() self.portal = self.getPortal()
self.setSystemPreference() self.setSystemPreference()
self.bootstrapWebSite() self.bootstrapWebSite()
createZODBPythonScript(
self.portal.portal_skins.custom,
"ContributionTool_isURLIngestionPermitted",
"url",
"return True",
)
self.tic() self.tic()
def beforeTearDown(self): def beforeTearDown(self):
portal = self.portal portal = self.portal
removeZODBPythonScript(
portal.portal_skins.custom,
'ContributionTool_isURLIngestionPermitted',
)
module_id_list = [ module_id_list = [
'web_page_module', 'web_page_module',
'web_site_module', 'web_site_module',
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
from Products.ERP5Type.tests.ERP5TypeLiveTestCase import ERP5TypeLiveTestCase from Products.ERP5Type.tests.ERP5TypeLiveTestCase import ERP5TypeLiveTestCase
from Products.ERP5Type.tests.utils import createZODBPythonScript, removeZODBPythonScript
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
import random import random
import string import string
...@@ -57,9 +58,19 @@ class TestIngestion(ERP5TypeLiveTestCase): ...@@ -57,9 +58,19 @@ class TestIngestion(ERP5TypeLiveTestCase):
self.login() self.login()
self.portal = self.getPortal() self.portal = self.getPortal()
self.setSystemPreference() self.setSystemPreference()
createZODBPythonScript(
self.portal.portal_skins.custom,
"ContributionTool_isURLIngestionPermitted",
"url",
"return True",
)
def beforeTearDown(self): def beforeTearDown(self):
portal = self.portal portal = self.portal
removeZODBPythonScript(
portal.portal_skins.custom,
'ContributionTool_isURLIngestionPermitted',
)
# delete created documents by test # delete created documents by test
for path in self._path_to_delete_list: for path in self._path_to_delete_list:
document = portal.unrestrictedTraverse(path, None) document = portal.unrestrictedTraverse(path, None)
......
return url in ('http://www.erp5.com/login_form', 'http://www.erp5.com/nosuch_view')
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>url</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ContributionTool_isURLIngestionPermitted</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>url</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ContributionTool_isURLIngestionPermitted</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -632,6 +632,9 @@ class ContributionTool(BaseTool): ...@@ -632,6 +632,9 @@ class ContributionTool(BaseTool):
read filename and content_type read filename and content_type
return file_object, filename, content_type tuple return file_object, filename, content_type tuple
""" """
isURLIngestionPermitted = self._getTypeBasedMethod('isURLIngestionPermitted')
if isURLIngestionPermitted is None or not isURLIngestionPermitted(url):
raise Unauthorized("URL ingestion not allowed")
# Quote path part of url # Quote path part of url
url = reencodeUrlEscapes(url) url = reencodeUrlEscapes(url)
# build a new file from the url # build a new file from the url
......
...@@ -55,6 +55,7 @@ import urllib2 ...@@ -55,6 +55,7 @@ import urllib2
import httplib import httplib
import urlparse import urlparse
import base64 import base64
import mock
# test files' home # test files' home
TEST_FILES_HOME = os.path.join(os.path.dirname(__file__), 'test_document') TEST_FILES_HOME = os.path.join(os.path.dirname(__file__), 'test_document')
...@@ -110,7 +111,8 @@ class IngestionTestCase(ERP5TypeTestCase): ...@@ -110,7 +111,8 @@ class IngestionTestCase(ERP5TypeTestCase):
assert not activity_status assert not activity_status
self.portal.portal_caches.clearAllCache() self.portal.portal_caches.clearAllCache()
# Cleanup portal_skins # Cleanup portal_skins
script_id_list = ('Document_getPropertyDictFromContent', script_id_list = ('ContributionTool_isURLIngestionPermitted',
'Document_getPropertyDictFromContent',
'Document_getPropertyDictFromInput', 'Document_getPropertyDictFromInput',
'Document_getPropertyDictFromFilename', 'Document_getPropertyDictFromFilename',
'Document_getPropertyDictFromUserLogin', 'Document_getPropertyDictFromUserLogin',
...@@ -149,6 +151,12 @@ class TestIngestion(IngestionTestCase): ...@@ -149,6 +151,12 @@ class TestIngestion(IngestionTestCase):
self.portal_catalog = self.getCatalogTool() self.portal_catalog = self.getCatalogTool()
self.createDefaultCategoryList() self.createDefaultCategoryList()
self.setSimulatedNotificationScript() self.setSimulatedNotificationScript()
createZODBPythonScript(
self.portal.portal_skins.custom,
"ContributionTool_isURLIngestionPermitted",
"url",
"return True",
)
super(TestIngestion, self).afterSetUp() super(TestIngestion, self).afterSetUp()
def setSimulatedNotificationScript(self, sequence=None, sequence_list=None, **kw): def setSimulatedNotificationScript(self, sequence=None, sequence_list=None, **kw):
...@@ -1957,6 +1965,34 @@ return result ...@@ -1957,6 +1965,34 @@ return result
self.assertEqual(new_doc.getData(), 'Hello World!') self.assertEqual(new_doc.getData(), 'Hello World!')
self.assertEqual(new_doc.getValidationState(), 'submitted') self.assertEqual(new_doc.getValidationState(), 'submitted')
def test_ContributionTool_isURLIngestionPermitted(self):
# default behavior when no type based method is to refuse ingestion
with mock.patch.object(
self.portal.portal_contributions.__class__,
'_getTypeBasedMethod',
return_value=None,
):
with self.assertRaisesRegex(Unauthorized, "URL ingestion not allowed"):
self.portal.portal_contributions.newContent(url='https://www.erp5.com')
# and it can be customized by script
self.portal.portal_skins.custom.ContributionTool_isURLIngestionPermitted.ZPythonScript_edit(
'url', 'return url == "https://www.erp5.com"',
)
self.portal.portal_contributions.newContent(url="https://www.erp5.com")
self.tic()
for url in (
"https://www.erp5.com/", # with trailing slash
"https://www.erp5.com/path",
"https://www.erp5.com/?query",
"https://www.nexedi.com/",
"file:///tmp",
"/tmp",
):
with self.assertRaisesRegex(Unauthorized, "URL ingestion not allowed"):
self.portal.portal_contributions.newContent(url=url)
self.tic()
def test_User_Portal_Type_parameter_is_honoured(self): def test_User_Portal_Type_parameter_is_honoured(self):
"""Check that given portal_type is always honoured """Check that given portal_type is always honoured
""" """
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment