Exemple #1
0
    def continue_response(self):
        key = request.form['request_key']
        relay_state = from_session('relay_state')
        if key and key in self.responses:
            _response = self.responses.pop(key)
            auth_req = self.ticket.pop(key)
            if 'confirm' in request.form:
                return _response, 200
            elif 'delete' in request.form:
                destination = self.get_destination(
                    auth_req, auth_req.issuer.text
                )
                error_info = get_spid_error(
                        AUTH_NO_CONSENT
                    )
                response = create_error_response(
                    {
                        'response': {
                            'attrs': {
                                'in_response_to': auth_req.id,
                                'destination': destination
                            }
                        },
                        'issuer': {
                            'attrs': {
                                'name_qualifier': 'something',
                            },
                            'text': self.server.config.entityid
                        },
                    },
                    {
                        'status_code': error_info[0],
                        'status_message': error_info[1]
                    }
                ).to_xml()
                self.app.logger.debug(
                    'Error response: \n{}'.format(response)
                )
                key_file = self.server.config.key_file
                cert_file = self.server.config.cert_file

                pkey = open(key_file, 'rb').read()
                cert = open(cert_file, 'rb').read()
                response = sign_http_post(response, pkey, cert)
                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
Exemple #2
0
 def continue_response(self):
     key = request.form['request_key']
     relay_state = from_session('relay_state')
     if key and key in self.responses:
         _response = self.responses.pop(key)
         auth_req = self.ticket.pop(key)
         if 'confirm' in request.form:
             return _response, 200
         elif 'delete' in request.form:
             destination = self.get_destination(
                 auth_req, auth_req.issuer.text
             )
             error_info = get_spid_error(
                 AUTH_NO_CONSENT
             )
             response = create_error_response(
                 {
                     'response': {
                         'attrs': {
                             'in_response_to': auth_req.id,
                             'destination': destination
                         }
                     },
                     'issuer': {
                         'attrs': {
                             'name_qualifier': 'something',
                         },
                         '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,
             )
             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
Exemple #3
0
    def _build_failed_response(self, authn_request, destination, key,
                               relay_state):
        """
        Build and return a failed response
        """

        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
Exemple #4
0
 def single_logout_service(self):
     """
     SLO endpoint
     """
     try:
         spid_request = self._parse_message(action='logout')
         issuer_name = spid_request.saml_tree.issuer.text
         _slo = self._sp_single_logout_service(issuer_name)
         if _slo is None:
             self._raise_error(
                 'Impossibile trovare un servizio di'
                 ' Single Logout per il service provider {}'.format(
                     issuer_name))
         response_binding = _slo.get('Binding')
         logger.info('Response binding: \n{}'.format(response_binding))
         destination = _slo.get('Location')
         response = create_logout_response(
             {
                 'logout_response': {
                     'attrs': {
                         'in_response_to': spid_request.saml_tree.id,
                         'destination': destination
                     }
                 },
                 'issuer': {
                     'attrs': {
                         'name_qualifier': 'something',
                     },
                     'text': self._config.entity_id
                 }
             }, {
                 'status_code': STATUS_SUCCESS
             }).to_xml()
         relay_state = spid_request.data.relay_state or ''
         if response_binding == BINDING_HTTP_POST:
             response = sign_http_post(response,
                                       self._config.idp_key,
                                       self._config.idp_certificate,
                                       message=True,
                                       assertion=False)
             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
         elif response_binding == BINDING_HTTP_REDIRECT:
             query_string = sign_http_redirect(
                 response,
                 self._config.idp_key,
                 relay_state,
             )
             location = '{}?{}'.format(destination, query_string)
             if location:
                 return redirect(location)
     except RequestParserError as err:
         self._raise_error(err.args[0])
     except SignatureVerificationError as err:
         self._raise_error(err.args[0])
     except UnknownEntityIDError as err:
         self._raise_error(err.args[0])
     except DeserializationError as err:
         return self._handle_errors(err.initial_data, err.details)
     except MetadataLoadError as err:
         self._raise_error(err.args[0])
     abort(400)
Exemple #5
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
 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)
Exemple #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
Exemple #8
0
    def single_logout_service(self):
        """
        SLO endpoint
        """

        self.app.logger.debug("req: '%s'", request)
        try:
            spid_request = self._parse_message(action='logout')
            issuer_name = spid_request.saml_tree.issuer.text
            # TODO: retrieve the following data from some custom structure
            _slo = self._sp_single_logout_service(issuer_name)
            if _slo is None:
                self._raise_error(
                    'Impossibile trovare un servizio di'\
                    ' Single Logout per il service provider {}'.format(
                        issuer_name
                    )
                )
            response_binding = _slo[0].get('binding')
            self.app.logger.debug(
                'Response binding: \n{}'.format(
                    response_binding
                )
            )
            destination = _slo[0].get('location')
            response = create_logout_response(
                {
                    'logout_response': {
                        'attrs': {
                            'in_response_to': spid_request.saml_tree.id,
                            'destination': destination
                        }
                    },
                    'issuer': {
                        'attrs': {
                            'name_qualifier': 'something',
                        },
                        'text': self.server.config.entityid
                    }
                },
                {
                    'status_code': STATUS_SUCCESS
                }
            ).to_xml()
            key_file = self.server.config.key_file
            cert_file = self.server.config.cert_file
            key = open(key_file, 'rb').read()
            cert = open(cert_file, 'rb').read()
            relay_state = spid_request.data.relay_state or ''
            if response_binding == BINDING_HTTP_POST:
                response = sign_http_post(
                    response, key, cert,
                    message=True, assertion=False
                )
                rendered_template = render_template(
                    'form_http_post.html',
                    **{
                        'action': destination,
                        'relay_state': relay_state,
                        'message': response,
                        'message_type': 'SAMLResponse'
                    }
                )
                return rendered_template, 200
            elif response_binding == BINDING_HTTP_REDIRECT:
                query_string = sign_http_redirect(response, key, relay_state)
                location = '{}?{}'.format(destination, query_string)
                if location:
                    return redirect(location)
        except RequestParserError as err:
            self._raise_error(err.args[0])
        except SignatureVerificationError as err:
            self._raise_error(err.args[0])
        except UnknownEntityIDError as err:
            self._raise_error(err.args[0])
        except DeserializationError as err:
            return self._handle_errors(err.initial_data, err.details)
        abort(400)
Exemple #9
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