def test_authn_request_http_redirect_right_signature(self): xml_message = generate_authn_request() pkey = open(os.path.join(DATA_DIR, 'sp.key'), 'rb').read() query_string = sign_http_redirect(xml_message, pkey, req_type='SAMLRequest') self.assertEqual(len(self.idp_server.ticket), 0) self.assertEqual(len(self.idp_server.responses), 0) response = self.test_client.get('/sso-test?{}'.format(query_string), follow_redirects=True) self.assertEqual(response.status_code, 200) response_text = response.get_data(as_text=True) self.assertIn( '<form class="Form Form--spaced u-margin-bottom-l " name="login" method="post" action="/login">', response_text) self.assertEqual(len(self.idp_server.ticket), 1) self.assertEqual(len(self.idp_server.responses), 0) key = list(self.idp_server.ticket.keys())[0] xmlstr = SAMLTree(self.idp_server.ticket[key]._xml_doc) xml_message = ET.fromstring(xml_message) xml_message = SAMLTree(xml_message) self.assertEqual(xml_message.id, xmlstr.id)
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)
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)
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)