Ejemplo n.º 1
0
 def test_create_light_response_no_auth_statement(self):
     root = Element(Q_NAMES['saml2p:Response'], nsmap=EIDAS_NAMESPACES)
     SubElement(root, Q_NAMES['saml2:Assertion'])
     saml = SAMLResponse(ElementTree(root))
     response = saml.create_light_response()
     self.assertIsNone(response.ip_address)
     self.assertIsNone(response.level_of_assurance)
Ejemplo n.º 2
0
    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))
Ejemplo n.º 3
0
 def test_create_light_response_failed_response(self):
     self.maxDiff = None
     with cast(BinaryIO,
               (DATA_DIR / 'saml_response_failed.xml').open('rb')) as f:
         response = SAMLResponse(parse_xml(f), 'relay123')
     self.assertEqual(response.create_light_response(),
                      self.create_light_response(False))
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
    def test_create_light_response_not_encrypted(self):
        self.maxDiff = None
        with cast(BinaryIO, (DATA_DIR / 'saml_response.xml').open('rb')) as f:
            saml_response = SAMLResponse(parse_xml(f), 'relay123')

        light_response = saml_response.create_light_response()
        self.assertEqual(light_response, self.create_light_response(True))
Ejemplo n.º 6
0
 def test_encrypt_assertion_no_assertion(self):
     root = Element(Q_NAMES['saml2p:Response'])
     response = SAMLResponse((ElementTree(root)))
     # Nothing to encrypt.
     self.assertFalse(
         response.encrypt_assertion(CERT_FILE, XmlBlockCipher.AES256_CBC,
                                    XmlKeyTransport.RSA_OAEP_MGF1P))
Ejemplo n.º 7
0
 def test_str(self):
     self.assertEqual(
         str(SAMLResponse(ElementTree(Element('root')), 'relay')),
         "relay_state = 'relay', document = <?xml version='1.0' encoding='utf-8' standalone='yes'?>\n<root/>\n"
     )
     self.assertEqual(str(SAMLResponse(None, None)),
                      'relay_state = None, document = None')
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
 def test_sign_response_without_issuer(self):
     root = Element(Q_NAMES['saml2p:Response'])
     SubElement(root, Q_NAMES['saml2:Assertion'])
     response = SAMLResponse(ElementTree(root))
     response.sign_response(**SIGNATURE_OPTIONS)
     self.assertIsNotNone(response.response_signature)
     self.assertIsNone(response.assertion_signature)
     self.assertEqual(root.index(response.response_signature), 0)
Ejemplo n.º 10
0
    def test_create_light_response_with_unsupported_sub_status(self):
        with cast(BinaryIO,
                  (DATA_DIR / 'saml_response_failed_unsupported_sub_status.xml'
                   ).open('rb')) as f:
            response = SAMLResponse(parse_xml(f), 'relay123')

        expected = self.create_light_response(False)
        expected.status.sub_status_code = None
        self.assertEqual(response.create_light_response(), expected)
Ejemplo n.º 11
0
 def test_sign_assertion_with_issuer(self):
     root = Element(Q_NAMES['saml2p:Response'])
     assertion = SubElement(root, Q_NAMES['saml2:Assertion'])
     SubElement(assertion, Q_NAMES['saml2:Issuer'])
     response = SAMLResponse(ElementTree(root))
     self.assertTrue(response.sign_assertion(**SIGNATURE_OPTIONS))
     self.assertIsNone(response.response_signature)
     self.assertIsNotNone(response.assertion_signature)
     self.assertEqual(assertion.index(response.assertion_signature), 1)
Ejemplo n.º 12
0
 def test_verify_and_remove_signature_bad_reference(self, signatures_mock):
     root = Element('root')
     signature = SubElement(root, 'signature')
     child = SubElement(root, 'child')
     signatures_mock.return_value = [SignatureInfo(signature, (child, ))]
     response = SAMLResponse(ElementTree(root))
     with self.assertRaisesMessage(
             SecurityError, 'Signature does not reference parent element'):
         response._verify_and_remove_signature(signature, 'cert.pem')
     self.assertEqual(signatures_mock.mock_calls, [call(root, 'cert.pem')])
Ejemplo n.º 13
0
 def test_verify_and_remove_signature_not_found(self, signatures_mock):
     root = Element('root')
     signature = SubElement(root, 'signature')
     SubElement(root, 'child')
     signatures_mock.return_value = [SignatureInfo(signature, (root, ))]
     response = SAMLResponse(ElementTree(root))
     with self.assertRaisesMessage(SecurityError, 'Signature not found'):
         response._verify_and_remove_signature(Element('signature2'),
                                               'cert.pem')
     self.assertEqual(signatures_mock.mock_calls, [call(root, 'cert.pem')])
Ejemplo n.º 14
0
    def test_create_light_response_with_status_version_mismatch(self):
        with cast(
                BinaryIO,
            (DATA_DIR /
             'saml_response_failed_version_mismatch.xml').open('rb')) as f:
            response = SAMLResponse(parse_xml(f), 'relay123')

        expected = self.create_light_response(False)
        expected.status.status_code = StatusCode.REQUESTER
        expected.status.sub_status_code = SubStatusCode.VERSION_MISMATCH
        self.assertEqual(response.create_light_response(), expected)
Ejemplo n.º 15
0
 def test_sign_assertion_response_signed(self):
     root = Element(Q_NAMES['saml2p:Response'])
     SubElement(root, Q_NAMES['saml2:Assertion'])
     response_signature = SubElement(root, Q_NAMES['ds:Signature'])
     response = SAMLResponse(ElementTree(root))
     with self.assertRaisesMessage(SecurityError,
                                   'response signature is already present'):
         response.sign_assertion(**SIGNATURE_OPTIONS)
     self.assertIs(response.response_signature,
                   response_signature)  # Preserved
     self.assertIsNone(response.assertion_signature)
Ejemplo n.º 16
0
 def test_sign_assertion_decrypted(self):
     root = Element(Q_NAMES['saml2p:Response'])
     assertion = SubElement(
         SubElement(root, Q_NAMES['saml2:EncryptedAssertion']),
         Q_NAMES['saml2:Assertion'])
     response_signature = SubElement(root, Q_NAMES['ds:Signature'])
     assertion_signature = SubElement(assertion, Q_NAMES['ds:Signature'])
     response = SAMLResponse(ElementTree(root))
     self.assertFalse(response.sign_assertion(**SIGNATURE_OPTIONS))
     self.assertIs(response.response_signature,
                   response_signature)  # Preserved
     self.assertIs(response.assertion_signature,
                   assertion_signature)  # Preserved
Ejemplo n.º 17
0
 def test_create_light_response_decrypted(self):
     self.maxDiff = None
     with cast(BinaryIO,
               (DATA_DIR / 'saml_response_decrypted.xml').open('rb')) as f:
         response = SAMLResponse(parse_xml(f), 'relay123')
     light_response = self.create_light_response(
         True,
         level_of_assurance=LevelOfAssurance.SUBSTANTIAL,
         ip_address='217.31.205.1',
         id='_751e557772344aa59e9e3f35d2c9f6d6',
         in_response_to_id='e399fb9b-9454-4284-831f-4aa33d83757e',
         issuer='urn:microsoft:cgg2010:fpsts')
     self.assertEqual(response.create_light_response(), light_response)
Ejemplo n.º 18
0
    def get_saml_response(self, key_file: Optional[str],
                          cert_file: Optional[str]) -> SAMLResponse:
        """
        Extract and decrypt a SAML response from POST data.

        :param key_file: An optional path to a key to decrypt the response.
        :param cert_file: An optional path to a certificate to verify the response.
        :return: A SAML response.
        """
        raw_response = b64decode(
            self.request.POST.get('SAMLResponse',
                                  '').encode('ascii')).decode('utf-8')
        LOGGER.debug('Raw SAML Response: %s', raw_response)

        try:
            response = SAMLResponse(parse_xml(raw_response),
                                    self.request.POST.get('RelayState'))
        except XMLSyntaxError as e:
            raise ParseError(str(e)) from None

        LOGGER.info(
            '[#%r] Received SAML response: id=%r, issuer=%r, in_response_to_id=%r',
            self.log_id, response.id, response.issuer,
            response.in_response_to_id)

        if cert_file:
            response.verify_response(cert_file)
        if key_file:
            response.decrypt(key_file)
        if cert_file:
            response.verify_assertion(cert_file)
        return response
Ejemplo n.º 19
0
    def test_decrypt(self):
        self.maxDiff = None
        with cast(BinaryIO,
                  (DATA_DIR / 'saml_response_decrypted.xml').open('rb')) as f:
            document_decrypted = f.read()
        with cast(BinaryIO,
                  (DATA_DIR / 'saml_response_encrypted.xml').open('rb')) as f:
            document_encrypted = f.read()

        response = SAMLResponse(parse_xml(document_encrypted))
        self.assertEqual(response.decrypt(KEY_FILE), 1)
        self.assertXMLEqual(
            dump_xml(response.document).decode('utf-8'),
            document_decrypted.decode('utf-8'))
Ejemplo n.º 20
0
 def test_create_light_response_with_extra_elements(self):
     self.maxDiff = None
     with cast(BinaryIO, (DATA_DIR / 'saml_response.xml').open('rb')) as f:
         response = SAMLResponse(parse_xml(f), 'relay123')
     SubElement(
         response.document.find(".//{}".format(
             Q_NAMES['saml2p:StatusCode'])), 'something')
     SubElement(
         response.document.find(".//{}".format(
             Q_NAMES['saml2:AuthnStatement'])), 'something')
     SubElement(
         response.document.find(".//{}".format(
             Q_NAMES['saml2:AuthnContext'])), 'something')
     self.assertEqual(response.create_light_response(),
                      self.create_light_response(True))
Ejemplo n.º 21
0
 def test_assertion_too_many(self):
     root = Element(Q_NAMES['saml2p:Response'])
     SubElement(root, Q_NAMES['saml2:Assertion'])
     SubElement(SubElement(root, Q_NAMES['saml2:EncryptedAssertion']),
                Q_NAMES['saml2:Assertion'])
     with self.assertRaisesMessage(ParseError,
                                   'Too many assertion elements'):
         SAMLResponse(ElementTree(root)).assertion
Ejemplo n.º 22
0
 def test_assertion_exists_decrypted(self):
     root = Element(Q_NAMES['saml2p:Response'])
     encrypted_assertion = SubElement(root,
                                      Q_NAMES['saml2:EncryptedAssertion'])
     decrypted_assertion = SubElement(encrypted_assertion,
                                      Q_NAMES['saml2:Assertion'])
     self.assertIs(
         SAMLResponse(ElementTree(root)).assertion, decrypted_assertion)
Ejemplo n.º 23
0
 def test_assertion_signature_not_exists(self):
     # Base structure
     root = Element(Q_NAMES['saml2p:Response'])
     assertion = SubElement(root, Q_NAMES['saml2:Assertion'])
     # Place a few signature elements as booby traps
     SubElement(root, Q_NAMES['ds:Signature'])
     SubElement(SubElement(assertion, Q_NAMES['saml2:Issuer']),
                Q_NAMES['ds:Signature'])
     self.assertIsNone(SAMLResponse(ElementTree(root)).assertion_signature)
Ejemplo n.º 24
0
    def test_encrypt_assertion_without_encrypted_assertion_elm(self):
        root = Element(Q_NAMES['saml2p:Response'])
        first_child = SubElement(root, 'FirstChild')
        assertion = SubElement(root, Q_NAMES['saml2:Assertion'])
        SubElement(assertion, Q_NAMES['saml2:Issuer']).text = 'CZ.NIC'
        third_child = SubElement(root, 'ThirdChild')
        response = SAMLResponse((ElementTree(root)))

        # Encryption happened.
        self.assertTrue(
            response.encrypt_assertion(CERT_FILE, XmlBlockCipher.AES256_CBC,
                                       XmlKeyTransport.RSA_OAEP_MGF1P))
        # Order of elements kept.
        self.assertIs(root[0], first_child)
        self.assertIs(root[2], third_child)
        # <Assertion> replaced with <EncryptedAssertion>.
        self.assertEqual(root[1].tag, Q_NAMES['saml2:EncryptedAssertion'])
        self.assertEqual(root[1][0].tag, Q_NAMES['xmlenc:EncryptedData'])
        # Make sure we can decrypt the result.
        self.assertEqual(response.decrypt(KEY_FILE), 1)
Ejemplo n.º 25
0
    def test_create_light_response_correct_id_and_issuer(self):
        self.maxDiff = None
        view = IdentityProviderResponseView()
        view.request = self.factory.post(self.url)

        with cast(TextIO, (DATA_DIR / 'saml_response.xml').open('r')) as f:
            view.saml_response = SAMLResponse(parse_xml(f.read()), 'relay123')

        light_response = view.create_light_response('test-light-response-issuer')
        self.assertEqual(light_response.id, 'test-saml-response-id')  # Preserved
        self.assertEqual(light_response.in_response_to_id, 'test-saml-request-id')  # Preserved
        self.assertEqual(light_response.issuer, 'test-light-response-issuer')  # Replaced
Ejemplo n.º 26
0
 def test_create_light_response_unrecognized_auth_context_class(self):
     root = Element(Q_NAMES['saml2p:Response'], {
         'ID': 'id',
         'InResponseTo': 'id0'
     },
                    nsmap=EIDAS_NAMESPACES)
     context_class = SubElement(
         SubElement(
             SubElement(SubElement(root, Q_NAMES['saml2:Assertion']),
                        Q_NAMES['saml2:AuthnStatement']),
             Q_NAMES['saml2:AuthnContext']),
         Q_NAMES['saml2:AuthnContextClassRef'])
     context_class.text = 'saml2:AuthnContextClassRef:unrecognized'
     saml = SAMLResponse(ElementTree(root))
     response = saml.create_light_response()
     self.assertEqual(response.id, 'id')
     self.assertEqual(response.in_response_to_id, 'id0')
     self.assertTrue(response.status.failure)
     self.assertEqual(response.status.status_code, StatusCode.RESPONDER)
     self.assertIn('saml2:AuthnContextClassRef:unrecognized',
                   response.status.status_message)
     self.assertIsNone(response.level_of_assurance)
Ejemplo n.º 27
0
    def test_from_light_response(self):
        self.maxDiff = None
        saml_response = SAMLResponse.from_light_response(
            self.create_light_response(True), 'saml-request-issuer',
            'test/destination', datetime(2017, 12, 11, 14, 12, 5, 148000),
            timedelta(minutes=5))

        with cast(TextIO,
                  (DATA_DIR /
                   'saml_response_from_light_response.xml').open('r')) as f2:
            data = f2.read()
        self.assertXMLEqual(
            dump_xml(saml_response.document).decode('utf-8'), data)
Ejemplo n.º 28
0
    def test_create_light_response_auth_context_class_alias_not_used(self):
        root = Element(Q_NAMES['saml2p:Response'], {
            'ID': 'id',
            'InResponseTo': 'id0'
        },
                       nsmap=EIDAS_NAMESPACES)
        context_class = SubElement(
            SubElement(
                SubElement(SubElement(root, Q_NAMES['saml2:Assertion']),
                           Q_NAMES['saml2:AuthnStatement']),
                Q_NAMES['saml2:AuthnContext']),
            Q_NAMES['saml2:AuthnContextClassRef'])
        context_class.text = LevelOfAssurance.HIGH.value

        saml = SAMLResponse(ElementTree(root))
        response = saml.create_light_response(
            {context_class.text: LevelOfAssurance.LOW})

        self.assertEqual(response.id, 'id')
        self.assertEqual(response.in_response_to_id, 'id0')
        self.assertFalse(response.status.failure)
        self.assertIsNone(response.status.status_code)
        self.assertEqual(response.level_of_assurance,
                         LevelOfAssurance.HIGH)  # Not overridden
Ejemplo n.º 29
0
 def test_assertion_signature_exists_decrypted(self):
     # Base structure
     root = Element(Q_NAMES['saml2p:Response'])
     encrypted_assertion = SubElement(root,
                                      Q_NAMES['saml2:EncryptedAssertion'])
     decrypted_assertion = SubElement(encrypted_assertion,
                                      Q_NAMES['saml2:Assertion'])
     # Place a few signature elements as booby traps
     SubElement(root, Q_NAMES['ds:Signature'])
     SubElement(SubElement(decrypted_assertion, Q_NAMES['saml2:Issuer']),
                Q_NAMES['ds:Signature'])
     # This one must be found
     signature = SubElement(decrypted_assertion, Q_NAMES['ds:Signature'])
     self.assertIs(
         SAMLResponse(ElementTree(root)).assertion_signature, signature)
Ejemplo n.º 30
0
 def test_response_signature_not_exists(self):
     # Base structure
     root = Element(Q_NAMES['saml2p:Response'])
     assertion = SubElement(root, Q_NAMES['saml2:Assertion'])
     encrypted_assertion = SubElement(root,
                                      Q_NAMES['saml2:EncryptedAssertion'])
     decrypted_assertion = SubElement(encrypted_assertion,
                                      Q_NAMES['saml2:Assertion'])
     # Place a few signature elements as booby traps
     SubElement(assertion, Q_NAMES['ds:Signature'])
     SubElement(decrypted_assertion, Q_NAMES['ds:Signature'])
     SubElement(SubElement(assertion, Q_NAMES['saml2:Issuer']),
                Q_NAMES['ds:Signature'])
     SubElement(SubElement(decrypted_assertion, Q_NAMES['saml2:Issuer']),
                Q_NAMES['ds:Signature'])
     # No signature must be found
     self.assertIsNone(SAMLResponse(ElementTree(root)).response_signature)