diff --git a/tests/test_datasource.py b/tests/test_datasource.py index c62d5074f..9a770df31 100644 --- a/tests/test_datasource.py +++ b/tests/test_datasource.py @@ -185,7 +185,7 @@ def test_python_datasource_errors(pub, error_email, http_requests, emails, caplo with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd: pub.site_options.write(fd) - # running with disabled python expressions + # running with forbidden python expressions pub.loggederror_class.wipe() datasource = { 'type': 'formula', @@ -202,6 +202,25 @@ def test_python_datasource_errors(pub, error_email, http_requests, emails, caplo assert logged_error.workflow_id is None assert logged_error.summary == 'Unauthorized Python Usage' + # exception list + with open(os.path.join(pub.app_dir, 'allowed-python.txt'), 'w') as fd: + fd.write('blah\n') + + pub.loggederror_class.wipe() + assert data_sources.get_items(datasource) == [] + assert pub.loggederror_class.count() == 1 + assert pub.loggederror_class.select()[0].summary == 'Unauthorized Python Usage' + + with open(os.path.join(pub.app_dir, 'allowed-python.txt'), 'a') as fd: + fd.write('%r\n' % ['foo', 'bar']) + + pub.loggederror_class.wipe() + assert data_sources.get_items(datasource) == [ + ('foo', 'foo', 'foo', {'id': 'foo', 'text': 'foo'}), + ('bar', 'bar', 'bar', {'id': 'bar', 'text': 'bar'}), + ] + assert pub.loggederror_class.count() == 0 + def test_python_datasource_with_evalutils(pub): plain_list = [ diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py index 8b8d5d72c..4112f80a6 100644 --- a/wcs/backoffice/management.py +++ b/wcs/backoffice/management.py @@ -3462,7 +3462,9 @@ class FormBackOfficeStatusPage(FormStatusPage): ('template', '%s / %s' % (_('Template'), _('Django Expression')), 'template'), ('html_template', _('HTML Template (WYSIWYG)'), 'html_template'), ] - if get_publisher().has_site_option('disable-python-expressions'): + if get_publisher().has_site_option('disable-python-expressions') or get_publisher().has_site_option( + 'forbid-python-expressions' + ): options = [x for x in options if x[0] != 'python-condition'] form.add( RadiobuttonsWidget, @@ -3481,7 +3483,10 @@ class FormBackOfficeStatusPage(FormStatusPage): 'data-dynamic-display-value': 'django-condition', }, ) - if not get_publisher().has_site_option('disable-python-expressions'): + if not ( + get_publisher().has_site_option('disable-python-expressions') + or get_publisher().has_site_option('forbid-python-expressions') + ): form.add( StringWidget, 'python-condition', diff --git a/wcs/qommon/misc.py b/wcs/qommon/misc.py index 3b829048c..53a63644b 100644 --- a/wcs/qommon/misc.py +++ b/wcs/qommon/misc.py @@ -1187,12 +1187,19 @@ class UnauthorizedPythonUsage(Exception): def eval_python(expression, *args, **kwargs): # Inherently unsafe, abort if support is forbidden. if get_publisher().has_site_option('forbid-python-expressions'): - get_publisher().record_error( - _('Unauthorized Python Usage'), - notify=True, - record=True, - ) - raise UnauthorizedPythonUsage() + allowed_python_filename = os.path.join(get_publisher().app_dir, 'allowed-python.txt') + allowed = False + if os.path.exists(allowed_python_filename): + with open(allowed_python_filename) as fd: + if expression in fd.read().splitlines(): + allowed = True + if not allowed: + get_publisher().record_error( + _('Unauthorized Python Usage'), + notify=True, + record=True, + ) + raise UnauthorizedPythonUsage() # noqa pylint: disable=eval-used return eval(expression, *args, **kwargs)