summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wood <john.wood@rackspace.com>2013-12-03 12:50:25 -0800
committerJohn Wood <john.wood@rackspace.com>2013-12-03 12:50:25 -0800
commit0254b25ab6d5d97320031f36d1383809a81e96f5 (patch)
tree0499601e433b21efbc49972fdbc94ef87a241529
parent013ddd9e16c2ce369e86c08f5157d73834ca0af1 (diff)
parent537ca7b06eeae6a303d5677549460edc8f2b16b4 (diff)
downloadpython-barbicanclient-0254b25ab6d5d97320031f36d1383809a81e96f5.tar.gz
Merge pull request #28 from jfwood/master
Add verifications entity logic to the client library.
-rw-r--r--.gitignore2
-rw-r--r--barbicanclient/base.py2
-rw-r--r--barbicanclient/client.py12
-rw-r--r--barbicanclient/common/auth.py13
-rw-r--r--barbicanclient/common/config.py45
-rw-r--r--barbicanclient/common/utils.py10
-rw-r--r--barbicanclient/keep.py93
-rw-r--r--barbicanclient/orders.py19
-rw-r--r--barbicanclient/secrets.py6
-rw-r--r--barbicanclient/test/common/test_auth.py23
-rw-r--r--barbicanclient/test/test_client.py119
-rw-r--r--barbicanclient/test/test_client_orders.py131
-rw-r--r--barbicanclient/test/test_client_secrets.py185
-rw-r--r--barbicanclient/test/test_client_verifications.py135
-rw-r--r--barbicanclient/verifications.py145
15 files changed, 848 insertions, 92 deletions
diff --git a/.gitignore b/.gitignore
index 00f981f..db9daae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,8 @@ pip-log.txt
.coverage
.tox
nosetests.xml
+coverage.xml
+flake8.log
# pyenv
.python-version
diff --git a/barbicanclient/base.py b/barbicanclient/base.py
index ca157a1..5e63f88 100644
--- a/barbicanclient/base.py
+++ b/barbicanclient/base.py
@@ -29,7 +29,7 @@ class BaseEntityManager(object):
def total(self):
"""
- Returns the toatl number of entities stored in Barbican.
+ Returns the total number of entities stored in Barbican.
"""
href = '{0}/{1}'.format(self.api.base_url, self.entity)
params = {'limit': 0, 'offset': 0}
diff --git a/barbicanclient/client.py b/barbicanclient/client.py
index 5888311..a03ec48 100644
--- a/barbicanclient/client.py
+++ b/barbicanclient/client.py
@@ -21,6 +21,7 @@ import requests
from barbicanclient.openstack.common.gettextutils import _
from barbicanclient import orders
from barbicanclient import secrets
+from barbicanclient import verifications
LOG = logging.getLogger(__name__)
@@ -49,8 +50,8 @@ class HTTPAuthError(HTTPError):
class Client(object):
- def __init__(self, auth_plugin=None, endpoint=None, tenant_id=None,
- insecure=False):
+ def __init__(self, session=None, auth_plugin=None, endpoint=None,
+ tenant_id=None, insecure=False):
"""
Barbican client object used to interact with barbican service.
@@ -65,7 +66,7 @@ class Client(object):
"""
LOG.debug(_("Creating Client object"))
- self._session = requests.Session()
+ self._session = session or requests.Session()
self.verify = not insecure
self.auth_plugin = auth_plugin
@@ -98,6 +99,7 @@ class Client(object):
self.base_url = '{0}/{1}'.format(self._barbican_url, self._tenant_id)
self.secrets = secrets.SecretManager(self)
self.orders = orders.OrderManager(self)
+ self.verifications = verifications.VerificationManager(self)
def get(self, href, params=None):
headers = {'Accept': 'application/json'}
@@ -124,12 +126,12 @@ class Client(object):
return resp.json()
def _check_status_code(self, resp):
- status = resp.status_code
+ status = resp.status_code if resp else None
LOG.debug('Response status {0}'.format(status))
if status == 401:
LOG.error('Auth error: {0}'.format(self._get_error_message(resp)))
raise HTTPAuthError('{0}'.format(self._get_error_message(resp)))
- if status >= 500:
+ if not status or status >= 500:
LOG.error('5xx Server error: {0}'.format(
self._get_error_message(resp)
))
diff --git a/barbicanclient/common/auth.py b/barbicanclient/common/auth.py
index 2c91c58..c663191 100644
--- a/barbicanclient/common/auth.py
+++ b/barbicanclient/common/auth.py
@@ -17,6 +17,7 @@ import logging
from keystoneclient.v2_0 import client as ksclient
from keystoneclient import exceptions
+
LOG = logging.getLogger(__name__)
@@ -28,15 +29,15 @@ class AuthException(Exception):
class KeystoneAuthV2(object):
def __init__(self, auth_url='', username='', password='',
- tenant_name='', tenant_id='', insecure=False):
+ tenant_name='', tenant_id='', insecure=False, keystone=None):
if not all([auth_url, username, password, tenant_name or tenant_id]):
raise ValueError('Please provide auth_url, username, password,'
' and tenant_id or tenant_name)')
- self._keystone = ksclient.Client(username=username,
- password=password,
- tenant_name=tenant_name,
- auth_url=auth_url,
- insecure=insecure)
+ self._keystone = keystone or ksclient.Client(username=username,
+ password=password,
+ tenant_name=tenant_name,
+ auth_url=auth_url,
+ insecure=insecure)
self._barbican_url = None
#TODO(dmend): make these configurable
self._service_type = 'keystore'
diff --git a/barbicanclient/common/config.py b/barbicanclient/common/config.py
index 865b748..4f68f99 100644
--- a/barbicanclient/common/config.py
+++ b/barbicanclient/common/config.py
@@ -46,48 +46,3 @@ def parse_args(args=None, usage=None, default_config_files=None):
version=__version__,
usage=usage,
default_config_files=default_config_files)
-
-
-def setup_logging():
- """
- Sets up the logging options
- """
-
- if CONF.log_config:
- # Use a logging configuration file for all settings...
- if os.path.exists(CONF.log_config):
- logging.config.fileConfig(CONF.log_config)
- return
- else:
- raise RuntimeError("Unable to locate specified logging "
- "config file: %s" % CONF.log_config)
-
- root_logger = logging.root
- if CONF.debug:
- root_logger.setLevel(logging.DEBUG)
- elif CONF.verbose:
- root_logger.setLevel(logging.INFO)
- else:
- root_logger.setLevel(logging.WARNING)
-
- formatter = logging.Formatter(CONF.log_format, CONF.log_date_format)
-
- if CONF.use_syslog:
- try:
- facility = getattr(logging.handlers.SysLogHandler,
- CONF.syslog_log_facility)
- except AttributeError:
- raise ValueError(_("Invalid syslog facility"))
-
- handler = logging.handlers.SysLogHandler(address='/dev/log',
- facility=facility)
- elif CONF.log_file:
- logfile = CONF.log_file
- if CONF.log_dir:
- logfile = os.path.join(CONF.log_dir, logfile)
- handler = logging.handlers.WatchedFileHandler(logfile)
- else:
- handler = logging.StreamHandler(sys.stdout)
-
- handler.setFormatter(formatter)
- root_logger.addHandler(handler)
diff --git a/barbicanclient/common/utils.py b/barbicanclient/common/utils.py
deleted file mode 100644
index 55b1a0d..0000000
--- a/barbicanclient/common/utils.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import urllib
-
-
-def proc_template(template, **kwargs):
- """
- Processes a templated URL by substituting the
- dictionary args and returning the strings.
- """
- return template.format(**dict([(k, urllib.quote(v))
- for k, v in kwargs.items()]))
diff --git a/barbicanclient/keep.py b/barbicanclient/keep.py
index eead191..f0e5f77 100644
--- a/barbicanclient/keep.py
+++ b/barbicanclient/keep.py
@@ -36,6 +36,7 @@ class Keep:
self._add_store_args()
self._add_get_args()
self._add_list_args()
+ self._add_verify_args()
self._add_delete_args()
def _get_main_parser(self):
@@ -44,9 +45,9 @@ class Keep:
)
parser.add_argument('command',
metavar='<entity>',
- choices=['order', 'secret'],
+ choices=['order', 'secret', 'verification'],
help='Entity used for command, e.g.,'
- ' order, secret.')
+ ' order, secret, verification.')
auth_group = parser.add_mutually_exclusive_group()
auth_group.add_argument('--no-auth', '-N', action='store_true',
help='Do not use authentication.')
@@ -84,6 +85,27 @@ class Keep:
'option should be used with caution.')
return parser
+ def _add_verify_args(self):
+ verify_parser = self.subparsers.add_parser('verify',
+ help='Create a new '
+ 'verification.')
+ verify_parser.add_argument('--type', '-t', default='image',
+ help='resource type to verify, '
+ 'such as "image".')
+
+ verify_parser.add_argument('--ref', '-r',
+ help='reference URI to '
+ 'resource to verify.')
+
+ verify_parser.add_argument('--action', '-a', default='vm_attach',
+ help='action to perform on '
+ 'resource, such as "vm_attach".')
+
+ verify_parser.add_argument('--impersonation', '-i', default=True,
+ help='is impersonation allowed '
+ 'for the resource.')
+ verify_parser.set_defaults(func=self.verify)
+
def _add_create_args(self):
create_parser = self.subparsers.add_parser('create',
help='Create a new order.')
@@ -104,7 +126,8 @@ class Keep:
default='application/octet-stream',
help='the type/format of the secret to be'
' generated (default: %(default)s).')
- create_parser.add_argument('--expiration', '-x', help='the expiration '
+ create_parser.add_argument('--expiration', '-x',
+ help='the expiration '
'time for the secret in ISO 8601 format.')
create_parser.set_defaults(func=self.create)
@@ -116,7 +139,8 @@ class Keep:
store_parser.add_argument('--name', '-n',
help='a human-friendly name.')
store_parser.add_argument('--payload', '-p', help='the unencrypted'
- ' secret; if provided, you must also provide'
+ ' secret; if provided, '
+ 'you must also provide'
' a payload_content_type')
store_parser.add_argument('--payload-content-type', '-t',
help='the type/format of the provided '
@@ -127,7 +151,8 @@ class Keep:
help='required if --payload-content-type is'
' "application/octet-stream".')
store_parser.add_argument('--algorithm', '-a', default='aes',
- help='the algorithm (default: %(default)s).')
+ help='the algorithm (default: '
+ '%(default)s).')
store_parser.add_argument('--bit-length', '-b', default=256,
help='the bit length '
'(default: %(default)s).',
@@ -142,19 +167,23 @@ class Keep:
def _add_delete_args(self):
delete_parser = self.subparsers.add_parser(
'delete',
- help='Delete a secret or an order by providing its href.'
+ help='Delete a secret, order or '
+ 'verification by providing its href.'
)
delete_parser.add_argument('URI', help='The URI reference for the'
- ' secret or order')
+ ' secret, order '
+ 'or verification')
delete_parser.set_defaults(func=self.delete)
def _add_get_args(self):
get_parser = self.subparsers.add_parser(
'get',
- help='Retrieve a secret or an order by providing its URI.'
+ help='Retrieve a secret, order or '
+ 'verification by providing its URI.'
)
- get_parser.add_argument('URI', help='The URI reference for the secret'
- ' or order.')
+ get_parser.add_argument('URI', help='The URI reference '
+ 'for the secret, '
+ 'order or verification.')
get_parser.add_argument('--decrypt', '-d', help='if specified, keep'
' will retrieve the unencrypted secret data;'
' the data type can be specified with'
@@ -170,9 +199,11 @@ class Keep:
def _add_list_args(self):
list_parser = self.subparsers.add_parser('list',
- help='List secrets or orders')
- list_parser.add_argument('--limit', '-l', default=10, help='specify t'
- 'he limit to the number of items to list per'
+ help='List secrets, '
+ 'orders or '
+ 'verifications')
+ list_parser.add_argument('--limit', '-l', default=10, help='specify '
+ 'the limit to the number of items to list per'
' page (default: %(default)s; maximum: 100)',
type=int)
list_parser.add_argument('--offset', '-o', default=0, help='specify t'
@@ -211,8 +242,14 @@ class Keep:
def delete(self, args):
if args.command == 'secret':
self.client.secrets.delete(args.URI)
- else:
+ elif args.command == 'verification':
+ self.client.verifications.delete(args.URI)
+ elif args.command == 'order':
self.client.orders.delete(args.URI)
+ else:
+ self.parser.exit(status=1, message='ERROR: delete is only '
+ 'supported for secrets, '
+ 'orders or verifications\n')
def get(self, args):
if args.command == 'secret':
@@ -221,19 +258,43 @@ class Keep:
args.payload_content_type)
else:
print self.client.secrets.get(args.URI)
- else:
+ elif args.command == 'verification':
+ print self.client.verifications.get(args.URI)
+ elif args.command == 'order':
print self.client.orders.get(args.URI)
+ else:
+ self.parser.exit(status=1, message='ERROR: get is only '
+ 'supported for secrets, '
+ 'orders or verifications\n')
def list(self, args):
if args.command == 'secret':
ls = self.client.secrets.list(args.limit, args.offset)
- else:
+ elif args.command == 'verification':
+ ls = self.client.verifications.list(args.limit, args.offset)
+ elif args.command == 'order':
ls = self.client.orders.list(args.limit, args.offset)
+ else:
+ self.parser.exit(status=1, message='ERROR: get list is only '
+ 'supported for secrets, '
+ 'orders or verifications\n')
for obj in ls:
print obj
print '{0}s displayed: {1} - offset: {2}'.format(args.command, len(ls),
args.offset)
+ def verify(self, args):
+ if args.command == 'verification':
+ verify = self.client.verifications\
+ .create(resource_type=args.type,
+ resource_ref=args.ref,
+ resource_action=args.action,
+ impersonation_allowed=args.impersonation)
+ print verify
+ else:
+ self.parser.exit(status=1, message='ERROR: verify is only '
+ 'supported for verifications\n')
+
def execute(self, **kwargs):
args = self.parser.parse_args(kwargs.get('argv'))
if args.no_auth:
diff --git a/barbicanclient/orders.py b/barbicanclient/orders.py
index 3747306..7b2f3e3 100644
--- a/barbicanclient/orders.py
+++ b/barbicanclient/orders.py
@@ -29,6 +29,9 @@ class Order(object):
Builds an order object from a dictionary.
"""
self.order_ref = order_dict['order_ref']
+
+ self.error_status_code = order_dict.get('error_status_code', None)
+ self.error_reason = order_dict.get('error_reason', None)
self.status = order_dict.get('status')
self.created = timeutils.parse_isotime(order_dict['created'])
if order_dict.get('updated') is not None:
@@ -38,13 +41,19 @@ class Order(object):
self.secret_ref = order_dict.get('secret_ref')
def __str__(self):
- return ("Order - order href: {0}\n"
+ strg = ("Order - order href: {0}\n"
" secret href: {1}\n"
" created: {2}\n"
" status: {3}\n"
- .format(self.order_ref, self.secret_ref,
- self.created, self.status)
- )
+ ).format(self.order_ref, self.secret_ref,
+ self.created, self.status)
+
+ if self.error_status_code:
+ strg = ''.join([strg, (" error_status_code: {0}\n"
+ " error_reason: {1}\n"
+ ).format(self.error_status_code,
+ self.error_reason)])
+ return strg
def __repr__(self):
return 'Order(order_ref={0})'.format(self.order_ref)
@@ -121,7 +130,7 @@ class OrderManager(base.BaseEntityManager):
:param offset: Offset orders to begin list
:returns: list of Order objects
"""
- LOG.debug('Listing orders - offest {0} limit {1}'.format(offset,
+ LOG.debug('Listing orders - offset {0} limit {1}'.format(offset,
limit))
href = '{0}/{1}'.format(self.api.base_url, self.entity)
params = {'limit': limit, 'offset': offset}
diff --git a/barbicanclient/secrets.py b/barbicanclient/secrets.py
index a479f2c..a1e9b12 100644
--- a/barbicanclient/secrets.py
+++ b/barbicanclient/secrets.py
@@ -140,6 +140,12 @@ class SecretManager(base.BaseEntityManager):
raise ValueError('secret_ref is required.')
if not content_type:
secret = self.get(secret_ref)
+ if secret.content_types is None:
+ raise ValueError('Secret has no encrypted data to decrypt.')
+ if 'default' not in secret.content_types:
+ raise ValueError("Must specify decrypt content-type as "
+ "secret does not specify a 'default' "
+ "content-type.")
content_type = secret.content_types['default']
headers = {'Accept': content_type}
return self.api.get_raw(secret_ref, headers)
diff --git a/barbicanclient/test/common/test_auth.py b/barbicanclient/test/common/test_auth.py
index 640301e..d31bcef 100644
--- a/barbicanclient/test/common/test_auth.py
+++ b/barbicanclient/test/common/test_auth.py
@@ -12,12 +12,35 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+
+import mock
import unittest2 as unittest
from barbicanclient.common import auth
class WhenTestingKeystoneAuthentication(unittest.TestCase):
+
+ def setUp(self):
+ self.keystone_client = mock.MagicMock()
+
+ self.auth_url = 'https://www.yada.com'
+ self.username = 'user'
+ self.password = 'pw'
+ self.tenant_id = '1234'
+
+ self.keystone_auth = auth.KeystoneAuthV2(auth_url=self.auth_url,
+ username=self.username,
+ password=self.password,
+ tenant_id=self.tenant_id,
+ keystone=
+ self.keystone_client)
+
def test_endpoint_username_password_tenant_are_required(self):
with self.assertRaises(ValueError):
keystone = auth.KeystoneAuthV2()
+
+ def test_get_barbican_url(self):
+ barbican_url = 'https://www.barbican.com'
+ self.keystone_auth._barbican_url = barbican_url
+ self.assertEquals(barbican_url, self.keystone_auth.barbican_url)
diff --git a/barbicanclient/test/test_client.py b/barbicanclient/test/test_client.py
index c464568..8905264 100644
--- a/barbicanclient/test/test_client.py
+++ b/barbicanclient/test/test_client.py
@@ -13,13 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import json
-
import mock
import unittest2 as unittest
from barbicanclient import client
-from barbicanclient.common import auth
+from barbicanclient.openstack.common import timeutils
+from barbicanclient.openstack.common import jsonutils
class FakeAuth(object):
@@ -30,7 +29,24 @@ class FakeAuth(object):
self.tenant_id = tenant_id
-class WhenTestingClient(unittest.TestCase):
+class FakeResp(object):
+ def __init__(self, status_code, response_dict=None, content=None):
+ self.status_code = status_code
+ self.response_dict = response_dict
+ self.content = content
+
+ def json(self):
+ if self.response_dict is None:
+ return None
+ resp = self.response_dict
+ resp['title'] = 'some title here'
+ return resp
+
+ def content(self):
+ return self.content
+
+
+class WhenTestingClientInit(unittest.TestCase):
def setUp(self):
self.auth_endpoint = 'https://localhost:5000/v2.0/'
self.auth_token = 'fake_auth_token'
@@ -91,3 +107,98 @@ class WhenTestingClient(unittest.TestCase):
c = client.Client(auth_plugin=self.fake_auth)
with self.assertRaises(client.HTTPClientError):
c._check_status_code(resp)
+
+
+class WhenTestingClientWithSession(unittest.TestCase):
+ def setUp(self):
+ self.endpoint = 'https://localhost:9311/v1/'
+ self.tenant_id = '1234567'
+
+ self.entity = 'dummy-entity'
+ base = self.endpoint + self.tenant_id + "/"
+ self.entity_base = base + self.entity + "/"
+ self.entity_href = self.entity_base + '1234'
+
+ self.entity_name = 'name'
+ self.entity_dict = {'name': self.entity_name}
+
+ self.session = mock.MagicMock()
+
+ self.client = client.Client(session=self.session,
+ endpoint=self.endpoint,
+ tenant_id=self.tenant_id)
+
+ def test_should_post(self):
+ self.session.post.return_value = FakeResp(200, {'entity_ref':
+ self.entity_href})
+
+ resp_dict = self.client.post(self.entity, self.entity_dict)
+
+ self.assertEqual(self.entity_href, resp_dict['entity_ref'])
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.session.post.call_args
+ url = args[0]
+ self.assertEqual(self.entity_base, url)
+
+ # Verify that correct information was sent in the call.
+ data = jsonutils.loads(kwargs['data'])
+ self.assertEqual(self.entity_name, data['name'])
+
+ def test_should_get(self):
+ self.session.get.return_value = FakeResp(200, {'name':
+ self.entity_name})
+
+ resp_dict = self.client.get(self.entity_href)
+
+ self.assertEqual(self.entity_name, resp_dict['name'])
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.session.get.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ # Verify that correct information was sent in the call.
+ headers = kwargs['headers']
+ self.assertEqual('application/json', headers['Accept'])
+
+ def test_should_get_raw(self):
+ self.session.get.return_value = FakeResp(200, content='content')
+
+ headers = {'Accept': 'application/octet-stream'}
+ content = self.client.get_raw(self.entity_href, headers)
+
+ self.assertEqual('content', content)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.session.get.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ # Verify that correct information was sent in the call.
+ headers = kwargs['headers']
+ self.assertEqual('application/octet-stream', headers['Accept'])
+
+ def test_should_delete(self):
+ self.session.delete.return_value = FakeResp(200)
+
+ self.client.delete(self.entity_href)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.session.delete.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+
+class BaseEntityResource(unittest.TestCase):
+ def _setUp(self, entity):
+ self.endpoint = 'https://localhost:9311/v1/'
+ self.tenant_id = '1234567'
+
+ self.entity = entity
+ base = self.endpoint + self.tenant_id + "/"
+ self.entity_base = base + self.entity + "/"
+ self.entity_href = self.entity_base + '1234'
+
+ self.api = mock.MagicMock()
+ self.api.base_url = base[:-1]
diff --git a/barbicanclient/test/test_client_orders.py b/barbicanclient/test/test_client_orders.py
new file mode 100644
index 0000000..b6f1d99
--- /dev/null
+++ b/barbicanclient/test/test_client_orders.py
@@ -0,0 +1,131 @@
+# 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.
+
+from barbicanclient import orders
+from barbicanclient.openstack.common import timeutils
+from barbicanclient.test import test_client
+from barbicanclient.test import test_client_secrets as test_secrets
+
+
+class OrderData(object):
+ def __init__(self):
+ self.created = str(timeutils.utcnow())
+
+ self.secret = test_secrets.SecretData()
+ self.status = 'ACTIVE'
+ self.order_dict = {'created': self.created,
+ 'status': self.status,
+ 'secret': self.secret.get_dict()}
+
+ def get_dict(self, order_ref, secret_ref=None):
+ order = self.order_dict
+ order['order_ref'] = order_ref
+ if secret_ref:
+ order['secret_ref'] = secret_ref
+ return order
+
+
+class WhenTestingOrders(test_client.BaseEntityResource):
+
+ def setUp(self):
+ self._setUp('orders')
+
+ self.order = OrderData()
+
+ self.manager = orders.OrderManager(self.api)
+
+ def test_should_entity_str(self):
+ order_obj = orders.Order(self.order.get_dict(self.entity_href))
+ order_obj.error_status_code = '500'
+ order_obj.error_reason = 'Something is broken'
+ self.assertIn('status: ' + self.order.status,
+ str(order_obj))
+ self.assertIn('error_status_code: 500', str(order_obj))
+
+ def test_should_entity_repr(self):
+ order_obj = orders.Order(self.order.get_dict(self.entity_href))
+ self.assertIn('order_ref=' + self.entity_href,
+ repr(order_obj))
+
+ def test_should_create(self):
+ self.api.post.return_value = {'order_ref': self.entity_href}
+
+ order_href = self.manager\
+ .create(name=self.order.secret.name,
+ algorithm=self.order.secret.algorithm,
+ payload_content_type=self.order.secret.content)
+
+ self.assertEqual(self.entity_href, order_href)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.post.call_args
+ entity_resp = args[0]
+ self.assertEqual(self.entity, entity_resp)
+
+ # Verify that correct information was sent in the call.
+ order_req = args[1]
+ self.assertEqual(self.order.secret.name, order_req['secret']['name'])
+ self.assertEqual(self.order.secret.algorithm,
+ order_req['secret']['algorithm'])
+ self.assertEqual(self.order.secret.payload_content_type,
+ order_req['secret']['payload_content_type'])
+
+ def test_should_get(self):
+ self.api.get.return_value = self.order.get_dict(self.entity_href)
+
+ order = self.manager.get(order_ref=self.entity_href)
+ self.assertIsInstance(order, orders.Order)
+ self.assertEqual(self.entity_href, order.order_ref)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.get.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ def test_should_delete(self):
+ self.manager.delete(order_ref=self.entity_href)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.delete.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ def test_should_get_list(self):
+ order_resp = self.order.get_dict(self.entity_href)
+ self.api.get.return_value = {"orders":
+ [order_resp for v in xrange(3)]}
+
+ orders_list = self.manager.list(limit=10, offset=5)
+ self.assertTrue(len(orders_list) == 3)
+ self.assertIsInstance(orders_list[0], orders.Order)
+ self.assertEqual(self.entity_href, orders_list[0].order_ref)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.get.call_args
+ url = args[0]
+ self.assertEqual(self.entity_base[:-1], url)
+
+ # Verify that correct information was sent in the call.
+ params = args[1]
+ self.assertEqual(10, params['limit'])
+ self.assertEqual(5, params['offset'])
+
+ def test_should_fail_get_no_href(self):
+ with self.assertRaises(ValueError):
+ self.manager.get(None)
+
+ def test_should_fail_delete_no_href(self):
+ with self.assertRaises(ValueError):
+ self.manager.delete(None)
diff --git a/barbicanclient/test/test_client_secrets.py b/barbicanclient/test/test_client_secrets.py
new file mode 100644
index 0000000..2645314
--- /dev/null
+++ b/barbicanclient/test/test_client_secrets.py
@@ -0,0 +1,185 @@
+# 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.
+
+from barbicanclient.test import test_client
+from barbicanclient import secrets
+from barbicanclient.openstack.common import timeutils
+
+
+class SecretData(object):
+ def __init__(self):
+ self.name = 'Self destruction sequence'
+ self.payload = 'the magic words are squeamish ossifrage'
+ self.payload_content_type = 'text/plain'
+ self.content = 'text/plain'
+ self.algorithm = 'AES'
+ self.created = str(timeutils.utcnow())
+
+ self.secret_dict = {'name': self.name,
+ 'status': 'ACTIVE',
+ 'algorithm': self.algorithm,
+ 'created': self.created}
+
+ def get_dict(self, secret_ref=None, content_types_dict=None):
+ secret = self.secret_dict
+ if secret_ref:
+ secret['secret_ref'] = secret_ref
+ if content_types_dict:
+ secret['content_types'] = content_types_dict
+ return secret
+
+
+class WhenTestingSecrets(test_client.BaseEntityResource):
+
+ def setUp(self):
+ self._setUp('secrets')
+
+ self.secret = SecretData()
+
+ self.manager = secrets.SecretManager(self.api)
+
+ def test_should_entity_str(self):
+ secret_obj = secrets.Secret(self.secret.get_dict(self.entity_href))
+ self.assertIn('name: ' + self.secret.name,
+ str(secret_obj))
+
+ def test_should_entity_repr(self):
+ secret_obj = secrets.Secret(self.secret.get_dict(self.entity_href))
+ self.assertIn('name="{0}"'.format(self.secret.name), repr(secret_obj))
+
+ def test_should_store(self):
+ self.api.post.return_value = {'secret_ref': self.entity_href}
+
+ secret_href = self.manager\
+ .store(name=self.secret.name,
+ payload=self.secret.payload,
+ payload_content_type=self.secret.content)
+
+ self.assertEqual(self.entity_href, secret_href)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.post.call_args
+ entity_resp = args[0]
+ self.assertEqual(self.entity, entity_resp)
+
+ # Verify that correct information was sent in the call.
+ secret_req = args[1]
+ self.assertEqual(self.secret.name, secret_req['name'])
+ self.assertEqual(self.secret.payload, secret_req['payload'])
+ self.assertEqual(self.secret.payload_content_type,
+ secret_req['payload_content_type'])
+
+ def test_should_get(self):
+ self.api.get.return_value = self.secret.get_dict(self.entity_href)
+
+ secret = self.manager.get(secret_ref=self.entity_href)
+ self.assertIsInstance(secret, secrets.Secret)
+ self.assertEqual(self.entity_href, secret.secret_ref)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.get.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ def test_should_decrypt_with_content_type(self):
+ decrypted = 'decrypted text here'
+ self.api.get_raw.return_value = decrypted
+
+ secret = self.manager.decrypt(secret_ref=self.entity_href,
+ content_type='application/octet-stream')
+ self.assertEqual(decrypted, secret)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.get_raw.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ # Verify that correct information was sent in the call.
+ headers = args[1]
+ self.assertEqual('application/octet-stream', headers['Accept'])
+
+ def test_should_decrypt_without_content_type(self):
+ content_types_dict = {'default': 'application/octet-stream'}
+ self.api.get.return_value = self.secret.get_dict(self.entity_href,
+ content_types_dict)
+ decrypted = 'decrypted text here'
+ self.api.get_raw.return_value = decrypted
+
+ secret = self.manager.decrypt(secret_ref=self.entity_href)
+ self.assertEqual(decrypted, secret)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.get.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.get_raw.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ # Verify that correct information was sent in the call.
+ headers = args[1]
+ self.assertEqual('application/octet-stream', headers['Accept'])
+
+ def test_should_delete(self):
+ self.manager.delete(secret_ref=self.entity_href)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.delete.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ def test_should_get_list(self):
+ secret_resp = self.secret.get_dict(self.entity_href)
+ self.api.get.return_value = {"secrets":
+ [secret_resp for v in xrange(3)]}
+
+ secrets_list = self.manager.list(limit=10, offset=5)
+ self.assertTrue(len(secrets_list) == 3)
+ self.assertIsInstance(secrets_list[0], secrets.Secret)
+ self.assertEqual(self.entity_href, secrets_list[0].secret_ref)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.get.call_args
+ url = args[0]
+ self.assertEqual(self.entity_base[:-1], url)
+
+ # Verify that correct information was sent in the call.
+ params = args[1]
+ self.assertEqual(10, params['limit'])
+ self.assertEqual(5, params['offset'])
+
+ def test_should_fail_get_no_href(self):
+ with self.assertRaises(ValueError):
+ self.manager.get(None)
+
+ def test_should_fail_decrypt_no_content_types(self):
+ self.api.get.return_value = self.secret.get_dict(self.entity_href)
+
+ with self.assertRaises(ValueError):
+ self.manager.decrypt(secret_ref=self.entity_href)
+
+ def test_should_fail_decrypt_no_default_content_type(self):
+ content_types_dict = {'no-default': 'application/octet-stream'}
+ self.api.get.return_value = self.secret.get_dict(self.entity_href,
+ content_types_dict)
+
+ with self.assertRaises(ValueError):
+ self.manager.decrypt(secret_ref=self.entity_href)
+
+ def test_should_fail_delete_no_href(self):
+ with self.assertRaises(ValueError):
+ self.manager.delete(None)
diff --git a/barbicanclient/test/test_client_verifications.py b/barbicanclient/test/test_client_verifications.py
new file mode 100644
index 0000000..d7f3b8f
--- /dev/null
+++ b/barbicanclient/test/test_client_verifications.py
@@ -0,0 +1,135 @@
+# 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.
+
+from barbicanclient import verifications as vers
+from barbicanclient.openstack.common import timeutils
+from barbicanclient.test import test_client
+
+
+class VerificationData(object):
+ def __init__(self):
+ self.created = str(timeutils.utcnow())
+
+ self.resource_type = 'image'
+ self.resource_ref = 'http://www.image.com/v1/images/1234'
+ self.resource_action = 'vm_attach'
+ self.impersonation_allowed = True
+
+ self.verification_dict = {'created': self.created,
+ 'resource_type': self.resource_type,
+ 'resource_ref': self.resource_ref,
+ 'resource_action': self.resource_action,
+ 'impersonation_allowed':
+ self.impersonation_allowed}
+
+ def get_dict(self, verification_ref):
+ verify = self.verification_dict
+ verify['verification_ref'] = verification_ref
+ return verify
+
+
+class WhenTestingVerifications(test_client.BaseEntityResource):
+
+ def setUp(self):
+ self._setUp('verifications')
+
+ self.verify = VerificationData()
+
+ self.manager = vers.VerificationManager(self.api)
+
+ def test_should_entity_str(self):
+ verif_obj = vers.Verification(self.verify.get_dict(self.entity_href))
+ verif_obj.error_status_code = '500'
+ verif_obj.error_reason = 'Something is broken'
+ self.assertIn('resource_type: ' + self.verify.resource_type,
+ str(verif_obj))
+ self.assertIn('error_status_code: 500', str(verif_obj))
+
+ def test_should_entity_repr(self):
+ verif = vers.Verification(self.verify.get_dict(self.entity_href))
+ self.assertIn('verification_ref=' + self.entity_href,
+ repr(verif))
+
+ def test_should_create(self):
+ self.api.post.return_value = {'verification_ref': self.entity_href}
+
+ order_href = self.manager\
+ .create(resource_type=self.verify.resource_type,
+ resource_ref=self.verify.resource_ref,
+ resource_action=self.verify.resource_action)
+
+ self.assertEqual(self.entity_href, order_href)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.post.call_args
+ entity_resp = args[0]
+ self.assertEqual(self.entity, entity_resp)
+
+ # Verify that correct information was sent in the call.
+ verify_req = args[1]
+ self.assertEqual(self.verify.resource_type,
+ verify_req['resource_type'])
+ self.assertEqual(self.verify.resource_action,
+ verify_req['resource_action'])
+ self.assertEqual(self.verify.resource_ref,
+ verify_req['resource_ref'])
+
+ def test_should_get(self):
+ self.api.get.return_value = self.verify.get_dict(self.entity_href)
+
+ verify = self.manager.get(verification_ref=self.entity_href)
+ self.assertIsInstance(verify, vers.Verification)
+ self.assertEqual(self.entity_href, verify.verif_ref)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.get.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ def test_should_delete(self):
+ self.manager.delete(verification_ref=self.entity_href)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.delete.call_args
+ url = args[0]
+ self.assertEqual(self.entity_href, url)
+
+ def test_should_get_list(self):
+ verify_resp = self.verify.get_dict(self.entity_href)
+ self.api.get.return_value = {"verifications":
+ [verify_resp for v in xrange(3)]}
+
+ verifies = self.manager.list(limit=10, offset=5)
+ self.assertTrue(len(verifies) == 3)
+ self.assertIsInstance(verifies[0], vers.Verification)
+ self.assertEqual(self.entity_href, verifies[0].verif_ref)
+
+ # Verify the correct URL was used to make the call.
+ args, kwargs = self.api.get.call_args
+ url = args[0]
+ self.assertEqual(self.entity_base[:-1], url)
+
+ # Verify that correct information was sent in the call.
+ params = args[1]
+ self.assertEqual(10, params['limit'])
+ self.assertEqual(5, params['offset'])
+
+ def test_should_fail_get_no_href(self):
+ with self.assertRaises(ValueError):
+ self.manager.get(None)
+
+ def test_should_fail_delete_no_href(self):
+ with self.assertRaises(ValueError):
+ self.manager.delete(None)
diff --git a/barbicanclient/verifications.py b/barbicanclient/verifications.py
new file mode 100644
index 0000000..f5e9225
--- /dev/null
+++ b/barbicanclient/verifications.py
@@ -0,0 +1,145 @@
+# 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.
+from barbicanclient import base
+from barbicanclient.openstack.common.gettextutils import _
+from barbicanclient.openstack.common import log as logging
+from barbicanclient.openstack.common import timeutils
+
+
+LOG = logging.getLogger(__name__)
+
+
+class Verification(object):
+
+ def __init__(self, verif_dict):
+ """
+ Builds a verification object from a dictionary.
+ """
+ self.verif_ref = verif_dict['verification_ref']
+ self.resource_type = verif_dict['resource_type']
+ self.resource_ref = verif_dict['resource_ref']
+ self.resource_action = verif_dict['resource_action']
+ self.impersonation_allowed = verif_dict['impersonation_allowed']
+ self.is_verified = verif_dict.get('is_verified', False)
+
+ self.error_status_code = verif_dict.get('error_status_code', None)
+ self.error_reason = verif_dict.get('error_reason', None)
+ self.status = verif_dict.get('status')
+ self.created = timeutils.parse_isotime(verif_dict['created'])
+ if verif_dict.get('updated') is not None:
+ self.updated = timeutils.parse_isotime(verif_dict['updated'])
+ else:
+ self.updated = None
+
+ def __str__(self):
+ strg = ("Verification - verification href: {0}\n"
+ " resource_type: {1}\n"
+ " resource_ref: {2}\n"
+ " resource_action: {3}\n"
+ " impersonation_allowed: {4}\n"
+ " is_verified: {5}\n"
+ " created: {6}\n"
+ " status: {7}\n"
+ ).format(self.verif_ref,
+ self.resource_type,
+ self.resource_ref,
+ self.resource_action,
+ self.impersonation_allowed,
+ self.is_verified,
+ self.created,
+ self.status)
+
+ if self.error_status_code:
+ strg = ''.join([strg, (" error_status_code: {0}\n"
+ " error_reason: {1}\n"
+ ).format(self.error_status_code,
+ self.error_reason)])
+ return strg
+
+ def __repr__(self):
+ return 'Verification(verification_ref={0})'.format(self.verif_ref)
+
+
+class VerificationManager(base.BaseEntityManager):
+
+ def __init__(self, api):
+ super(VerificationManager, self).__init__(api, 'verifications')
+
+ def create(self,
+ resource_type=None,
+ resource_ref=None,
+ resource_action=None,
+ impersonation_allowed=False):
+ """
+ Creates a new Verification in Barbican
+
+ :param resource_type: Type of resource to verify
+ :param resource_ref: Reference to resource
+ :param resource_action: Action to be performed on or with the resource
+ :param impersonation_allowed: True if users/projects interacting
+ : with resource can be impersonated
+ :returns: Verification href for the created verification
+ """
+ LOG.debug(_("Creating verification"))
+
+ verif_dict = {'resource_type': resource_type,
+ 'resource_ref': resource_ref,
+ 'resource_action': resource_action,
+ 'impersonation_allowed': impersonation_allowed}
+ self._remove_empty_keys(verif_dict)
+
+ LOG.debug(_("Request body: {0}").format(verif_dict))
+
+ resp = self.api.post(self.entity, verif_dict)
+ return resp['verification_ref']
+
+ def get(self, verification_ref):
+ """
+ Returns a verification object
+
+ :param verification_ref: The href for the verification instance
+ """
+ LOG.debug(_("Getting verification - "
+ "Verification href: {0}").format(verification_ref))
+ if not verification_ref:
+ raise ValueError('verif_ref is required.')
+ resp = self.api.get(verification_ref)
+ return Verification(resp)
+
+ def delete(self, verification_ref):
+ """
+ Deletes a verification
+
+ :param verification_ref: The href for the verification instance
+ """
+ if not verification_ref:
+ raise ValueError('verif_ref is required.')
+ self.api.delete(verification_ref)
+
+ def list(self, limit=10, offset=0):
+ """
+ Lists all verifications for the tenant
+
+ :param limit: Max number of verifications returned
+ :param offset: Offset verifications to begin list
+ :returns: list of Verification objects
+ """
+ LOG.debug('Listing verifications - '
+ 'offset {0} limit {1}'.format(offset, limit))
+ href = '{0}/{1}'.format(self.api.base_url, self.entity)
+ params = {'limit': limit, 'offset': offset}
+ resp = self.api.get(href, params)
+
+ return [Verification(o) for o in resp['verifications']]