def test_verify_response_nia(self):
        with cast(TextIO, (DATA_DIR / 'nia_test_response.xml').open('r')) as f:
            tree = parse_xml(f.read())
            remove_extra_xml_whitespace(tree)

        response = SAMLResponse(tree)
        response.verify_response(NIA_CERT_FILE)
    def test_verify_assertion_nia_not_decrypted(self):
        with cast(TextIO, (DATA_DIR / 'nia_test_response.xml').open('r')) as f:
            tree = parse_xml(f.read())
            remove_extra_xml_whitespace(tree)

        response = SAMLResponse(tree)
        self.assertFalse(response.verify_assertion(NIA_CERT_FILE))
    def test_encrypt_xml_node(self):
        supported_ciphers = set(XmlBlockCipher)  # type: Set[XmlBlockCipher]
        if LIBXMLSEC_VERSION < (1, 2, 27):  # pragma: no cover
            supported_ciphers -= {
                XmlBlockCipher.AES128_GCM, XmlBlockCipher.AES192_GCM,
                XmlBlockCipher.AES256_GCM
            }

        for cipher in supported_ciphers:
            with cast(BinaryIO,
                      (DATA_DIR /
                       'saml_response_decrypted.xml').open('rb')) as f:
                document = parse_xml(f.read())
            remove_extra_xml_whitespace(document.getroot())
            original = dump_xml(document).decode()

            # Encrypt <Assertion>
            assertion = document.find(".//{}".format(
                Q_NAMES['saml2:Assertion']))
            encrypt_xml_node(assertion, CERT_FILE, cipher,
                             XmlKeyTransport.RSA_OAEP_MGF1P)

            # <Assertion> replaced with <EncryptedData>
            self.assertIsNone(
                document.find(".//{}".format(Q_NAMES['saml2:Assertion'])))
            enc_data = document.find(".//{}/{}".format(
                Q_NAMES['saml2:EncryptedAssertion'],
                Q_NAMES['xmlenc:EncryptedData']))
            self.assertIsNotNone(enc_data)
            self.assertEqual(enc_data[0].get('Algorithm'), cipher.value)

            # Verify that the original and decrypted document match.
            self.assertEqual(decrypt_xml(document, KEY_FILE), 1)
            decrypted = dump_xml(document).decode()
            self.assertEqual(original, decrypted)
    def test_verify_xml_signatures_success(self):
        with cast(TextIO, (DATA_DIR / 'signed_response.xml').open('r')) as f:
            tree = parse_xml(f.read())

        remove_extra_xml_whitespace(
            tree)  # Reverts pretty printing applied after signing
        verify_xml_signatures(tree, CERT_FILE)
    def test_verify_response_without_assertion(self):
        with cast(TextIO,
                  (DATA_DIR / 'signed_failed_response.xml').open('r')) as f:
            tree = parse_xml(f.read())
            remove_extra_xml_whitespace(tree)

        response = SAMLResponse(tree)
        response.verify_response(CERT_FILE)
    def test_get_saml_response_invalid_signature(self):
        with cast(TextIO, (DATA_DIR / 'signed_response_and_assertion.xml').open('r')) as f:
            tree = parse_xml(f.read())
        remove_extra_xml_whitespace(tree)
        saml_response_encoded = b64encode(dump_xml(tree, pretty_print=False)).decode('ascii')

        view = IdentityProviderResponseView()
        view.request = self.factory.post(self.url, {'SAMLResponse': saml_response_encoded})
        self.assertRaises(SecurityError, view.get_saml_response, None, WRONG_CERT_FILE)
    def load_saml_request(self, signed=False) -> Tuple[str, str]:
        path = 'saml_request.xml' if not signed else 'saml_request_signed.xml'
        with cast(BinaryIO, (DATA_DIR / path).open('rb')) as f:
            saml_request_pretty = f.read()

        saml_request = parse_xml(saml_request_pretty)
        remove_extra_xml_whitespace(saml_request)
        saml_request_encoded = b64encode(
            dump_xml(saml_request, pretty_print=False))
        return saml_request_pretty.decode(
            'utf-8'), saml_request_encoded.decode('ascii')
 def test_remove_extra_xml_whitespace_text_preserved(self):
     for text in ' abc', 'abc':
         with self.subTest(text=text):
             root, child, grandchild = self.create_tree(text)
             remove_extra_xml_whitespace(root)
             self.assertEqual(root.text, text)
             self.assertEqual(root.tail, text)
             self.assertEqual(child.text, text)
             self.assertEqual(child.tail, text)
             self.assertEqual(grandchild.text, text)
             self.assertEqual(grandchild.tail, text)
 def test_remove_extra_xml_whitespace_leaf_node_text_empty(self):
     for text in None, '':
         with self.subTest(text=text):
             root, child, grandchild = self.create_tree(' ')
             grandchild.text = text
             remove_extra_xml_whitespace(root)
             self.assertIsNone(root.text)
             self.assertIsNone(root.tail)
             self.assertIsNone(child.text)
             self.assertIsNone(child.tail)
             self.assertIsNone(grandchild.text)
             self.assertIsNone(grandchild.tail)
 def test_remove_extra_xml_whitespace_various_whitespace(self):
     for space in None, '', ' ', ' \n\t':
         with self.subTest(space=space):
             root, child, grandchild = self.create_tree(space)
             grandchild.text = None
             remove_extra_xml_whitespace(root)
             self.assertIsNone(root.text)
             self.assertIsNone(root.tail)
             self.assertIsNone(child.text)
             self.assertIsNone(child.tail)
             self.assertIsNone(grandchild.text)
             self.assertIsNone(grandchild.tail)
    def test_get_saml_response_signed_and_encrypted(self):
        with cast(TextIO, (DATA_DIR / 'nia_test_response.xml').open('r')) as f:
            tree = parse_xml(f.read())
        remove_extra_xml_whitespace(tree)
        saml_response_encoded = b64encode(dump_xml(tree, pretty_print=False)).decode('ascii')
        view = IdentityProviderResponseView()
        view.request = self.factory.post(self.url, {'SAMLResponse': saml_response_encoded, 'RelayState': 'relay123'})
        saml_response = view.get_saml_response(KEY_FILE, NIA_CERT_FILE)
        self.assertEqual(saml_response.relay_state, 'relay123')

        with cast(TextIO, (DATA_DIR / 'nia_test_response_decrypted_verified.xml').open('r')) as f:
            decrypted_verified_xml = f.read()
        self.assertXMLEqual(dump_xml(saml_response.document).decode('utf-8'), decrypted_verified_xml)
 def test_remove_extra_xml_whitespace_leaf_node_whitespace_text_preserved(
         self):
     for text in ' ', ' \t\n':
         with self.subTest(text=text):
             root, child, grandchild = self.create_tree(' ')
             grandchild.text = text
             remove_extra_xml_whitespace(root)
             self.assertIsNone(root.text)
             self.assertIsNone(root.tail)
             self.assertIsNone(child.text)
             self.assertIsNone(child.tail)
             self.assertEqual(grandchild.text, text)
             self.assertIsNone(grandchild.tail)
    def test_get_saml_response_signed(self):
        with cast(TextIO, (DATA_DIR / 'signed_response_and_assertion.xml').open('r')) as f:
            tree = parse_xml(f.read())
        remove_extra_xml_whitespace(tree)
        saml_response_encoded = b64encode(dump_xml(tree, pretty_print=False)).decode('ascii')

        view = IdentityProviderResponseView()
        view.request = self.factory.post(self.url, {'SAMLResponse': saml_response_encoded, 'RelayState': 'relay123'})
        saml_response = view.get_saml_response(None, CERT_FILE)
        self.assertEqual(saml_response.relay_state, 'relay123')

        root = Element(Q_NAMES['saml2p:Response'], {'ID': 'id-response'},
                       nsmap={'saml2': EIDAS_NAMESPACES['saml2'], 'saml2p': EIDAS_NAMESPACES['saml2p']})
        assertion = SubElement(root, Q_NAMES['saml2:Assertion'], {'ID': 'id-0uuid4'})
        SubElement(assertion, Q_NAMES['saml2:Issuer']).text = 'Test Issuer'
        self.assertXMLEqual(dump_xml(saml_response.document).decode('utf-8'), dump_xml(root).decode('utf-8'))
 def test_post_with_signed_saml_response(self):
     with cast(TextIO,
               (DATA_DIR /
                'signed_response_and_assertion.xml').open('r')) as f:
         tree = parse_xml(f.read())
     remove_extra_xml_whitespace(tree)
     saml_response_encoded = b64encode(dump_xml(
         tree, pretty_print=False)).decode('ascii')
     response = self.client.post(self.url,
                                 {'SAMLResponse': saml_response_encoded})
     self.assertIn(
         '\n  <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">',
         response.context['saml_response'])
     self.assertIn(
         '\n    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">',
         response.context['saml_response'])
     self.assertEqual(response.context['relay_state'], 'None')
     self.assertContains(response, '<code>None</code>')
     self.assertContains(response,
                         '<pre style="white-space: pre-wrap;">&lt;?xml')