From 52b137dbddf0d3abbab9d63ab7f3473a90f79732 Mon Sep 17 00:00:00 2001 From: Rutger Prins Date: Wed, 28 Mar 2018 17:03:50 +0200 Subject: [PATCH 01/21] Enable client key passphrase for JWT token authentication request * Also add code coverage for SSL requests --- cabby/abstract.py | 33 +++----- cabby/dispatcher.py | 131 ++++++++++++++++++------------- requirements-dev.txt | 1 + tests/ssl_test_files/client.key | 30 +++++++ tests/ssl_test_files/client.pem | 23 ++++++ tests/ssl_test_files/root_ca.key | 27 +++++++ tests/ssl_test_files/root_ca.pem | 24 ++++++ tests/ssl_test_files/root_ca.srl | 1 + tests/test_ssl.py | 69 ++++++++++++++++ 9 files changed, 260 insertions(+), 79 deletions(-) create mode 100644 tests/ssl_test_files/client.key create mode 100644 tests/ssl_test_files/client.pem create mode 100644 tests/ssl_test_files/root_ca.key create mode 100644 tests/ssl_test_files/root_ca.pem create mode 100644 tests/ssl_test_files/root_ca.srl create mode 100644 tests/test_ssl.py diff --git a/cabby/abstract.py b/cabby/abstract.py index 4577a61..dc6a953 100644 --- a/cabby/abstract.py +++ b/cabby/abstract.py @@ -152,7 +152,9 @@ def prepare_generic_session(self): password=self.password if not self.jwt_url else None, cert_file=self.cert_file, key_file=self.key_file, - verify_ssl=(self.ca_cert or self.verify_ssl)) + key_password=self.key_password, + ca_cert=self.ca_cert, + verify_ssl=self.verify_ssl) def _execute_request(self, request, uri=None, service_type=None): ''' @@ -161,7 +163,6 @@ def _execute_request(self, request, uri=None, service_type=None): A service is defined by ``uri`` parameter or is chosen from pre-cached services by ``service_type``. ''' - if not uri and not service_type: raise NoURIProvidedError('URI or service_type needed') elif not uri: @@ -181,28 +182,12 @@ def _execute_request(self, request, uri=None, service_type=None): self.refresh_jwt_token(session=session) session = dispatcher.set_jwt_token(session, self.jwt_token) - if self.key_password: - # If key_password is provided - message = dispatcher.send_taxii_request( - session, - self._prepare_url(uri), - request, - taxii_binding=self.taxii_binding, - # Details in case key_password is provided - tls_details={ - 'cert_file': self.cert_file, - 'key_file': self.key_file, - 'key_password': self.key_password, - 'ca_cert': self.ca_cert - }, - timeout=self.timeout) - else: - message = dispatcher.send_taxii_request( - session, - self._prepare_url(uri), - request, - taxii_binding=self.taxii_binding, - timeout=self.timeout) + message = dispatcher.send_taxii_request( + session, + self._prepare_url(uri), + request, + taxii_binding=self.taxii_binding, + timeout=self.timeout) return message diff --git a/cabby/dispatcher.py b/cabby/dispatcher.py index b4dff46..a4de645 100644 --- a/cabby/dispatcher.py +++ b/cabby/dispatcher.py @@ -1,4 +1,5 @@ from collections import namedtuple +import json import os import ssl import sys @@ -33,8 +34,8 @@ def raise_http_error(status_code, response_stream=None): raise HTTPError(status_code) -def send_taxii_request(session, url, request, taxii_binding=None, - tls_details=None, timeout=None): +def send_taxii_request( + session, url, request, taxii_binding=None, timeout=None): ''' Send XML message to a TAXII service and parse a response. ''' @@ -50,12 +51,28 @@ def send_taxii_request(session, url, request, taxii_binding=None, url_scheme=furl.furl(url).scheme, message_binding=taxii_binding) - if tls_details and tls_details.get('key_password'): + stream, headers = request_stream(session, url, request_body, timeout) + + gen = _parse_response(stream, headers, version=request.version) + obj = next(gen) + + if obj == const.STREAM_MARKER: + return gen + elif hasattr(obj, 'status_type'): + if obj.status_type != 'SUCCESS': + raise UnsuccessfulStatusError(obj) + else: + return None + return obj + + +def request_stream(session, url, request_body, timeout, headers=None): + if session._cabby_key_password: # Workaround until # https://github.com/kennethreitz/requests/issues/2519 is fixed try: - response = get_response_using_key_pass( - url, request_body, session, timeout=timeout, **tls_details) + response = request_with_key_password( + session, url, request_body, timeout, headers) except urllib.error.HTTPError as e: log.error( "Error while connecting to {}".format(url), @@ -64,8 +81,12 @@ def send_taxii_request(session, url, request, taxii_binding=None, stream, headers = response, response.headers else: - response = session.post(url, data=request_body, stream=True, - timeout=timeout) + response = session.post( + url, + data=request_body, + stream=True, + timeout=timeout, + headers=headers) if not response.ok: raise_http_error(response.status_code, response.raw) @@ -81,17 +102,7 @@ def send_taxii_request(session, url, request, taxii_binding=None, stream = gzip.GzipFile(fileobj=stream) - gen = _parse_response(stream, headers, version=request.version) - obj = next(gen) - - if obj == const.STREAM_MARKER: - return gen - elif hasattr(obj, 'status_type'): - if obj.status_type != 'SUCCESS': - raise UnsuccessfulStatusError(obj) - else: - return None - return obj + return stream, headers def _cleanup_batch(curr_elem, batch): @@ -292,28 +303,32 @@ def __call__(self, r): return r -def get_generic_session(proxies=None, headers=None, - username=None, password=None, - cert_file=None, key_file=None, - verify_ssl=True): +def get_generic_session( + proxies=None, + headers=None, + username=None, + password=None, + cert_file=None, + key_file=None, + key_password=None, + ca_cert=None, + verify_ssl=True): session = requests.Session() - session.verify = verify_ssl - + if ca_cert: + session.verify = ca_cert + else: + session.verify = verify_ssl if proxies: session.proxies = proxies - if headers: session.headers.update(headers) - + session.headers['User-Agent'] = 'Cabby {}'.format(cabby_version) if username and password: session.auth = HTTPBasicAuth(username, password) - - session.headers['User-Agent'] = 'Cabby {}'.format(cabby_version) - if cert_file and key_file: session.cert = (cert_file, key_file) - + session._cabby_key_password = key_password return session @@ -351,27 +366,26 @@ def get_taxii_session(session, url_scheme='https', content_type=None, return session -def obtain_jwt_token(session, jwt_url, username, password): +def obtain_jwt_token(session, jwt_url, username, password, timeout=None): log.info("Obtaining JWT token from {}".format(jwt_url)) - response = session.post(jwt_url, json={ - 'username': username, - 'password': password - }) + request_data = json.dumps({'username': username, 'password': password}) + request_body = request_data.encode('utf-8') + headers = {'Content-Type': 'application/json'} - if not response.ok: - raise_http_error(response.status_code, response.raw) + stream, headers = request_stream( + session, jwt_url, request_body, timeout, headers) + response_body = stream.read().decode('utf-8') + response_data = json.loads(response_body) - body = response.json() - if 'token' not in body: - log.debug("Incorrect JWT response:\n{}".format(body)) + if 'token' not in response_data: + log.debug("Incorrect JWT response:\n{}".format(response_body)) raise ValueError("No token found in JWT auth response") - return body['token'] - + return response_data['token'] -def get_response_using_key_pass(url, data, session, cert_file, key_file, - key_password, ca_cert=None, timeout=None): +def request_with_key_password( + session, url, request_body, timeout=None, headers=None): if sys.version_info < (2, 7, 9): raise ValueError( 'Key password specification is not supported in Python < v2.7.9') @@ -379,32 +393,39 @@ def get_response_using_key_pass(url, data, session, cert_file, key_file, if session.auth: # Using Requests Session's auth handlers to fill in proper headers DummyRequest = namedtuple('DummyRequest', ['headers']) - headers = session.auth(DummyRequest(headers=session.headers)).headers + request_headers = session.auth( + DummyRequest(headers=session.headers)).headers else: - headers = session.headers + request_headers = session.headers + if headers: + request_headers.update(headers) + + # Take the TLS details from the session object and use them with urllib. + # See also 'get_generic_session' which sets many of these attributes. + # session 'verify' attribute can be a bool or a path to a CA bundle: + if not isinstance(session.verify, bool): + ca_cert = session.verify context = ssl.create_default_context( ssl.Purpose.CLIENT_AUTH, cafile=ca_cert) + cert_file, key_file = session.cert + key_password = session._cabby_key_password context.load_cert_chain(cert_file, key_file, password=key_password) - if not session.verify and not ca_cert: - context.verify_mode = ssl.CERT_NONE - elif session.verify: + if session.verify: context.verify_mode = ssl.CERT_REQUIRED - if not ca_cert: context.set_default_verify_paths() + else: + context.verify_mode = ssl.CERT_NONE handlers = [urllib.request.HTTPSHandler(context=context)] - if session.proxies: - handlers.append( - urllib.request.ProxyHandler(session.proxies)) + handlers.append(urllib.request.ProxyHandler(session.proxies)) opener = urllib.request.build_opener(*handlers) - - request = urllib.request.Request(url, data, headers) + request = urllib.request.Request(url, request_body, request_headers) if timeout: return opener.open(request, timeout=timeout) diff --git a/requirements-dev.txt b/requirements-dev.txt index bc4eb7c..8f2c2eb 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,3 +5,4 @@ pytest-cov setuptools-git zest.releaser flake8 +werkzeug diff --git a/tests/ssl_test_files/client.key b/tests/ssl_test_files/client.key new file mode 100644 index 0000000..7efab1a --- /dev/null +++ b/tests/ssl_test_files/client.key @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-256-CBC,B67E948DAB86C06E36B8BB6BA3EF27E9 + +D9JTUYv4cYUCdd9ZIs3yDdiCRr3ogsLWkXmHYiJdHmMGw/FDXSm+EkJKWPVMDDAp +hg6AaRVjtLHQwe8lPpD8COizOHgktXiWiZ1vkfz9Pyb4JHmiB6LY5zhnY8pb/2xx +2F8ZI3rbsVkIVUjeqN2PL+I8VM/FFZ2PPp4O57UnY2aDMJlscE0fvGrUrZpwGasz +CTr7S58r8w5cWqAR12J3Qw5crUxf/2YvcxJ1xlaLzVMnYUBVVTxrg6S+3jmsBq9F +fbz6GgxRSRoltSeV4WmCaeSrG4lwHx+yZLCHjaje5gijMfOuDq5MwVjS9FtiKnxq +ZnEDHFYEevBKFXwlPZYGQcDiTBuyet0n8UWpl1mXMc5gFzgEtz+QLWAc6Ws+Q9C5 ++hXnbTWY3XRMGVR2qraUa3tTVolUrBdlwvf515oiy/H67OqVhIH5EmCvumA7yvjd +PFJdWlMSpLVHGeABYiez+S9n5qk2J5lGW6jjnt0uh+cR9ZllerQbhUgkCWs81xMV +e2FbUdRRdcSbc1QNLtEMtw6PaG5Rupcni8hLyZrR9c6RVQSSKDJimGXLS/hHlEn3 +/uxbKM4kzFRLZbXiNB4kidyzVMLZePwc4FCtVzEHtmG6nKSydskCn/Q/KTMMTdb/ +6lO33v+sxuLtx7tDmBhR1WqU0/uXEklUu7HBIfEt8dKpe1ZV/ox+LwvUQjdHN01m +REMv1s+q+wGRPiKYdukvXyfphi1rXRjAUzTXwD90fGpgCfiVXdcEO/XlP5sKlfdL +sbitpCciWcVuJmqKpKH6buYATlm0bOtE6+dWrdjnMSRyGSGjQFT4BZlfuCBP1C12 +utl2jPRi44uiIVviezBvi54eplv+2OypP/muVQYebLw19H+fLtqV0m1yVtThP1cT +vURavF+ftLui87LCf7282g3y53JP7cU0QIVFruIVAPnw0Yw4rrCEptgN0tSCYv8V +mk/ACOAQ2JF69kS8+W+2MYPrwyt/Rwk7K0oWeX/tP8clp9wZ1bizGwkXgQcWiwvX +Axj+egWnGplI9EJh893uecFeEtvQf5UBtmVXgzGDcpUbZG7+VBftnEiWiOvPdGjj +/pY2FhXsmcDKFGFt3z3To6urMgVgwmq+o/viycs+VN2q45wxIFFEzY8AsIlaPtzP +q+LTGY4pyS4Lfyvt5WR+/pMyf494cdfFl6+H2gBgGOZYJzeYGehiryZWPl1CNCmF +ATEAjNZ6haEeZrXmy5YdlulBjrFkMpAbznmQdIRlTk8v/2IjpeIYecg+JkSnFM6t +r6fxTFFdZ9rlQmR3UNAcCkoQSDkN6MSWITz07ej26x+xlMSRHAVIPAwXiDrrqViR +G6cpWYp+MTzNaQvG/Yt2kcqWI7VrkoJ4DjoGlj/PkBCn+i6Q/fsIX19QsKr6wLRO +aPbDZN/2vBrjLY1DHmiYbXdVZ+oQhlTTBkGd+t/d335How4AsVPwaiMjrciDJiZz +VBs3TbytGfkRzHrIpLxgRQw52OLiPRAdA5J3ReS8pJ5dQgP17+maeC4eap18fYgR +KbqfigvYEeOofm1Lhc2rkyZEeXLq9St3Q0CN0+Gdej4G7tQbH0cITXG/2RqHeVaZ +-----END RSA PRIVATE KEY----- diff --git a/tests/ssl_test_files/client.pem b/tests/ssl_test_files/client.pem new file mode 100644 index 0000000..126a67b --- /dev/null +++ b/tests/ssl_test_files/client.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDvzCCAqcCCQDNW0hGu6KfqDANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC +TkwxFjAUBgNVBAgMDU5vcnRoLUhvbGxhbmQxEjAQBgNVBAcMCUFtc3RlcmRhbTET +MBEGA1UECgwKRWNsZWN0aWNJUTETMBEGA1UECwwKQ2FiYnkgVGVzdDETMBEGA1UE +AwwKY2FiYnktdGVzdDEjMCEGCSqGSIb3DQEJARYUY2FiYnlAZWNsZWN0aWNpcS5j +b20wHhcNMTgwMzI4MTI1NjUxWhcNMjEwMTE1MTI1NjUxWjCBpDELMAkGA1UEBhMC +TkwxFjAUBgNVBAgMDU5vcnRoLUhvbGxhbmQxEjAQBgNVBAcMCUFtc3RlcmRhbTET +MBEGA1UECgwKRWNsZWN0aWNJUTETMBEGA1UECwwKQ2FiYnkgVGVzdDEaMBgGA1UE +AwwRY2FiYnktdGVzdC1jbGllbnQxIzAhBgkqhkiG9w0BCQEWFGNhYmJ5QGVjbGVj +dGljaXEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzDmsgKh6 +xSoTxyFkjbhQvoYZAQ7oniAN5u87KssY1Qml14b55oz2PO1BdY8DS/TEYJlW3xkB +NR10KGlbqnGyR0NnDjjd4E7KF+0ZV6rlF2d50judY/cibHXUp+N/qChJOyEzCKbD +uR7hPFsRicVW1LxyM2XW5s1t6ABsCCDsuQfxvTwumSzznzCTSeJaZQMB478R0eQx +RaOry28tgOYgSDCEWQjzjPiQIt1cl/CGz3qiLDNURRQIYEHl/3RwD29AWQHuycTN +SQXGdsJPA06wv7W3XQV7n+zt8YTA5d4tS9Gt6ZYpby0tovapxFvZSatpLrBl1/mm +wzArOXp8LDRmewIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCkaNcQHZT/BgIpaVUP +qsHyh5RkE/1tQwbFNYHnWrRcWnphcwZyGX7Oj1rpF5e2+YEt3ucpqKGOECfm/QKK +H9t5nCYE2KPBIiNhtiPUljAdejP0ysNqkuSLtkDd7sVJUGlldXsJ3pVPC1jKA4/p +XlY5FjdVo5QyIPPQ0x/oS8nmxyd/fybKtdO097Gb0m8SrgAHkUHM6osmaij9mSim +UPCHClUrP9VuGml4gK0XXfOiil8+DSdmUNX9Mf020lo9JUJFnne3Xku+jWL8RvtC +9Iwx0f4Twi7m45Vsp1jIXH5zQru62TEt8n5KJ6w0EzceyqzKeeeq75VWhodG5HaU +wdt7 +-----END CERTIFICATE----- diff --git a/tests/ssl_test_files/root_ca.key b/tests/ssl_test_files/root_ca.key new file mode 100644 index 0000000..b2c590d --- /dev/null +++ b/tests/ssl_test_files/root_ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAv0Iz3k0dzt7pNsZJl7uzz15n3CLl8ig2j/lnwJaguK4bTLid +XqvvSrCWQo+v7ospCNVMUHN97sqywKFRINPRx/Oit7YXzULTTmP3GdyCsA8XdEtp +VxHkr/LdjqnnBsfP5SYem9qvO2VEnpDiHRTBvM/kouHHTNP13PqVdEBBv8RH/Bnm +FZplyfl7hgUWnU60wFqixUEF40GhahR8zXHodCYQcJCucIIRMrSooP+UOhzdm4Sv +5XOpz5ChvDU0iLz8DYVZsTgz53uiAmxmF9SerCGaWNuTe1Kz0L/NI3nYhzjyxP8o +jve4+ShvVLs1mSS/4jCdrmKvWmnfw2VQPAZwkQIDAQABAoIBAD2wCq6+zMh2mV3z +YrUv83UFDBayotwSIFcbqF0zuC8Y6PE78nYgmpPKlQxu793OwLN6qJyiQ5ILitAa +t+IQr+8AJBGBB/hKf4EfOQn8V6KVm4kE8bsFqG8ftFpZbox/Ugyk2OaICFDt/s3u +CSA8G6jcnO/0RqX/D6PwptfSCeLhyzY8FmvIa8QKSqVyw74T8lANFNri2uBQxMOv +oxZ90GABQAkA6lihBCSFUBW28y+joQadtlBgmlhXbhNOMKVmCcYBo+fAHFTr/zKr +Ayyzv5RQSb2jMv8vDpAcKZIv44ifwMIu5Se3sNNJa0Vjx1JjwYRVfCW7x/KM/OUy +fHNYzuECgYEA6DLwDmAaSTr81fihGgzQCCTAenTfrPckZR/bcjseBfW4HeASl+13 ++L0A1wUGrMulXsNKZjlsqWXLIm14jXeB3+uH6fG6jOOG3VoesxJoxJ/8CihjHaz/ +NaAkv/k3gsR9WumKNCMkVLcYgO6/9QyDadH8JwCLTmtP1kUqRAVWKXUCgYEA0tz2 +DGrKTu8rv+7XiMYsjsRA8AwUDvu3ouNMszO7DxzR+6T/MqnA6YYI1HR6bmNdA1W2 +OCiWv8csYuklxpW3O3mn3K5E2fD8irwv4lqfUdLKiYFJiO/AZanNfCtU2ATqUxFz +jtPfTVtMYc4rvEvfUiNmkpeO4wA9SVbm589wqy0CgYEA3xCoOzcpqXYolKogojTy +2Q/tdPa4NqLJ7Pf4yjlus00GTnUHh7qSyXtQxj/G1NNij7HO1HuQHwBycCV4bXcE +ZogPbrhRfa3KzvUXjvI9gb8r0tpPtZ+PIQYyp9YT9OUsg9zdbw7fhR7MRHiYIiKe +UFdxBErqiTBgZzjUzLnPt00CgYA7vEkf/2jMr8jPGNSKdMnOsB5NYr6T+A9v38Cs +y6zkfR+nkHiTW6/A+C2sMks01oVVGoUCp8z9C2mxo6cF+Qftm2tIgz4SF8kTbHiM +QWv8/CBQkAXK+1Cf/FiwIn5orAvN+JWqEq+h9IlQ891D/KKJXGoEgVcDS+AQ1MOZ +TBT5CQKBgE1qh1BUw50sfBqsLRDKrG1VXy6RYba6cJwVErjGLBLnoLNmEBCnFIhk +t+1osKVBO7ZYpFjsvF171tVvL2M1DAfum6plYnovTW+ioylGdsVRikNQkqNuWdww +PcXa9FdTFh0b6ar2Nv1+s2XVPHvVksBhjWgysn1tR1R/itgtBTQ9 +-----END RSA PRIVATE KEY----- diff --git a/tests/ssl_test_files/root_ca.pem b/tests/ssl_test_files/root_ca.pem new file mode 100644 index 0000000..7848d91 --- /dev/null +++ b/tests/ssl_test_files/root_ca.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEEjCCAvqgAwIBAgIJAMSDzbM9xjhHMA0GCSqGSIb3DQEBCwUAMIGdMQswCQYD +VQQGEwJOTDEWMBQGA1UECAwNTm9ydGgtSG9sbGFuZDESMBAGA1UEBwwJQW1zdGVy +ZGFtMRMwEQYDVQQKDApFY2xlY3RpY0lRMRMwEQYDVQQLDApDYWJieSBUZXN0MRMw +EQYDVQQDDApjYWJieS10ZXN0MSMwIQYJKoZIhvcNAQkBFhRjYWJieUBlY2xlY3Rp +Y2lxLmNvbTAeFw0xODAzMjgxMjUzNTBaFw0yMTAxMTUxMjUzNTBaMIGdMQswCQYD +VQQGEwJOTDEWMBQGA1UECAwNTm9ydGgtSG9sbGFuZDESMBAGA1UEBwwJQW1zdGVy +ZGFtMRMwEQYDVQQKDApFY2xlY3RpY0lRMRMwEQYDVQQLDApDYWJieSBUZXN0MRMw +EQYDVQQDDApjYWJieS10ZXN0MSMwIQYJKoZIhvcNAQkBFhRjYWJieUBlY2xlY3Rp +Y2lxLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9CM95NHc7e +6TbGSZe7s89eZ9wi5fIoNo/5Z8CWoLiuG0y4nV6r70qwlkKPr+6LKQjVTFBzfe7K +ssChUSDT0cfzore2F81C005j9xncgrAPF3RLaVcR5K/y3Y6p5wbHz+UmHpvarztl +RJ6Q4h0UwbzP5KLhx0zT9dz6lXRAQb/ER/wZ5hWaZcn5e4YFFp1OtMBaosVBBeNB +oWoUfM1x6HQmEHCQrnCCETK0qKD/lDoc3ZuEr+Vzqc+Qobw1NIi8/A2FWbE4M+d7 +ogJsZhfUnqwhmljbk3tSs9C/zSN52Ic48sT/KI73uPkob1S7NZkkv+Iwna5ir1pp +38NlUDwGcJECAwEAAaNTMFEwHQYDVR0OBBYEFJLjDK/tYibETbI4wZPcilqVmvZ1 +MB8GA1UdIwQYMBaAFJLjDK/tYibETbI4wZPcilqVmvZ1MA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQELBQADggEBAJg7OTXhEmmbCbYEuZJmvwYXQFysSPfFQSG3 +v5o1/vB50hUY/8ghukuCuo960tRfJxvw2VdefwuXK9O2LmdqwRmUxeWlsmWlIsL3 +CYukUtsWvjk4Nwr119kKSqBCa5pd7dSU2S25DxHhVcYlDp7BvTx3ix0miD+buhOx +T+fgwvA18Y+M4JyELNLLV2fB+vNqjZnbGyDw/nXBYfu2CgHWxzlpjHw+8LEIwZx5 +c/dyqMqojmISVnYE+Fya4aiVwKLkky38ox6CXfL5smSHRbgqbRdWqQT+ZAnscsWh +FO9naXA5aIBWY800uX9iErke729QkegJv15lS7mOHmeTPWoGUtk= +-----END CERTIFICATE----- diff --git a/tests/ssl_test_files/root_ca.srl b/tests/ssl_test_files/root_ca.srl new file mode 100644 index 0000000..670bfe2 --- /dev/null +++ b/tests/ssl_test_files/root_ca.srl @@ -0,0 +1 @@ +CD5B4846BBA29FA8 diff --git a/tests/test_ssl.py b/tests/test_ssl.py new file mode 100644 index 0000000..af692ee --- /dev/null +++ b/tests/test_ssl.py @@ -0,0 +1,69 @@ +import socket +import threading + +import pytest +import werkzeug.serving + +import cabby +import fixtures11 + + +@pytest.fixture +def httpsserver(request): + port = get_free_port() + server_key = 'tests/ssl_test_files/root_ca.key' + server_cert = 'tests/ssl_test_files/root_ca.pem' + server = werkzeug.serving.make_server( + host='127.0.0.1', + port=port, + app=remote_taxii_app, + passthrough_errors=True, + ssl_context=(server_cert, server_key)) + server_thread = threading.Thread(target=server.serve_forever) + server_thread.start() + request.addfinalizer(server.shutdown) + return server + + +def get_free_port(): + s = socket.socket() + s.bind(('', 0)) + port = s.getsockname()[1] + s.close() + return port + + +def remote_taxii_app(env, start_response): + if env['PATH_INFO'] == '/auth': + start_response('200 OK', [('Content-Type', 'application/json')]) + return [b'{"token": "123"}'] + elif env['PATH_INFO'] == fixtures11.DISCOVERY_PATH: + taxii_headers = [ + ('Content-Type', 'application/xml'), + ('X-TAXII-Content-Type', 'urn:taxii.mitre.org:message:xml:1.1'), + ] + start_response('200 OK', taxii_headers) + return [fixtures11.DISCOVERY_RESPONSE.encode()] + else: + raise Exception('Unknown test path') + + +def test_jwt_auth_with_ssl(httpsserver): + # Serve a JWT token + host, port = httpsserver.server_address + client = cabby.create_client( + host=host, + port=port, + use_https=True, + discovery_path=fixtures11.DISCOVERY_PATH) + client.set_auth( + username='cabby', + password='test', + ca_cert='tests/ssl_test_files/root_ca.pem', + cert_file='tests/ssl_test_files/client.pem', + key_file='tests/ssl_test_files/client.key', + key_password='cabby-test', + jwt_auth_url='/auth', + verify_ssl=True) + services = client.discover_services() + assert len(services) == 4 From b87a152be1f1507a87434c61fad6c4cd4c0ba34b Mon Sep 17 00:00:00 2001 From: Rutger Prins Date: Thu, 29 Mar 2018 11:16:59 +0200 Subject: [PATCH 02/21] Add additional tests for SSL authentication and fix minor bug ca_cert variable in request_with_key_password might be undefined before use. --- cabby/dispatcher.py | 1 + tests/ssl_test_files/client_no_pass.key | 27 ++++++++++++ tests/ssl_test_files/client_no_pass.pem | 23 +++++++++++ tests/ssl_test_files/root_ca.srl | 1 - tests/test_ssl.py | 55 +++++++++++++++++++++---- 5 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 tests/ssl_test_files/client_no_pass.key create mode 100644 tests/ssl_test_files/client_no_pass.pem delete mode 100644 tests/ssl_test_files/root_ca.srl diff --git a/cabby/dispatcher.py b/cabby/dispatcher.py index a4de645..16d38fe 100644 --- a/cabby/dispatcher.py +++ b/cabby/dispatcher.py @@ -404,6 +404,7 @@ def request_with_key_password( # See also 'get_generic_session' which sets many of these attributes. # session 'verify' attribute can be a bool or a path to a CA bundle: + ca_cert = None if not isinstance(session.verify, bool): ca_cert = session.verify context = ssl.create_default_context( diff --git a/tests/ssl_test_files/client_no_pass.key b/tests/ssl_test_files/client_no_pass.key new file mode 100644 index 0000000..7d3275e --- /dev/null +++ b/tests/ssl_test_files/client_no_pass.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA0fYP7Q2xmfLoVNjtFsOQwTtYzQcvuR/3mxSXWDrDM/FcSgbX +spedvf26tzr8PX68YljJN9IwaRVx+20lykH8ZPKminYSht8RdcLUXH+vzpFqGFFd +edKsO+//apNw87KV6rYONp+nbE5HJ7GRiAj3Aop5EAtHEjANdJAizyYsMoJiImtP +f8doritQ11QSs59mTPsyRuM2ZRvu3CzloBEzx/ClTXSVAMP7jZFanxXcwnrJYSbL +/BKxrbPVyzPYBeMjx5V9GUg/YZcDC62C+s0x1CQjacK9meSIx7sXRap3OPFbZ/9c +/OWJiTxx6lzywecoELRX/nfDqsr1gn4/v+GvXwIDAQABAoIBAA7fIO2nhKbk37Py +0YhFPeGR9I5BLg4sx3sCkF29e8oYFHNh3LFXr+KfMDR+RxOMAIfuxgHgL3GiBTrL +1ltnJRt5XHZ+On23GyN+M1CB8s/s2Nj0GmzgkTaFn0/LNbrtMVU4o/UWheNUABI9 +r5M1H1ncuQp74gLVyH4zH2QQzhydorho/RQ4C8wHIm/yUu2RlrlmoV/CCtuFql7Q +EOCwfuf3wom/LUbL2mFNdqYg2LS5ChzFps69zeiO7LF7cppqGAM6hQmtvacmUC7z +H1l1Qn6c7uWxI4zezvEF+zEYg2bZhV7Bm2G4k0H5NyC3lP9DD9OjAem9SQaVtO46 +b7MLrzECgYEA8ypZBcT5YuKH6v5M/ndzY5tek2PrTuOuzkNY9XC3NgOafjyunbxR +o7kn6z6ttpaV8nr43PP5k5FiWyek9FxZp893myEWbxpCZ4s8iKGQs13ICqb3NhAb +kmLqEeaHkgqGt2pISOnwKwq323wWhl9iYOlipgpLa2E5aBTGOAAq/esCgYEA3QsP +AdwNcVTL9irThaBnVZUw0UViv5oaoYIVJBfkjAZeLSi5Oar1+SGErmD8c/RtrB07 +FRABemjRk1Kz1Y1DM4qP6ldWlFaNqvvm+sH8MXtazNeHaFLWUZ5zeh33hV48FUBH +5CG9nXGWSOU/KNYbYk2oFS4fnlVUeXfmsKSBE10CgYBfU46qsFmD5oKaIS9V4sYd +ml8tMNKijqeMvOI29gUc67S5IFjkBVuL754yntPC7K3D7Wl6VTrWGvyP9663DS0o +mDCvY/1DeOvnY7JLbesoJe+yHVp0m0Pz00sn0VP8cJv3c3b6/prkhMMnDDJYYzRS +Aaxmo0qFwgubPemnMomoGQKBgF2OAqoM0vu2oiTsd15FR7cnT+Qi8+qYdNEK15vR +KQBC6bU/WTYZL8Zj815C2lbRi2GfdZQylA2VM66hAFBQW7MC4GqU0KY0A/3sZBSZ +6f2fcgzk5AC5ntAoukNjT5H5EoEEFeluhmyO2Ma9kH/eLvTqXUupm+RNxVUr9E2r +Mp5ZAoGAJWKK/jR2Bl/f/mNasbmoxs86wwCAHLd8hJ3YMxiN0sRkQfroV/+9En2H +LhWqQHIb+lQQzECwnayf4K+SxQUoR5GxOzLe8QIzcsaFfmSjA7pxlIqvzXBVCQKy +/kNpbVHGK/SsA0/YDdFA5XA9lO23+dL0lxsoptScrHP0cTKoMtI= +-----END RSA PRIVATE KEY----- diff --git a/tests/ssl_test_files/client_no_pass.pem b/tests/ssl_test_files/client_no_pass.pem new file mode 100644 index 0000000..ca73a9d --- /dev/null +++ b/tests/ssl_test_files/client_no_pass.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDwTCCAqkCCQDNW0hGu6KfqTANBgkqhkiG9w0BAQsFADCBnTELMAkGA1UEBhMC +TkwxFjAUBgNVBAgMDU5vcnRoLUhvbGxhbmQxEjAQBgNVBAcMCUFtc3RlcmRhbTET +MBEGA1UECgwKRWNsZWN0aWNJUTETMBEGA1UECwwKQ2FiYnkgVGVzdDETMBEGA1UE +AwwKY2FiYnktdGVzdDEjMCEGCSqGSIb3DQEJARYUY2FiYnlAZWNsZWN0aWNpcS5j +b20wHhcNMTgwMzI5MDkwODEzWhcNMjEwMTE2MDkwODEzWjCBpjELMAkGA1UEBhMC +TkwxFjAUBgNVBAgMDU5vcnRoLUhvbGxhbmQxEjAQBgNVBAcMCUFtc3RlcmRhbTET +MBEGA1UECgwKRWNsZWN0aWNJUTEOMAwGA1UECwwFQ2FiYnkxITAfBgNVBAMMGGNh +YmJ5LXRlc3Qtbm8tcGFzc3BocmFzZTEjMCEGCSqGSIb3DQEJARYUY2FiYnlAZWNs +ZWN0aWNpcS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDR9g/t +DbGZ8uhU2O0Ww5DBO1jNBy+5H/ebFJdYOsMz8VxKBteyl529/bq3Ovw9frxiWMk3 +0jBpFXH7bSXKQfxk8qaKdhKG3xF1wtRcf6/OkWoYUV150qw77/9qk3DzspXqtg42 +n6dsTkcnsZGICPcCinkQC0cSMA10kCLPJiwygmIia09/x2iuK1DXVBKzn2ZM+zJG +4zZlG+7cLOWgETPH8KVNdJUAw/uNkVqfFdzCeslhJsv8ErGts9XLM9gF4yPHlX0Z +SD9hlwMLrYL6zTHUJCNpwr2Z5IjHuxdFqnc48Vtn/1z85YmJPHHqXPLB5ygQtFf+ +d8OqyvWCfj+/4a9fAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACh1tE0dIfOF0Mi2 +52976pChfkUGNlVmCbkI8UdwcPq9W3a298s8xbg2s+BnxBZhdRgF4xRepq4Db3h9 +S7f9iQ0iCHN61yh0UNS/NgsM/ALGhHFCv1sP8ajs3vMDa4P4X5xoBavc+Pf+TKIm +CFq/yfXhnMXzfTvLh82zzaE8LM3x+IpthxLkq3F80OXNgyRs5gVFI3poln8TMWiB +I1PnrzBRJcHaod67Mly/iHjH0Zwy6jgkEgrbWLmzkmUXU2kEryt0HlaGO4pj5/2A +CeZqZxjgzp5NSkdFUEauHYH6bzwhSv+BT0jZOgm5+zt/lOGU8a1Y+BJmc2UhgWQ1 +Rhf+7yc= +-----END CERTIFICATE----- diff --git a/tests/ssl_test_files/root_ca.srl b/tests/ssl_test_files/root_ca.srl deleted file mode 100644 index 670bfe2..0000000 --- a/tests/ssl_test_files/root_ca.srl +++ /dev/null @@ -1 +0,0 @@ -CD5B4846BBA29FA8 diff --git a/tests/test_ssl.py b/tests/test_ssl.py index af692ee..65a2c74 100644 --- a/tests/test_ssl.py +++ b/tests/test_ssl.py @@ -3,6 +3,8 @@ import pytest import werkzeug.serving +import werkzeug.wrappers +from six.moves import urllib import cabby import fixtures11 @@ -34,29 +36,32 @@ def get_free_port(): def remote_taxii_app(env, start_response): - if env['PATH_INFO'] == '/auth': + path = env['PATH_INFO'] + + if path == '/auth': start_response('200 OK', [('Content-Type', 'application/json')]) return [b'{"token": "123"}'] - elif env['PATH_INFO'] == fixtures11.DISCOVERY_PATH: + + if path == fixtures11.DISCOVERY_PATH: taxii_headers = [ ('Content-Type', 'application/xml'), ('X-TAXII-Content-Type', 'urn:taxii.mitre.org:message:xml:1.1'), ] start_response('200 OK', taxii_headers) return [fixtures11.DISCOVERY_RESPONSE.encode()] - else: - raise Exception('Unknown test path') + raise Exception('Unknown test path') -def test_jwt_auth_with_ssl(httpsserver): - # Serve a JWT token + +def test_set_auth_ssl(httpsserver): host, port = httpsserver.server_address client = cabby.create_client( host=host, port=port, use_https=True, discovery_path=fixtures11.DISCOVERY_PATH) - client.set_auth( + + auth_params = dict( username='cabby', password='test', ca_cert='tests/ssl_test_files/root_ca.pem', @@ -65,5 +70,41 @@ def test_jwt_auth_with_ssl(httpsserver): key_password='cabby-test', jwt_auth_url='/auth', verify_ssl=True) + + # Test client certificate with key_password. + # This doesn't use the requests library, instead + # it uses urllib2 or 3 through the 'request_with_key_password' function: + client.set_auth(**auth_params) + services = client.discover_services() + assert len(services) == 4 + + # Test we get the correct error when forgetting a key_password: + missing_key_password = dict(auth_params, key_password=None) + client.set_auth(**missing_key_password) + error_msg = "Key file is encrypted but key password was not provided" + with pytest.raises(ValueError, matches=error_msg): + client.discover_services() + + # Test that server certificate authentication can fail: + invalid_server_ca_cert = dict(auth_params, ca_cert=None) + client.set_auth(**invalid_server_ca_cert) + error_msg = "certificate verify failed" + with pytest.raises(urllib.error.URLError, matches=error_msg): + client.discover_services() + + # Test that verify_ssl=False ignores the invalid certificate: + invalid_server_ca_cert_no_verify = dict( + auth_params, ca_cert=None, verify_ssl=False) + client.set_auth(**invalid_server_ca_cert_no_verify) + services = client.discover_services() + assert len(services) == 4 + + # Test client certificate with key_password. + # This uses the requests library as usual: + client_key_without_passphrase = dict( + auth_params, + key_file='tests/ssl_test_files/client_no_pass.key', + cert_file='tests/ssl_test_files/client_no_pass.pem') + client.set_auth(**client_key_without_passphrase) services = client.discover_services() assert len(services) == 4 From 0c3f9caf42f407a565416887cb002449c482f2b1 Mon Sep 17 00:00:00 2001 From: wouter bolsterlee Date: Thu, 29 Mar 2018 12:13:52 +0200 Subject: [PATCH 03/21] Preparing release 0.1.19 --- CHANGES.rst | 6 ++++++ cabby/_version.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index d1bd844..8d6b01a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,12 @@ Changelog ========= +0.1.19 (2018-03-29) +------------------- + +* Enable client key passphrase for JWT token authentication request + (`pr#50 `_) + 0.1.18 (2017-06-19) ------------------- * Dependencies upgraded (`changes `_). diff --git a/cabby/_version.py b/cabby/_version.py index 5cbfc84..0ae72db 100644 --- a/cabby/_version.py +++ b/cabby/_version.py @@ -3,4 +3,4 @@ This module defines the package version for use in __init__.py and setup.py. """ -__version__ = '0.1.19a1' +__version__ = '0.1.19' From 304d094ea4c22cf8d61d220418640aa77e1c9fa1 Mon Sep 17 00:00:00 2001 From: wouter bolsterlee Date: Thu, 29 Mar 2018 12:47:41 +0200 Subject: [PATCH 04/21] only include .rst doc source files in sdist --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index e273b19..7796a18 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,5 +2,5 @@ include LICENSE.rst include CHANGES.rst include README.rst include requirements.txt -recursive-include docs * +recursive-include docs *.rst global-exclude *.pyc From 3f6565f1d676f593775c2c67a776cb24b2f8252a Mon Sep 17 00:00:00 2001 From: wouter bolsterlee Date: Thu, 29 Mar 2018 12:48:03 +0200 Subject: [PATCH 05/21] drop setuptools-git dev dependency ...since it causes all version controlled files to be included in the sdist, which we do not want. --- requirements-dev.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 8f2c2eb..eb0a7a0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,7 +2,6 @@ httpretty pytest pytest-cov -setuptools-git zest.releaser flake8 werkzeug From e79ade785677f800b45a242c7aa5c5b819c73076 Mon Sep 17 00:00:00 2001 From: wouter bolsterlee Date: Thu, 29 Mar 2018 12:51:33 +0200 Subject: [PATCH 06/21] Prepare 0.1.20 release --- CHANGES.rst | 5 +++++ cabby/_version.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8d6b01a..2b4949f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,11 @@ Changelog ========= +0.1.20 (2018-03-29) +------------------- + +* Only include relevant files in release packages. + 0.1.19 (2018-03-29) ------------------- diff --git a/cabby/_version.py b/cabby/_version.py index 0ae72db..a3561a3 100644 --- a/cabby/_version.py +++ b/cabby/_version.py @@ -3,4 +3,4 @@ This module defines the package version for use in __init__.py and setup.py. """ -__version__ = '0.1.19' +__version__ = '0.1.20' From f2eb7d3c93efadccce6d3d79e4a1f386da67d218 Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 10:32:38 +0100 Subject: [PATCH 07/21] work on travis config --- .travis.yml | 21 ++++++++++++--------- requirements-dev.txt | 1 + 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index ecc235a..15fea9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,16 @@ language: python +dist: xenial python: - "2.7" -# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors -install: - - pip install -r requirements-dev.txt - - pip install coveralls -# command to run tests, e.g. python setup.py test -script: - PYTHONPATH=.:$PYTHONPATH py.test --cov ./cabby -# coverage run --source=opentaxii setup.py test + - "3.5" + - "3.6" + - "3.7" + +install: + - pip install coveralls -r requirements-dev.txt + +script: + - py.test --cov cabby + after_success: - coveralls + - coveralls diff --git a/requirements-dev.txt b/requirements-dev.txt index eb0a7a0..42e0ed8 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,6 +2,7 @@ httpretty pytest pytest-cov +pytest-pythonpath zest.releaser flake8 werkzeug From 06a0aab57874144750b26389970752f950779fd5 Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 10:32:49 +0100 Subject: [PATCH 08/21] "matches" is not a valid kwarg --- tests/test_ssl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_ssl.py b/tests/test_ssl.py index 65a2c74..52851a5 100644 --- a/tests/test_ssl.py +++ b/tests/test_ssl.py @@ -82,14 +82,14 @@ def test_set_auth_ssl(httpsserver): missing_key_password = dict(auth_params, key_password=None) client.set_auth(**missing_key_password) error_msg = "Key file is encrypted but key password was not provided" - with pytest.raises(ValueError, matches=error_msg): + with pytest.raises(ValueError, match=error_msg): client.discover_services() # Test that server certificate authentication can fail: invalid_server_ca_cert = dict(auth_params, ca_cert=None) client.set_auth(**invalid_server_ca_cert) error_msg = "certificate verify failed" - with pytest.raises(urllib.error.URLError, matches=error_msg): + with pytest.raises(urllib.error.URLError, match=error_msg): client.discover_services() # Test that verify_ssl=False ignores the invalid certificate: From 813ac4d2926b929b221528fd7e493dde871c0fcb Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 10:34:50 +0100 Subject: [PATCH 09/21] only test master branch --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 15fea9e..99aae7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ +branches: + - master + language: python dist: xenial python: From 2e125bdec02a6fa732ff6368fc653c64313b085b Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 11:32:55 +0100 Subject: [PATCH 10/21] use python3 in docker, simplify --- Dockerfile | 75 +++++++++----------------------------------------- docker-help.sh | 26 +++++++++++++++++ 2 files changed, 39 insertions(+), 62 deletions(-) create mode 100755 docker-help.sh diff --git a/Dockerfile b/Dockerfile index a99416d..4ad5b30 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,62 +1,13 @@ -# Set the base image to Python -FROM alpine:3.4 -MAINTAINER EclecticIQ - -# Volume for possible input -VOLUME [ "/input" ] - -# Create the working dir and set the working directory -WORKDIR / - -# Setup Python -RUN apk add --no-cache -U \ - ca-certificates \ - build-base \ - libxml2 \ - libxml2-dev \ - libxslt \ - libxslt-dev \ - make \ - python-dev \ - py-pip \ - python \ - && pip install --upgrade pip setuptools - - -# Setup Requirements -COPY ./ /cabby -RUN pip install -r /cabby/requirements.txt \ - && cd /cabby \ - && python setup.py install - -# Cleanup -RUN apk del build-base \ - libxml2-dev \ - libxslt-dev \ - python-dev \ - build-base \ - && rm -rf /var/cache/apk/* \ - && rm -r /root/.cache \ - && rm -f requirements.txt \ - && rm -rf /cabby - -RUN { echo '#!/bin/sh';\ - echo 'echo "';\ - echo ' Commands to be run:';\ - echo ' taxii-discovery ';\ - echo ' taxii-poll';\ - echo ' taxii-collections';\ - echo ' taxii-push ';\ - echo ' taxii-subscription';\ - echo ' taxii-proxy';\ - echo '';\ - echo 'e.g. docker run -ti eclecticiq/cabby taxii-discovery --path https://test.taxiistand.com/read-write/services/discovery';\ - echo '';\ - echo 'More information available at: http://cabby.readthedocs.org';\ - echo 'Or you can choose to drop back into a shell by providing: bash as the command:';\ - echo '';\ - echo 'docker run -ti cabby bash"'; } > /help.sh && chmod 750 /help.sh - -# Give help, unless command is given -CMD [ "/help.sh" ] - +# dockerfile for running tests in jenkins etc. +FROM python:3-slim-stretch +RUN python3 -m venv --system-site-packages /venv +ENV PATH=/venv/bin:$PATH + +COPY ./requirements.txt ./requirements-dev.txt /cabby +RUN pip install -r /cabby/requirements-dev.txt +COPY . /cabby +RUN pip install -e /cabby + +COPY ./docker-help.sh / +RUN sh -c "cat /docker-help.sh >> /root/.bashrc" +CMD ["/docker-help.sh"] diff --git a/docker-help.sh b/docker-help.sh new file mode 100755 index 0000000..66d1cc3 --- /dev/null +++ b/docker-help.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +_cmd="" +if [ $0 = '/docker-help.sh' ]; then + _cmd="docker run -it eclecticiq/cabby " +fi + +cat < Date: Tue, 20 Nov 2018 11:34:26 +0100 Subject: [PATCH 11/21] add maintainer label --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 4ad5b30..141b890 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ # dockerfile for running tests in jenkins etc. FROM python:3-slim-stretch +LABEL maintainer="EclecticIQ " RUN python3 -m venv --system-site-packages /venv ENV PATH=/venv/bin:$PATH From 44616b5243e6dbf1b67b8332a9f943a561cce594 Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 13:02:02 +0100 Subject: [PATCH 12/21] no need to copy docker-help.sh twice --- Dockerfile | 5 ++--- docker-help.sh | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 141b890..9939458 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,5 @@ RUN pip install -r /cabby/requirements-dev.txt COPY . /cabby RUN pip install -e /cabby -COPY ./docker-help.sh / -RUN sh -c "cat /docker-help.sh >> /root/.bashrc" -CMD ["/docker-help.sh"] +RUN sh -c "cat /cabby/docker-help.sh >> /root/.bashrc" +CMD ["/cabby/docker-help.sh"] diff --git a/docker-help.sh b/docker-help.sh index 66d1cc3..5fa749d 100755 --- a/docker-help.sh +++ b/docker-help.sh @@ -1,7 +1,7 @@ #!/bin/sh _cmd="" -if [ $0 = '/docker-help.sh' ]; then +if [ $0 = '/cabby/docker-help.sh' ]; then _cmd="docker run -it eclecticiq/cabby " fi From b2e076f45c5c90e908eb4d861d000eea2e4b123b Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 13:03:35 +0100 Subject: [PATCH 13/21] remove unnecessary comment --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9939458..66f6403 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,3 @@ -# dockerfile for running tests in jenkins etc. FROM python:3-slim-stretch LABEL maintainer="EclecticIQ " RUN python3 -m venv --system-site-packages /venv From 38893973857c32a3bf144aa838655096df2d5b13 Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 13:06:58 +0100 Subject: [PATCH 14/21] fix urls in readme --- README.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 7c0e77d..1b13077 100644 --- a/README.rst +++ b/README.rst @@ -3,28 +3,28 @@ cabby Python TAXII client implementation from `EclecticIQ `_. -:Source: https://github.com/EclecticIQ/cabby +:Source: https://github.com/eclecticiq/cabby :Documentation: http://cabby.readthedocs.org :Information: https://www.eclecticiq.com :Download: https://pypi.python.org/pypi/cabby/ |travis badge| |landscape.io badge| |coveralls.io badge| |docs badge| |requirements badge| -.. |travis badge| image:: https://travis-ci.org/EclecticIQ/cabby.svg?branch=master - :target: https://travis-ci.org/EclecticIQ/cabby +.. |travis badge| image:: https://travis-ci.org/eclecticiq/cabby.svg?branch=master + :target: https://travis-ci.org/eclecticiq/cabby :alt: Build Status -.. |landscape.io badge| image:: https://landscape.io/github/EclecticIQ/cabby/master/landscape.svg?style=flat - :target: https://landscape.io/github/EclecticIQ/cabby/master +.. |landscape.io badge| image:: https://landscape.io/github/eclecticiq/cabby/master/landscape.svg?style=flat + :target: https://landscape.io/github/eclecticiq/cabby/master :alt: Code Health -.. |coveralls.io badge| image:: https://coveralls.io/repos/EclecticIQ/cabby/badge.svg - :target: https://coveralls.io/r/EclecticIQ/cabby +.. |coveralls.io badge| image:: https://coveralls.io/repos/eclecticiq/cabby/badge.svg + :target: https://coveralls.io/r/eclecticiq/cabby :alt: Coverage Status .. |docs badge| image:: https://readthedocs.org/projects/cabby/badge/?version=latest :alt: Documentation Status :scale: 100% :target: https://readthedocs.org/projects/cabby/ -.. |requirements badge| image:: https://requires.io/github/EclecticIQ/cabby/requirements.svg?branch=master - :target: https://requires.io/github/EclecticIQ/cabby/requirements/?branch=master +.. |requirements badge| image:: https://requires.io/github/eclecticiq/cabby/requirements.svg?branch=master + :target: https://requires.io/github/eclecticiq/cabby/requirements/?branch=master :alt: Requirements Status A simple Python library for interacting with TAXII servers. From 781c76a309ab21ff2cee3df915d9121f3909754d Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 13:27:20 +0100 Subject: [PATCH 15/21] normalize and deduplicate docker run instructions --- README.rst | 2 +- docker-help.sh | 9 ++++++++- docs/user.rst | 29 ++--------------------------- 3 files changed, 11 insertions(+), 29 deletions(-) diff --git a/README.rst b/README.rst index 1b13077..4d90275 100644 --- a/README.rst +++ b/README.rst @@ -37,7 +37,7 @@ From version 0.1.13, the docker image is based on 'Alpine' linux. This means the To run cabby using docker, execute the following: - docker run --rm=true eclecticiq/cabby:latest taxii-discovery --path https://test.taxiistand.com/read-only/services/discovery + docker run --rm eclecticiq/cabby taxii-discovery --path https://test.taxiistand.com/read-only/services/discovery Feedback -------- diff --git a/docker-help.sh b/docker-help.sh index 5fa749d..c30d058 100755 --- a/docker-help.sh +++ b/docker-help.sh @@ -2,7 +2,7 @@ _cmd="" if [ $0 = '/cabby/docker-help.sh' ]; then - _cmd="docker run -it eclecticiq/cabby " + _cmd="docker run --rm eclecticiq/cabby " fi cat < Date: Tue, 20 Nov 2018 13:30:22 +0100 Subject: [PATCH 16/21] remove landscape.io --- README.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.rst b/README.rst index 4d90275..74bc22b 100644 --- a/README.rst +++ b/README.rst @@ -13,9 +13,6 @@ Python TAXII client implementation from `EclecticIQ .. |travis badge| image:: https://travis-ci.org/eclecticiq/cabby.svg?branch=master :target: https://travis-ci.org/eclecticiq/cabby :alt: Build Status -.. |landscape.io badge| image:: https://landscape.io/github/eclecticiq/cabby/master/landscape.svg?style=flat - :target: https://landscape.io/github/eclecticiq/cabby/master - :alt: Code Health .. |coveralls.io badge| image:: https://coveralls.io/repos/eclecticiq/cabby/badge.svg :target: https://coveralls.io/r/eclecticiq/cabby :alt: Coverage Status From 76a3eede0d719a7a95216d5fa8068bbbbfab1d25 Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 13:32:08 +0100 Subject: [PATCH 17/21] remove obsolete info on docker image --- README.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.rst b/README.rst index 74bc22b..d13f1d2 100644 --- a/README.rst +++ b/README.rst @@ -30,8 +30,6 @@ A simple Python library for interacting with TAXII servers. Docker -------- -From version 0.1.13, the docker image is based on 'Alpine' linux. This means the size of the Image was reduced from 311MB to 74MB - To run cabby using docker, execute the following: docker run --rm eclecticiq/cabby taxii-discovery --path https://test.taxiistand.com/read-only/services/discovery From 5959162def6c436c3b0fbec57d00433ced87fd2a Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 13:42:44 +0100 Subject: [PATCH 18/21] really remove landscape.io --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d13f1d2..f4914b5 100644 --- a/README.rst +++ b/README.rst @@ -8,7 +8,7 @@ Python TAXII client implementation from `EclecticIQ :Information: https://www.eclecticiq.com :Download: https://pypi.python.org/pypi/cabby/ -|travis badge| |landscape.io badge| |coveralls.io badge| |docs badge| |requirements badge| +|travis badge| |coveralls.io badge| |docs badge| |requirements badge| .. |travis badge| image:: https://travis-ci.org/eclecticiq/cabby.svg?branch=master :target: https://travis-ci.org/eclecticiq/cabby From 57bb615d1678e4e3896fcd1a77565e2491b5b154 Mon Sep 17 00:00:00 2001 From: Andreas Lutro Date: Tue, 20 Nov 2018 13:44:19 +0100 Subject: [PATCH 19/21] fix indentation --- README.rst | 10 +++++----- docs/user.rst | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index f4914b5..439a414 100644 --- a/README.rst +++ b/README.rst @@ -17,12 +17,12 @@ Python TAXII client implementation from `EclecticIQ :target: https://coveralls.io/r/eclecticiq/cabby :alt: Coverage Status .. |docs badge| image:: https://readthedocs.org/projects/cabby/badge/?version=latest - :alt: Documentation Status - :scale: 100% - :target: https://readthedocs.org/projects/cabby/ + :alt: Documentation Status + :scale: 100% + :target: https://readthedocs.org/projects/cabby/ .. |requirements badge| image:: https://requires.io/github/eclecticiq/cabby/requirements.svg?branch=master - :target: https://requires.io/github/eclecticiq/cabby/requirements/?branch=master - :alt: Requirements Status + :target: https://requires.io/github/eclecticiq/cabby/requirements/?branch=master + :alt: Requirements Status A simple Python library for interacting with TAXII servers. diff --git a/docs/user.rst b/docs/user.rst index 2aa82ac..392ad55 100644 --- a/docs/user.rst +++ b/docs/user.rst @@ -191,7 +191,7 @@ To ease the threshold for trying out Cabby, it is possible to use the image prov .. code-block:: shell - $ docker run --rm cabby bash + $ docker run --rm cabby bash This will show you some helpful information on what commands are available, and then give you an interactive shell to play around in. From cd2cf9ed633c7a738b7dfac7fd807b05d1672474 Mon Sep 17 00:00:00 2001 From: Shumatsu Date: Fri, 4 Oct 2019 22:45:43 +0200 Subject: [PATCH 20/21] Update Dockerfile with missing / Resolves eclecticiq/cabby#64 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 66f6403..1c02927 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ LABEL maintainer="EclecticIQ " RUN python3 -m venv --system-site-packages /venv ENV PATH=/venv/bin:$PATH -COPY ./requirements.txt ./requirements-dev.txt /cabby +COPY ./requirements.txt ./requirements-dev.txt /cabby/ RUN pip install -r /cabby/requirements-dev.txt COPY . /cabby RUN pip install -e /cabby From ab27b1bccfdedcd3dd7bea5aed8fb091863fa1fb Mon Sep 17 00:00:00 2001 From: Gram Date: Mon, 7 Oct 2019 16:19:03 +0200 Subject: [PATCH 21/21] improve server error message --- cabby/exceptions.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cabby/exceptions.py b/cabby/exceptions.py index 9133aba..9aca3ad 100644 --- a/cabby/exceptions.py +++ b/cabby/exceptions.py @@ -18,12 +18,11 @@ class InvalidResponseError(ClientException): class UnsuccessfulStatusError(ClientException): def __init__(self, taxii_status, *args, **kwargs): - super(UnsuccessfulStatusError, self).__init__( - _status_to_message(taxii_status), *args, **kwargs) + msg = "Server Error: {}".format(_status_to_message(self.raw)) + super(UnsuccessfulStatusError, self).__init__(msg, *args, **kwargs) self.status = taxii_status.status_type self.text = taxii_status.to_text() - self.raw = taxii_status