Commit 032ab585 authored by Rafael Monnerat's avatar Rafael Monnerat

slapos_cloud: Update constraints to prevent set improper data on request

   Try to guarantee that data is consistent and wont fail later on
   while been converted to XML with special attention to non-XML
   compatibles characters, like backspace.

   Include constraint to ensure title is XML compatible on Software
   Instance, Instance Tree and Slave Instance. (Not required for Compute
   Node as far as I checked).

   Copy text_content (parameter), sla_xml constraints from
   Software Instance to Instance Tree and Slave Instance.

   Copy connection_xml constraint from Software to Slave Instance for
   consistency.

   Add more tests to ensure that an Instance cannot be created if data
   is invalid, or not XML compatible.

   Add utility scripts to validate XML and XML Marshaller compatibility
parent d4d2fe4b
......@@ -34,7 +34,8 @@ from zExceptions import Unauthorized
from DateTime import DateTime
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Acquisition import aq_base, aq_inner
from slapos.util import dumps
from lxml import etree
def cloneDocumentWithANewPortalType(obj, portal_type):
import erp5.portal_type
......@@ -345,3 +346,17 @@ def Base_updateRelatedContentWithoutReindextion(self, previous_category_url, new
o.Base_updateRelatedContentWithoutReindextion(previous_o_category_url,
new_o_category_url)
def isValidXml(self, value, REQUEST=None):
if REQUEST is not None:
raise Unauthorized
# No better way them this for now
etree.fromstring(value)
return True
def isValidXmlMarshaller(self, value, REQUEST=None):
if REQUEST is not None:
raise Unauthorized
# No better way them this for now
dumps(value)
return True
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.Base_isValidXml(context.getSlaXml())</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>sla_xml_valid_xml_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Sla XML is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Sla XML expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getSlaXml() is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.Base_isValidXml(context.getTextContent())</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>text_content_valid_xml_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Instance XML is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Instance XML expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getTextContent() is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.Base_isValidXmlMarshaller(context.getTitle())</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>title_xml_marshaller_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Title is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Title expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getTextContent() is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.getConnectionXmlAsDict() or True</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>connection_xml_valid_xml_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Connection XML is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Connection XML expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getConnectionXml() is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.getSlaXmlAsDict() or True</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>sla_xml_valid_xml_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Sla XML is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Sla XML expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getSlaXml() is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.getInstanceXmlAsDict() or True</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>text_content_valid_xml_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Instance XML is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Instance XML expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getTextContent() is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.Base_isValidXmlMarshaller(context.getTitle())</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>title_xml_marshaller_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Title is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Title expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getTextContent() is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TALES Constraint" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_identity_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>_range_criterion</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>expression</string> </key>
<value> <string>python: context.Base_isValidXmlMarshaller(context.getTitle())</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>title_xml_marshaller_constraint</string> </value>
</item>
<item>
<key> <string>int_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>membership_criterion_category</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>message_expression_error</string> </key>
<value> <string>Title is invalid: ${error}</string> </value>
</item>
<item>
<key> <string>message_expression_false</string> </key>
<value> <string>Title expression was false</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>TALES Constraint</string> </value>
</item>
<item>
<key> <string>string_index</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>test_method_id</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>test_tales_expression</string> </key>
<value> <string>python: context.getTextContent() is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>isValidXml</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>SlapOSCloud</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_isValidXml</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>isValidXmlMarshaller</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>SlapOSCloud</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_isValidXmlMarshaller</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -26,21 +26,29 @@
#
##############################################################################
def _decode_with_json(value):
# Ensure value is serisalisable as json
return json.loads(json.dumps(value))
from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin
from Products.ERP5Type.Core.Workflow import ValidationFailed
from erp5.component.test.SlapOSTestCaseMixin import SlapOSTestCaseMixin, \
TemporaryAlarmScript, \
SlapOSTestCaseMixinWithAbort
from DateTime import DateTime
from App.Common import rfc1123_date
import json
import hashlib
from binascii import hexlify
from lxml import etree
import transaction
BACKSPACE_CHAR = "\x08"
BELL_CHAR = "\x07"
HORIZONTAL_TAB_CHAR = "\x09"
def hashData(data):
return hexlify(hashlib.sha1(json.dumps(data, sort_keys=True)).digest())
def _decode_with_json(value):
# Ensure value is serisalisable as json
return json.loads(json.dumps(value))
class TestSlapOSCloudSlapOSCacheMixin(
SlapOSTestCaseMixin):
......@@ -372,13 +380,63 @@ class TestSlapOSCloudSlapOSCacheMixin(
self.assertEqual(False,
installation.setBuildingStatus("TEST123 %s" % installation.getUid()))
class TestBase_isValidXmlMarshaller(SlapOSTestCaseMixinWithAbort):
def test_Base_isValidXmlMarshaller(self):
self.assertTrue(self.portal.Base_isValidXmlMarshaller("""<instance/>"""))
self.assertTrue(self.portal.Base_isValidXmlMarshaller("""<instance></instance>"""))
self.assertTrue(self.portal.Base_isValidXmlMarshaller("""<instance>"""))
self.assertTrue(self.portal.Base_isValidXmlMarshaller(""))
self.assertTrue(self.portal.Base_isValidXmlMarshaller("""<insta"""))
self.assertTrue(self.portal.Base_isValidXmlMarshaller({HORIZONTAL_TAB_CHAR: 1}))
self.assertTrue(self.portal.Base_isValidXmlMarshaller([HORIZONTAL_TAB_CHAR, 1]))
self.assertTrue(self.portal.Base_isValidXmlMarshaller(HORIZONTAL_TAB_CHAR))
self.assertTrue(self.portal.Base_isValidXmlMarshaller({'zz': {'aa'}}))
self.assertTrue(self.portal.Base_isValidXmlMarshaller({}))
self.assertTrue(self.portal.Base_isValidXmlMarshaller(1))
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller,
"<instance>%s</instance>" % BACKSPACE_CHAR)
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, BACKSPACE_CHAR)
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, {BACKSPACE_CHAR: 1})
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, [BACKSPACE_CHAR, 1])
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, BELL_CHAR)
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, {BELL_CHAR: 1})
self.assertRaises(ValueError,
self.portal.Base_isValidXmlMarshaller, [BELL_CHAR, 1])
class TestBase_isValidXml(SlapOSTestCaseMixinWithAbort):
def test_Base_isValidXml(self):
self.assertTrue(self.portal.Base_isValidXml("<instance/>"))
self.assertTrue(self.portal.Base_isValidXml("<instance></instance>"))
self.assertTrue(
self.portal.Base_isValidXml("<instance>%s</instance>" % HORIZONTAL_TAB_CHAR))
self.assertRaises(etree.XMLSyntaxError, self.portal.Base_isValidXml, "<instance>")
self.assertRaises(etree.XMLSyntaxError, self.portal.Base_isValidXml, "")
self.assertRaises(ValueError, self.portal.Base_isValidXml, None)
self.assertRaises(etree.XMLSyntaxError, self.portal.Base_isValidXml, "<insta")
self.assertRaises(etree.XMLSyntaxError, self.portal.Base_isValidXml,
"<instance>%s</instance>" % BACKSPACE_CHAR)
class TestSlapOSCloudSoftwareInstance(
SlapOSTestCaseMixin):
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
self.project = self.addProject()
self._makeTree(self.project)
self.tic()
self.instance_tree = self.addInstanceTree(self.project)
self.software_instance = self.instance_tree.getSuccessorValue()
self.tic()
def test_getXmlAsDict(self):
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
......@@ -391,6 +449,16 @@ class TestSlapOSCloudSoftwareInstance(
self.software_instance._getXmlAsDict(simple_parameter_sample_xml),
{'p1é': 'v1é', 'p2é': 'v2é'})
def test_getXmlAsDict_invalid_backspace_char(self):
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é%(bs)s">v1é%(bs)s</parameter>
<parameter id="p2é%(bs)s">v2é%(bs)s</parameter>
</instance>
""" % {'bs' : BACKSPACE_CHAR}
self.assertRaises(etree.XMLSyntaxError,
self.software_instance._getXmlAsDict, simple_parameter_sample_xml)
def test_getInstanceXmlAsDict(self):
self.software_instance.setTextContent("""<?xml version='1.0' encoding='utf-8'?>
<instance>
......@@ -402,6 +470,26 @@ class TestSlapOSCloudSoftwareInstance(
self.software_instance.getInstanceXmlAsDict(),
{'p1é': 'v1é', 'p2é': 'v2é'})
# Ensure SoftwareInstanceConstraint catches eventual the problems.
self.assertEqual(self.software_instance.checkConsistency(), [])
def test_getInstanceXmlAsDict_invalid_backspace_char(self):
self.software_instance.setTextContent("""<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é%(bs)s">v1é</parameter>
<parameter id="p2é">v2é%(bs)s</parameter>
</instance>
""" % {'bs' : BACKSPACE_CHAR})
self.assertRaises(etree.XMLSyntaxError,
self.software_instance.getInstanceXmlAsDict)
# Ensure SoftwareInstanceConstraint catches eventual the problems.
consistency_list = self.software_instance.checkConsistency()
self.assertEqual(len(consistency_list), 1, consistency_list)
self.assertTrue(
str(consistency_list[0].getTranslatedMessage()).startswith(
'Instance XML is invalid: invalid character'), consistency_list)
def test_getSlaXmlAsDict(self):
self.software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
<instance>
......@@ -413,6 +501,26 @@ class TestSlapOSCloudSoftwareInstance(
self.software_instance.getSlaXmlAsDict(),
{'p1é': 'v1é', 'p2é': 'v2é'})
# Ensure SoftwareInstanceConstraint catches eventual the problems.
self.assertEqual(self.software_instance.checkConsistency(), [])
def test_getSlaXmlAsDict_invalid_backspace_char(self):
self.software_instance.setSlaXml("""<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é%(bs)s</parameter>
</instance>
""" % {'bs' : BACKSPACE_CHAR})
self.assertRaises(etree.XMLSyntaxError,
self.software_instance.getSlaXmlAsDict)
# Ensure SoftwareInstanceConstraint catches eventual the problems.
consistency_list = self.software_instance.checkConsistency()
self.assertEqual(len(consistency_list), 1, consistency_list)
self.assertTrue(
str(consistency_list[0].getTranslatedMessage()).startswith(
'Sla XML is invalid: PCDATA invalid Char value'), consistency_list)
def test_getConnectionXmlAsDict(self):
self.software_instance.setConnectionXml("""<?xml version='1.0' encoding='utf-8'?>
<instance>
......@@ -424,6 +532,38 @@ class TestSlapOSCloudSoftwareInstance(
self.software_instance.getConnectionXmlAsDict(),
{'p1é': 'v1é', 'p2é': 'v2é'})
# Ensure SoftwareInstanceConstraint catches eventual the problems.
self.assertEqual(self.software_instance.checkConsistency(), [])
def test_getConnectionXmlAsDict_invalid_backspace_char(self):
self.software_instance.setConnectionXml("""<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é%(bs)s</parameter>
</instance>
""" % {'bs' : BACKSPACE_CHAR})
self.assertRaises(etree.XMLSyntaxError,
self.software_instance.getConnectionXmlAsDict)
# Ensure SoftwareInstanceConstraint catches eventual the problems.
consistency_list = self.software_instance.checkConsistency()
self.assertEqual(len(consistency_list), 1, consistency_list)
self.assertTrue(
str(consistency_list[0].getTranslatedMessage()).startswith(
'Connection XML is invalid: PCDATA invalid Char value'),
consistency_list)
def test_getTitle_invalid_backspace_char(self):
self.software_instance.setTitle("v2é%(bs)s" % ({'bs' : BACKSPACE_CHAR}))
# Ensure SoftwareInstanceConstraint catches eventual the problems.
consistency_list = self.software_instance.checkConsistency()
self.assertEqual(len(consistency_list), 1, consistency_list)
self.assertTrue(
str(consistency_list[0].getTranslatedMessage()).startswith(
'Title is invalid: All strings must be XML compatible'),
consistency_list)
def test_instanceXmlToDict(self):
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
......@@ -436,6 +576,28 @@ class TestSlapOSCloudSoftwareInstance(
# different from getXmlAsDict it don't encode things as utf-8
{u'p1é': u'v1é', u'p2é': u'v2é'})
def test_instanceXmlToDict_invalid_xml(self):
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é%(bs)s</parameter>
</instance>
""" % {'bs' : BACKSPACE_CHAR}
self.assertEqual(
self.software_instance._instanceXmlToDict(simple_parameter_sample_xml),
# if the XML is invalid, it return {} by default
{})
simple_parameter_sample_xml = """<?xml version='1.0' encoding='utf-8'?>
<instance>
<parameter id="p1é">v1é</parameter>
<parameter id="p2é">v2é%(bs)s</parameter>
""" % {'bs' : BACKSPACE_CHAR}
self.assertEqual(
self.software_instance._instanceXmlToDict(simple_parameter_sample_xml),
# This dont raise because it isn't converted verified converted as XML.
{})
def test_asParameterDict_not_allocated(self):
self.assertRaises(ValueError,
self.software_instance._asParameterDict)
......@@ -494,6 +656,25 @@ class TestSlapOSCloudSoftwareInstance(
self.assertEqual([(u'', u'ip_address_1')],
self.start_requested_software_instance._getInstanceTreeIpList())
class TestSlapOSCloudSlaveInstance(
TestSlapOSCloudSoftwareInstance):
def _skipTest(self):
pass
test_asParameterDict_not_allocated = _skipTest
test_getInstanceTreeIpList = _skipTest
test_asParameterDict = _skipTest
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
self.project = self.addProject()
self.tic()
self.instance_tree = self.addInstanceTree(project=self.project, shared=True)
self.software_instance = self.instance_tree.getSuccessorValue()
self.tic()
class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
SlapOSTestCaseMixin):
......@@ -533,7 +714,7 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.portal.REQUEST['disable_isTestRun'] = True
self.login(self.compute_node_user_id)
user = self.getPortalObject().portal_membership.getAuthenticatedMember().getUserName()
user = self.portal.portal_membership.getAuthenticatedMember().getUserName()
self.compute_node.setAccessStatus(self.compute_node_id)
refresh_etag = self.compute_node._calculateRefreshEtag()
......@@ -568,7 +749,8 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.assertEqual(first_etag, etag)
self.assertEqual(first_body_fingerprint, hashData(body))
self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList()))
self.assertEqual(current_activity_count,
len(self.portal.portal_activities.getMessageList()))
self.tic()
......@@ -627,7 +809,8 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.assertEqual(second_etag, etag)
self.assertEqual(first_body_fingerprint, hashData(body))
self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList()))
self.assertEqual(current_activity_count,
len(self.portal.portal_activities.getMessageList()))
self.tic()
......@@ -676,7 +859,8 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.assertEqual(third_etag, etag)
self.assertEqual(third_body_fingerprint, hashData(body))
self.assertEqual(current_activity_count, len(self.portal.portal_activities.getMessageList()))
self.assertEqual(current_activity_count,
len(self.portal.portal_activities.getMessageList()))
self.tic()
......@@ -705,4 +889,181 @@ class TestSlapOSCloudSlapOSComputeNodeMixin_getCacheComputeNodeInformation(
self.assertEqual(fourth_body_fingerprint, hashData(body))
self.assertEqual(0, len(self.portal.portal_activities.getMessageList()))
class TestSlapOSCloudInstanceInvalidRequest(SlapOSTestCaseMixin):
def afterSetUp(self):
SlapOSTestCaseMixin.afterSetUp(self)
self.project = self.addProject()
def generateUnsafeXml_bad_string(self):
return '<?xml version="1.0" encoding="utf-8"?><instance><parameter '\
'id="%s">%s\x08</parameter></instance>' % \
("paramé".decode("UTF-8").encode("UTF-8"),
self.generateNewId().decode("UTF-8").encode("UTF-8"))
def _test_invalid(self, title=None, instance_xml=None, sla_xml=None, shared=False):
self._makeComputeNode(self.project)
self._makeComplexComputeNode(self.project, with_slave=not shared)
self.tic()
if title is None:
title = self.generateNewSoftwareTitle()
# Create second Slave with invalid char (backspace)
instance_tree = self.portal.instance_tree_module\
.newContent(portal_type="Instance Tree")
instance_tree.validate()
instance_tree.edit(
title=self.generateNewSoftwareTitle(),
reference="TESTSI-%s" % self.generateNewId(),
# No need
destination_section_value=None,
follow_up_value=self.project
)
if not instance_xml:
instance_xml = self.generateSafeXml()
if not sla_xml:
sla_xml = self.generateSafeXml()
software_instance_kw = dict(
software_release=self.start_requested_software_instance.getUrlString(),
software_type=self.start_requested_software_instance.getSourceReference(),
instance_xml=instance_xml,
sla_xml=sla_xml,
shared=shared,
software_title=title,
state='started',
project_reference=self.project.getReference()
)
# Ensure this produces a consistent instance first, to start.
instance_tree_kw = software_instance_kw.copy()
instance_tree_kw.update({
"instance_xml" : self.generateSafeXml(),
"sla_xml" : self.generateSafeXml(),
"software_title" : instance_tree.getTitle()
})
instance_tree.requestStart(**instance_tree_kw)
with TemporaryAlarmScript(self.portal, 'Item_getSubscriptionStatus', "'subscribed'"):
with self.assertRaises(ValidationFailed):
instance_tree.requestInstance(**software_instance_kw)
# Abort transaction to prevent interaction workflow to trigger,
# and to the temporary script be removed
transaction.abort()
def test_invalid_backspace_char_on_software_title(self):
title = "abc\08x" + self.generateNewSoftwareTitle()
self._test_invalid(title=title)
def test_invalid_backspace_char_on_slave_title(self):
title = "abc\08x" + self.generateNewSoftwareTitle()
self._test_invalid(title=title, shared=True)
def test_invalid_backspace_char_on_software_paramameter_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid(instance_xml=invalid_xml)
def test_invalid_backspace_char_on_slave_paramameter_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid(instance_xml=invalid_xml, shared=True)
def test_invalid_backspace_char_on_software_sla_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid(sla_xml=invalid_xml)
def test_invalid_backspace_char_on_slave_sla_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid(sla_xml=invalid_xml, shared=True)
def _test_invalid_connection_parameter(self, connection_xml, shared=False):
self._makeComputeNode(self.project)
self._makeComplexComputeNode(self.project, with_slave=not shared)
self.tic()
# Create second Slave with invalid char (backspace)
instance_tree = self.portal.instance_tree_module\
.newContent(portal_type="Instance Tree")
instance_tree.validate()
instance_tree.edit(
title=self.generateNewSoftwareTitle(),
reference="TESTSI-%s" % self.generateNewId(),
# No need
destination_section_value=None,
follow_up_value=self.project
)
software_instance_kw = dict(
software_release=self.start_requested_software_instance.getUrlString(),
software_type=self.start_requested_software_instance.getSourceReference(),
instance_xml=self.generateSafeXml(),
sla_xml=self.generateSafeXml(),
shared=shared,
software_title=instance_tree.getTitle(),
state='started',
project_reference=self.project.getReference()
)
instance_tree.requestStart(**software_instance_kw)
with TemporaryAlarmScript(self.portal, 'Item_getSubscriptionStatus', "'subscribed'"):
instance_tree.requestInstance(**software_instance_kw)
software_instance = instance_tree.getSuccessorValue()
with self.assertRaises(ValidationFailed):
software_instance.updateConnection(connection_xml=connection_xml)
def test_invalid_backspace_char_on_software_connection_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid_connection_parameter(invalid_xml)
def test_invalid_backspace_char_on_slave_connection_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid_connection_parameter(invalid_xml, shared=True)
def _test_invalid_instance_tree(self, title=None,
instance_xml=None, sla_xml=None):
self._makeComputeNode(self.project)
self._makeComplexComputeNode(self.project)
self.tic()
if title is None:
title = self.generateNewSoftwareTitle()
# Create second Slave with invalid char (backspace)
instance_tree = self.portal.instance_tree_module\
.newContent(portal_type="Instance Tree")
instance_tree.validate()
instance_tree.edit(
title=title,
reference="TESTSI-%s" % self.generateNewId(),
# No need
destination_section_value=None,
follow_up_value=self.project
)
if not instance_xml:
instance_xml = self.generateSafeXml()
if not sla_xml:
sla_xml = self.generateSafeXml()
instance_tree_kw = dict(
software_release=self.start_requested_software_instance.getUrlString(),
software_type=self.start_requested_software_instance.getSourceReference(),
instance_xml=instance_xml,
sla_xml=sla_xml,
shared=False,
software_title=title,
state='started',
project_reference=self.project.getReference()
)
with self.assertRaises(ValidationFailed):
instance_tree.requestStart(**instance_tree_kw)
def test_invalid_backspace_char_on_instance_title(self):
title = "abc\08x" + self.generateNewSoftwareTitle()
self._test_invalid_instance_tree(title=title)
def test_invalid_backspace_char_on_instance_paramameter_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid_instance_tree(instance_xml=invalid_xml)
def test_invalid_backspace_char_on_instance_sla_xml(self):
invalid_xml = self.generateUnsafeXml_bad_string()
self._test_invalid_instance_tree(sla_xml=invalid_xml)
......@@ -51,7 +51,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance>
</instance>
"""
sla_xml = "test"
sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True
state = "started"
......@@ -165,7 +167,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance>
</instance>
"""
sla_xml = "test"
sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True
# Only started, stopped, destroyed
......@@ -229,7 +233,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance>
</instance>
"""
sla_xml = "test"
sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True
state = "started"
......@@ -257,7 +263,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance>
</instance>
"""
sla_xml = "test"
sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True
state = "started"
......@@ -298,7 +306,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance>
</instance>
"""
sla_xml = "test"
sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True
state = "started"
......@@ -335,7 +345,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance>
</instance>
"""
sla_xml = "test"
sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True
state = "started"
......@@ -404,7 +416,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance>
</instance>
"""
sla_xml = "test"
sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True
state = "started"
......@@ -518,7 +532,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance>
</instance>
"""
sla_xml = "test"
sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True
person.requestSoftwareInstance(
......@@ -559,7 +575,9 @@ class TestSlapOSCorePersonRequest(SlapOSTestCaseMixin):
<instance>
</instance>
"""
sla_xml = "test"
sla_xml = """<?xml version="1.0" encoding="utf-8"?>
<instance />
"""
shared = True
person.requestSoftwareInstance(
......
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