summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Mendizabal <douglas.mendizabal@rackspace.com>2013-09-01 01:41:32 -0500
committerDouglas Mendizabal <douglas.mendizabal@rackspace.com>2013-09-01 01:41:32 -0500
commit7c41da3a2890fa92f15447d97683b93cf3cfc853 (patch)
tree3c9b36dcac9f828e39d7ab2b7b30f7b3fc0cb466
parentbab914d1cbc656ced395b0862fbc0120f79f6f63 (diff)
downloadpython-barbicanclient-7c41da3a2890fa92f15447d97683b93cf3cfc853.tar.gz
Moved secret create and list into secret manager
-rw-r--r--barbicanclient/base.py27
-rw-r--r--barbicanclient/client.py163
-rw-r--r--barbicanclient/common/auth.py3
-rw-r--r--barbicanclient/secrets.py76
-rw-r--r--barbicanclient/test/common/test_auth.py6
-rw-r--r--barbicanclient/test/test_client.py22
6 files changed, 188 insertions, 109 deletions
diff --git a/barbicanclient/base.py b/barbicanclient/base.py
new file mode 100644
index 0000000..449b07a
--- /dev/null
+++ b/barbicanclient/base.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2013 Rackspace, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+Base utilites to build API operation managers.
+"""
+
+class BaseEntityManager(object):
+ def __init__(self, api, entity):
+ self.api = api
+ self.entity = entity
+
+ def _remove_empty_keys(self, dictionary):
+ for k in dictionary.keys():
+ if dictionary[k] is None:
+ dictionary.pop(k)
diff --git a/barbicanclient/client.py b/barbicanclient/client.py
index 3b08597..4955f8a 100644
--- a/barbicanclient/client.py
+++ b/barbicanclient/client.py
@@ -1,8 +1,10 @@
import json
import os
+import urlparse
import requests
+from barbicanclient import secrets
from barbicanclient.secrets import Secret
from barbicanclient.orders import Order
from barbicanclient.common import auth
@@ -20,9 +22,8 @@ class Client(object):
SECRETS_PATH = 'secrets'
ORDERS_PATH = 'orders'
- def __init__(self, auth=True,
- auth_endpoint=None, user=None, password=None, tenant=None,
- key=None, token=None, **kwargs):
+ def __init__(self, auth_plugin=None, endpoint=None, tenant_id=None,
+ **kwargs):
"""
Authenticate and connect to the service endpoint, which can be
received through authentication.
@@ -30,18 +31,9 @@ class Client(object):
Environment variables will be used by default when their corresponding
arguments are not passed in.
- :param auth: Whether the client should use keystone
- authentication, defaults to True
- :param auth_endpoint: The keystone URL used for authentication
- required if auth=True
- default: env('OS_AUTH_URL')
- :param user: keystone user account, required if auth=True
- default: env('OS_USERNAME')
- :param password: password associated with the user
- required if auth=Tru
- default: env('OS_PASSWORD')
- :param tenant: The tenant ID
- default: env('OS_TENANT_NAME')
+ :param auth_plugin: Authentication backend plugin
+ defaults to None
+ :param endpoint: Barbican endpoint url
:param key: The API key or password to auth with
:keyword param endpoint: The barbican endpoint to connect to
@@ -50,34 +42,49 @@ class Client(object):
LOG.debug(_("Creating Client object"))
- self.env = kwargs.get('fake_env') or env
-
- if auth:
- LOG.debug(_('Using authentication with keystone'))
- self._auth_endpoint = auth_endpoint or self.env('OS_AUTH_URL')
- self._user = user or self.env('OS_USERNAME')
- self._password = password or self.env('OS_PASSWORD')
- self._tenant = tenant or self.env('OS_TENANT_NAME')
- if not all([self._auth_endpoint, self._user,
- self._password, self._tenant]):
- raise ValueError('Authentication requires an endpoint, user, '
- 'password, and tenant.')
- #TODO(dmend): remove these
- self._auth_endpoint = auth_endpoint or self.env('OS_AUTH_URL')
- self._user = user or self.env('OS_USERNAME')
- self._tenant = tenant or self.env('OS_TENANT_NAME')
- self._key = key or self._password
-
- 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 = kwargs.get('authenticate') or auth.authenticate
- self.request = kwargs.get('request') or requests.request
- self._endpoint = (kwargs.get('endpoint') or
- self.env('BARBICAN_ENDPOINT'))
- self._cacert = kwargs.get('cacert')
- self.connect(token=(token or self.env('AUTH_TOKEN')))
+ self._session = requests.Session()
+ self.auth_plugin = auth_plugin
+
+ if self.auth_plugin is not None:
+ self._barbican_url = self.auth_plugin.barbican_url
+ self._tenant_id = self.auth_plugin.tenant_id
+ self._session.headers.update(
+ {'X-Auth-Token': self.auth_plugin.auth_token}
+ )
+ else:
+ if endpoint is None:
+ raise ValueError('Barbican endpoint url must be provided, or '
+ 'must be available from auth_plugin')
+ if tenant_id is None:
+ raise ValueError('Tenant ID must be provided, or must be available'
+ ' from auth_plugin')
+ if endpoint.endswith('/'):
+ self._barbican_url = endpoint[:-1]
+ else:
+ self._barbican_url = endpoint
+ self._tenant_id = tenant_id
+
+ self.base_url = '{0}/{1}'.format(self._barbican_url, self._tenant_id)
+ self.secrets = secrets.SecretManager(self)
+
+ # self.env = kwargs.get('fake_env') or env
+
+ # #TODO(dmend): remove these
+ # self._auth_endpoint = kwargs.get('auth_endpoint') or self.env('OS_AUTH_URL')
+ # self._user = kwargs.get('user') or self.env('OS_USERNAME')
+ # self._tenant = kwargs.get('tenant') or self.env('OS_TENANT_NAME')
+ # self._key = kwargs.get('key')
+
+ # 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 = kwargs.get('authenticate') or auth.authenticate
+ # self.request = kwargs.get('request') or requests.request
+ # self._endpoint = (kwargs.get('endpoint') or
+ # self.env('BARBICAN_ENDPOINT'))
+ # self._cacert = kwargs.get('cacert')
+ # self.connect(token=(kwargs.get('token') or self.env('AUTH_TOKEN')))
@property
def _conn(self):
@@ -194,40 +201,15 @@ class Client(object):
bit_length=None,
cypher_type=None,
expiration=None):
- """
- Creates and returns a Secret object with all of its metadata filled in.
-
- :param name: A friendly name for the secret
- :param payload: The unencrypted secret
- :param payload_content_type: The format/type of the secret
- :param payload_content_encoding: The encoding of the secret
- :param algorithm: The algorithm the secret is used with
- :param bit_length: The bit length of the secret
- :param cypher_type: The cypher type (e.g. block cipher mode)
- :param expiration: The expiration time of the secret in ISO 8601 format
- """
- LOG.debug(_("Creating secret of payload content type {0}").format(
- payload_content_type))
- href = "{0}/{1}".format(self._tenant, self.SECRETS_PATH)
- LOG.debug(_("href: {0}").format(href))
- secret_dict = {}
- secret_dict['name'] = name
- secret_dict['payload'] = payload
- secret_dict['payload_content_type'] = payload_content_type
- secret_dict['payload_content_encoding'] = payload_content_encoding
- secret_dict['algorithm'] = algorithm
- secret_dict['cypher_type'] = cypher_type
- secret_dict['bit_length'] = bit_length
- secret_dict['expiration'] = expiration
- self._remove_empty_keys(secret_dict)
- LOG.debug(_("Request body: {0}").format(secret_dict))
- hdrs, body = self._perform_http(href=href,
- method='POST',
- request_body=json.dumps(secret_dict))
-
- LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body))
-
- return self.get_secret(body['secret_ref'])
+ """Deprecated"""
+ self.secrets.create(name=name,
+ payload=payload,
+ payload_content_type=payload_content_type,
+ payload_content_encoding=payload_content_encoding,
+ algorithm=algorithm,
+ bit_length=bit_length,
+ mode=cypher_type,
+ expiration=expiration)
def delete_secret_by_id(self, secret_id):
"""
@@ -411,11 +393,6 @@ class Client(object):
LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body))
return Order(self._conn, body)
- def _remove_empty_keys(self, dictionary):
- for k in dictionary.keys():
- if dictionary[k] is None:
- dictionary.pop(k)
-
def _perform_http(self, method, href, request_body='', headers={},
parse_json=True):
"""
@@ -457,6 +434,28 @@ class Client(object):
return response.headers, resp_body
+ def _request(self, url, method, headers):
+ resp = self._session.request()
+
+ def get(self, path, params):
+ url = '{0}/{1}/'.format(self.base_url, path)
+ headers = {'content-type': 'application/json'}
+ resp = self._session.get(url, params=params, headers=headers)
+ self._check_status_code(resp)
+ return resp.json()
+
+ def post(self, path, data):
+ url = '{0}/{1}/'.format(self.base_url, path)
+ headers = {'content-type': 'application/json'}
+ resp = self._session.post(url, data=json.dumps(data), headers=headers)
+ self._check_status_code(resp)
+ return resp.json()
+
+ #TODO(dmend): beef this up
+ def _check_status_code(self, resp):
+ status = resp.status_code
+ print('status {0}'.format(status))
+
def env(*vars, **kwargs):
"""Search for the first defined of possibly many env vars
diff --git a/barbicanclient/common/auth.py b/barbicanclient/common/auth.py
index 9f6051b..205d91d 100644
--- a/barbicanclient/common/auth.py
+++ b/barbicanclient/common/auth.py
@@ -35,6 +35,9 @@ class KeystoneAuth(object):
self._service_type = 'keystore'
self._endpoint_type = 'publicURL'
+ self.tenant_name = self._keystone.tenant_name
+ self.tenant_id = self._keystone.tenant_id
+
@property
def auth_token(self):
return self._keystone.auth_token
diff --git a/barbicanclient/secrets.py b/barbicanclient/secrets.py
index d5ad472..c052940 100644
--- a/barbicanclient/secrets.py
+++ b/barbicanclient/secrets.py
@@ -1,6 +1,13 @@
from urlparse import urlparse
+
+from openstack.common import log as logging
from openstack.common.timeutils import parse_isotime
+from barbicanclient import base
+
+
+LOG = logging.getLogger(__name__)
+
class Secret(object):
@@ -8,12 +15,10 @@ class Secret(object):
A secret is any data the user has stored in the key management system.
"""
- def __init__(self, connection, secret_dict):
+ def __init__(self, secret_dict):
"""
- Builds a secret object from a json representation. Includes the
- connection object for subtasks.
+ Builds a secret object from a dictionary.
"""
- self.connection = connection
self.secret_ref = secret_dict.get('secret_ref')
self.created = parse_isotime(secret_dict.get('created'))
self.status = secret_dict.get('status')
@@ -60,3 +65,66 @@ class Secret(object):
self.payload_content_encoding, self.bit_length,
self.algorithm, self.cypher_type, self.expiration)
)
+
+
+class SecretManager(base.BaseEntityManager):
+
+ def __init__(self, api):
+ super(SecretManager, self).__init__(api, 'secrets')
+
+ def create(self,
+ name=None,
+ payload=None,
+ payload_content_type=None,
+ payload_content_encoding=None,
+ algorithm=None,
+ bit_length=None,
+ mode=None,
+ expiration=None):
+ """
+ Stores a new secret in Barbican
+
+ :param name: A friendly name for the secret
+ :param payload: The unencrypted secret data
+ :param payload_content_type: The format/type of the secret data
+ :param payload_content_encoding: The encoding of the secret data
+ :param algorithm: The algorithm barbican should use to encrypt
+ :param bit_length: The bit length of the key used for ecnryption
+ :param mode: The algorithm mode (e.g. CBC or CTR mode)
+ :param expiration: The expiration time of the secret in ISO 8601 format
+ :returns: Secret ID for the stored secret
+ """
+ LOG.debug("Creating secret of payload content type {0}".format(
+ payload_content_type))
+ href = self.entity
+ LOG.debug("href: {0}".format(href))
+
+ secret_dict = dict()
+ secret_dict['name'] = name
+ secret_dict['payload'] = payload
+ secret_dict['payload_content_type'] = payload_content_type
+ secret_dict['payload_content_encoding'] = payload_content_encoding
+ secret_dict['algorithm'] = algorithm
+ #TODO(dmend): Change this to 'mode'
+ secret_dict['cypher_type'] = mode
+ secret_dict['bit_length'] = bit_length
+ secret_dict['expiration'] = expiration
+ self._remove_empty_keys(secret_dict)
+
+ LOG.debug("Request body: {0}".format(secret_dict))
+
+ resp = self.api.post(self.entity, secret_dict)
+ #TODO(dmend): return secret object?
+ #secret = Secret(resp)
+ secret_id = resp['secret_ref'].split('/')[-1]
+
+ return secret_id
+
+ def list(self, limit=10, offset=0):
+
+ LOG.debug('Listing secrets - offset {0} limit {1}'.format(offset,
+ limit))
+ params = {'limit': limit, 'offset': offset}
+ resp = self.api.get(self.entity, params)
+
+ return resp
diff --git a/barbicanclient/test/common/test_auth.py b/barbicanclient/test/common/test_auth.py
index 8376d1b..d91e970 100644
--- a/barbicanclient/test/common/test_auth.py
+++ b/barbicanclient/test/common/test_auth.py
@@ -18,12 +18,6 @@ from barbicanclient.common import auth
class WhenTestingKeystoneAuthentication(unittest.TestCase):
- def setUp(self):
- self.keystone = auth.KeystoneAuth(endpoint='endpoint_url',
- username='user',
- password='password',
- tenant_name='demo')
-
def test_endpoint_username_password_tenant_are_required(self):
with self.assertRaises(ValueError):
keystone = auth.KeystoneAuth()
diff --git a/barbicanclient/test/test_client.py b/barbicanclient/test/test_client.py
index 8ef1e6b..418dba9 100644
--- a/barbicanclient/test/test_client.py
+++ b/barbicanclient/test/test_client.py
@@ -29,10 +29,6 @@ class WhenTestingClient(unittest.TestCase):
self.user = 'user'
self.password = 'password'
self.tenant = 'tenant'
- self.keystone = auth.KeystoneAuth(endpoint=self.auth_endpoint,
- username=self.user,
- password=self.password,
- tenant_name=self.tenant)
self.key = 'key'
self.endpoint = 'http://localhost:9311/v1/'
@@ -66,17 +62,7 @@ class WhenTestingClient(unittest.TestCase):
authenticate=self.authenticate,
request=self.request,
endpoint=self.endpoint,
- auth=False)
-
- def test_authenticated_client_requires_endpoint_user_pw_tenant(self):
- with self.assertRaises(ValueError):
- c = client.Client(auth=True)
- with self.assertRaises(ValueError):
- c = client.Client() # default auth=True
- c=client.Client(auth_endpoint=self.auth_endpoint, user=self.user,
- password=self.password, tenant=self.tenant,
- #TODO(dmend): remove authenticate below
- authenticate=self.authenticate)
+ tenant_id='test_tenant')
def test_should_connect_with_token(self):
self.assertFalse(self.authenticate.called)
@@ -88,7 +74,8 @@ class WhenTestingClient(unittest.TestCase):
key=self.key,
tenant=self.tenant,
authenticate=self.authenticate,
- endpoint=self.endpoint)
+ endpoint=self.endpoint,
+ tenant_id='test_tenant')
self.authenticate\
.assert_called_once_with(self.auth_endpoint,
self.user,
@@ -116,7 +103,8 @@ class WhenTestingClient(unittest.TestCase):
token=self.auth_token,
authenticate=self.authenticate,
request=self.request,
- endpoint=self.endpoint)
+ endpoint=self.endpoint,
+ tenant_id='test_tenant')
def test_should_create_secret(self):
body = {'status': "ACTIVE",