deprecations: forbid import of new python expressions (#72093)
This commit is contained in:
parent
78f2796266
commit
86f28b8037
|
@ -7,11 +7,11 @@ import pytest
|
|||
from quixote.http_request import Upload as QuixoteUpload
|
||||
|
||||
from wcs import fields
|
||||
from wcs.backoffice.deprecations import DeprecationsScanAfterJob
|
||||
from wcs.blocks import BlockDef
|
||||
from wcs.backoffice.deprecations import DeprecatedElementsDetected, DeprecationsScanAfterJob
|
||||
from wcs.blocks import BlockDef, BlockdefImportError
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.data_sources import NamedDataSource, NamedDataSourceImportError
|
||||
from wcs.formdef import FormDef, FormdefImportError
|
||||
from wcs.mail_templates import MailTemplate
|
||||
from wcs.qommon.form import UploadedFile
|
||||
from wcs.qommon.http_request import HTTPRequest
|
||||
|
@ -23,8 +23,8 @@ from wcs.wf.geolocate import GeolocateWorkflowStatusItem
|
|||
from wcs.wf.jump import JumpWorkflowStatusItem
|
||||
from wcs.wf.notification import SendNotificationWorkflowStatusItem
|
||||
from wcs.wf.redirect_to_url import RedirectToUrlWorkflowStatusItem
|
||||
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef
|
||||
from wcs.wscalls import NamedWsCall
|
||||
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef, WorkflowImportError
|
||||
from wcs.wscalls import NamedWsCall, NamedWsCallImportError
|
||||
|
||||
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app, login
|
||||
from .test_all import create_superuser
|
||||
|
@ -525,3 +525,124 @@ def test_deprecations_inspect_pages_old_format(pub):
|
|||
|
||||
resp = app.get('/backoffice/studio/deprecations/')
|
||||
assert resp.pyquery('.section--python-condition li a')
|
||||
|
||||
|
||||
def test_deprecations_on_import(pub):
|
||||
formdef = FormDef()
|
||||
formdef.name = 'foobar'
|
||||
formdef.fields = [
|
||||
fields.PageField(id='1', label='page1', condition={'type': 'python', 'value': 'True'}),
|
||||
]
|
||||
formdef.store()
|
||||
|
||||
blockdef = BlockDef()
|
||||
blockdef.name = 'foobar'
|
||||
blockdef.fields = [
|
||||
fields.StringField(id='2', label='python_prefill', prefill={'type': 'formula', 'value': '1 + 2'}),
|
||||
]
|
||||
blockdef.store()
|
||||
|
||||
workflow = Workflow(name='test')
|
||||
st0 = workflow.add_status('Status0', 'st0')
|
||||
sendsms = st0.add_action('sendsms', id='_sendsms')
|
||||
sendsms.to = 'xxx'
|
||||
sendsms.condition = {'type': 'python', 'value': 'True'}
|
||||
sendsms.parent = st0
|
||||
st0.items.append(sendsms)
|
||||
workflow.store()
|
||||
|
||||
data_source = NamedDataSource(name='ds_python')
|
||||
data_source.data_source = {'type': 'formula', 'value': repr([('1', 'un'), ('2', 'deux')])}
|
||||
data_source.store()
|
||||
|
||||
wscall = NamedWsCall()
|
||||
wscall.name = 'Hello'
|
||||
wscall.request = {'url': 'http://example.net', 'qs_data': {'a': '=1+2'}}
|
||||
wscall.store()
|
||||
|
||||
mail_template = MailTemplate() # no python expression in mail templates
|
||||
mail_template.name = 'Hello2'
|
||||
mail_template.subject = 'plop'
|
||||
mail_template.body = 'plop [ezt] plop'
|
||||
mail_template.store()
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
job.check_deprecated_elements_in_object(formdef)
|
||||
formdef_xml = formdef.export_to_xml()
|
||||
FormDef.import_from_xml_tree(formdef_xml)
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
job.check_deprecated_elements_in_object(blockdef)
|
||||
blockdef_xml = blockdef.export_to_xml()
|
||||
BlockDef.import_from_xml_tree(blockdef_xml)
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
job.check_deprecated_elements_in_object(workflow)
|
||||
workflow_xml = workflow.export_to_xml()
|
||||
Workflow.import_from_xml_tree(workflow_xml)
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
job.check_deprecated_elements_in_object(data_source)
|
||||
data_source_xml = data_source.export_to_xml()
|
||||
NamedDataSource.import_from_xml_tree(data_source_xml)
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
job.check_deprecated_elements_in_object(wscall)
|
||||
wscall_xml = wscall.export_to_xml()
|
||||
NamedWsCall.import_from_xml_tree(wscall_xml)
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
job.check_deprecated_elements_in_object(mail_template)
|
||||
mail_template_xml = mail_template.export_to_xml()
|
||||
MailTemplate.import_from_xml_tree(mail_template_xml)
|
||||
|
||||
if not pub.site_options.has_section('options'):
|
||||
pub.site_options.add_section('options')
|
||||
pub.site_options.set('options', 'forbid-new-python-expressions', 'true')
|
||||
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
|
||||
pub.site_options.write(fd)
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
with pytest.raises(DeprecatedElementsDetected) as excinfo:
|
||||
job.check_deprecated_elements_in_object(formdef)
|
||||
assert str(excinfo.value) == 'Python expression detected'
|
||||
with pytest.raises(FormdefImportError) as excinfo:
|
||||
FormDef.import_from_xml_tree(formdef_xml)
|
||||
assert str(excinfo.value) == 'Python expression detected'
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
with pytest.raises(DeprecatedElementsDetected) as excinfo:
|
||||
job.check_deprecated_elements_in_object(blockdef)
|
||||
assert str(excinfo.value) == 'Python expression detected'
|
||||
with pytest.raises(BlockdefImportError) as excinfo:
|
||||
BlockDef.import_from_xml_tree(blockdef_xml)
|
||||
assert str(excinfo.value) == 'Python expression detected'
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
with pytest.raises(DeprecatedElementsDetected) as excinfo:
|
||||
job.check_deprecated_elements_in_object(workflow)
|
||||
assert str(excinfo.value) == 'Python expression detected'
|
||||
with pytest.raises(WorkflowImportError) as excinfo:
|
||||
Workflow.import_from_xml_tree(workflow_xml)
|
||||
assert str(excinfo.value) == 'Python expression detected'
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
with pytest.raises(DeprecatedElementsDetected) as excinfo:
|
||||
job.check_deprecated_elements_in_object(data_source)
|
||||
assert str(excinfo.value) == 'Python expression detected'
|
||||
with pytest.raises(NamedDataSourceImportError) as excinfo:
|
||||
NamedDataSource.import_from_xml_tree(data_source_xml)
|
||||
assert str(excinfo.value) == 'Python expression detected'
|
||||
|
||||
job = DeprecationsScanAfterJob()
|
||||
with pytest.raises(DeprecatedElementsDetected) as excinfo:
|
||||
job.check_deprecated_elements_in_object(wscall)
|
||||
assert str(excinfo.value) == 'Python expression detected'
|
||||
with pytest.raises(NamedWsCallImportError) as excinfo:
|
||||
NamedWsCall.import_from_xml_tree(wscall_xml)
|
||||
assert str(excinfo.value) == 'Python expression detected'
|
||||
|
||||
# no python expressions
|
||||
job = DeprecationsScanAfterJob()
|
||||
job.check_deprecated_elements_in_object(mail_template)
|
||||
MailTemplate.import_from_xml_tree(mail_template_xml)
|
||||
|
|
|
@ -31,10 +31,15 @@ from wcs.qommon import _, ezt, template
|
|||
from wcs.qommon.afterjobs import AfterJob
|
||||
from wcs.qommon.template import Template
|
||||
from wcs.wf.export_to_model import UploadValidationError
|
||||
from wcs.wf.form import FormWorkflowStatusItem
|
||||
from wcs.workflows import Workflow
|
||||
from wcs.wscalls import NamedWsCall
|
||||
|
||||
|
||||
class DeprecatedElementsDetected(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class DeprecationsDirectory(Directory):
|
||||
do_not_call_in_templates = True
|
||||
_q_exports = ['', 'scan']
|
||||
|
@ -409,3 +414,28 @@ class DeprecationsScanAfterJob(AfterJob):
|
|||
fd,
|
||||
indent=2,
|
||||
)
|
||||
|
||||
def check_deprecated_elements_in_object(self, obj):
|
||||
if not get_publisher().has_site_option('forbid-new-python-expressions'):
|
||||
# for perfs, don't check object if nothing is forbidden
|
||||
return
|
||||
|
||||
self.report_lines = []
|
||||
objects = [obj]
|
||||
if isinstance(obj, Workflow):
|
||||
for status in obj.possible_status:
|
||||
for item in status.items:
|
||||
if isinstance(item, FormWorkflowStatusItem) and item.formdef:
|
||||
objects.append(item.formdef)
|
||||
if obj.variables_formdef:
|
||||
objects.append(obj.variables_formdef)
|
||||
if obj.backoffice_fields_formdef:
|
||||
objects.append(obj.backoffice_fields_formdef)
|
||||
|
||||
self.check_objects(objects)
|
||||
|
||||
for report_line in self.report_lines:
|
||||
if 'python' in report_line['category'] and get_publisher().has_site_option(
|
||||
'forbid-new-python-expressions'
|
||||
):
|
||||
raise DeprecatedElementsDetected(_('Python expression detected'))
|
||||
|
|
|
@ -179,12 +179,17 @@ class BlockDef(StorableObject):
|
|||
return root
|
||||
|
||||
@classmethod
|
||||
def import_from_xml(cls, fd, include_id=False, check_datasources=True):
|
||||
def import_from_xml(cls, fd, include_id=False, check_datasources=True, check_deprecated=True):
|
||||
try:
|
||||
tree = ET.parse(fd)
|
||||
except Exception:
|
||||
raise ValueError()
|
||||
blockdef = cls.import_from_xml_tree(tree, include_id=include_id, check_datasources=check_datasources)
|
||||
blockdef = cls.import_from_xml_tree(
|
||||
tree,
|
||||
include_id=include_id,
|
||||
check_datasources=check_datasources,
|
||||
check_deprecated=check_deprecated,
|
||||
)
|
||||
|
||||
if blockdef.slug:
|
||||
try:
|
||||
|
@ -197,7 +202,11 @@ class BlockDef(StorableObject):
|
|||
return blockdef
|
||||
|
||||
@classmethod
|
||||
def import_from_xml_tree(cls, tree, include_id=False, check_datasources=True, **kwargs):
|
||||
def import_from_xml_tree(
|
||||
cls, tree, include_id=False, check_datasources=True, check_deprecated=True, **kwargs
|
||||
):
|
||||
from wcs.backoffice.deprecations import DeprecatedElementsDetected, DeprecationsScanAfterJob
|
||||
|
||||
blockdef = cls()
|
||||
if tree.find('name') is None or not tree.find('name').text:
|
||||
raise BlockdefImportError(_('Missing name'))
|
||||
|
@ -279,6 +288,14 @@ class BlockDef(StorableObject):
|
|||
details[_('Unknown datasources')].update(unknown_datasources)
|
||||
raise BlockdefImportUnknownReferencedError(_('Unknown referenced objects'), details=details)
|
||||
|
||||
if check_deprecated:
|
||||
# check for deprecated elements
|
||||
job = DeprecationsScanAfterJob()
|
||||
try:
|
||||
job.check_deprecated_elements_in_object(blockdef)
|
||||
except DeprecatedElementsDetected as e:
|
||||
raise BlockdefImportError(str(e))
|
||||
|
||||
return blockdef
|
||||
|
||||
def get_usage_fields(self):
|
||||
|
|
|
@ -51,6 +51,10 @@ from .qommon.xml_storage import XmlStorableObject
|
|||
data_source_functions = {}
|
||||
|
||||
|
||||
class NamedDataSourceImportError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class DataSourceError(Exception):
|
||||
pass
|
||||
|
||||
|
@ -913,9 +917,22 @@ class NamedDataSource(XmlStorableObject):
|
|||
return root
|
||||
|
||||
@classmethod
|
||||
def import_from_xml_tree(cls, tree, include_id=False, **kwargs):
|
||||
data_source = super().import_from_xml_tree(tree, include_id=include_id, **kwargs)
|
||||
def import_from_xml_tree(cls, tree, include_id=False, check_deprecated=True, **kwargs):
|
||||
from wcs.backoffice.deprecations import DeprecatedElementsDetected, DeprecationsScanAfterJob
|
||||
|
||||
data_source = super().import_from_xml_tree(
|
||||
tree, include_id=include_id, check_deprecated=check_deprecated, **kwargs
|
||||
)
|
||||
DataSourceCategory.object_category_xml_import(data_source, tree, include_id=include_id)
|
||||
|
||||
if check_deprecated:
|
||||
# check for deprecated elements
|
||||
job = DeprecationsScanAfterJob()
|
||||
try:
|
||||
job.check_deprecated_elements_in_object(data_source)
|
||||
except DeprecatedElementsDetected as e:
|
||||
raise NamedDataSourceImportError(str(e))
|
||||
|
||||
return data_source
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -1465,7 +1465,9 @@ class FormDef(StorableObject):
|
|||
return root
|
||||
|
||||
@classmethod
|
||||
def import_from_xml(cls, fd, include_id=False, fix_on_error=False, check_datasources=True):
|
||||
def import_from_xml(
|
||||
cls, fd, include_id=False, fix_on_error=False, check_datasources=True, check_deprecated=True
|
||||
):
|
||||
try:
|
||||
tree = ET.parse(fd)
|
||||
except Exception:
|
||||
|
@ -1475,6 +1477,7 @@ class FormDef(StorableObject):
|
|||
include_id=include_id,
|
||||
fix_on_error=fix_on_error,
|
||||
check_datasources=check_datasources,
|
||||
check_deprecated=check_deprecated,
|
||||
)
|
||||
|
||||
if formdef.url_name:
|
||||
|
@ -1497,8 +1500,15 @@ class FormDef(StorableObject):
|
|||
|
||||
@classmethod
|
||||
def import_from_xml_tree(
|
||||
cls, tree, include_id=False, fix_on_error=False, snapshot=False, check_datasources=True
|
||||
cls,
|
||||
tree,
|
||||
include_id=False,
|
||||
fix_on_error=False,
|
||||
snapshot=False,
|
||||
check_datasources=True,
|
||||
check_deprecated=True,
|
||||
):
|
||||
from wcs.backoffice.deprecations import DeprecatedElementsDetected, DeprecationsScanAfterJob
|
||||
from wcs.carddef import CardDef
|
||||
|
||||
formdef = cls()
|
||||
|
@ -1714,6 +1724,14 @@ class FormDef(StorableObject):
|
|||
details[_('Unknown datasources')].update(unknown_datasources)
|
||||
raise FormdefImportUnknownReferencedError(_('Unknown referenced objects'), details=details)
|
||||
|
||||
if check_deprecated:
|
||||
# check for deprecated elements
|
||||
job = DeprecationsScanAfterJob()
|
||||
try:
|
||||
job.check_deprecated_elements_in_object(formdef)
|
||||
except DeprecatedElementsDetected as e:
|
||||
raise FormdefImportError(str(e))
|
||||
|
||||
return formdef
|
||||
|
||||
def finish_tests_xml_import(self):
|
||||
|
|
|
@ -31,7 +31,7 @@ class XmlStorableObject(StorableObject):
|
|||
first_byte = fd.read(1)
|
||||
fd.seek(0)
|
||||
if first_byte == b'<':
|
||||
return cls.import_from_xml(fd, include_id=True)
|
||||
return cls.import_from_xml(fd, include_id=True, check_deprecated=False)
|
||||
else:
|
||||
obj = StorableObject.storage_load(fd)
|
||||
obj._upgrade_must_store = True
|
||||
|
@ -84,12 +84,12 @@ class XmlStorableObject(StorableObject):
|
|||
sub.text = role.name
|
||||
|
||||
@classmethod
|
||||
def import_from_xml(cls, fd, include_id=False):
|
||||
def import_from_xml(cls, fd, include_id=False, check_deprecated=True):
|
||||
try:
|
||||
tree = ET.parse(fd)
|
||||
except Exception:
|
||||
raise ValueError()
|
||||
return cls.import_from_xml_tree(tree, include_id=include_id)
|
||||
return cls.import_from_xml_tree(tree, include_id=include_id, check_deprecated=check_deprecated)
|
||||
|
||||
@classmethod
|
||||
def import_from_xml_tree(cls, tree, include_id=False, **kwargs):
|
||||
|
|
|
@ -1233,12 +1233,17 @@ class Workflow(StorableObject):
|
|||
return root
|
||||
|
||||
@classmethod
|
||||
def import_from_xml(cls, fd, include_id=False, check_datasources=True):
|
||||
def import_from_xml(cls, fd, include_id=False, check_datasources=True, check_deprecated=True):
|
||||
try:
|
||||
tree = ET.parse(fd)
|
||||
except Exception:
|
||||
raise ValueError()
|
||||
workflow = cls.import_from_xml_tree(tree, include_id=include_id, check_datasources=check_datasources)
|
||||
workflow = cls.import_from_xml_tree(
|
||||
tree,
|
||||
include_id=include_id,
|
||||
check_datasources=check_datasources,
|
||||
check_deprecated=check_deprecated,
|
||||
)
|
||||
|
||||
if workflow.slug and cls.get_by_slug(workflow.slug):
|
||||
# slug already in use, reset so a new one will be generated on store()
|
||||
|
@ -1247,7 +1252,11 @@ class Workflow(StorableObject):
|
|||
return workflow
|
||||
|
||||
@classmethod
|
||||
def import_from_xml_tree(cls, tree, include_id=False, snapshot=False, check_datasources=True):
|
||||
def import_from_xml_tree(
|
||||
cls, tree, include_id=False, snapshot=False, check_datasources=True, check_deprecated=True
|
||||
):
|
||||
from wcs.backoffice.deprecations import DeprecatedElementsDetected, DeprecationsScanAfterJob
|
||||
|
||||
workflow = cls()
|
||||
if tree.find('name') is None or not tree.find('name').text:
|
||||
raise WorkflowImportError(_('Missing name'))
|
||||
|
@ -1359,6 +1368,14 @@ class Workflow(StorableObject):
|
|||
_('Unknown referenced objects'), details=unknown_referenced_objects_details
|
||||
)
|
||||
|
||||
if check_deprecated:
|
||||
# check for deprecated elements
|
||||
job = DeprecationsScanAfterJob()
|
||||
try:
|
||||
job.check_deprecated_elements_in_object(workflow)
|
||||
except DeprecatedElementsDetected as e:
|
||||
raise WorkflowImportError(str(e))
|
||||
|
||||
return workflow
|
||||
|
||||
def get_list_of_roles(
|
||||
|
|
|
@ -44,6 +44,10 @@ from .qommon.template import Template
|
|||
from .qommon.xml_storage import XmlStorableObject
|
||||
|
||||
|
||||
class NamedWsCallImportError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class PayloadError(Exception):
|
||||
pass
|
||||
|
||||
|
@ -280,6 +284,24 @@ class NamedWsCall(XmlStorableObject):
|
|||
if self.request.get('post_data'):
|
||||
yield from self.request.get('post_data').values()
|
||||
|
||||
@classmethod
|
||||
def import_from_xml_tree(cls, tree, include_id=False, check_deprecated=True, **kwargs):
|
||||
from wcs.backoffice.deprecations import DeprecatedElementsDetected, DeprecationsScanAfterJob
|
||||
|
||||
wscall = super().import_from_xml_tree(
|
||||
tree, include_id=include_id, check_deprecated=check_deprecated, **kwargs
|
||||
)
|
||||
|
||||
if check_deprecated:
|
||||
# check for deprecated elements
|
||||
job = DeprecationsScanAfterJob()
|
||||
try:
|
||||
job.check_deprecated_elements_in_object(wscall)
|
||||
except DeprecatedElementsDetected as e:
|
||||
raise NamedWsCallImportError(str(e))
|
||||
|
||||
return wscall
|
||||
|
||||
def export_request_to_xml(self, element, attribute_name, **kwargs):
|
||||
request = getattr(self, attribute_name)
|
||||
for attr in ('url', 'request_signature_key', 'method', 'timeout', 'cache_duration'):
|
||||
|
|
Loading…
Reference in New Issue