Beispiel #1
0
    def get_saml_request(self, country_parameter: str,
                         cert_file: Optional[str]) -> SAMLRequest:
        """
        Extract and decrypt a SAML request from POST data.

        :param country_parameter: A parameter containing citizen country code.
        :param cert_file: The path of a certificate to verify the signature.
        :return: A SAML request.
        """
        try:
            request = SAMLRequest(
                parse_xml(
                    b64decode(
                        self.request.POST.get('SAMLRequest',
                                              '').encode('ascii'))),
                self.request.POST[country_parameter].upper(),
                self.request.POST.get('RelayState'))
        except XMLSyntaxError as e:
            raise ParseError(str(e)) from None

        LOGGER.info('[#%r] Received SAML request: id=%r, issuer=%r',
                    self.log_id, request.id, request.issuer)
        if cert_file:
            request.verify_request(cert_file)
        return request
 def test_sign_request_with_issuer(self):
     root = Element(Q_NAMES['saml2p:AuthnRequest'])
     SubElement(root, Q_NAMES['saml2:Issuer'])
     request = SAMLRequest(ElementTree(root))
     request.sign_request(**SIGNATURE_OPTIONS)
     self.assertIsNotNone(request.request_signature)
     self.assertEqual(root.index(request.request_signature), 1)
 def test_create_light_request_without_extensions(self):
     root = Element(Q_NAMES['saml2p:AuthnRequest'], nsmap=EIDAS_NAMESPACES)
     saml_request = SAMLRequest(ElementTree(root), 'CZ', 'relay123')
     expected = LightRequest(citizen_country_code='CZ',
                             relay_state='relay123',
                             requested_attributes=OrderedDict())
     self.assertEqual(saml_request.create_light_request(), expected)
 def test_str(self):
     self.assertEqual(
         str(SAMLRequest(ElementTree(Element('root')), 'CZ', 'relay')),
         "citizen_country_code = 'CZ', relay_state = 'relay', document = "
         "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n<root/>\n"
     )
     self.assertEqual(
         str(SAMLRequest(None, None, None)),
         'citizen_country_code = None, relay_state = None, document = None')
 def test_sign_request_already_exists(self):
     root = Element(Q_NAMES['saml2p:AuthnRequest'])
     signature = SubElement(root, Q_NAMES['ds:Signature'])
     SubElement(root, Q_NAMES['saml2:Issuer'])
     request = SAMLRequest(ElementTree(root))
     with self.assertRaisesMessage(SecurityError,
                                   'Request signature already exists.'):
         request.sign_request(**SIGNATURE_OPTIONS)
     self.assertIs(request.request_signature, signature)
    def test_create_light_request_success(self):
        self.maxDiff = None
        with cast(TextIO, (DATA_DIR / 'saml_request.xml').open('r')) as f:
            data = f.read()

        saml_request = SAMLRequest(parse_xml(data), 'CA', 'relay123')
        self.assertEqual(
            saml_request.create_light_request().get_data_as_dict(),
            LIGHT_REQUEST_DICT)
    def test_from_light_request_invalid_id(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 = '0day'

        with self.assert_validation_error(
                'id', "Light request id is not a valid XML id: '0day'"):
            SAMLRequest.from_light_request(
                request, 'test/destination',
                datetime(2017, 12, 11, 14, 12, 5, 148000))
    def test_create_light_request_extra_elements(self):
        self.maxDiff = None
        with cast(TextIO, (DATA_DIR / 'saml_request.xml').open('r')) as f:
            document = parse_xml(f.read())

        SubElement(document.getroot(), 'extra').text = 'extra'
        SubElement(
            document.find(".//{}".format(
                Q_NAMES['eidas:RequestedAttributes'])), 'extra').text = 'extra'

        saml_request = SAMLRequest(document, 'CA', 'relay123')
        self.assertEqual(
            saml_request.create_light_request().get_data_as_dict(),
            LIGHT_REQUEST_DICT)
 def test_request_signature_not_exists(self):
     root = Element(Q_NAMES['saml2p:AuthnRequest'])
     # Booby trap
     SubElement(SubElement(root, Q_NAMES['saml2:Issuer']),
                Q_NAMES['ds:Signature'])
     # No signature must be found
     self.assertIsNone(SAMLRequest(ElementTree(root)).request_signature)
Beispiel #10
0
    def post(self, request: HttpRequest) -> HttpResponse:
        """Handle a HTTP POST request."""
        try:
            preset = PRESETS[int(request.POST.get('Request', ''))]
        except (ValueError, KeyError):
            return HttpResponseBadRequest()

        light_request = LightRequest(
            id=create_xml_uuid(),
            issuer=CONNECTOR_SETTINGS.service_provider['request_issuer'],
            level_of_assurance=LevelOfAssurance.LOW,
            provider_name="Demo Service Provider",
            sp_type=ServiceProviderType.PUBLIC,
            relay_state=request.POST.get('RelayState') or None,
            origin_country_code='EU',
            citizen_country_code=request.POST.get('Country'),
            name_id_format=preset.id_format,
            requested_attributes={name: [] for name in preset.attributes}
        )
        if not light_request.citizen_country_code:
            # Use a placeholder to get through light request validation.
            light_request.citizen_country_code = COUNTRY_PLACEHOLDER
        self.saml_request = SAMLRequest.from_light_request(light_request, '/dest', datetime.utcnow())
        signature_options = CONNECTOR_SETTINGS.service_provider['response_signature']
        if signature_options and signature_options.get('key_file') and signature_options.get('cert_file'):
            self.saml_request.sign_request(**signature_options)
        return self.get(request)
Beispiel #11
0
    def create_saml_request(
            self, issuer: str,
            signature_options: Optional[Dict[str, str]]) -> SAMLRequest:
        """
        Create a SAML request from a light request.

        :param issuer: Issuer of the SAML request.
        :param signature_options: Optional options to create a signed request: `key_file`, `cert_file`.
        `signature_method`, abd `digest_method`.
        :return: A SAML request.
        """
        # Replace the original issuer with our issuer registered at the Identity Provider.
        self.light_request.issuer = issuer

        destination = self.request.build_absolute_uri(
            reverse('identity-provider-response'))
        saml_request = SAMLRequest.from_light_request(self.light_request,
                                                      destination,
                                                      datetime.utcnow())
        LOGGER.info('[#%r] Created SAML request: id=%r, issuer=%r',
                    self.log_id, saml_request.id, saml_request.issuer)

        if signature_options and signature_options.get(
                'key_file') and signature_options.get('cert_file'):
            saml_request.sign_request(**signature_options)
        return saml_request
 def test_create_light_request_our_issuer_set(self):
     saml_request_xml, _saml_request_encoded = self.load_saml_request()
     view = ServiceProviderRequestView()
     view.saml_request = SAMLRequest(parse_xml(saml_request_xml), 'ca',
                                     'xyz')
     light_request = view.create_light_request('test-saml-request-issuer',
                                               'test-light-request-issuer')
     self.assertEqual(light_request.issuer, 'test-light-request-issuer')
 def test_verify_request_not_found(self, signatures_mock):
     root = Element(Q_NAMES['saml2p:AuthnRequest'])
     SubElement(root, Q_NAMES['ds:Signature'])
     signatures_mock.return_value = [
         SignatureInfo(Element(Q_NAMES['ds:Signature']), (root, ))
     ]
     with self.assertRaisesMessage(SecurityError, 'Signature not found'):
         SAMLRequest(ElementTree(root)).verify_request('cert.pem')
     self.assertEqual(signatures_mock.mock_calls, [call(root, 'cert.pem')])
 def test_create_light_request_wrong_issuer(self):
     saml_request_xml, _saml_request_encoded = self.load_saml_request()
     view = ServiceProviderRequestView()
     view.saml_request = SAMLRequest(parse_xml(saml_request_xml), 'ca',
                                     'xyz')
     with self.assertRaisesMessage(SecurityError,
                                   'Invalid SAML request issuer'):
         view.create_light_request('wrong-saml-issuer',
                                   'test-light-request-issuer')
 def test_verify_request_wrong_parent(self, signatures_mock):
     root = Element(Q_NAMES['saml2p:AuthnRequest'])
     signature = SubElement(root, Q_NAMES['ds:Signature'])
     signatures_mock.return_value = [
         SignatureInfo(signature, (Element('whatever'), ))
     ]
     with self.assertRaisesMessage(
             SecurityError, 'Signature does not reference parent element'):
         SAMLRequest(ElementTree(root)).verify_request('cert.pem')
     self.assertEqual(signatures_mock.mock_calls, [call(root, 'cert.pem')])
    def test_create_light_request_missing_attribute_name(self):
        root = Element(Q_NAMES['saml2p:AuthnRequest'], nsmap=EIDAS_NAMESPACES)
        extensions = SubElement(root, Q_NAMES['saml2p:Extensions'])
        attributes = SubElement(extensions,
                                Q_NAMES['eidas:RequestedAttributes'])
        SubElement(attributes, Q_NAMES['eidas:RequestedAttribute'])

        saml_request = SAMLRequest(ElementTree(root), 'CZ', 'relay123')
        self.assert_validation_error(
            '<saml2p:AuthnRequest><saml2p:Extensions><eidas:RequestedAttributes><eidas:RequestedAttribute>',
            "Missing attribute 'Name'", saml_request.create_light_request)
    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_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_create_light_request_invalid_root_element(self):
     root = Element('wrongRoot')
     saml_request = SAMLRequest(ElementTree(root), 'CZ', 'relay123')
     self.assert_validation_error('<wrongRoot>',
                                  "Wrong root element: 'wrongRoot'",
                                  saml_request.create_light_request)
 def test_verify_request(self, signatures_mock):
     root = Element(Q_NAMES['saml2p:AuthnRequest'])
     signature = SubElement(root, Q_NAMES['ds:Signature'])
     signatures_mock.return_value = [SignatureInfo(signature, (root, ))]
     SAMLRequest(ElementTree(root)).verify_request('cert.pem')
     self.assertEqual(signatures_mock.mock_calls, [call(root, 'cert.pem')])
 def test_issuer_none(self):
     root = Element(Q_NAMES['saml2p:AuthnRequest'], nsmap=EIDAS_NAMESPACES)
     request = SAMLRequest(ElementTree(root), 'CZ', 'relay123')
     self.assertIsNone(request.issuer)
 def test_issuer(self):
     root = Element(Q_NAMES['saml2p:AuthnRequest'], nsmap=EIDAS_NAMESPACES)
     SubElement(root, Q_NAMES['saml2:Issuer']).text = 'test-issuer'
     request = SAMLRequest(ElementTree(root), 'CZ', 'relay123')
     self.assertEqual(request.issuer, 'test-issuer')
 def test_id(self):
     root = Element(Q_NAMES['saml2p:AuthnRequest'], {'ID': 'test-id'},
                    nsmap=EIDAS_NAMESPACES)
     request = SAMLRequest(ElementTree(root), 'CZ', 'relay123')
     self.assertEqual(request.id, 'test-id')
 def test_verify_request_none(self, signatures_mock):
     root = Element(Q_NAMES['saml2p:AuthnRequest'])
     with self.assertRaisesMessage(SecurityError,
                                   'Signature does not exist'):
         SAMLRequest(ElementTree(root)).verify_request('cert.pem')
     self.assertEqual(signatures_mock.mock_calls, [])