From bc5621e4e7942988266bb6e1d2b3241c6c7b12ed Mon Sep 17 00:00:00 2001 From: Kevin McDonald Date: Tue, 23 Apr 2013 13:55:43 -0500 Subject: [PATCH 1/2] Adds Generic Auth Concept to Client API: Adds 'auth' keyword argument to SoftLayer.Client(). This object should respond to get_headers(). If just username and api_key are passed in, BasicAuthentication will be used for auth. --- SoftLayer/API.py | 42 ++++++++++++++++++++--------- SoftLayer/__init__.py | 7 ++--- SoftLayer/tests/API/client_tests.py | 22 +++++++++++++++ 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/SoftLayer/API.py b/SoftLayer/API.py index bfe27ffd4..ea1141229 100644 --- a/SoftLayer/API.py +++ b/SoftLayer/API.py @@ -17,6 +17,23 @@ API_BASE_URL = API_PUBLIC_ENDPOINT +class BasicAuthentication(object): + def __init__(self, username, api_key): + self.username = username + self.api_key = api_key + + def get_headers(self): + return { + 'authenticate': { + 'username': self.username, + 'apiKey': self.api_key, + } + } + + def __repr__(self): + return "" % (self.username) + + class Client(object): """ A SoftLayer API client. @@ -33,6 +50,8 @@ class Client(object): network. :param integer timeout: timeout for API requests :param boolean verbose: prints details about every HTTP request if true + :param auth: an object which responds to get_headers() to be inserted into + the xml-rpc headers. Example: `BasicAuthentication` Usage: @@ -46,16 +65,19 @@ class Client(object): _prefix = "SoftLayer_" def __init__(self, service_name=None, id=None, username=None, api_key=None, - endpoint_url=None, timeout=None, verbose=False): + endpoint_url=None, timeout=None, verbose=False, auth=None): self._service_name = service_name self.verbose = verbose self._headers = {} self._raw_headers = {} - self.username = username or API_USERNAME or \ - os.environ.get('SL_USERNAME') or '' - self.api_key = api_key or API_KEY or os.environ.get('SL_API_KEY') or '' - self.set_authentication(self.username, self.api_key) + self.auth = auth + if self.auth is None: + username = username or API_USERNAME or \ + os.environ.get('SL_USERNAME') or '' + api_key = api_key or API_KEY or os.environ.get('SL_API_KEY') or '' + self.auth = BasicAuthentication(username, api_key) + self.set_authentication(username, api_key) if id is not None: self.set_init_parameter(int(id)) @@ -226,11 +248,7 @@ def call(self, service, method, *args, **kwargs): offset = kwargs.get('offset', 0) if headers is None: - headers = { - 'authenticate': { - 'username': self.username, - 'apiKey': self.api_key, - }} + headers = self.auth.get_headers() http_headers = { 'User-Agent': USER_AGENT, @@ -366,8 +384,8 @@ def call_handler(*args, **kwargs): return call_handler def __repr__(self): - return "" \ - % (self._endpoint_url, self.username) + return "" \ + % (self._endpoint_url, self.auth) __str__ = __repr__ diff --git a/SoftLayer/__init__.py b/SoftLayer/__init__.py index 920e5a73b..5a5eef472 100644 --- a/SoftLayer/__init__.py +++ b/SoftLayer/__init__.py @@ -16,7 +16,8 @@ """ from SoftLayer.consts import VERSION -from API import Client, API_PUBLIC_ENDPOINT, API_PRIVATE_ENDPOINT +from API import ( + Client, BasicAuthentication, API_PUBLIC_ENDPOINT, API_PRIVATE_ENDPOINT) from DNS import DNSManager from CCI import CCIManager from metadata import MetadataManager @@ -27,6 +28,6 @@ __author__ = 'SoftLayer Technologies, Inc.' __license__ = 'The BSD License' __copyright__ = 'Copyright 2013 SoftLayer Technologies, Inc.' -__all__ = ['Client', 'SoftLayerError', 'SoftLayerAPIError', - 'API_PUBLIC_ENDPOINT', 'API_PRIVATE_ENDPOINT', +__all__ = ['Client', 'BasicAuthentication', 'SoftLayerError', + 'SoftLayerAPIError', 'API_PUBLIC_ENDPOINT', 'API_PRIVATE_ENDPOINT', 'DNSManager', 'CCIManager', 'MetadataManager'] diff --git a/SoftLayer/tests/API/client_tests.py b/SoftLayer/tests/API/client_tests.py index 072167957..79227c61f 100644 --- a/SoftLayer/tests/API/client_tests.py +++ b/SoftLayer/tests/API/client_tests.py @@ -339,3 +339,25 @@ def test_iter_call(self, _call): AttributeError, lambda: list(self.client.iter_call( 'SERVICE', 'METHOD', iter=True, chunk=0))) + + +class TestBasicAuthentication(unittest.TestCase): + def setUp(self): + self.auth = SoftLayer.BasicAuthentication('USERNAME', 'APIKEY') + + def test_attribs(self): + self.assertEquals(self.auth.username, 'USERNAME') + self.assertEquals(self.auth.api_key, 'APIKEY') + + def test_get_headers(self): + self.assertEquals(self.auth.get_headers(), { + 'authenticate': { + 'username': 'USERNAME', + 'apiKey': 'APIKEY', + } + }) + + def test_repr(self): + s = repr(self.auth) + self.assertIn('BasicAuthentication', s) + self.assertIn('USERNAME', s) From cc6e1b8367217c7437b33767ea7990e37e453bea Mon Sep 17 00:00:00 2001 From: Kevin McDonald Date: Tue, 23 Apr 2013 15:05:01 -0500 Subject: [PATCH 2/2] API: Adds AuthenticationBase --- SoftLayer/API.py | 7 ++++++- SoftLayer/tests/API/client_tests.py | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/SoftLayer/API.py b/SoftLayer/API.py index ea1141229..b6b6d2b64 100644 --- a/SoftLayer/API.py +++ b/SoftLayer/API.py @@ -17,7 +17,12 @@ API_BASE_URL = API_PUBLIC_ENDPOINT -class BasicAuthentication(object): +class AuthenticationBase(object): + def get_headers(self): + raise NotImplementedError + + +class BasicAuthentication(AuthenticationBase): def __init__(self, username, api_key): self.username = username self.api_key = api_key diff --git a/SoftLayer/tests/API/client_tests.py b/SoftLayer/tests/API/client_tests.py index 79227c61f..dc4cfbe22 100644 --- a/SoftLayer/tests/API/client_tests.py +++ b/SoftLayer/tests/API/client_tests.py @@ -341,6 +341,12 @@ def test_iter_call(self, _call): 'SERVICE', 'METHOD', iter=True, chunk=0))) +class TestAuthenticationBase(unittest.TestCase): + def test_get_headers(self): + auth = SoftLayer.API.AuthenticationBase() + self.assertRaises(NotImplementedError, auth.get_headers) + + class TestBasicAuthentication(unittest.TestCase): def setUp(self): self.auth = SoftLayer.BasicAuthentication('USERNAME', 'APIKEY')