Beispiel #1
0
    def test_issuer_in_response_and_assertion(self):
        # https://github.com/italia/spid-testenv2/issues/145
        response = create_response(
            {
                'response': {
                    'attrs': {
                        'in_response_to': 'test_12345',
                        'destination': 'http://some.dest.nation'
                    }
                },
                'issuer': {
                    'attrs': {
                        'name_qualifier': 'http://test_id.entity',
                    },
                    'text': 'http://test_id.entity'
                },
                'name_id': {
                    'attrs': {
                        'name_qualifier': 'http://test_id.entity',
                    }
                },
                'subject_confirmation_data': {
                    'attrs': {
                        'recipient': 'http://test_id.entity',
                    }
                },
                'audience': {
                    'text': 'http://test_sp_id.entity',
                },
                'authn_context_class_ref': {
                    'text': SPID_LEVEL_1
                }
            }, {'status_code': STATUS_SUCCESS}, {})
        issuers = response._element.findall('.//{%s}Issuer' % SAML)
        self.assertEqual(len(issuers), 2)
        for issuer in issuers:
            self.assertEqual(issuer.get('NameQualifier'),
                             'http://test_id.entity')
            self.assertEqual(issuer.text, 'http://test_id.entity')
        self.assertEqual(issuers[0].getparent().tag, '{%s}Response' % SAMLP)
        self.assertEqual(issuers[1].getparent().tag, '{%s}Assertion' % SAML)

        self.assertTrue(
            validate_xml(response.to_xml(),
                         'testenv/xsd/saml-schema-protocol-2.0.xsd'),
            "The resulting XML is invalid")
Beispiel #2
0
 def test_session_index_in_response(self):
     # https://github.com/italia/spid-testenv2/issues/160
     response = create_response(
         {
             'response': {
                 'attrs': {
                     'in_response_to': 'test_8989',
                     'destination': 'http://des.nation'
                 }
             },
             'issuer': {
                 'attrs': {
                     'name_qualifier': 'http://test_id2.entity',
                 },
                 'text': 'http://test_id2.entity'
             },
             'name_id': {
                 'attrs': {
                     'name_qualifier': 'http://test_id2.entity',
                 }
             },
             'subject_confirmation_data': {
                 'attrs': {
                     'recipient': 'http://test_id2.entity',
                 }
             },
             'audience': {
                 'text': 'http://test_sp_id2.entity',
             },
             'authn_context_class_ref': {
                 'text': SPID_LEVEL_1
             }
         }, {'status_code': STATUS_SUCCESS}, {})
     authn_statements = response._element.findall('.//{%s}AuthnStatement' %
                                                  SAML)
     self.assertEqual(len(authn_statements), 1)
     authn_statement = authn_statements[0]
     self.assertTrue(authn_statement.get('SessionIndex', False))
     self.assertTrue(
         validate_xml(response.to_xml(),
                      'testenv/xsd/saml-schema-protocol-2.0.xsd'),
         "The resulting XML is invalid")
Beispiel #3
0
 def test_no_authenticating_authority_in_assertion(self):
     # See issue https://github.com/italia/spid-testenv2/issues/68
     response = create_response(
         {
             'response': {
                 'attrs': {
                     'in_response_to': 'test_12345',
                     'destination': 'http://some.dest.nation'
                 }
             },
             'issuer': {
                 'attrs': {
                     'name_qualifier': 'http://test_id.entity',
                 },
                 'text': 'http://test_id.entity'
             },
             'name_id': {
                 'attrs': {
                     'name_qualifier': 'http://test_id.entity',
                 }
             },
             'subject_confirmation_data': {
                 'attrs': {
                     'recipient': 'http://test_id.entity',
                 }
             },
             'audience': {
                 'text': 'http://test_sp_id.entity',
             },
             'authn_context_class_ref': {
                 'text': SPID_LEVEL_1
             }
         }, {'status_code': STATUS_SUCCESS}, {})
     authenticating_authorities = response._element.findall(
         './/{%s}AuthenticatingAuthority' % SAML)
     self.assertEqual(len(authenticating_authorities), 0)
Beispiel #4
0
    def login(self):
        """
        Login endpoint (verify user credentials)
        """

        key = from_session('request_key')
        relay_state = from_session('relay_state')
        logger.info('Request key: {}'.format(key))
        if key and key in self.ticket:
            authn_request = self.ticket[key]
            sp_id = authn_request.issuer.text.strip()
            destination = self.get_destination(authn_request, sp_id)
            authn_context = authn_request.requested_authn_context
            spid_level = authn_context.authn_context_class_ref.text.strip()
            if request.method == 'GET':
                # inject extra data in form login based on spid level
                extra_challenge = self._verify_spid(level=spid_level,
                                                    **{'key': key})
                rendered_form = render_template(
                    'login.html', **{
                        'action':
                        url_for('login'),
                        'request_key':
                        key,
                        'relay_state':
                        relay_state,
                        'extra_challenge':
                        extra_challenge,
                        'show_response_options':
                        self._config.show_response_options,
                    })
                return rendered_form, 200

            if 'confirm' in request.form:
                # verify optional challenge based on spid level
                verified = self._verify_spid(level=spid_level,
                                             verify=True,
                                             **{
                                                 'key': key,
                                                 'data': request.form
                                             })
                if verified:
                    # verify user credentials
                    user_id, user = self.user_manager.get(
                        request.form['username'], request.form['password'],
                        sp_id)
                    if user_id is not None:
                        # setup response
                        _audience = sp_id
                        _destination = destination
                        _recipient_subj = destination
                        _issuer_text = self._config.entity_id
                        _pkey = self._config.idp_key
                        _cert = self._config.idp_certificate
                        # setup custom response elements (if any)
                        wrong_destination = request.form.get(
                            'wrong_destination', False)
                        if wrong_destination:
                            _destination = '{}wrong/bad/'.format(_destination)
                        wrong_relay_state = request.form.get(
                            'wrong_relay_state', False)
                        if wrong_relay_state:
                            relay_state = '{}wrong'.format(relay_state)
                        wrong_audience = request.form.get(
                            'wrong_audience', False)
                        if wrong_audience:
                            _audience = '{}/wrong/bad/'.format(_audience)
                        wrong_recipient_subj = request.form.get(
                            'wrong_recipient_subj', False)
                        if wrong_recipient_subj:
                            _recipient_subj = 'badrecipient'
                        wrong_issuer = request.form.get('wrong_issuer', False)
                        if wrong_issuer:
                            _issuer_text = 'wrongissuer123'
                        has_assertion = not request.form.get(
                            'no_assertion', False)
                        bad_status_code = request.form.get(
                            'bad_status_code', False)
                        wrong_conditions_notbefore = request.form.get(
                            'wrong_conditions_notbefore')
                        wrong_conditions_notonorafter = request.form.get(
                            'wrong_conditions_notonorafter')
                        wrong_subj_notonorafter = request.form.get(
                            'wrong_subj_notonorafter')
                        wrong_subj_inresponseto = request.form.get(
                            'wrong_subj_inresponseto')
                        custom_spid_level = request.form.get('spid_level')
                        if custom_spid_level:
                            spid_level = self._spid_levels[int(
                                custom_spid_level)]
                        sign_assertion = request.form.get(
                            'sign_assertion', False)
                        sign_message = request.form.get('sign_message', False)

                        custom_private_key = request.form.get('private_key')
                        if custom_private_key:
                            _pkey = bytes(custom_private_key.encode('utf-8'))
                        custom_certificate = request.form.get('certificate')
                        if custom_certificate:
                            _pkey = bytes(custom_certificate.encode('utf-8'))

                        _conditions = {'conditions': {'attrs': {}}}

                        _subj_extra = {}
                        if wrong_subj_inresponseto:
                            _subj_extra[
                                'in_response_to'] = 'inresponsetowron134'

                        if wrong_conditions_notbefore:
                            _conditions['conditions']['attrs'][
                                'not_before'] = wrong_conditions_notbefore + ':00Z'
                        if wrong_conditions_notonorafter:
                            _conditions['conditions']['attrs'][
                                'not_on_or_after'] = wrong_conditions_notonorafter + ':00Z'
                        if wrong_subj_notonorafter:
                            _subj_extra[
                                'not_on_or_after'] = wrong_subj_notonorafter + ':00Z'

                        _status_code = STATUS_AUTHN_FAILED if bad_status_code else STATUS_SUCCESS

                        identity = user['attrs'].copy()
                        logger.debug('Unfiltered data: {}'.format(identity))
                        atcs_idx = getattr(
                            authn_request, 'attribute_consuming_service_index',
                            None)
                        logger.info(
                            'AttributeConsumingServiceIndex: {}'.format(
                                atcs_idx))
                        sp_metadata = self._registry.get(sp_id)
                        required = []
                        optional = []
                        if atcs_idx and sp_metadata:
                            attrs = sp_metadata.attributes(atcs_idx)
                            required = [el for el in attrs.get('required')]
                            optional = [el for el in attrs.get('optional')]

                        for attr_name, val in list(identity.items()):
                            _type = self._attribute_type(attr_name)
                            identity[attr_name] = (_type, val)

                        _identity = self._filter_attributes(
                            identity, required, optional)

                        logger.debug('Filtered data: {}'.format(_identity))

                        _response_data = {
                            'response': {
                                'attrs': {
                                    'in_response_to': authn_request.id,
                                    'destination': _destination
                                }
                            },
                            'issuer': {
                                'attrs': {
                                    'name_qualifier': self._config.entity_id,
                                },
                                'text': _issuer_text
                            },
                            'name_id': {
                                'attrs': {
                                    'name_qualifier': self._config.entity_id,
                                }
                            },
                            'subject_confirmation_data': {
                                'attrs': {
                                    'recipient': _recipient_subj
                                }
                            },
                            'audience': {
                                'text': _audience
                            },
                            'authn_context_class_ref': {
                                'text': spid_level
                            }
                        }

                        _response_data.update(_conditions)
                        if _subj_extra:
                            _response_data['subject_confirmation_data'][
                                'attrs'].update(_subj_extra)

                        response = create_response(
                            _response_data, {'status_code': _status_code},
                            _identity.copy(),
                            has_assertion=has_assertion)
                        response = sign_http_post(response.to_xml(),
                                                  _pkey,
                                                  _cert,
                                                  message=sign_message,
                                                  assertion=sign_assertion)
                        logger.info('Response: \n{}'.format(response))
                        rendered_template = render_template(
                            'form_http_post.html', **{
                                'action':
                                destination,
                                'relay_state':
                                relay_state,
                                'message':
                                base64.b64encode(response).decode('ascii'),
                                'message_type':
                                'SAMLResponse'
                            })
                        self.responses[key] = rendered_template
                        # Setup confirmation page data
                        rendered_response = render_template(
                            'confirm.html', **{
                                'destination_service':
                                sp_id,
                                'lines':
                                escape(response.decode('utf-8')).splitlines(),
                                'attrs':
                                list(_identity.keys()),
                                'action':
                                '/continue-response',
                                'request_key':
                                key,
                                'show_response_options':
                                self._config.show_response_options,
                            })
                        return rendered_response, 200
            elif 'delete' in request.form:
                error_info = get_spid_error(AUTH_NO_CONSENT)
                response = create_error_response(
                    {
                        'response': {
                            'attrs': {
                                'in_response_to': authn_request.id,
                                'destination': destination
                            }
                        },
                        'issuer': {
                            'attrs': {
                                'name_qualifier': self._config.entity_id,
                            },
                            'text': self._config.entity_id
                        },
                    }, {
                        'status_code': error_info[0],
                        'status_message': error_info[1]
                    }).to_xml()
                logger.info('Error response: \n{}'.format(response))
                response = sign_http_post(
                    response,
                    self._config.idp_key,
                    self._config.idp_certificate,
                )
                del self.ticket[key]
                rendered_template = render_template(
                    'form_http_post.html', **{
                        'action': destination,
                        'relay_state': relay_state,
                        'message': base64.b64encode(response).decode('ascii'),
                        'message_type': 'SAMLResponse'
                    })
                return rendered_template, 200
        return render_template('403.html'), 403
Beispiel #5
0
 def test_sign_http_redirect(self):
     # https://github.com/italia/spid-testenv2/issues/175
     response_xmlstr = create_response(
         {
             'response': {
                 'attrs': {
                     'in_response_to': 'test_3210',
                     'destination': 'http://redirect'
                 }
             },
             'issuer': {
                 'attrs': {
                     'name_qualifier': 'http://test_id.entity',
                 },
                 'text': 'http://test_id.entity'
             },
             'name_id': {
                 'attrs': {
                     'name_qualifier': 'http://test_id.entity',
                 }
             },
             'subject_confirmation_data': {
                 'attrs': {
                     'recipient': 'http://test_id.entity',
                 }
             },
             'audience': {
                 'text': 'http://test_sp_id.entity',
             },
             'authn_context_class_ref': {
                 'text': SPID_LEVEL_1
             }
         }, {
             'status_code': STATUS_SUCCESS
         }, {}).to_xml()
     with open(os.path.join(DATA_DIR, 'test.key'),
               'r') as fp, open(os.path.join(DATA_DIR, 'test.crt'),
                                'r') as fp2:
         pkey = fp.read().encode('utf-8')
         cert = fp2.read().encode('utf-8')
         verifier = RSA_VERIFIERS[SIG_RSA_SHA256]
         # No relay state
         url = sign_http_redirect(response_xmlstr, pkey, relay_state=None)
         query = parse_qs(url)
         self.assertIn('Signature', query)
         self.assertIn('SigAlg', query)
         self.assertNotIn('RelayState', query)
         self.assertIn('SAMLResponse', query)
         saml_response = query.get('SAMLResponse')[0]
         sig_alg = query.get('SigAlg')[0]
         signature = query.get('Signature')[0]
         signature = b64decode(signature)
         signed_data = '&'.join([
             urlencode({'SAMLResponse': saml_response}),
             urlencode({'SigAlg': sig_alg})
         ])
         signed_data = signed_data.encode('ascii')
         verified = verifier.verify(
             load_pem_x509_certificate(
                 cert, backend=default_backend()).public_key(),
             bytes(signed_data), bytes(signature))
         self.assertTrue(verified)
         # No relay state (2)
         url = sign_http_redirect(response_xmlstr, pkey, relay_state='')
         query = parse_qs(url)
         self.assertIn('Signature', query)
         self.assertIn('SigAlg', query)
         self.assertNotIn('RelayState', query)
         self.assertIn('SAMLResponse', query)
         saml_response = query.get('SAMLResponse')[0]
         sig_alg = query.get('SigAlg')[0]
         signature = query.get('Signature')[0]
         signature = b64decode(signature)
         signed_data = '&'.join([
             urlencode({'SAMLResponse': saml_response}),
             urlencode({'SigAlg': sig_alg})
         ])
         signed_data = signed_data.encode('ascii')
         verified = verifier.verify(
             load_pem_x509_certificate(
                 cert, backend=default_backend()).public_key(),
             bytes(signed_data), bytes(signature))
         self.assertTrue(verified)
         # with relay state
         url = sign_http_redirect(response_xmlstr,
                                  pkey,
                                  relay_state='somevalue')
         query = parse_qs(url)
         self.assertIn('Signature', query)
         self.assertIn('SigAlg', query)
         self.assertIn('RelayState', query)
         self.assertIn('SAMLResponse', query)
         saml_response = query.get('SAMLResponse')[0]
         sig_alg = query.get('SigAlg')[0]
         signature = query.get('Signature')[0]
         relay_state = query.get('RelayState')[0]
         signature = b64decode(signature)
         signed_data = '&'.join([
             urlencode({'SAMLResponse': saml_response}),
             urlencode({'RelayState': relay_state}),
             urlencode({'SigAlg': sig_alg})
         ])
         signed_data = signed_data.encode('ascii')
         verified = verifier.verify(
             load_pem_x509_certificate(
                 cert, backend=default_backend()).public_key(),
             bytes(signed_data), bytes(signature))
         self.assertTrue(verified)
Beispiel #6
0
 def test_sign_http_post(self):
     # https://github.com/italia/spid-testenv2/issues/169
     response_xmlstr = create_response(
         {
             'response': {
                 'attrs': {
                     'in_response_to': 'test_12345',
                     'destination': 'http://post'
                 }
             },
             'issuer': {
                 'attrs': {
                     'name_qualifier': 'http://test_id.entity',
                 },
                 'text': 'http://test_id.entity'
             },
             'name_id': {
                 'attrs': {
                     'name_qualifier': 'http://test_id.entity',
                 }
             },
             'subject_confirmation_data': {
                 'attrs': {
                     'recipient': 'http://test_id.entity',
                 }
             },
             'audience': {
                 'text': 'http://test_sp_id.entity',
             },
             'authn_context_class_ref': {
                 'text': SPID_LEVEL_1
             }
         }, {
             'status_code': STATUS_SUCCESS
         }, {}).to_xml()
     with open(os.path.join(DATA_DIR, 'test.key'),
               'r') as fp, open(os.path.join(DATA_DIR, 'test.crt'),
                                'r') as fp2:
         pkey = fp.read().encode('utf-8')
         cert = fp2.read().encode('utf-8')
         # No signature at all
         decoded_response = sign_http_post(response_xmlstr,
                                           pkey,
                                           cert,
                                           message=False,
                                           assertion=False)
         tree = etree.fromstring(decoded_response)
         signature_values = tree.findall(
             './/{http://www.w3.org/2000/09/xmldsig#}SignatureValue')
         digest_values = tree.findall(
             './/{http://www.w3.org/2000/09/xmldsig#}DigestValue')
         self.assertEqual(len(signature_values), 0)
         self.assertEqual(len(digest_values), 0)
         with pytest.raises(InvalidInput) as excinfo:  # noqa: F841
             XMLVerifier().verify(tree, x509_cert=cert)
         # Signed response
         decoded_response = sign_http_post(response_xmlstr,
                                           pkey,
                                           cert,
                                           message=True,
                                           assertion=False)
         tree = etree.fromstring(decoded_response)
         signature_values = tree.findall(
             './/{http://www.w3.org/2000/09/xmldsig#}SignatureValue')
         digest_values = tree.findall(
             './/{http://www.w3.org/2000/09/xmldsig#}DigestValue')
         self.assertEqual(len(signature_values), 1)
         self.assertEqual(len(digest_values), 1)
         XMLVerifier().verify(tree, x509_cert=cert)
         # Signed assertion
         decoded_response = sign_http_post(response_xmlstr,
                                           pkey,
                                           cert,
                                           message=False,
                                           assertion=True)
         tree = etree.fromstring(decoded_response)
         signature_values = tree.findall(
             './/{http://www.w3.org/2000/09/xmldsig#}SignatureValue')
         digest_values = tree.findall(
             './/{http://www.w3.org/2000/09/xmldsig#}DigestValue')
         self.assertEqual(len(signature_values), 1)
         self.assertEqual(len(digest_values), 1)
         XMLVerifier().verify(tree, x509_cert=cert)
         # Signed response and assertion together
         decoded_response = sign_http_post(response_xmlstr,
                                           pkey,
                                           cert,
                                           message=True,
                                           assertion=True)
         tree = etree.fromstring(decoded_response)
         signature_values = tree.findall(
             './/{http://www.w3.org/2000/09/xmldsig#}SignatureValue')
         digest_values = tree.findall(
             './/{http://www.w3.org/2000/09/xmldsig#}DigestValue')
         self.assertEqual(len(signature_values), 2)
         self.assertEqual(len(digest_values), 2)
         XMLVerifier().verify(tree, x509_cert=cert)
Beispiel #7
0
    def login(self):
        """
        Login endpoint (verify user credentials)
        """

        key = from_session('request_key')
        relay_state = from_session('relay_state')
        self.app.logger.debug('Request key: {}'.format(key))
        if key and key in self.ticket:
            authn_request = self.ticket[key]
            sp_id = authn_request.issuer.text
            destination = self.get_destination(authn_request, sp_id)
            authn_context = authn_request.requested_authn_context
            spid_level = authn_context.authn_context_class_ref.text
            if request.method == 'GET':
                # inject extra data in form login based on spid level
                extra_challenge = self._verify_spid(level=spid_level, **{'key': key})
                rendered_form = render_template(
                    'login.html',
                    **{
                        'action': url_for('login'),
                        'request_key': key,
                        'relay_state': relay_state,
                        'extra_challenge': extra_challenge
                    }
                )
                return rendered_form, 200

            if 'confirm' in request.form:
                # verify optional challenge based on spid level
                verified = self._verify_spid(
                    level=spid_level,
                    verify=True, **{
                        'key': key, 'data': request.form
                    }
                )
                if verified:
                    # verify user credentials
                    user_id, user = self.user_manager.get(
                        request.form['username'],
                        request.form['password'],
                        sp_id
                    )
                    if user_id is not None:
                        # setup response
                        identity = user['attrs'].copy()
                        self.app.logger.debug(
                            'Unfiltered data: {}'.format(identity)
                        )
                        atcs_idx = getattr(authn_request, 'attribute_consuming_service_index', None)
                        self.app.logger.debug(
                            'AttributeConsumingServiceIndex: {}'.format(
                                atcs_idx
                            )
                        )
                        if atcs_idx:
                            # TODO: Remove this pysaml2 dependency
                            attrs = self.server.wants(sp_id, atcs_idx)
                            required = [el.get('name') for el in attrs.get('required')]
                            optional = [el.get('name') for el in attrs.get('optional')]
                        else:
                            required = []
                            optional = []

                        for k, v in identity.items():
                            if k in self._spid_main_fields:
                                _type = self._spid_attributes['primary'][k]
                            else:
                                _type = self._spid_attributes['secondary'][k]
                            identity[k] = (_type, v)

                        _identity = {}
                        for _key in required:
                            _identity[_key] = identity[_key]
                        for _key in optional:
                            _identity[_key] = identity[_key]

                        self.app.logger.debug(
                            'Filtered data: {}'.format(_identity)
                        )

                        response_xmlstr = create_response(
                            {
                                'response': {
                                    'attrs': {
                                        'in_response_to': authn_request.id,
                                        'destination': destination
                                    }
                                },
                                'issuer': {
                                    'attrs': {
                                        'name_qualifier': self._config.entity_id,
                                    },
                                    'text': self._config.entity_id
                                },
                                'name_id': {
                                    'attrs': {
                                        'name_qualifier': self._config.entity_id,
                                    }
                                },

                                'subject_confirmation_data': {
                                    'attrs': {
                                        'recipient': destination
                                    }
                                },
                                'audience': {
                                    'text': sp_id
                                },
                                'authn_context_class_ref': {
                                    'text': spid_level
                                }
                            },
                            {
                                'status_code': STATUS_SUCCESS
                            },
                            _identity.copy()
                        ).to_xml()
                        response = sign_http_post(
                            response_xmlstr,
                            self._config.idp_key,
                            self._config.idp_certificate,
                        )
                        self.app.logger.debug(
                            'Response: \n{}'.format(response)
                        )
                        rendered_template = render_template(
                            'form_http_post.html',
                            **{
                                'action': destination,
                                'relay_state': relay_state,
                                'message': response,
                                'message_type': 'SAMLResponse'
                            }
                        )
                        self.responses[key] = rendered_template
                        # Setup confirmation page data
                        rendered_response = render_template(
                            'confirm.html',
                            **{
                                'destination_service': sp_id,
                                'lines': escape(
                                    response_xmlstr.decode('ascii')
                                ).splitlines(),
                                'attrs': _identity.keys(),
                                'action': '/continue-response',
                                'request_key': key
                            }
                        )
                        return rendered_response, 200
            elif 'delete' in request.form:
                error_info = get_spid_error(
                    AUTH_NO_CONSENT
                )
                response = create_error_response(
                    {
                        'response': {
                            'attrs': {
                                'in_response_to': authn_request.id,
                                'destination': destination
                            }
                        },
                        'issuer': {
                            'attrs': {
                                'name_qualifier': self._config.entity_id,
                            },
                            'text': self._config.entity_id
                        },
                    },
                    {
                        'status_code': error_info[0],
                        'status_message': error_info[1]
                    }
                ).to_xml()
                self.app.logger.debug(
                    'Error response: \n{}'.format(response)
                )
                response = sign_http_post(
                    response,
                    self._config.idp_key,
                    self._config.idp_certificate,
                )
                del self.ticket[key]
                rendered_template = render_template(
                    'form_http_post.html',
                    **{
                        'action': destination,
                        'relay_state': relay_state,
                        'message': response,
                        'message_type': 'SAMLResponse'
                    }
                )
                return rendered_template, 200
        return render_template('403.html'), 403
Beispiel #8
0
    def _build_success_response(self, user, authn_request, spid_level,
                                destination, sp_id, relay_state):
        """
        Build and return a successful response
        """

        identity = user['attrs'].copy()
        self.app.logger.debug('Unfiltered data: {}'.format(identity))
        atcs_idx = getattr(authn_request, 'attribute_consuming_service_index',
                           None)
        self.app.logger.debug(
            'AttributeConsumingServiceIndex: {}'.format(atcs_idx))
        sp_metadata = self._registry.get(sp_id)
        required = []
        optional = []
        if atcs_idx and sp_metadata:
            attrs = sp_metadata.attributes(atcs_idx)
            required = [el for el in attrs.get('required')]
            optional = [el for el in attrs.get('optional')]

        for attr_name, val in identity.items():
            _type = self._attribute_type(attr_name)
            identity[attr_name] = (_type, val)

        _identity = {}
        # TODO: refactor a bit the following snippet
        for _key in required:
            try:
                _identity[_key] = identity[_key]
            except KeyError:
                _identity[_key] = ('', self._attribute_type(_key))
        for _key in optional:
            try:
                _identity[_key] = identity[_key]
            except KeyError:
                _identity[_key] = ('', self._attribute_type(_key))

        self.app.logger.debug('Filtered data: {}'.format(_identity))

        response_xmlstr = create_response(
            {
                'response': {
                    'attrs': {
                        'in_response_to': authn_request.id,
                        'destination': destination
                    }
                },
                'issuer': {
                    'attrs': {
                        'name_qualifier': self._config.entity_id,
                    },
                    'text': self._config.entity_id
                },
                'name_id': {
                    'attrs': {
                        'name_qualifier': self._config.entity_id,
                    }
                },
                'subject_confirmation_data': {
                    'attrs': {
                        'recipient': destination
                    }
                },
                'audience': {
                    'text': sp_id
                },
                'authn_context_class_ref': {
                    'text': spid_level
                }
            }, {
                'status_code': STATUS_SUCCESS
            }, _identity.copy()).to_xml()
        response = sign_http_post(
            response_xmlstr,
            self._config.idp_key,
            self._config.idp_certificate,
        )
        rendered_template = render_template(
            'form_http_post.html', **{
                'action': destination,
                'relay_state': relay_state,
                'message': response,
                'message_type': 'SAMLResponse'
            })
        return rendered_template, response_xmlstr, _identity