Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
    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)
Ejemplo n.º 6
0
    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)