From 99f6d9f3678cac74a07d15e091e99edfb2a1d280 Mon Sep 17 00:00:00 2001 From: Arash Ghoreyshi Date: Thu, 20 Jun 2013 16:47:38 -0500 Subject: [PATCH] Fix authentication and service endpoint fetching Make the client library take environment variables by default --- barbicanclient/client.py | 64 +++++++++++++++++++++++++++++++--------- keep | 41 +++++++++---------------- tests/client_test.py | 18 +++++++++-- 3 files changed, 80 insertions(+), 43 deletions(-) diff --git a/barbicanclient/client.py b/barbicanclient/client.py index acf77240..7f6fb752 100644 --- a/barbicanclient/client.py +++ b/barbicanclient/client.py @@ -2,6 +2,7 @@ import eventlet eventlet.monkey_patch(socket=True, select=True) import json +import os import requests from barbicanclient.secrets import Secret @@ -22,24 +23,36 @@ class Connection(object): SECRETS_PATH = 'secrets' ORDERS_PATH = 'orders' - def __init__(self, auth_endpoint, user, key, tenant, - token=None, authenticate=None, request=None, **kwargs): + def __init__(self, auth_endpoint=None, user=None, key=None, tenant=None, + token=None, authenticate=None, request=None, fake_env=None, + **kwargs): """ + Authenticate and connect to the endpoint + :param auth_endpoint: The auth URL to authenticate against + default: env('OS_AUTH_URL') :param user: The user to authenticate as + default: env('OS_USERNAME') :param key: The API key or password to auth with + default: env('OS_PASSWORD') + :param tenant: The tenant ID + default: env('OS_TENANT_NAME') """ LOG.debug(_("Creating Connection object")) - self._auth_endpoint = auth_endpoint + self.env = fake_env or env + self._auth_endpoint = auth_endpoint or self.env('OS_AUTH_URL') + self._user = user or self.env('OS_USERNAME') + self._key = key or self.env('OS_PASSWORD') + self._tenant = tenant or self.env('OS_TENANT_NAME') + if not all([self._auth_endpoint, self._user, self._key, self._tenant]): + raise ClientException("The authorization endpoint, username, key," + " and tenant name should either be passed i" + "n or defined as environment variables.") self.authenticate = authenticate or auth.authenticate self.request = request or requests.request - self._user = user - self._key = key - self._tenant = tenant - self._endpoint = (kwargs.get('endpoint') - or 'https://barbican.api.rackspacecloud.com/v1/') + self._endpoint = kwargs.get('endpoint') self._cacert = kwargs.get('cacert') self.connect(token=token) @@ -47,8 +60,7 @@ class Connection(object): @property def _conn(self): """ - Property to enable decorators to work - properly + Property to enable decorators to work properly """ return self @@ -62,6 +74,10 @@ class Connection(object): """The fully-qualified URI of the endpoint""" return self._endpoint + @endpoint.setter + def endpoint(self, value): + self._endpoint = value + def connect(self, token=None): """ Establishes a connection. If token is not None the @@ -81,11 +97,12 @@ class Connection(object): self.auth_token = token else: LOG.debug(_("Authenticating token")) - self._endpoint, self.auth_token = self.authenticate( + self.endpoint, self.auth_token = self.authenticate( self._auth_endpoint, self._user, self._key, self._tenant, + service_type='key-store', endpoint=self._endpoint, cacert=self._cacert ) @@ -149,7 +166,7 @@ class Connection(object): cypher_type=None, expiration=None): """ - Creates and returns a Secret object with all of its metadata filled in. + Creates and returns an Order object with all of its metadata filled in. arguments: mime_type - The MIME type of the secret @@ -356,11 +373,15 @@ class Connection(object): if not isinstance(request_body, str): request_body = json.dumps(request_body) - url = urljoin(self._endpoint, href) + if not self.endpoint.endswith('/'): + self.endpoint += '/' + + url = urljoin(self.endpoint, href) + + headers['X-Auth-Token'] = self.auth_token response = self.request(method=method, url=url, data=request_body, headers=headers) - # Check if the status code is 2xx class if not response.ok: LOG.error('Bad response: {0}'.format(response.status_code)) @@ -376,3 +397,18 @@ class Connection(object): resp_body = '' return response.headers, resp_body + + +def env(*vars, **kwargs): + """Search for the first defined of possibly many env vars + + Returns the first environment variable defined in vars, or + returns the default defined in kwargs. + + Source: Keystone's shell.py + """ + for v in vars: + value = os.environ.get(v, None) + if value: + return value + return kwargs.get('default', '') diff --git a/keep b/keep index adcd4a14..d9b49559 100755 --- a/keep +++ b/keep @@ -1,7 +1,6 @@ #!/usr/bin/env python import argparse -import os from barbicanclient import client @@ -24,23 +23,26 @@ class Keep: choices=["order", "secret"], help="type to operate on") parser.add_argument('--auth_endpoint', '-A', - default=env('OS_AUTH_URL'), + default=client.env('OS_AUTH_URL'), help='the URL to authenticate against (default: ' '%(default)s)') - parser.add_argument('--user', '-U', default=env('OS_USERNAME'), + parser.add_argument('--user', '-U', default=client.env('OS_USERNAME'), help='the user to authenticate as (default: %(de' 'fault)s)') - parser.add_argument('--password', '-P', default=env('OS_PASSWORD'), + parser.add_argument('--password', '-P', + default=client.env('OS_PASSWORD'), help='the API key or password to authenticate with' ' (default: %(default)s)') - parser.add_argument('--tenant', '-T', default=env('OS_TENANT_NAME'), + parser.add_argument('--tenant', '-T', + default=client.env('OS_TENANT_NAME'), help='the tenant ID (default: %(default)s)') - parser.add_argument('--endpoint', '-E', default=env('SERVICE_ENDPOINT') - , help='the URL of the barbican server (default: %' + parser.add_argument('--endpoint', '-E', + default=client.env('SERVICE_ENDPOINT'), + help='the URL of the barbican server (default: %' '(default)s)') - parser.add_argument('--token', '-K', default=env('SERVICE_TOKEN'), - help='the authentication token (default: %(default' - ')s)') + parser.add_argument('--token', '-K', + default=client.env('SERVICE_TOKEN'), help='the au' + 'thentication token (default: %(default)s)') return parser def add_create_args(self): @@ -149,8 +151,8 @@ class Keep: l = self.conn.list_orders(args.limit, args.offset) for i in l[0]: print i - print 'Displayed {0} {1}s - offset: {2}'.format(len(l[0]), args.type, - args.offset) + print '{0}s displayed: {1} - offset: {2}'.format(args.type, len(l[0]), + args.offset) def execute(self): args = self.parser.parse_args() @@ -161,21 +163,6 @@ class Keep: args.func(args) -def env(*vars, **kwargs): - """Search for the first defined of possibly many env vars - - Returns the first environment variable defined in vars, or - returns the default defined in kwargs. - - Source: Keystone's shell.py - """ - for v in vars: - value = os.environ.get(v, None) - if value: - return value - return kwargs.get('default', '') - - def main(): k = Keep() k.execute() diff --git a/tests/client_test.py b/tests/client_test.py index 44205c3b..eeebf908 100644 --- a/tests/client_test.py +++ b/tests/client_test.py @@ -40,6 +40,8 @@ class WhenTestingConnection(unittest.TestCase): self.auth_token = 'token' self.href = 'http://localhost:9311/v1/12345/orders' + self.fake_env = MagicMock() + self.fake_env.return_value = None self.authenticate = MagicMock() self.authenticate.return_value = (self.endpoint, self.auth_token) self.request = MagicMock() @@ -62,7 +64,8 @@ class WhenTestingConnection(unittest.TestCase): self.key, self.tenant, token=self.auth_token, authenticate=self.authenticate, - request=self.request) + request=self.request, + endpoint=self.endpoint) def test_should_connect_with_token(self): self.assertFalse(self.authenticate.called) @@ -79,6 +82,7 @@ class WhenTestingConnection(unittest.TestCase): self.user, self.key, self.tenant, + service_type='key-store', endpoint=self.endpoint, cacert=None ) @@ -89,6 +93,16 @@ class WhenTestingConnection(unittest.TestCase): self.assertEqual(self.tenant, self.connection._tenant) self.assertEqual(self.endpoint, self.connection._endpoint) + def test_should_raise_for_bad_args(self): + with self.assertRaises(ClientException): + self.connection = client.Connection(None, self.user, + self.key, self.tenant, + fake_env=self.fake_env, + token=self.auth_token, + authenticate=self.authenticate, + request=self.request, + endpoint=self.endpoint) + def test_should_create_secret(self): body = {'status': 'ACTIVE', 'content_types': {'default': 'text/plain'}, @@ -318,7 +332,7 @@ class WhenTestingConnection(unittest.TestCase): parse_json=False) self.assertEqual(self.request.return_value.content, body) - def test_should_raise_exception(self): + def test_should_raise_for_bad_response(self): self._setup_request() self.request.return_value.ok = False self.request.return_value.status_code = 404