diff --git a/README.rst b/README.rst index 9c5125a..23f9a57 100644 --- a/README.rst +++ b/README.rst @@ -77,11 +77,16 @@ API Versioning ============== It may be useful to lock your application to a particular version of the Trovebox API. This ensures that future API updates won't cause unexpected breakages. +To do this, configure your Trovebox client as follows: -To do this, add the optional ``api_version`` parameter when creating the client object:: + client.configure(api_version=2) - from trovebox import Trovebox - client = Trovebox(api_version=2) +SSL Verification +================ +If you connect to your Trovebox server over HTTPS, its SSL certificate is automatically verified. +You can configure your Trovebox client to bypass this verification step: + + client.configure(ssl_verify=False) Commandline Tool ================ diff --git a/tests/unit/test_http.py b/tests/unit/test_http.py index 2e82dae..82df5ed 100644 --- a/tests/unit/test_http.py +++ b/tests/unit/test_http.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import os import json +import mock import httpretty try: import unittest2 as unittest # Python2.6 @@ -219,7 +220,8 @@ class TestHttp(unittest.TestCase): @httpretty.activate def test_get_with_api_version(self): """Check that an API version can be specified for the get method""" - self.client = trovebox.Trovebox(host=self.test_host, api_version=1) + self.client = trovebox.Trovebox(host=self.test_host) + self.client.configure(api_version=1) self._register_uri(httpretty.GET, uri="http://%s/v1/%s" % (self.test_host, self.test_endpoint)) @@ -228,13 +230,39 @@ class TestHttp(unittest.TestCase): @httpretty.activate def test_post_with_api_version(self): """Check that an API version can be specified for the post method""" - self.client = trovebox.Trovebox(host=self.test_host, api_version=1, - **self.test_oauth) + self.client = trovebox.Trovebox(host=self.test_host, **self.test_oauth) + self.client.configure(api_version=1) self._register_uri(httpretty.POST, uri="http://%s/v1/%s" % (self.test_host, self.test_endpoint)) self.client.post(self.test_endpoint) + @mock.patch.object(trovebox.http.requests, 'Session') + def test_get_with_ssl_verify_disabled(self, mock_session): + """Check that SSL verification can be disabled for the get method""" + session = mock_session.return_value.__enter__.return_value + session.get.return_value.text = "response text" + session.get.return_value.status_code = 200 + session.get.return_value.json.return_value = self.test_data + + self.client = trovebox.Trovebox(host=self.test_host, **self.test_oauth) + self.client.configure(ssl_verify=False) + self.client.get(self.test_endpoint) + self.assertEqual(session.verify, False) + + @mock.patch.object(trovebox.http.requests, 'Session') + def test_post_with_ssl_verify_disabled(self, mock_session): + """Check that SSL verification can be disabled for the post method""" + session = mock_session.return_value.__enter__.return_value + session.post.return_value.text = "response text" + session.post.return_value.status_code = 200 + session.post.return_value.json.return_value = self.test_data + + self.client = trovebox.Trovebox(host=self.test_host, **self.test_oauth) + self.client.configure(ssl_verify=False) + self.client.post(self.test_endpoint) + self.assertEqual(session.verify, False) + @httpretty.activate def test_post_file(self): """Check that a file can be posted""" diff --git a/trovebox/http.py b/trovebox/http.py index bd99ddb..4cc0faf 100644 --- a/trovebox/http.py +++ b/trovebox/http.py @@ -31,14 +31,22 @@ class Http(object): The config_file parameter is used to specify an alternate config file. If the host parameter is specified, no config file is loaded and OAuth tokens (consumer*, token*) can optionally be specified. - All requests will include the api_version path, if specified. - This should be used to ensure that your application will continue to work - even if the Trovebox API is updated to a new revision. """ + + _CONFIG_DEFAULTS = {"api_version" : None, + "ssl_verify" : True, + } + def __init__(self, config_file=None, host=None, consumer_key='', consumer_secret='', token='', token_secret='', api_version=None): - self._api_version = api_version + + self.config = dict(self._CONFIG_DEFAULTS) + + if api_version is not None: + print("Deprecation Warning: api_version should be set by " + "calling the configure function") + self.config["api_version"] = api_version self._logger = logging.getLogger("trovebox") @@ -53,6 +61,20 @@ class Http(object): self.last_params = None self.last_response = None + def configure(self, **kwds): + """ + Update Trovebox HTTP client configuration. + + :param api_version: Include a Trovebox API version in all requests. + This can be used to ensure that your application will continue + to work even if the Trovebox API is updated to a new revision. + [default: None] + :param ssl_verify: If true, HTTPS SSL certificates will always be + verified [default: True] + """ + for item in kwds: + self.config[item] = kwds[item] + def get(self, endpoint, process_response=True, **params): """ Performs an HTTP GET from the specified endpoint (API path), @@ -76,6 +98,7 @@ class Http(object): auth = None with requests.Session() as session: + session.verify = self.config["ssl_verify"] response = session.get(url, params=params, auth=auth) self._logger.info("============================") @@ -114,6 +137,7 @@ class Http(object): self.auth.token, self.auth.token_secret) with requests.Session() as session: + session.verify = self.config["ssl_verify"] if files: # Need to pass parameters as URL query, so they get OAuth signed response = session.post(url, params=params, @@ -153,8 +177,8 @@ class Http(object): if not endpoint.startswith("/"): endpoint = "/" + endpoint - if self._api_version is not None: - endpoint = "/v%d%s" % (self._api_version, endpoint) + if self.config["api_version"] is not None: + endpoint = "/v%d%s" % (self.config["api_version"], endpoint) return urlunparse((scheme, host, endpoint, '', '', '')) @staticmethod