def test_decrypt_xml_with_document_not_encrypted(self):
     with cast(BinaryIO, (DATA_DIR / 'saml_response.xml').open('rb')) as f:
         document = parse_xml(f.read())
     expected = dump_xml(document).decode('utf-8')
     self.assertEqual(decrypt_xml(document, KEY_FILE), 0)
     actual = dump_xml(document).decode('utf-8')
     self.assertXMLEqual(expected, actual)
Esempio n. 2
0
    def get_context_data(self, **kwargs) -> dict:
        """Adjust template context data."""
        context = super().get_context_data(**kwargs)

        if self.saml_request:
            context['connector_endpoint'] = reverse('country-selector')
            encoded_saml_request = b64encode(
                dump_xml(self.saml_request.document,
                         pretty_print=False)).decode('ascii')
            context['saml_request'] = encoded_saml_request
            context['saml_request_xml'] = dump_xml(
                self.saml_request.document).decode('ascii')
            relay_state = self.saml_request.relay_state
            country = self.saml_request.citizen_country_code
        else:
            country = None
            relay_state = None

        context['presets'] = [(i, preset.label)
                              for i, preset in enumerate(PRESETS)]
        context['relay_state'] = relay_state or ''
        context[
            'country'] = country if country and country != COUNTRY_PLACEHOLDER else ''
        context['country_parameter'] = CONNECTOR_SETTINGS.service_provider[
            'country_parameter']
        return context
    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_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)
Esempio n. 5
0
 def test_serialize_attributes_not_empty(self):
     root = Element('root')
     serialize_attributes(root, 'tagName', OrderedDict([
         ('name1', ['value1', 'value2']),
         ('name2', []),
     ]))
     expected = Element('root')
     attributes = SubElement(expected, 'tagName')
     attribute = SubElement(attributes, 'attribute')
     SubElement(attribute, 'definition').text = 'name1'
     SubElement(attribute, 'value').text = 'value1'
     SubElement(attribute, 'value').text = 'value2'
     attribute = SubElement(attributes, 'attribute')
     SubElement(attribute, 'definition').text = 'name2'
     self.assertEqual(dump_xml(root).decode('utf-8'), dump_xml(expected).decode('utf-8'))
 def test_export_xml_ok(self):
     self.maxDiff = None
     user = XMLUser(name=XMLName(first_name='John', last_name='Doe'),
                    superuser=True,
                    department=Department.TECH_DEP)
     self.assertEqual(
         dump_xml(user.export_xml()).decode('utf8'), XML_USER_DATA)
    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'))
Esempio n. 8
0
    def test_export_xml_full_sample(self):
        self.maxDiff = None

        with cast(BinaryIO, (DATA_DIR / 'light_request.xml').open('rb')) as f:
            data = f.read()
            request = LightRequest.load_xml(parse_xml(data))
        self.assertEqual(dump_xml(request.export_xml()), data)
    def test_post_success(self):
        self.maxDiff = None
        request = LightRequest(**LIGHT_REQUEST_DICT)
        self.cache_mock.get_and_remove.return_value = dump_xml(request.export_xml()).decode('utf-8')

        token, encoded = self.get_token()
        response = self.client.post(self.url, {'test_token': encoded})

        # Context
        self.assertIn('saml_request', response.context)
        self.assertEqual(response.context['identity_provider_endpoint'],
                         'https://test.example.net/identity-provider-endpoint')
        self.assertEqual(response.context['relay_state'], 'relay123')
        self.assertEqual(response.context['error'], None)

        # SAML Request
        saml_request_xml = b64decode(response.context['saml_request'].encode('utf-8')).decode('utf-8')
        self.assertIn(request.id, saml_request_xml)  # light_request.id preserved
        self.assertIn('<saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">'
                      'https://test.example.net/saml/idp.xml</saml2:Issuer>', saml_request_xml)
        self.assertIn('Destination="http://testserver/IdentityProviderResponse"', saml_request_xml)
        self.assertIn('<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo>', saml_request_xml)

        # Rendering
        self.assertContains(response, 'Redirect to Identity Provider is in progress')
        self.assertContains(response,
                            '<form class="auto-submit" action="https://test.example.net/identity-provider-endpoint"')
        self.assertContains(response, '<input type="hidden" name="SAMLRequest" value="{}"'.format(
            response.context['saml_request']))
        self.assertContains(response, '<input type="hidden" name="RelayState" value="relay123"/>')
        self.assertNotIn(b'An error occurred', response.content)
Esempio n. 10
0
 def test_export_xml_minimal_sample(self):
     request = LightRequest(
         citizen_country_code='CA', id='test-light-request-id',
         level_of_assurance=LevelOfAssurance.LOW, requested_attributes={})
     with cast(BinaryIO, (DATA_DIR / 'light_request_minimal.xml').open('rb')) as f:
         data = f.read()
     self.assertEqual(dump_xml(request.export_xml()), data)
    def test_sign_xml_node_without_id(self, uuid_mock):
        self.maxDiff = None
        root = Element(Q_NAMES['saml2p:Response'], nsmap=self.USED_NAMESPACES)
        assertion = SubElement(root, Q_NAMES['saml2:Assertion'])
        SubElement(assertion, Q_NAMES['saml2:Issuer']).text = 'Test Issuer'
        sign_xml_node(root, position=0, **SIGNATURE_OPTIONS)

        with cast(TextIO, (DATA_DIR / 'signed_response.xml').open('r')) as f:
            self.assertXMLEqual(dump_xml(root).decode('utf-8'), f.read())
    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 test_post_success(self):
        self.maxDiff = None
        light_response = self.get_light_response()
        self.cache_mock.get_and_remove.return_value = dump_xml(
            light_response.export_xml()).decode('utf-8')

        token, encoded = self.get_token()
        response = self.client.post(self.url, {'test_response_token': encoded})

        # Context
        self.assertEqual(response.context['error'], None)
        self.assertIn('saml_response', response.context)
        self.assertEqual(response.context['service_provider_endpoint'],
                         '/DemoServiceProviderResponse')
        self.assertEqual(response.context['relay_state'], 'relay123')

        # SAML Response
        saml_response_xml = b64decode(
            response.context['saml_response'].encode('utf-8')).decode('utf-8')
        self.assertIn(light_response.id,
                      saml_response_xml)  # light_response.id preserved
        self.assertIn('<saml2:Issuer>test-saml-response-issuer</saml2:Issuer>',
                      saml_response_xml)
        self.assertIn('Destination="/DemoServiceProviderResponse"',
                      saml_response_xml)
        # No pretty-printing - it invalidates signatures
        self.assertIn(
            '><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo>',
            saml_response_xml)
        # Response and assertion signatures
        self.assertEqual(
            saml_response_xml.count(
                '<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">'), 2)
        self.assertEqual(
            saml_response_xml.count(
                'Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"'), 2)
        self.assertEqual(
            saml_response_xml.count(
                'Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"'), 2)

        # Rendering
        self.assertContains(response,
                            'Redirect to Service Provider is in progress')
        self.assertContains(response, 'eidas_node/connector/formautosubmit.js')
        self.assertContains(
            response,
            '<form class="auto-submit" action="/DemoServiceProviderResponse"')
        self.assertContains(
            response,
            '<input type="hidden" name="SAMLResponse" value="{}"'.format(
                response.context['saml_response']))
        self.assertContains(
            response,
            '<input type="hidden" name="RelayState" value="relay123"/>')
        self.assertNotIn(b'An error occurred', response.content)
 def test_get_saml_request_valid_signature(self):
     saml_request_xml, saml_request_encoded = self.load_saml_request(
         signed=True)
     view = ServiceProviderRequestView()
     view.request = self.factory.post(self.url, {
         'SAMLRequest': saml_request_encoded,
         'country_param': 'ca'
     })
     saml_request = view.get_saml_request('country_param', CERT_FILE)
     self.assertXMLEqual(
         dump_xml(saml_request.document).decode('utf-8'), saml_request_xml)
    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_get_saml_request_without_relay_state(self):
     saml_request_xml, saml_request_encoded = self.load_saml_request()
     view = ServiceProviderRequestView()
     view.request = self.factory.post(self.url, {
         'SAMLRequest': saml_request_encoded,
         'country_param': 'ca'
     })
     saml_request = view.get_saml_request('country_param', None)
     self.assertXMLEqual(
         dump_xml(saml_request.document).decode('utf-8'), saml_request_xml)
     self.assertEqual(saml_request.citizen_country_code, 'CA')
     self.assertEqual(saml_request.relay_state, None)
    def test_from_light_request(self):
        self.maxDiff = None
        saml_request = SAMLRequest.from_light_request(
            LightRequest(**LIGHT_REQUEST_DICT), 'test/destination',
            datetime(2017, 12, 11, 14, 12, 5, 148000))

        with cast(TextIO, (DATA_DIR / 'saml_request.xml').open('r')) as f2:
            data = f2.read()
        self.assertXMLEqual(
            dump_xml(saml_request.document).decode('utf-8'), data)
        self.assertEqual(saml_request.relay_state, 'relay123')
        self.assertEqual(saml_request.citizen_country_code, 'CA')
    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)
    def test_get_saml_response_encrypted(self):
        with cast(BinaryIO, (DATA_DIR / 'saml_response_encrypted.xml').open('rb')) as f:
            saml_response_xml = f.read()

        with cast(TextIO, (DATA_DIR / 'saml_response_decrypted.xml').open('r')) as f2:
            decrypted_saml_response_xml = f2.read()

        view = IdentityProviderResponseView()
        view.request = self.factory.post(self.url, {'SAMLResponse': b64encode(saml_response_xml).decode('ascii'),
                                                    'RelayState': 'relay123'})
        saml_response = view.get_saml_response(KEY_FILE, None)
        self.assertEqual(saml_response.relay_state, 'relay123')
        self.assertXMLEqual(dump_xml(saml_response.document).decode('utf-8'), decrypted_saml_response_xml)
    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'))
    def test_get_saml_response_fix_not_needed(self):
        self.maxDiff = None

        for name in 'saml_response_failed.xml', 'saml_response.xml':
            with self.subTest(name=name):
                with cast(BinaryIO, (DATA_DIR / name).open('rb')) as f:
                    saml_response_xml = f.read()

                view = CzNiaResponseView()
                view.request = self.factory.post(self.url,
                                                 {'SAMLResponse': b64encode(saml_response_xml).decode('ascii')})
                saml_response = view.get_saml_response(None, None)
                self.assertXMLEqual(dump_xml(saml_response.document).decode('utf-8'),
                                    saml_response_xml.decode('utf-8'))
Esempio n. 22
0
    def get_context_data(self, **kwargs) -> dict:
        """Adjust template context data."""
        context = super().get_context_data(**kwargs)
        context['error'] = self.error

        if self.saml_response:
            context[
                'service_provider_endpoint'] = CONNECTOR_SETTINGS.service_provider[
                    'endpoint']
            saml_response_xml = dump_xml(self.saml_response.document,
                                         pretty_print=False)
            context['saml_response'] = b64encode(saml_response_xml).decode(
                'ascii')
            context['relay_state'] = self.saml_response.relay_state or ''
        return context
Esempio n. 23
0
    def get_context_data(self, **kwargs) -> dict:
        """Adjust template context data."""
        context = super().get_context_data(**kwargs)
        context['error'] = self.error

        if self.saml_request:
            encoded_saml_request = b64encode(
                dump_xml(self.saml_request.document,
                         pretty_print=False)).decode('ascii')
            context[
                'identity_provider_endpoint'] = PROXY_SERVICE_SETTINGS.identity_provider[
                    'endpoint']
            context['saml_request'] = encoded_saml_request
            context['relay_state'] = self.saml_request.relay_state or ''
        return context
    def test_get_light_request_success(self):
        orig_light_request = LightRequest(**LIGHT_REQUEST_DICT)
        self.cache_mock.get_and_remove.return_value = dump_xml(orig_light_request.export_xml()).decode('utf-8')
        token, encoded = self.get_token()

        view = ProxyServiceRequestView()
        view.request = self.factory.post(self.url, {'test_token': encoded})
        view.light_token = token
        view.storage = IgniteStorage('test.example.net', 1234, 'test-proxy-service-request-cache', '')

        light_request = view.get_light_request()
        self.assertEqual(light_request, orig_light_request)
        self.maxDiff = None
        self.assertEqual(self.client_mock.mock_calls,
                         [call.connect('test.example.net', 1234),
                          call.get_cache('test-proxy-service-request-cache'),
                          call.get_cache().get_and_remove('request-token-id')])
    def test_post_load_auxiliary_data(self):
        self.maxDiff = None
        light_response = self.get_light_response()
        self.cache_mock.get_and_remove.side_effect = [
            dump_xml(light_response.export_xml()).decode('utf-8'),
            "{}",
        ]

        token, encoded = self.get_token()
        response = self.client.post(self.url, {'test_response_token': encoded})
        self.assertEqual(response.status_code, 200)
        self.maxDiff = None
        self.assertSequenceEqual(self.client_mock.mock_calls[-3:], [
            call.connect('test.example.net', 1234),
            call.get_cache('aux-cache'),
            call.get_cache().get_and_remove('aux-test-saml-request-id')
        ])
Esempio n. 26
0
    def post(self, request: HttpRequest) -> HttpResponse:
        """Handle a HTTP POST request."""
        saml_response_xml = b64decode(self.request.POST.get('SAMLResponse', '').encode('ascii')).decode('utf-8')
        if saml_response_xml:
            # Verify signatures
            cert_file = (CONNECTOR_SETTINGS.service_provider['response_signature'] or {}).get('cert_file')
            if cert_file:
                response = SAMLResponse(parse_xml(saml_response_xml))
                response.verify_response(cert_file)
                response.verify_assertion(cert_file)

            # Reformat with pretty printing for display
            saml_response_xml = dump_xml(parse_xml(saml_response_xml)).decode('utf-8')

        self.saml_response = saml_response_xml
        self.relay_state = self.request.POST.get('RelayState')
        return self.get(request)
    def test_from_light_response_minimal(self):
        self.maxDiff = None
        status = Status(failure=False)
        response = self.create_light_response(True,
                                              ip_address=None,
                                              status=status,
                                              attributes={})
        saml_response = SAMLResponse.from_light_response(
            response, None, None, datetime(2017, 12, 11, 14, 12, 5, 148000),
            timedelta(minutes=5))

        with cast(
                TextIO,
            (DATA_DIR /
             'saml_response_from_light_response_minimal.xml').open('r')) as f2:
            data = f2.read()
        self.assertXMLEqual(
            dump_xml(saml_response.document).decode('utf-8'), data)
    def test_from_light_request_minimal(self):
        self.maxDiff = None

        with cast(BinaryIO,
                  (DATA_DIR / 'light_request_minimal.xml').open('rb')) as f:
            request = LightRequest.load_xml(parse_xml(f))
        request.id = 'test-saml-request-id'

        saml_request = SAMLRequest.from_light_request(
            request, 'test/destination',
            datetime(2017, 12, 11, 14, 12, 5, 148000))

        with cast(TextIO,
                  (DATA_DIR / 'saml_request_minimal.xml').open('r')) as f2:
            data = f2.read()
        self.assertXMLEqual(
            dump_xml(saml_request.document).decode('utf-8'), data)
        self.assertEqual(saml_request.relay_state, None)
        self.assertEqual(saml_request.citizen_country_code, 'CA')
    def test_from_light_response_version_mismatch(self):
        self.maxDiff = None
        status = Status(failure=True,
                        sub_status_code=SubStatusCode.VERSION_MISMATCH,
                        status_message='Oops.')
        response = self.create_light_response(False,
                                              issuer=None,
                                              ip_address=None,
                                              status=status)
        saml_response = SAMLResponse.from_light_response(
            response, None, None, datetime(2017, 12, 11, 14, 12, 5, 148000),
            timedelta(minutes=5))

        with cast(TextIO,
                  (DATA_DIR /
                   'saml_response_from_light_response_version_mismatch.xml'
                   ).open('r')) as f2:
            data = f2.read()
        self.assertXMLEqual(
            dump_xml(saml_response.document).decode('utf-8'), data)
 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')