コード例 #1
0
    def test02SendQuery(self):
        query_binding = AttributeQuerySslSOAPBinding()
        
        attribute_query = AttributeQueryFactory.create()
        attribute_query.subject.nameID.format = self.__class__.SUBJECT_FORMAT
        attribute_query.subject.nameID.value = self.__class__.SUBJECT
        attribute_query.issuerName = '/O=Site A/CN=Authorisation Service'


        attribute = Attribute()
        attribute.name = 'urn:ndg:saml:emailaddress'
        attribute.friendlyName = 'emailAddress'
        attribute.nameFormat = 'http://www.w3.org/2001/XMLSchema'
        
        attribute_query.attributes.append(attribute)
        
        query_binding.clockSkewTolerance = 2.
        query_binding.sslCACertDir = self.__class__.CLIENT_CACERT_DIR
        query_binding.sslCertFilePath = self.__class__.CLIENT_CERT_FILEPATH
        query_binding.sslPriKeyFilePath = self.__class__.CLIENT_PRIKEY_FILEPATH
        query_binding.sslValidDNs = self.__class__.VALID_DNS
        
        response = query_binding.send(attribute_query, 
                                      uri=self.__class__.SERVICE_URI)
        
        # Convert back to ElementTree instance read for string output
        samlResponseElem = ResponseElementTree.toXML(response)
        
        print("SAML Response ...")
        print(ElementTree.tostring(samlResponseElem))
        print("Pretty print SAML Response ...")
        print(prettyPrint(samlResponseElem))
        
        self.assert_(response.status.statusCode.value==StatusCode.SUCCESS_URI)
コード例 #2
0
    def test01ValidQuery(self):        
        binding = AuthzDecisionQuerySslSOAPBinding()
        binding.actions.append(Action())
        binding.actions[0].namespace = EsgAuthzServiceTestCase.ACTION_NS_URI
        binding.actions[0].value = EsgAuthzServiceTestCase.ACTION    
        binding.resourceURI = EsgAuthzServiceTestCase.RESOURCE_URI
        binding.subjectID = EsgAuthzServiceTestCase.SUBJECT
        binding.issuerName = EsgAuthzServiceTestCase.ISSUER_NAME
        
        # SSL Context Proxy settings
        binding.sslCACertDir = EsgAuthzServiceTestCase.CA_DIR

        # Add tolerance of 1 second for clock skew either side of issue instant
        # and not before / notOnOrAfter times
        binding.clockSkewTolerance = 1
        
        response = binding.send(uri=EsgAuthzServiceTestCase.AUTHZ_SERVICE_URI)
        samlResponseElem = ResponseElementTree.toXML(response)
        
        print("SAML Response ...")
        print(ElementTree.tostring(samlResponseElem))
        print("Pretty print SAML Response ...")
        print(prettyPrint(samlResponseElem))


        self.assert_(response.status.statusCode.value == \
                     StatusCode.SUCCESS_URI)
        self.assert_(response.inResponseTo == binding.query.id)
        self.assert_(response.assertions[0])
        self.assert_(response.assertions[0].subject.nameID.value == \
                     binding.query.subject.nameID.value)
        self.assert_(response.assertions[0].authzDecisionStatements[0])
        self.assert_(response.assertions[0].authzDecisionStatements[0
                                            ].decision == DecisionType.PERMIT)
コード例 #3
0
 def display_result(self, response):
     # Convert back to ElementTree instance read for string output
     saml_response_elem = ResponseElementTree.toXML(response)
     
     if self.pretty_print:
         print(prettyPrint(saml_response_elem))
     else:
         print(ElementTree.tostring(saml_response_elem))
コード例 #4
0
ファイル: test_saml_core.py プロジェクト: cedadev/ndg_saml
 def _serialize_authz_decision_query_response(self):
     response = SAMLUtil.create_authz_decision_query_response()
     
     # Create ElementTree Assertion Element
     responseElem = ResponseElementTree.toXML(response)
     self.assertTrue(ElementTree.iselement(responseElem))
     
     # Serialise to output        
     xmlOutput = prettyPrint(responseElem)
     return xmlOutput
コード例 #5
0
ファイル: test_saml.py プロジェクト: chaosphere2112/ndg_saml
 def _serializeAuthzDecisionQueryResponse(self):
     response = self._createAuthzDecisionQueryResponse()
     
     # Create ElementTree Assertion Element
     responseElem = ResponseElementTree.toXML(response)
     self.assert_(ElementTree.iselement(responseElem))
     
     # Serialise to output        
     xmlOutput = prettyPrint(responseElem)
     return xmlOutput
コード例 #6
0
    def _do_test(self, resourceContentsStr, expected_status, expected_decision):
        """Constructs, sends and evaluates the response from a SAML SOAP request
        using the XACML-SAML profile, with specified resource contents.
        """
        # Load the AuthorisationServiceMiddleware and
        # SOAPQueryInterfaceMiddleware so that the authorisation service can be
        # called.
        self.__class__.INI_FILEPATH = os.path.join(self.__class__.THIS_DIR, 
                                                   self.__class__.INI_FILE)
        wsgiapp = loadapp('config:'+self.__class__.INI_FILEPATH)
        self.app = paste.fixture.TestApp(wsgiapp)

        # Construct a SOAP request.
        (header, request) = self._makeRequest(resourceContentsStr,
                                              issuer=self.ISSUER_DN)

        # Send the SOAP request to the authorisation service.
        httpResponse = self.app.post(self.AUTHZ_SERVICE_URI, 
                                          params=request,
                                          headers=header,
                                          status=200)
        log.debug("Response status=%d", httpResponse.status)

        # Parse the SOAP response.
        envelope = SOAPEnvelope()
        respFile = StringIO(httpResponse.body)
        envelope.parse(respFile)

        # Extract the SAML response.
        samlAuthzResponse = ResponseElementTree.fromXML(envelope.body.elem[0])

#        serialisedResponse = pickle.dumps(samlAuthzResponse)
#        response2 = pickle.loads(serialisedResponse)

        assertions = samlAuthzResponse.assertions
        (assertion,
         error_status,
         error_message) = XacmlSamlPepFilter._evaluate_assertions(assertions,
                                                        self.SUBJECT_ID,
                                                        self.RESOURCE_URI,
                                                        self.AUTHZ_SERVICE_URI)
        if expected_status is None:
            self.assertTrue(error_status is None,
                            ("Unexpected error %d: %s" %
                             (0 if error_status is None else error_status,
                              error_message)))

            self.assertEqual(
                assertion.statements[0].xacmlContextResponse.results[0
                                                            ].decision.value,
                expected_decision)
        else:
            self.assertEqual(error_status, expected_status)
コード例 #7
0
ファイル: etree.py プロジェクト: cedadev/ndg_security_common
 def fromXML(cls, elem, **kw):
     """Extend base method adding mapping for ESG Group/Role Attribute Value
      
     @type elem: ElementTree.Element
     @param elem: ESGF Group/Role attribute value as ElementTree.Element
     @rtype: ndg.security.common.saml_utils.etree.ESGFGroupRoleAttributeValue
     @return: ESGF Group/Role attribute value 
     """
     toSAMLTypeMap = kw.get('customToSAMLTypeMap', [])
     toSAMLTypeMap.append(
                     ESGFGroupRoleAttributeValueElementTree.factoryMatchFunc)
     kw['customToSAMLTypeMap'] = toSAMLTypeMap
     
     return ResponseElementTree.fromXML(elem, **kw)
コード例 #8
0
ファイル: test_saml.py プロジェクト: chaosphere2112/ndg_saml
 def test05CreateAttributeQueryResponse(self):
     response = self._createAttributeQueryResponse()
     
     # Create ElementTree Assertion Element
     responseElem = ResponseElementTree.toXML(response)
     
     self.assert_(ElementTree.iselement(responseElem))
     
     # Serialise to output        
     xmlOutput = prettyPrint(responseElem)       
     self.assert_(len(xmlOutput))
     print("\n"+"_"*80)
     print(xmlOutput)
     print("_"*80)
コード例 #9
0
 def _parseResponse(self, responseStr):
     """Helper to parse a response from a string"""
     soapResponse = SOAPEnvelope()
     
     responseStream = StringIO()
     responseStream.write(responseStr)
     responseStream.seek(0)
     
     soapResponse.parse(responseStream)
     
     print("Parsed response ...")
     print(soapResponse.serialize())
     
     response = ResponseElementTree.fromXML(soapResponse.body.elem[0])
     return response
コード例 #10
0
 def _getSAMLResponse(self, responseBody):
     """Deserialise response string into ElementTree element"""
     soapResponse = SOAPEnvelope()
     
     responseStream = StringIO()
     responseStream.write(responseBody.decode())
     responseStream.seek(0)
     
     soapResponse.parse(responseStream)
     
     print("Parsed response ...")
     print((soapResponse.serialize()))
     
     response = ResponseElementTree.fromXML(soapResponse.body.elem[0])
     
     return response
コード例 #11
0
    def _getAttributes(self, subject):
        """Makes a query for the attributes and returns them.
        The attribute names used in the SAML query are mapped to keys to use in
        the session.
        @type subject: basestring
        @param subject: subject for which the query is to be made
        @rtype: dict of basestring
        @return: attribute names and values
        """
        attributeQuery = self._attributeQueryClient.makeQuery()
        if self.attributeServiceUrl:
            log.debug("Using configured attribute service URL; %s",
                      self.attributeServiceUrl)
            attributeServiceUrl = self.attributeServiceUrl
        else:
            attributeServiceUrl = self._getAttributeService(subject)

        self._attributeQueryClient.setQuerySubjectId(attributeQuery, subject)
        attributeQuery.attributes.extend(ESGFDefaultQueryAttributes.ATTRIBUTES)

        response = self._attributeQueryClient.send(attributeQuery,
                                                   uri=attributeServiceUrl)

        samlResponseElem = ResponseElementTree.toXML(response)

        if log.isEnabledFor(logging.DEBUG):
            log.debug("Pretty print SAML Response ...")
            log.debug(prettyPrint(samlResponseElem))
            # log.debug(ElementTree.tostring(samlResponseElem))

        returnValues = {}
        for assertion in response.assertions:
            for attrStmt in assertion.attributeStatements:
                for attr in attrStmt.attributes:
                    attrKey = self.__class__.ATTRIBUTE_NAME_MAP.get(attr.name)
                    if attrKey:
                        if len(attr.attributeValues) > 1:
                            value = []
                            for attrVal in attr.attributeValues:
                                value.append(attrVal.value)
                        else:
                            value = attr.attributeValues[0].value
                        returnValues[attrKey] = value
                        log.debug("Received attribute: %s = %s", attrKey,
                                  value)

        return returnValues
コード例 #12
0
    def __call__(self, environ, start_response):
        """Logs the request and response stored in the session.

        @type environ: dict
        @param environ: WSGI environment variables dictionary
        @type start_response: function
        @param start_response: standard WSGI start response function
        @rtype: iterable
        @return: response
        """
        session = environ.get('beaker.session.ndg.security')
        if session:
            pepCtx = session.get(SamlPepFilterBase.PEPCTX_SESSION_KEYNAME)
            if pepCtx:
                request = pepCtx.get(
                    SamlPepFilterBase.PEPCTX_REQUEST_SESSION_KEYNAME)
                if isinstance(request, AuthzDecisionQuery):
                    requestEtree = AuthzDecisionQueryElementTree.toXML(request)
                    log.debug("AuthzDecisionQuery:\n%s",
                              etree_utils.prettyPrint(requestEtree))
                elif isinstance(request, XACMLAuthzDecisionQuery):
                    requestEtree = XACMLAuthzDecisionQueryElementTree.toXML(
                        request)
                    log.debug("XACMLAuthzDecisionQuery:\n%s",
                              etree_utils.prettyPrint(requestEtree))
                else:
                    log.error(
                        "Request stored in session is of unknown type: %s" %
                        type(request))

                response = pepCtx.get(
                    SamlPepFilterBase.PEPCTX_RESPONSE_SESSION_KEYNAME)
                if isinstance(response, Response):
                    responseEtree = ResponseElementTree.toXML(response)
                    log.debug("Response:\n%s",
                              etree_utils.prettyPrint(responseEtree))
                else:
                    log.error(
                        "Response stored in session is of unknown type: %s" %
                        type(response))

                timestamp = pepCtx.get(
                    SamlPepFilterBase.PEPCTX_TIMESTAMP_SESSION_KEYNAME)
                log.debug("Timestamp: %s", timestamp)
            log.debug("No PEP context found in session.")
        return self._app(environ, start_response)
コード例 #13
0
    def _getSAMLResponse(self, responseBody):
        """Deserialise response string into ElementTree element"""
        soapResponse = SOAPEnvelope()

        responseStream = StringIO()
        responseStream.write(responseBody)
        responseStream.seek(0)

        soapResponse.parse(responseStream)

        print("Parsed response ...")
        print(soapResponse.serialize())
        #        print(prettyPrint(soapResponse.elem))

        response = ResponseElementTree.fromXML(soapResponse.body.elem[0])

        return response
コード例 #14
0
ファイル: test_saml_core.py プロジェクト: cedadev/ndg_saml
    def test02_with_action_namespace(self):
        # Handle case where response has used an invalid namespace URI for the
        # action specified.  In the example below 'GET' is used which belongs
        # to the urn:oasis:names:tc:SAML:1.0:action:ghpp namespace.  However,
        # it has not been set so the parser should interpret it as the default
        # urn:oasis:names:tc:SAML:1.0:action:rwedc-negation -
        # 2.7.4.2 SAML 2 Core Spec. 15 March 2005
        saml_resp = '''
<samlp:Response ID="1a0c8a92-f408-4ab6-b352-dc9ae5f025cb" 
InResponseTo="d78f66ec-ddce-4a2a-81cf-6bb2bf5ea624" 
IssueInstant="2016-01-24T06:06:04.389161Z" Version="2.0" 
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<saml:Issuer 
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName" 
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">/C=GB/O=NDG/CN=localhost
</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</samlp:Status>
<saml:Assertion ID="41aaf9d3-b637-4d54-ac7f-0b35316c4558" 
IssueInstant="2016-01-24T06:06:04.586012Z" Version="2.0" 
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
<saml:Issuer 
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName">
/C=GB/O=NDG/CN=localhost
</saml:Issuer>
<saml:Subject>
<saml:NameID Format="urn:esg:openid" /></saml:Subject>
<saml:Conditions NotBefore="2016-01-24T06:06:04.586012Z" 
NotOnOrAfter="2016-01-25T06:06:04.586012Z" />
<saml:AuthzDecisionStatement Decision="Deny" 
Resource="http://localhost:8000/resource.html" >
<saml:Action Namespace="urn:oasis:names:tc:SAML:1.0:action:ghpp">GET
</saml:Action>
</saml:AuthzDecisionStatement>
</saml:Assertion></samlp:Response>
'''
        authz_decision_response_stream = StringIO()
        authz_decision_response_stream.write(saml_resp)
        authz_decision_response_stream.seek(0)

        tree = ElementTree.parse(authz_decision_response_stream)
        elem = tree.getroot()
        resp = ResponseElementTree.fromXML(elem)
        self.assertIsInstance(resp, Response, 'Expecting SAML Response type')
コード例 #15
0
ファイル: etree.py プロジェクト: cedadev/ndg_security_common
 def toXML(cls, response, **kw):
     """Extend base method adding mapping for ESG Group/Role Attribute Value 
     to enable ElementTree Attribute Value factory to render the XML output
     
     @type response: ndg.security.common.saml_utils.etree.ESGFGroupRoleAttributeValue
     @param response: ESGF Group/Role attribute value 
     @rtype: ElementTree.Element
     @return: ESGF Group/Role attribute value as ElementTree.Element
     """
     toXMLTypeMap = kw.get('customToXMLTypeMap', {})
     toXMLTypeMap[ESGFGroupRoleAttributeValue
                  ] = ESGFGroupRoleAttributeValueElementTree
     
     kw['customToXMLTypeMap'] = toXMLTypeMap
     
     # Convert to ElementTree representation to enable attachment to SOAP
     # response body
     return ResponseElementTree.toXML(response, **kw)
コード例 #16
0
    def __call__(self, environ, start_response):
        """Logs the request and response stored in the session.

        @type environ: dict
        @param environ: WSGI environment variables dictionary
        @type start_response: function
        @param start_response: standard WSGI start response function
        @rtype: iterable
        @return: response
        """
        session = environ.get('beaker.session.ndg.security')
        if session:
            pepCtx = session.get(SamlPepFilterBase.PEPCTX_SESSION_KEYNAME)
            if pepCtx:
                request = pepCtx.get(SamlPepFilterBase.PEPCTX_REQUEST_SESSION_KEYNAME)
                if isinstance(request, AuthzDecisionQuery):
                    requestEtree = AuthzDecisionQueryElementTree.toXML(request)
                    log.debug("AuthzDecisionQuery:\n%s",
                              etree_utils.prettyPrint(requestEtree))
                elif isinstance(request, XACMLAuthzDecisionQuery):
                    requestEtree = XACMLAuthzDecisionQueryElementTree.toXML(request)
                    log.debug("XACMLAuthzDecisionQuery:\n%s",
                              etree_utils.prettyPrint(requestEtree))
                else:
                    log.error("Request stored in session is of unknown type: %s"
                              % type(request))

                response = pepCtx.get(SamlPepFilterBase.PEPCTX_RESPONSE_SESSION_KEYNAME)
                if isinstance(response, Response):
                    responseEtree = ResponseElementTree.toXML(response)
                    log.debug("Response:\n%s",
                              etree_utils.prettyPrint(responseEtree))
                else:
                    log.error("Response stored in session is of unknown type: %s"
                              % type(response))

                timestamp = pepCtx.get(SamlPepFilterBase.PEPCTX_TIMESTAMP_SESSION_KEYNAME)
                log.debug("Timestamp: %s", timestamp)
            log.debug("No PEP context found in session.")
        return self._app(environ, start_response)
コード例 #17
0
ファイル: test_saml_core.py プロジェクト: cedadev/ndg_saml
    def test06_deserialize_authz_decision_response(self):
        xmlOutput = self._serialize_authz_decision_query_response()
        
        authzDecisionResponseStream = StringIO()
        authzDecisionResponseStream.write(xmlOutput)
        authzDecisionResponseStream.seek(0)

        tree = ElementTree.parse(authzDecisionResponseStream)
        elem = tree.getroot()
        response = ResponseElementTree.fromXML(elem)
        
        self.assertTrue(response.assertions[0])
        self.assertTrue(response.assertions[0].authzDecisionStatements[0])
        self.assertTrue(response.assertions[0].authzDecisionStatements[0
            ].decision == DecisionType.PERMIT)
        self.assertTrue(response.assertions[0].authzDecisionStatements[0
            ].resource == SAMLUtil.RESOURCE_URI)
        self.assertTrue(response.assertions[0].authzDecisionStatements[0
            ].decision == DecisionType.PERMIT)
        self.assertTrue(response.assertions[0].authzDecisionStatements[0
            ].actions[-1].namespace == Action.GHPP_NS_URI)
        self.assertTrue(response.assertions[0].authzDecisionStatements[0
            ].actions[-1].value == Action.HTTP_GET_ACTION)
コード例 #18
0
    def __call__(self, environ, start_response):
        soapRequestStream = environ['wsgi.input']
        soapRequest = SOAPEnvelope()
        soapRequest.parse(soapRequestStream)
        attributeQueryElem = soapRequest.body.elem[0]
        attributeQuery = AttributeQueryElementTree.fromXML(attributeQueryElem)
        
        print("Received request from client:\n")
        print soapRequest.prettyPrint()
        
        samlResponse = Response()
        
        samlResponse.issueInstant = datetime.utcnow()
        samlResponse.id = str(uuid4())
        samlResponse.issuer = Issuer()
        
        # SAML 2.0 spec says format must be omitted
        #samlResponse.issuer.format = Issuer.X509_SUBJECT
        samlResponse.issuer.value = \
                        "/O=NDG/OU=BADC/CN=attributeauthority.badc.rl.ac.uk"
        
        samlResponse.inResponseTo = attributeQuery.id
        
        assertion = Assertion()
        
        assertion.version = SAMLVersion(SAMLVersion.VERSION_20)
        assertion.id = str(uuid4())
        assertion.issueInstant = samlResponse.issueInstant
        
        assertion.conditions = Conditions()
        assertion.conditions.notBefore = assertion.issueInstant
        assertion.conditions.notOnOrAfter = assertion.conditions.notBefore + \
            timedelta(seconds=60*60*8)
        
        assertion.subject = Subject()  
        assertion.subject.nameID = NameID()
        assertion.subject.nameID.format = attributeQuery.subject.nameID.format
        assertion.subject.nameID.value = attributeQuery.subject.nameID.value

        assertion.attributeStatements.append(AttributeStatement())
        
        for attribute in attributeQuery.attributes:
            if attribute.name == SamlSoapBindingApp.FIRSTNAME_ATTRNAME:
                # special case handling for 'FirstName' attribute
                fnAttribute = Attribute()
                fnAttribute.name = attribute.name
                fnAttribute.nameFormat = attribute.nameFormat
                fnAttribute.friendlyName = attribute.friendlyName
    
                firstName = XSStringAttributeValue()
                firstName.value = self.firstName
                fnAttribute.attributeValues.append(firstName)
    
                assertion.attributeStatements[0].attributes.append(fnAttribute)
            
            elif attribute.name == SamlSoapBindingApp.LASTNAME_ATTRNAME:
                lnAttribute = Attribute()
                lnAttribute.name = attribute.name
                lnAttribute.nameFormat = attribute.nameFormat
                lnAttribute.friendlyName = attribute.friendlyName
    
                lastName = XSStringAttributeValue()
                lastName.value = self.lastName
                lnAttribute.attributeValues.append(lastName)
    
                assertion.attributeStatements[0].attributes.append(lnAttribute)
               
            elif attribute.name == SamlSoapBindingApp.EMAILADDRESS_ATTRNAME:
                emailAddressAttribute = Attribute()
                emailAddressAttribute.name = attribute.name
                emailAddressAttribute.nameFormat = attribute.nameFormat
                emailAddressAttribute.friendlyName = attribute.friendlyName
    
                emailAddress = XSStringAttributeValue()
                emailAddress.value = self.emailAddress
                emailAddressAttribute.attributeValues.append(emailAddress)
    
                assertion.attributeStatements[0].attributes.append(
                                                        emailAddressAttribute)
        
        samlResponse.assertions.append(assertion)
        
        samlResponse.status = Status()
        samlResponse.status.statusCode = StatusCode()
        samlResponse.status.statusCode.value = StatusCode.SUCCESS_URI        

        
        # Convert to ElementTree representation to enable attachment to SOAP
        # response body
        samlResponseElem = ResponseElementTree.toXML(samlResponse)
        xml = ElementTree.tostring(samlResponseElem)
        log.debug('Sending response to query:\n%s', xml)
        
        # Create SOAP response and attach the SAML Response payload
        soapResponse = SOAPEnvelope()
        soapResponse.create()
        soapResponse.body.elem.append(samlResponseElem)
        
        response = soapResponse.serialize()
        
        start_response("200 OK",
                       [('Content-length', str(len(response))),
                        ('Content-type', 'text/xml')])
        return [response]
コード例 #19
0
    def test01AttributeQuery(self):
        attributeQuery = AttributeQuery()
        attributeQuery.version = SAMLVersion(SAMLVersion.VERSION_20)
        attributeQuery.id = str(uuid4())
        attributeQuery.issueInstant = datetime.utcnow()
        
        attributeQuery.issuer = Issuer()
        attributeQuery.issuer.format = Issuer.X509_SUBJECT
        attributeQuery.issuer.value = \
                        "/O=NDG/OU=BADC/CN=attributeauthority.badc.rl.ac.uk"
                        
                        
        attributeQuery.subject = Subject()  
        attributeQuery.subject.nameID = NameID()
        attributeQuery.subject.nameID.format = SamlSoapBindingApp.NAMEID_FORMAT
        attributeQuery.subject.nameID.value = \
                                    "https://openid.localhost/philip.kershaw"
        
        # special case handling for 'FirstName' attribute
        fnAttribute = Attribute()
        fnAttribute.name = SamlSoapBindingApp.FIRSTNAME_ATTRNAME
        fnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
        fnAttribute.friendlyName = "FirstName"

        attributeQuery.attributes.append(fnAttribute)
    
        # special case handling for 'LastName' attribute
        lnAttribute = Attribute()
        lnAttribute.name = SamlSoapBindingApp.LASTNAME_ATTRNAME
        lnAttribute.nameFormat = "http://www.w3.org/2001/XMLSchema#string"
        lnAttribute.friendlyName = "LastName"

        attributeQuery.attributes.append(lnAttribute)
    
        # special case handling for 'LastName' attribute
        emailAddressAttribute = Attribute()
        emailAddressAttribute.name = SamlSoapBindingApp.EMAILADDRESS_ATTRNAME
        emailAddressAttribute.nameFormat = XMLConstants.XSD_NS+"#"+\
                                    XSStringAttributeValue.TYPE_LOCAL_NAME
        emailAddressAttribute.friendlyName = "emailAddress"

        attributeQuery.attributes.append(emailAddressAttribute)                                   
        
        elem = AttributeQueryElementTree.toXML(attributeQuery)
        soapRequest = SOAPEnvelope()
        soapRequest.create()
        soapRequest.body.elem.append(elem)
        
        request = soapRequest.serialize()
        
        header = {
            'soapAction': "http://www.oasis-open.org/committees/security",
            'Content-length': str(len(request)),
            'Content-type': 'text/xml'
        }
        response = self.app.post('/attributeauthority', 
                                 params=request, 
                                 headers=header, 
                                 status=200)
        print("Response status=%d" % response.status)

        soapResponse = SOAPEnvelope()
        
        responseStream = StringIO()
        responseStream.write(response.body)
        responseStream.seek(0)
        
        soapResponse.parse(responseStream)
        
        print("Parsed response ...")
        print(soapResponse.serialize())
#        print(prettyPrint(soapResponse.elem))
        
        response = ResponseElementTree.fromXML(soapResponse.body.elem[0])
        self.assert_(response.status.statusCode.value==StatusCode.SUCCESS_URI)
        self.assert_(response.inResponseTo == attributeQuery.id)
        self.assert_(response.assertions[0].subject.nameID.value == \
                     attributeQuery.subject.nameID.value)