def testParseDuration(self): """ Tests the parse_duration method of the OneLogin_Saml2_Utils """ duration = 'PT1393462294S' timestamp = 1393876825 parsed_duration = OneLogin_Saml2_Utils.parse_duration( duration, timestamp) self.assertEqual(2787339119, parsed_duration) parsed_duration_2 = OneLogin_Saml2_Utils.parse_duration(duration) self.assertTrue(parsed_duration_2 > parsed_duration) invalid_duration = 'PT1Y' self.assertRaisesRegexp(Exception, 'Unrecognised ISO 8601 date format', OneLogin_Saml2_Utils.parse_duration, invalid_duration) new_duration = 'P1Y1M' parsed_duration_4 = OneLogin_Saml2_Utils.parse_duration( new_duration, timestamp) self.assertEqual(1428091225, parsed_duration_4) neg_duration = '-P14M' parsed_duration_5 = OneLogin_Saml2_Utils.parse_duration( neg_duration, timestamp) self.assertEqual(1357243225, parsed_duration_5)
def testParseDuration(self): """ Tests the parse_duration method of the OneLogin_Saml2_Utils """ duration = 'PT1393462294S' timestamp = 1393876825 parsed_duration = OneLogin_Saml2_Utils.parse_duration(duration, timestamp) self.assertEqual(2787339119, parsed_duration) parsed_duration_2 = OneLogin_Saml2_Utils.parse_duration(duration) self.assertTrue(parsed_duration_2 > parsed_duration) invalid_duration = 'PT1Y' try: parsed_duration_3 = OneLogin_Saml2_Utils.parse_duration(invalid_duration) self.assertEqual(parsed_duration_3, 42) except Exception as e: self.assertIn('Unrecognised ISO 8601 date format', e.message) new_duration = 'P1Y1M' parsed_duration_4 = OneLogin_Saml2_Utils.parse_duration(new_duration, timestamp) self.assertEqual(1428091225, parsed_duration_4) neg_duration = '-P14M' parsed_duration_5 = OneLogin_Saml2_Utils.parse_duration(neg_duration, timestamp) self.assertEqual(1357243225, parsed_duration_5)
def _parse_metadata_xml(xml, entity_id): """ Given an XML document containing SAML 2.0 metadata, parse it and return a tuple of (public_key, sso_url, expires_at) for the specified entityID. Raises MetadataParseError if anything is wrong. """ if xml.tag == etree.QName(SAML_XML_NS, 'EntityDescriptor'): entity_desc = xml else: if xml.tag != etree.QName(SAML_XML_NS, 'EntitiesDescriptor'): raise MetadataParseError( Text("Expected root element to be <EntitiesDescriptor>, not {}" ).format(xml.tag)) entity_desc = xml.find(".//{}[@entityID='{}']".format( etree.QName(SAML_XML_NS, 'EntityDescriptor'), entity_id)) if entity_desc is None: raise MetadataParseError( f"Can't find EntityDescriptor for entityID {entity_id}") expires_at = None if "validUntil" in xml.attrib: expires_at = dateutil.parser.parse(xml.attrib["validUntil"]) if "cacheDuration" in xml.attrib: cache_expires = OneLogin_Saml2_Utils.parse_duration( xml.attrib["cacheDuration"]) cache_expires = datetime.datetime.fromtimestamp(cache_expires, tz=pytz.utc) if expires_at is None or cache_expires < expires_at: expires_at = cache_expires sso_desc = entity_desc.find(etree.QName(SAML_XML_NS, "IDPSSODescriptor")) if sso_desc is None: raise MetadataParseError("IDPSSODescriptor missing") if 'urn:oasis:names:tc:SAML:2.0:protocol' not in sso_desc.get( "protocolSupportEnumeration"): raise MetadataParseError("This IdP does not support SAML 2.0") # Now we just need to get the public_key and sso_url public_key = sso_desc.findtext("./{}//{}".format( etree.QName(SAML_XML_NS, "KeyDescriptor"), "{http://www.w3.org/2000/09/xmldsig#}X509Certificate")) if not public_key: raise MetadataParseError( "Public Key missing. Expected an <X509Certificate>") public_key = public_key.replace(" ", "") binding_elements = sso_desc.iterfind("./{}".format( etree.QName(SAML_XML_NS, "SingleSignOnService"))) sso_bindings = { element.get('Binding'): element.get('Location') for element in binding_elements } try: # The only binding supported by python-saml and python-social-auth is HTTP-Redirect: sso_url = sso_bindings[ 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'] except KeyError: raise MetadataParseError( "Unable to find SSO URL with HTTP-Redirect binding.") # lint-amnesty, pylint: disable=raise-missing-from return public_key, sso_url, expires_at
def _parse_metadata_xml(xml, entity_id): """ Given an XML document containing SAML 2.0 metadata, parse it and return a tuple of (public_key, sso_url, expires_at) for the specified entityID. Raises MetadataParseError if anything is wrong. """ if xml.tag == etree.QName(SAML_XML_NS, 'EntityDescriptor'): entity_desc = xml else: if xml.tag != etree.QName(SAML_XML_NS, 'EntitiesDescriptor'): raise MetadataParseError("Expected root element to be <EntitiesDescriptor>, not {}".format(xml.tag)) entity_desc = xml.find( ".//{}[@entityID='{}']".format(etree.QName(SAML_XML_NS, 'EntityDescriptor'), entity_id) ) if not entity_desc: raise MetadataParseError("Can't find EntityDescriptor for entityID {}".format(entity_id)) expires_at = None if "validUntil" in xml.attrib: expires_at = dateutil.parser.parse(xml.attrib["validUntil"]) if "cacheDuration" in xml.attrib: cache_expires = OneLogin_Saml2_Utils.parse_duration(xml.attrib["cacheDuration"]) cache_expires = datetime.datetime.fromtimestamp(cache_expires, tz=pytz.utc) if expires_at is None or cache_expires < expires_at: expires_at = cache_expires sso_desc = entity_desc.find(etree.QName(SAML_XML_NS, "IDPSSODescriptor")) if not sso_desc: raise MetadataParseError("IDPSSODescriptor missing") if 'urn:oasis:names:tc:SAML:2.0:protocol' not in sso_desc.get("protocolSupportEnumeration"): raise MetadataParseError("This IdP does not support SAML 2.0") # Now we just need to get the public_key and sso_url public_key = sso_desc.findtext("./{}//{}".format( etree.QName(SAML_XML_NS, "KeyDescriptor"), "{http://www.w3.org/2000/09/xmldsig#}X509Certificate" )) signing_public_key = sso_desc.findtext("./{}{}//{}".format( etree.QName(SAML_XML_NS, "KeyDescriptor"), "[@use='signing']", "{http://www.w3.org/2000/09/xmldsig#}X509Certificate" )) if signing_public_key and public_key != signing_public_key: public_key = signing_public_key if not public_key: raise MetadataParseError("Public Key missing. Expected an <X509Certificate>") public_key = public_key.replace(" ", "") binding_elements = sso_desc.iterfind("./{}".format(etree.QName(SAML_XML_NS, "SingleSignOnService"))) sso_bindings = {element.get('Binding'): element.get('Location') for element in binding_elements} try: # The only binding supported by python-saml and python-social-auth is HTTP-Redirect: sso_url = sso_bindings['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'] except KeyError: raise MetadataParseError("Unable to find SSO URL with HTTP-Redirect binding.") return public_key, sso_url, expires_at
def testParseDuration(self): """ Tests the parse_duration method of the OneLogin_Saml2_Utils """ duration = 'PT1393462294S' timestamp = 1393876825 parsed_duration = OneLogin_Saml2_Utils.parse_duration(duration, timestamp) self.assertEqual(2787339119, parsed_duration) parsed_duration_2 = OneLogin_Saml2_Utils.parse_duration(duration) self.assertTrue(parsed_duration_2 > parsed_duration) invalid_duration = 'PT1Y' with self.assertRaises(Exception) as context: OneLogin_Saml2_Utils.parse_duration(invalid_duration) exception = context.exception self.assertIn("Unrecognised ISO 8601 date format", str(exception)) new_duration = 'P1Y1M' parsed_duration_4 = OneLogin_Saml2_Utils.parse_duration(new_duration, timestamp) self.assertEqual(1428091225, parsed_duration_4) neg_duration = '-P14M' parsed_duration_5 = OneLogin_Saml2_Utils.parse_duration(neg_duration, timestamp) self.assertEqual(1357243225, parsed_duration_5)
def testParseDuration(self): """ Tests the parse_duration method of the OneLogin_Saml2_Utils """ duration = 'PT1393462294S' timestamp = 1393876825 parsed_duration = OneLogin_Saml2_Utils.parse_duration(duration, timestamp) self.assertEqual(2787339119, parsed_duration) parsed_duration_2 = OneLogin_Saml2_Utils.parse_duration(duration) self.assertTrue(parsed_duration_2 > parsed_duration) invalid_duration = 'PT1Y' with self.assertRaises(Exception) as context: OneLogin_Saml2_Utils.parse_duration(invalid_duration) exception = context.exception self.assertIn("Unrecognised ISO 8601 date format", str(exception)) new_duration = 'P1Y1M' parsed_duration_4 = OneLogin_Saml2_Utils.parse_duration(new_duration, timestamp) self.assertEqual(1428091225, parsed_duration_4) neg_duration = '-P14M' parsed_duration_5 = OneLogin_Saml2_Utils.parse_duration(neg_duration, timestamp) self.assertEqual(1357243225, parsed_duration_5)