source: TI12-security/trunk/python/ndg.security.test/ndg/security/test/unit/saml/test_saml.py @ 5538

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.test/ndg/security/test/unit/saml/test_saml.py@5538
Revision 5538, 9.8 KB checked in by pjkersha, 11 years ago (diff)

Working SAML Attribute Query with SOAP binding

Line 
1"""SAML unit test package
2
3NERC DataGrid Project
4"""
5__author__ = "P J Kershaw"
6__date__ = "21/07/09"
7__copyright__ = "(C) 2009 Science and Technology Facilities Council"
8__license__ = "BSD - see LICENSE file in top-level directory"
9__contact__ = "Philip.Kershaw@stfc.ac.uk"
10__revision__ = '$Id$'
11import logging
12logging.basicConfig(level=logging.DEBUG)
13   
14from datetime import datetime
15from uuid import uuid4
16from cStringIO import StringIO
17
18import unittest
19
20from xml.etree.ElementTree import iselement
21
22from ndg.security.common.saml import Assertion, Attribute, AttributeValue, \
23    AttributeStatement, SAMLVersion, XSStringAttributeValue, \
24    XSGroupRoleAttributeValue, AttributeQuery, Issuer, Subject, NameID
25from ndg.security.common.saml.xml import XMLConstants
26from ndg.security.common.saml.xml.etree import AssertionETreeObject, \
27    XSGroupRoleAttributeValueETreeObject, AttributeQueryETreeObject
28
29
30class SAMLUtil(object):
31    """SAML utility class based on ANL examples for Earth System Grid:
32    http://www.ci.uchicago.edu/wiki/bin/view/ESGProject/ESGSAMLAttributes#ESG_Attribute_Service
33    """
34   
35    def __init__(self):
36        """Set-up ESG core attributes, Group/Role and miscellaneous
37        attributes lists
38        """
39        self.firstName = None
40        self.lastName = None
41        self.emailAddress = None
42       
43        self.__groupRoleList = []
44        self.__miscAttrList = []
45
46    def addGroupRole(self, group, role):
47        """Add an ESG Group/Role attribute
48        @type group: basestring
49        @param group: group name
50        @type role: basestring
51        @param role: role name
52        """
53        self.__groupRoleList.append((group, role))
54   
55    def addAttribute(self, name, value):
56        """Add a generic attribute
57        @type name: basestring
58        @param name: attribute name
59        @type value: basestring
60        @param value: attribute value
61        """
62        self.__miscAttrList.append((name, value))
63
64    def buildAssertion(self):
65        """Create a SAML Assertion containing ESG core attributes: First
66        Name, Last Name, e-mail Address; ESG Group/Role type attributes
67        and generic attributes
68        @rtype: ndg.security.common.saml.Assertion
69        @return: new SAML Assertion object
70        """
71       
72        assertion = Assertion()
73        assertion.version = SAMLVersion(SAMLVersion.VERSION_20)
74        assertion.id = str(uuid4())
75        assertion.issueInstant = datetime.utcnow()
76
77        attributeStatement = AttributeStatement()
78       
79        for attribute in self.createAttributes():
80            attributeStatement.attributes.append(attribute)
81           
82        return assertion
83
84    def buildAttributeQuery(self, issuer, subjectNameID):
85       
86        attributeQuery = AttributeQuery()
87        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20)
88        attributeQuery.id = str(uuid4())
89        attributeQuery.issueInstant = datetime.utcnow()
90       
91        attributeQuery.issuer = Issuer()
92        attributeQuery.issuer.format = Issuer.X509_SUBJECT
93        attributeQuery.issuer.value = issuer
94                       
95        attributeQuery.subject = Subject() 
96        attributeQuery.subject.nameID = NameID()
97        attributeQuery.subject.nameID.format = "urn:esg:openid"
98        attributeQuery.subject.nameID.value = subjectNameID
99                                   
100        attributeQuery.attributes = self.createAttributes()
101       
102        return attributeQuery
103   
104    def createAttributes(self):
105       
106        attributes = []
107        if self.firstName is not None:   
108            # special case handling for 'FirstName' attribute
109            fnAttribute = Attribute()
110            fnAttribute.name = "urn:esg:first:name"
111            fnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
112            fnAttribute.friendlyName = "FirstName"
113
114            firstName = XSStringAttributeValue()
115            firstName.value = self.firstName
116            fnAttribute.attributeValues.append(firstName)
117
118            attributes.append(fnAttribute)
119       
120
121        if self.lastName is not None:
122            # special case handling for 'LastName' attribute
123            lnAttribute = Attribute()
124            lnAttribute.name = "urn:esg:last:name"
125            lnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
126            lnAttribute.friendlyName = "LastName"
127
128            lastName = XSStringAttributeValue()
129            lastName.value = self.lastName
130            lnAttribute.attributeValues.append(lastName)
131
132            attributes.append(lnAttribute)
133       
134
135        if self.emailAddress is not None:
136            # special case handling for 'LastName' attribute
137            emailAddressAttribute = Attribute()
138            emailAddressAttribute.name = "urn:esg:email:address"
139            emailAddressAttribute.nameFormat = XMLConstants.XSD_NS+"#"+\
140                                        XSStringAttributeValue.TYPE_LOCAL_NAME
141            emailAddressAttribute.friendlyName = "emailAddress"
142
143            emailAddress = XSStringAttributeValue()
144            emailAddress.value = self.emailAddress
145            emailAddressAttribute.attributeValues.append(emailAddress)
146
147            attributes.append(emailAddressAttribute)
148       
149        if len(self.__groupRoleList) > 0:
150            # custom group/role attribute to be added to attr statement
151            groupRoleAttribute = Attribute()
152            groupRoleAttribute.name = "GroupRole"
153            groupRoleAttribute.nameFormat = \
154                                    XSGroupRoleAttributeValue.TYPE_LOCAL_NAME
155
156            for group, role in self.__groupRoleList:
157                groupRole = XSGroupRoleAttributeValue()
158                groupRole.group = group
159                groupRole.role = role
160
161                groupRoleAttribute.attributeValues.append(groupRole)
162           
163            attributes.append(groupRoleAttribute)
164       
165        for name, value in self.__miscAttrList:
166            attribute = Attribute()
167            attribute.name = name
168            attribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
169
170            stringAttributeValue = XSStringAttributeValue()
171            stringAttributeValue.value = value
172            attribute.attributeValues.append(stringAttributeValue)
173
174            attributes.append(attribute)
175           
176        return attributes
177
178
179class SAMLTestCase(unittest.TestCase):
180    """Test SAML implementation for use with CMIP5 federation"""
181   
182    def test01CreateAssertion(self):
183        samlUtil = SAMLUtil()
184       
185        # ESG core attributes
186        samlUtil.firstName = "Philip"
187        samlUtil.lastName = "Kershaw"
188        samlUtil.emailAddress = "p.j.k@somewhere"
189       
190        # BADC specific attributes
191        badcRoleList = (
192            'urn:badc:security:authz:1.0:attr:admin', 
193            'urn:badc:security:authz:1.0:attr:rapid', 
194            'urn:badc:security:authz:1.0:attr:coapec', 
195            'urn:badc:security:authz:1.0:attr:midas', 
196            'urn:badc:security:authz:1.0:attr:quest', 
197            'urn:badc:security:authz:1.0:attr:staff'
198        )
199        for role in badcRoleList:
200            samlUtil.addAttribute("urn:badc:security:authz:1.0:attr", role)
201       
202        # ESG Group/Role type list
203        esgGroupRoleList = (
204            ("ESG-NCAR", "admin"),
205            ("ESG-PCMDI", "testUser"),
206        )
207        for group, role in esgGroupRoleList:
208            samlUtil.addGroupRole(group, role)
209       
210        # Make an assertion object
211        assertion = samlUtil.buildAssertion()
212       
213        # Create XML rendering class using the ElementTree implementation
214        assertionETreeObject = AssertionETreeObject()
215       
216        # Add mapping for ESG Group/Role Attribute Value to enable ElementTree
217        # Attribute Value factory to render the XML output
218        attributeValueETreeObjectClassMap = {
219            XSGroupRoleAttributeValue: XSGroupRoleAttributeValueETreeObject           
220        }
221       
222        # Create ElementTree Assertion Element
223        assertionElem = assertionETreeObject.create(assertion,
224                            customClassMap=attributeValueETreeObjectClassMap)
225       
226        self.assert_(iselement(assertionElem))
227       
228        # Serialise to output
229        xmlOutput = assertionETreeObject.prettyPrint()
230        self.assert_(len(xmlOutput))
231        print(xmlOutput)
232
233    def test02CreateAttributeQuery(self):
234        samlUtil = SAMLUtil()
235        samlUtil.firstName = ''
236        samlUtil.lastName = ''
237        samlUtil.emailAddress = ''
238        attributeQuery = samlUtil.buildAttributeQuery(
239                        "/O=NDG/OU=BADC/CN=attributeauthority.badc.rl.ac.uk",
240                        "https://openid.localhost/philip.kershaw")
241       
242        attributeQueryETreeObject = AttributeQueryETreeObject()
243        attributeQueryETreeObject.create(attributeQuery)
244        xmlOutput = attributeQueryETreeObject.prettyPrint()
245        print(xmlOutput)
246
247    def test03ParseAttributeQuery(self):
248        samlUtil = SAMLUtil()
249        samlUtil.firstName = ''
250        samlUtil.lastName = ''
251        samlUtil.emailAddress = ''
252        attributeQuery = samlUtil.buildAttributeQuery(
253                        "/O=NDG/OU=BADC/CN=attributeauthority.badc.rl.ac.uk",
254                        "https://openid.localhost/philip.kershaw")
255       
256        attributeQueryETreeObject = AttributeQueryETreeObject()
257        attributeQueryETreeObject.create(attributeQuery)
258        xmlOutput = attributeQueryETreeObject.prettyPrint()
259       
260        attributeQueryETreeObject2 = AttributeQueryETreeObject()
261       
262        attributeQueryStream = StringIO()
263        attributeQueryStream.write(xmlOutput)
264        attributeQueryStream.seek(0)
265       
266        attributeQuery2=attributeQueryETreeObject2.parse(attributeQueryStream)
267        xmlOutput2 = attributeQuery2.serialize()
268        self.assert_(xmlOutput == xmlOutput2)
269       
270       
271if __name__ == "__main__":
272    unittest.main()       
Note: See TracBrowser for help on using the repository browser.