source: TI12-security/trunk/python/ndg.security.test/ndg/security/test/elementTreeC14n/testC14n.py @ 3438

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.test/ndg/security/test/elementTreeC14n/testC14n.py@3438
Revision 3438, 11.6 KB checked in by pjkersha, 12 years ago (diff)

Tested with new version of ElementTree from Fred Lundh. Added tests for WS-Security SOAP message C14N.

  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2"""NDG ElementTreeC14n class unit tests
3
4NERC Data Grid Project
5"""
6__author__ = "P J Kershaw"
7__date__ = "03/01/07"
8__copyright__ = "(C) 2007 STFC & NERC"
9__license__ = \
10"""This software may be distributed under the terms of the Q Public
11License, version 1.0 or later."""
12__contact__ = "P.J.Kershaw@rl.ac.uk"
13__revision__ = '$Id: ElementTreeC14nTest.py 3202 2008-01-11 13:42:34Z pjkersha $'
14
15import unittest
16import os
17import sys
18import getpass
19import traceback
20
21from StringIO import StringIO
22
23from elementtree import ElementC14N as ETC14N
24from elementtree import ElementTree as ET
25
26# Minidom based Canonicalization from ZSI for comparison
27from ZSI.wstools.c14n import Canonicalize
28
29xpdVars = os.path.expandvars
30jnPath = os.path.join
31
32class ElementTreeC14nTestCase(unittest.TestCase):
33   
34    def setUp(self):
35       
36        if 'NDGSEC_INT_DEBUG' in os.environ:
37            import pdb
38            pdb.set_trace()         
39
40
41    def test1UTF8DocEncoding(self):
42        xml = '<?xml version="1.0" encoding="UTF-8"?>\n<a/>\n'
43        elem = ET.fromstring(xml)
44        f = StringIO()
45        ET.ElementTree(elem).write_c14n(f)
46        c14n = f.getvalue()
47
48   
49    def test2NormalizedLineBreaks(self):
50        xml = '<?xml version="1.0" encoding="UTF-8"?>\r\n<a/>\r\n'
51        elem = ET.fromstring(xml)
52        f = StringIO()
53        ET.ElementTree(elem).write_c14n(f)
54        c14n = f.getvalue()
55        self.failIf('\r' in c14n, "Carriage return \r char found in c14n")
56
57   
58    def test3NormalizedAttrVals(self):
59        pass
60
61   
62    def test4CharAndParsedEntityRefsReplaced(self):
63        pass
64
65   
66    def test5CDATASectionsReplaced(self):
67        xml = \
68"""<?xml version="1.0" encoding="UTF-8"?>
69<script>
70<![CDATA[
71function matchwo(a,b)
72{
73if (a < b && a > 0) then
74   {
75   print("Match");
76   return 1;
77   }
78else
79   {
80   print('Different');
81   return 0;
82   }
83}
84]]>
85</script>
86"""
87        elem = ET.fromstring(xml)
88        f = StringIO()
89        ET.ElementTree(elem).write_c14n(f)
90        c14n = f.getvalue()
91       
92        self.failIf('CDATA' in c14n, "CDATA not removed, c14n = %s" % c14n)
93        self.failUnless('&lt;' in c14n,
94                        "Less than not converted, c14n = %s" % c14n)
95        self.failUnless('&gt;' in c14n, 
96                        "Greater than not converted, c14n = %s" % c14n)
97        self.failUnless('&amp;' in c14n, 
98                        "Ampersand not converted, c14n = %s" % c14n)
99
100        # Test for double quotes / apostrophes?
101       
102   
103    def test6XMLDeclAndDTDRemoved(self):
104        xmlDecl = '<?xml version="1.0" encoding="UTF-8"?>'
105        dtd = \
106"""<!DOCTYPE note [
107  <!ELEMENT note (to,from,heading,body)>
108  <!ELEMENT to      (#PCDATA)>
109  <!ELEMENT from    (#PCDATA)>
110  <!ELEMENT heading (#PCDATA)>
111  <!ELEMENT body    (#PCDATA)>
112]>
113"""
114        xml = \
115"""%s
116%s<a/>""" % (xmlDecl, dtd)
117
118        elem = ET.fromstring(xml)
119        f = StringIO()
120        ET.ElementTree(elem).write_c14n(f)
121        c14n = f.getvalue()
122        self.failIf('<?xml version="1.0" encoding="UTF-8"?>' in c14n, 
123                    "XML Declaration not removed")
124        self.failIf(dtd in c14n, "DTD not removed")
125
126   
127    def test7EmptyElemsConvertedStartEndPairs(self):
128        elem = ET.fromstring('<?xml version="1.0" encoding="UTF-8"?><a/>')
129        f = StringIO()
130        ET.ElementTree(elem).write_c14n(f)
131        c14n = f.getvalue()
132        self.failUnless(c14n == '<a></a>', "C14N = %s" % c14n)
133
134         
135    def test8WhitespaceNormalized(self):
136        '''...outside the document element and within start and end tags'''
137        dat = \
138'''        1 2
139  3'''
140 
141        xml = \
142'''<?xml version="1.0" encoding="UTF-8"?>
143<doc xmlns="http://example.com/default">
144  <a
145     a2="2"   a1="1"
146  >%s</a>
147</doc>
148
149''' % dat
150        elem = ET.fromstring(xml)
151        f = StringIO()
152        ET.ElementTree(elem).write_c14n(f)
153        c14n = f.getvalue()
154        self.failUnless('a1="1" a2="2"' in c14n, 
155                        "Expecting single space between attributes")
156        self.failUnless(dat in c14n, 
157                        "Expecting element content to be preserved")
158       
159        sub = xml[xml.find('<a'):xml.find('</a>')]
160        self.failIf('\n' in sub, 
161                    "Expecting removal of line breaks for 'a' element")
162     
163     
164    def test9WhitespaceInCharContentRetained(self):
165        '''Nb. excludes chars removed during line break normalization'''
166
167       
168    def test10AttrValDelimitersSet2DblQuotes(self):
169        xml = \
170"""<?xml version="1.0" encoding="UTF-8"?>
171  <b y:a1='1' a3='"3"'
172     xmlns:y='http://example.com/y' y:a2='2'/>
173"""
174
175        elem = ET.fromstring(xml)
176        f = StringIO()
177        ET.ElementTree(elem).write_c14n(f)
178        c14n = f.getvalue()
179        self.failIf("'" in c14n, 
180                    "Expecting removal of apostrophes C14N = %s" % c14n)
181
182   
183    def test11SpecialCharsReplaced(self):
184        '''i.e. within attribute values and character content'''
185       
186       
187    def test12SuperflousNSdeclsRemoved(self):
188        extraNS = "http://example.com/default"
189        xml = \
190"""<?xml version="1.0" encoding="UTF-8"?>
191<doc xmlns:x="http://example.com/x" xmlns="%s">
192  <b y:a1='1' xmlns="%s" a3='"3"'
193     xmlns:y='http://example.com/y' y:a2='2'/>
194</doc>""" % (extraNS, extraNS)
195
196        elem = ET.fromstring(xml)
197        f = StringIO()
198        ET.ElementTree(elem).write_c14n(f)
199        c14n = f.getvalue()
200        print c14n
201       
202        # Namespace should now only occur once...
203        self.failUnless(c14n.find(extraNS) == c14n.rfind(extraNS), 
204                    "Expecting removal of extra NS %s in output = %s" % \
205                    (extraNS, c14n))
206       
207       
208    def test13DefAttrsAdded2EachElem(self):
209        '''Implies reference to doc schema??'''
210
211   
212    def test14LexicographicOrderApplied(self):
213        '''... to namespace declarations and attributes of each element'''
214        xml = \
215'''<?xml version="1.0" encoding="UTF-8"?>
216<doc xmlns:x="http://example.com/x" xmlns="http://example.com/default">
217  <b y:a1='1' xmlns="http://example.com/default" a3='"3"'
218     xmlns:y='http://example.com/y' y:a2='2'/>
219</doc>
220'''
221
222    def test15CmpZSIc14n(self):
223        elem = ETC14N.parse('./windows-ac.xml')
224        ETC14N.write(elem, './et-c14n-ac.xml')
225       
226        from xml.dom.ext.reader import PyExpat
227        reader = PyExpat.Reader()
228        dom = reader.fromStream(open('./windows-ac.xml'))
229       
230        zsiC14n = Canonicalize(dom)
231        etC14n = open('./et-c14n-ac.xml').read()
232        open('./zsi-c14n-ac.xml', 'w').write(zsiC14n)
233       
234        etC14n = open('./et-c14n-ac.xml').read()
235
236        self.failUnless(etC14n == zsiC14n, "ZSI C14N output differs")
237       
238    def test16Cmplxmlc14n(self):
239        from StringIO import StringIO
240
241        elem = ETC14N.parse('./windows-ac.xml')
242        ETC14N.write(elem, './et-c14n-ac-2.xml')
243       
244       
245        from lxml import etree as lxmlET
246       
247        lxmlElem = lxmlET.parse('./windows-ac.xml')
248        lxmlETf = StringIO()
249        lxmlElem.write_c14n(lxmlETf)
250        open('./lxml-c14n-ac.xml', 'w').write(lxmlETf.getvalue())
251       
252        f1 = open('./et-c14n-ac-2.xml')
253        etC14n = f1.read()
254        f1.close()
255       
256        self.failUnless(etC14n == lxmlETf.getvalue(),
257                        "lxml C14N output differs")
258       
259       
260    def test17InclusiveC14nWithXPath(self):
261        '''Inclusive Canonicalization of portions of a SOAP message extracted
262        using XPath'''
263       
264        # XPath
265        from xml.xpath.Context import Context
266        from xml import xpath
267       
268        # PyExpat Parse
269        from xml.dom.ext.reader import PyExpat
270        reader = PyExpat.Reader()
271        dom = reader.fromStream(open('./soapGetAttCertResponse.xml'))
272        processorNss = \
273        {
274            'wsu': \
275"http://docs.oasis-open.orgwss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
276        }
277   
278        ctxt = Context(dom, processorNss=processorNss)
279        refNodes = xpath.Evaluate('//*[@wsu:Id]', 
280                                  contextNode=dom, 
281                                  context=ctxt)
282       
283        # ElementTree - can't get find() to work - get elements 'manually' for
284        # now
285#        elem = ET.parse().getroot()
286#        binarySecurityToken = soapBody[0][0][0]
287#        soapBody = elem[1]
288        # TODO: but how to feed these into C14N??
289       
290        for refNode in refNodes:
291            # Get ref node and all it's children
292            refC14n = Canonicalize(refNode)
293
294            print "_"*80
295            print "Inclusive C14N %s:\n" % refNode.nodeName
296            print refC14n
297            open('soapGetAttCertResponse-%s-c14n.xml' % refNode.localName, 
298                 'w').write(refC14n)
299       
300       
301    def test18ExclC14nWithXPath(self):
302        '''Exclusive C14N applied to portions of a SOAP message by extracting
303        using XPath'''
304        from xml.xpath.Context import Context
305        from xml import xpath
306        from xml.dom.ext.reader import PyExpat
307        reader = PyExpat.Reader()
308        dom = reader.fromStream(open('./soapGetAttCertResponse.xml'))
309        processorNss = \
310        {
311            'wsu': \
312"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
313        }
314   
315        ctxt = Context(dom, processorNss=processorNss)
316        refNodes = xpath.Evaluate('//*[@wsu:Id]', 
317                                  contextNode=dom, 
318                                  context=ctxt)
319        for refNode in refNodes:
320            # Get ref node and all it's children
321            refSubsetList = getChildNodes(refNode)
322            refC14n = Canonicalize(dom, None, subset=refSubsetList,
323                                   unsuppressedPrefixes=[])
324
325            print "_"*80
326            print "Exclusive C14N %s:\n" % refNode.nodeName
327            print refC14n
328            open('soapGetAttCertResponse-%s-exclC14n.xml' % refNode.localName, 
329                 'w').write(refC14n)
330       
331       
332    def test19ExclC14nWithXPathAndInclusiveNamespacePrefixes(self):
333        '''Exclusive C14N applied to portions of a SOAP message by extracting
334        using XPath'''
335        from xml.xpath.Context import Context
336        from xml import xpath
337        from xml.dom.ext.reader import PyExpat
338        reader = PyExpat.Reader()
339        dom = reader.fromStream(open('./soapGetAttCertResponse.xml'))
340        processorNss = \
341        {
342            'wsu': \
343"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
344        }
345   
346        ctxt = Context(dom, processorNss=processorNss)
347        refNodes = xpath.Evaluate('//*[@wsu:Id]', 
348                                  contextNode=dom, 
349                                  context=ctxt)
350        for refNode in refNodes:
351            # Get ref node and all it's children
352            refSubsetList = getChildNodes(refNode)
353            refC14n = Canonicalize(dom, None, subset=refSubsetList,
354                                   unsuppressedPrefixes=['SOAP-ENV', 'ds'])
355
356            print "_"*80
357            print "Exclusive C14N with Prefixes %s:\n" % refNode.nodeName
358            print refC14n
359            open('soapGetAttCertResponse-%s-exclC14nWithInclPrefixes.xml' % \
360                 refNode.localName, 
361                 'w').write(refC14n)
362       
363
364def getChildNodes(node, nodeList=None):
365    if nodeList is None:
366        nodeList = [node] 
367    return _getChildNodes(node, nodeList=nodeList)
368           
369def _getChildNodes(node, nodeList=None):
370
371    if node.attributes is not None:
372        nodeList += node.attributes.values() 
373    nodeList += node.childNodes
374    for childNode in node.childNodes:
375        _getChildNodes(childNode, nodeList)
376    return nodeList
377
378if __name__ == "__main__":
379    unittest.main()
380
Note: See TracBrowser for help on using the repository browser.