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)
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)
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))
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
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
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)
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)
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)
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
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
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
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)
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
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')
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)
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)
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)
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]
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)