Package list python-castellan / f7e505a
Merge tag '0.17.0' into debian/queens castellan 0.17.0 release meta:version: 0.17.0 meta:diff-start: - meta:series: queens meta:release-type: release meta:pypi: no meta:first: no meta:release:Author: ChangBo Guo(gcb) <eric.guo@easystack.cn> meta:release:Commit: ChangBo Guo(gcb) <eric.guo@easystack.cn> meta:release:Change-Id: If4fdf2aadcddc5f8cd0843135a0afafa7803d553 meta:release:Code-Review+2: Sean McGinnis <sean.mcginnis@gmail.com> meta:release:Code-Review+2: Doug Hellmann <doug@doughellmann.com> meta:release:Workflow+1: Doug Hellmann <doug@doughellmann.com> Thomas Goirand 3 years ago
51 changed file(s) with 1534 addition(s) and 124 deletion(s). Raw diff Collapse all Expand all
4747
4848 # Sphinx
4949 doc/build
50 releasenotes/build
5051
5152 # pbr generates these
5253 AUTHORS
0 - job:
1 name: castellan-functional-vault
2 parent: openstack-tox-py27
3 description: |
4 Run tox functional-vault target
5 required-projects:
6 - name: openstack/castellan
7 vars:
8 tox_envlist: functional-vault
9
10 - job:
11 name: castellan-functional-devstack
12 parent: devstack
13 description: |
14 Run DevStack-based Castellan functional tests
15 pre-run: playbooks/devstack/pre.yaml
16 run: playbooks/devstack/run.yaml
17 post-run: playbooks/devstack/post.yaml
18 required-projects:
19 - name: openstack/castellan
20 - name: openstack/barbican
21 - name: openstack/python-barbicanclient
22 roles:
23 - zuul: openstack-infra/devstack
24 timeout: 9000
25 vars:
26 devstack_services:
27 # is there a way to disable all services? I only want barbican
28 ceilometer-acentral: False
29 ceilometer-acompute: False
30 ceilometer-alarm-evaluator: False
31 ceilometer-alarm-notifier: False
32 ceilometer-anotification: False
33 ceilometer-api: False
34 ceilometer-collector: False
35 horizon: False
36 s-account: False
37 s-container: False
38 s-object: False
39 s-proxy: False
40 devstack_plugins:
41 barbican: git://git.openstack.org/openstack/barbican
42 tox_environment:
43 PYTHONUNBUFFERED: 'true'
44 tox_install_siblings: False # I don't know what this means
45 tox_envlist: functional
46 zuul_work_dir: src/git.openstack.org/openstack/castellan
47
48 - project:
49 name: openstack/castellan
50 check:
51 jobs:
52 - castellan-functional-vault
53 - castellan-functional-devstack
54 gate:
55 jobs:
56 - castellan-functional-vault
57 - castellan-functional-devstack
2222 class KeystonePassword(password.Password):
2323 """This class represents a keystone password credential."""
2424
25 def __init__(self, password, username=None, user_id=None,
25 def __init__(self, password, auth_url=None, username=None, user_id=None,
2626 user_domain_id=None, user_domain_name=None, trust_id=None,
2727 domain_id=None, domain_name=None, project_id=None,
2828 project_name=None, project_domain_id=None,
2929 project_domain_name=None, reauthenticate=True):
3030 """Create a new Keystone Password Credential.
3131
32 :param string auth_url: Use this endpoint to connect to Keystone.
3233 :param string password: Password for authentication.
3334 :param string username: Username for authentication.
3435 :param string user_id: User ID for authentication.
4546 one is going to expire. (optional) default True
4647 """
4748
49 self._auth_url = auth_url
4850 self._user_id = user_id
4951 self._user_domain_id = user_domain_id
5052 self._user_domain_name = user_domain_name
5961
6062 super(KeystonePassword, self).__init__(username,
6163 password)
64
65 @property
66 def auth_url(self):
67 """This method returns an auth_url."""
68 return self._auth_url
6269
6370 @property
6471 def user_id(self):
2222 class KeystoneToken(token.Token):
2323 """This class represents a keystone token credential."""
2424
25 def __init__(self, token, trust_id=None, domain_id=None, domain_name=None,
26 project_id=None, project_name=None, project_domain_id=None,
27 project_domain_name=None, reauthenticate=True):
25 def __init__(self, token, auth_url=None, trust_id=None, domain_id=None,
26 domain_name=None, project_id=None, project_name=None,
27 project_domain_id=None, project_domain_name=None,
28 reauthenticate=True):
2829 """Create a new Keystone Token Credential.
2930
3031 :param string token: Token for authentication. The type of token
3132 formats accepted are UUID, PKI, and Fernet.
33 :param string auth_url: Use this endpoint to connect to Keystone.
3234 :param string trust_id: Trust ID for trust scoping.
3335 :param string domain_id: Domain ID for domain scoping.
3436 :param string domain_name: Domain name for domain scoping.
4042 one is going to expire. (optional) default True
4143 """
4244
45 self._auth_url = auth_url
4346 self._trust_id = trust_id
4447 self._domain_id = domain_id
4548 self._domain_name = domain_name
5053 self._reauthenticate = reauthenticate
5154
5255 super(KeystoneToken, self).__init__(token)
56
57 @property
58 def auth_url(self):
59 """This method returns an auth_url."""
60 return self._auth_url
5361
5462 @property
5563 def trust_id(self):
2727 class ManagedObject(object):
2828 """Base class to represent all managed objects."""
2929
30 def __init__(self, name=None, created=None):
30 def __init__(self, name=None, created=None, id=None):
3131 """Managed Object
3232
3333 :param name: the name of the managed object.
3434 :param created: the time a managed object was created.
35 :param id: the ID of the object, generated after storing the object.
3536 """
3637 self._name = name
3738
4142 else:
4243 raise ValueError('created must be of long type, actual type %s' %
4344 type(created))
45
46 self._id = id
47
48 @property
49 def id(self):
50 """Returns the ID of the managed object.
51
52 Returns the ID of the managed object or None if this object does not
53 have one. If the ID is None, the object has not been persisted yet.
54 """
55 return self._id
4456
4557 @property
4658 def name(self):
2424 class OpaqueData(managed_object.ManagedObject):
2525 """This class represents opaque data."""
2626
27 def __init__(self, data, name=None, created=None):
27 def __init__(self, data, name=None, created=None, id=None):
2828 """Create a new OpaqueData object.
2929
3030 Expected type for data is a bytestring.
3131 """
3232 self._data = data
33 super(OpaqueData, self).__init__(name=name, created=created)
33 super(OpaqueData, self).__init__(name=name, created=created, id=id)
3434
3535 @property
3636 def format(self):
2424 class Passphrase(managed_object.ManagedObject):
2525 """This class represents a passphrase."""
2626
27 def __init__(self, passphrase, name=None, created=None):
27 def __init__(self, passphrase, name=None, created=None, id=None):
2828 """Create a new Passphrase object.
2929
3030 The expected type for the passphrase is a bytestring.
3131 """
3232 self._passphrase = passphrase
33 super(Passphrase, self).__init__(name=name, created=created)
33 super(Passphrase, self).__init__(name=name, created=created, id=id)
3434
3535 @property
3636 def format(self):
2525 """This class represents private keys."""
2626
2727 def __init__(self, algorithm, bit_length, key,
28 name=None, created=None):
28 name=None, created=None, id=None):
2929 """Create a new PrivateKey object.
3030
3131 The arguments specify the algorithm and bit length for the asymmetric
3434 self._alg = algorithm
3535 self._bit_length = bit_length
3636 self._key = key
37 super(PrivateKey, self).__init__(name=name, created=created)
37 super(PrivateKey, self).__init__(name=name, created=created, id=id)
3838
3939 @property
4040 def algorithm(self):
2525 """This class represents public keys."""
2626
2727 def __init__(self, algorithm, bit_length, key,
28 name=None, created=None):
28 name=None, created=None, id=None):
2929 """Create a new PublicKey object.
3030
3131 The arguments specify the algorithm and bit length for the asymmetric
3535 self._alg = algorithm
3636 self._bit_length = bit_length
3737 self._key = key
38 super(PublicKey, self).__init__(name=name, created=created)
38 super(PublicKey, self).__init__(name=name, created=created, id=id)
3939
4040 @property
4141 def algorithm(self):
2525 """This class represents symmetric keys."""
2626
2727 def __init__(self, algorithm, bit_length, key,
28 name=None, created=None):
28 name=None, created=None, id=None):
2929 """Create a new SymmetricKey object.
3030
3131 The arguments specify the algorithm and bit length for the symmetric
3434 self._alg = algorithm
3535 self._bit_length = bit_length
3636 self._key = key
37 super(SymmetricKey, self).__init__(name=name, created=created)
37 super(SymmetricKey, self).__init__(name=name, created=created, id=id)
3838
3939 @property
4040 def algorithm(self):
2424 class X509(certificate.Certificate):
2525 """This class represents X.509 certificates."""
2626
27 def __init__(self, data, name=None, created=None):
27 def __init__(self, data, name=None, created=None, id=None):
2828 """Create a new X509 object.
2929
3030 The data should be in a bytestring.
3131 """
3232 self._data = data
33 super(X509, self).__init__(name=name, created=created)
33 super(X509, self).__init__(name=name, created=created, id=id)
3434
3535 @property
3636 def format(self):
5050 "'keystone_password' auth_type."),
5151
5252 # keystone credential opts
53 cfg.StrOpt('auth_url',
54 help="Use this endpoint to connect to Keystone."),
5355 cfg.StrOpt('user_id',
5456 help="User ID for authentication. Optional for "
5557 "'keystone_token' and 'keystone_password' auth_type."),
129131 elif conf.key_manager.auth_type == 'keystone_password':
130132 return keystone_password.KeystonePassword(
131133 conf.key_manager.password,
134 auth_url=conf.key_manager.auth_url,
132135 username=conf.key_manager.username,
133136 user_id=conf.key_manager.user_id,
134137 user_domain_id=conf.key_manager.user_domain_id,
152155
153156 return keystone_token.KeystoneToken(
154157 auth_token,
158 auth_url=conf.key_manager.auth_url,
155159 trust_id=conf.key_manager.trust_id,
156160 domain_id=conf.key_manager.domain_id,
157161 domain_name=conf.key_manager.domain_name,
1111 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1212 # License for the specific language governing permissions and limitations
1313 # under the License.
14 from castellan.key_manager import migration
1415 from oslo_config import cfg
16 from oslo_log import log as logging
1517 from oslo_utils import importutils
18 from stevedore import driver
19 from stevedore import exception
20
21 LOG = logging.getLogger(__name__)
1622
1723 key_manager_opts = [
18 cfg.StrOpt('api_class',
19 default='castellan.key_manager.barbican_key_manager'
20 '.BarbicanKeyManager',
21 help='The full class name of the key manager API class'),
24 cfg.StrOpt('backend',
25 default='barbican',
26 deprecated_name='api_class',
27 deprecated_group='key_manager',
28 help='Specify the key manager implementation. Options are '
29 '"barbican" and "vault". Default is "barbican". Will '
30 'support the values earlier set using '
31 '[key_manager]/api_class for some time.'),
2232 ]
2333
2434
2636 conf = configuration or cfg.CONF
2737 conf.register_opts(key_manager_opts, group='key_manager')
2838
29 cls = importutils.import_class(conf.key_manager.api_class)
30 return cls(configuration=conf)
39 try:
40 mgr = driver.DriverManager("castellan.drivers",
41 conf.key_manager.backend,
42 invoke_on_load=True,
43 invoke_args=[conf])
44 key_mgr = mgr.driver
45 except exception.NoMatches:
46 LOG.warning("Deprecation Warning : %s is not a stevedore based driver,"
47 " trying to load it as a class", conf.key_manager.backend)
48 cls = importutils.import_class(conf.key_manager.backend)
49 key_mgr = cls(configuration=conf)
50
51 return migration.handle_migration(conf, key_mgr)
4040 from castellan.key_manager import key_manager
4141
4242
43 from barbicanclient import client as barbican_client
43 from barbicanclient import client as barbican_client_import
4444 from barbicanclient import exceptions as barbican_exceptions
4545 from oslo_utils import timeutils
4646 from six.moves import urllib
5454 help='Version of the Barbican API, for example: "v1"'),
5555 cfg.StrOpt('auth_endpoint',
5656 default='http://localhost/identity/v3',
57 deprecated_name='auth_url',
58 deprecated_group='key_manager',
5759 help='Use this endpoint to connect to Keystone'),
5860 cfg.IntOpt('retry_delay',
5961 default=1,
117119 verify=self.conf.barbican.verify_ssl)
118120
119121 self._barbican_endpoint = self._get_barbican_endpoint(auth, sess)
120 self._barbican_client = barbican_client.Client(
122 self._barbican_client = barbican_client_import.Client(
121123 session=sess,
122124 endpoint=self._barbican_endpoint)
123125 self._current_context = context
124126
127 # TODO(pbourke): more fine grained exception handling - we are eating
128 # tracebacks here
125129 except Exception as e:
126130 LOG.error("Error creating Barbican client: %s", e)
127131 raise exception.KeyManagerError(reason=e)
133137 return self._barbican_client
134138
135139 def _get_keystone_auth(self, context):
136 auth_url = self.conf.barbican.auth_endpoint
137
138140 if context.__class__.__name__ is 'KeystonePassword':
139141 return identity.Password(
140 auth_url=auth_url,
142 auth_url=context.auth_url,
141143 username=context.username,
142144 password=context.password,
143145 user_id=context.user_id,
153155 reauthenticate=context.reauthenticate)
154156 elif context.__class__.__name__ is 'KeystoneToken':
155157 return identity.Token(
156 auth_url=auth_url,
158 auth_url=context.auth_url,
157159 token=context.token,
158160 trust_id=context.trust_id,
159161 domain_id=context.domain_id,
167169 # projects begin to use utils.credential_factory
168170 elif context.__class__.__name__ is 'RequestContext':
169171 return identity.Token(
170 auth_url=auth_url,
172 auth_url=self.conf.barbican.auth_endpoint,
171173 token=context.auth_token,
172 project_id=context.tenant)
174 project_id=context.project_id,
175 project_name=context.project_name,
176 project_domain_id=context.project_domain_id,
177 project_domain_name=context.project_domain_name)
173178 else:
174179 msg = _("context must be of type KeystonePassword, "
175180 "KeystoneToken, or RequestContext.")
199204 latest_version = raw_data[-1]
200205 api_version = latest_version.get('id')
201206
207 if endpoint[-1] != '/':
208 endpoint += '/'
209
202210 base_url = urllib.parse.urljoin(
203211 endpoint, api_version)
212
204213 return base_url
205214
206215 def create_key(self, context, algorithm, length,
477486 else:
478487 secret_data = self._get_secret_data(secret)
479488
489 if secret.secret_ref:
490 object_id = self._retrieve_secret_uuid(secret.secret_ref)
491 else:
492 object_id = None
493
480494 # convert created ISO8601 in Barbican to POSIX
481495 if secret.created:
482496 time_stamp = timeutils.parse_isotime(
488502 secret.bit_length,
489503 secret_data,
490504 secret.name,
491 created)
505 created,
506 object_id)
492507 else:
493508 return secret_type(secret_data,
494509 secret.name,
495 created)
510 created,
511 object_id)
496512
497513 def _get_secret(self, context, object_id):
498514 """Returns the metadata of the secret.
571587 uuid=managed_object_id)
572588 else:
573589 raise exception.KeyManagerError(reason=e)
590
591 def list(self, context, object_type=None, metadata_only=False):
592 """Retrieves a list of managed objects that match the criteria.
593
594 If no search criteria is given, all objects are returned.
595
596 :param context: contains information of the user and the environment
597 for the request (castellan/context.py)
598 :param object_type: the type of object to retrieve
599 :param metadata_only: whether secret data should be included
600 :raises KeyManagerError: if listing secrets fails
601 """
602 objects = []
603 barbican_client = self._get_barbican_client(context)
604
605 if object_type and object_type not in self._secret_type_dict:
606 msg = _("Invalid secret type: %s") % object_type
607 LOG.error(msg)
608 raise exception.KeyManagerError(reason=msg)
609
610 secret_type = self._secret_type_dict.get(object_type)
611
612 try:
613 secrets = barbican_client.secrets.list(secret_type=secret_type)
614 except (barbican_exceptions.HTTPAuthError,
615 barbican_exceptions.HTTPClientError,
616 barbican_exceptions.HTTPServerError) as e:
617 LOG.error(_("Error listing objects: %s"), e)
618 raise exception.KeyManagerError(reason=e)
619
620 for secret in secrets:
621 try:
622 obj = self._get_castellan_object(secret, metadata_only)
623 objects.append(obj)
624 except (barbican_exceptions.HTTPAuthError,
625 barbican_exceptions.HTTPClientError,
626 barbican_exceptions.HTTPServerError) as e:
627 LOG.warning(_("Error occurred while retrieving object "
628 "metadata, not adding it to the list: %s"), e)
629
630 return objects
108108 considered "non-existent" and completely invisible.
109109 """
110110 pass
111
112 def list(self, context, object_type=None, metadata_only=False):
113 """Lists the managed objects given the criteria.
114
115 Implementations should verify that the caller has permission to list
116 the managed objects and should only list the objects the caller has
117 access to by checking the context object (context). A NotAuthorized
118 exception should be raised if the caller lacks permission.
119
120 A list of managed objects or managed object metadata should be
121 returned, depending on the metadata_only flag. If no objects are
122 found, an empty list should be returned instead.
123 """
124 return []
0 # Copyright 2017 Red Hat, Inc.
1 # All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
13 # under the License.
14 import binascii
15 from castellan.common import exception
16 from castellan.common.objects import symmetric_key
17 from oslo_config import cfg
18 from oslo_log import log as logging
19
20 LOG = logging.getLogger(__name__)
21
22
23 def handle_migration(conf, key_mgr):
24 try:
25 conf.register_opt(cfg.StrOpt('fixed_key'), group='key_manager')
26 except cfg.DuplicateOptError:
27 pass
28
29 if conf.key_manager.fixed_key is not None and \
30 not conf.key_manager.backend.endswith('ConfKeyManager'):
31
32 LOG.warning("Using MigrationKeyManager to provide support for legacy"
33 " fixed_key encryption")
34
35 class MigrationKeyManager(type(key_mgr)):
36 def __init__(self, configuration):
37 self.fixed_key = configuration.key_manager.fixed_key
38 self.fixed_key_id = '00000000-0000-0000-0000-000000000000'
39 super(MigrationKeyManager, self).__init__(configuration)
40
41 def get(self, context, managed_object_id):
42 if managed_object_id == self.fixed_key_id:
43 LOG.debug("Processing request for secret associated"
44 " with fixed_key key ID")
45
46 if context is None:
47 raise exception.Forbidden()
48
49 key_bytes = bytes(binascii.unhexlify(self.fixed_key))
50 secret = symmetric_key.SymmetricKey('AES',
51 len(key_bytes) * 8,
52 key_bytes)
53 else:
54 secret = super(MigrationKeyManager, self).get(
55 context, managed_object_id)
56 return secret
57
58 def delete(self, context, managed_object_id):
59 if managed_object_id == self.fixed_key_id:
60 LOG.debug("Not deleting key associated with"
61 " fixed_key key ID")
62
63 if context is None:
64 raise exception.Forbidden()
65 else:
66 super(MigrationKeyManager, self).delete(context,
67 managed_object_id)
68
69 key_mgr = MigrationKeyManager(configuration=conf)
70
71 return key_mgr
4444 def get(self, context, managed_object_id, **kwargs):
4545 raise NotImplementedError()
4646
47 def list(self, context, object_type=None):
48 raise NotImplementedError()
49
4750 def delete(self, context, managed_object_id, **kwargs):
4851 raise NotImplementedError()
0 # Licensed under the Apache License, Version 2.0 (the "License"); you may
1 # not use this file except in compliance with the License. You may obtain
2 # a copy of the License at
3 #
4 # http://www.apache.org/licenses/LICENSE-2.0
5 #
6 # Unless required by applicable law or agreed to in writing, software
7 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
8 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
9 # License for the specific language governing permissions and limitations
10 # under the License.
11
12 """
13 Key manager implementation for Vault
14 """
15
16 import binascii
17 import os
18 import time
19 import uuid
20
21 from keystoneauth1 import loading
22 from oslo_config import cfg
23 from oslo_log import log as logging
24 import requests
25 import six
26
27 from castellan.common import exception
28 from castellan.common.objects import opaque_data as op_data
29 from castellan.common.objects import passphrase
30 from castellan.common.objects import private_key as pri_key
31 from castellan.common.objects import public_key as pub_key
32 from castellan.common.objects import symmetric_key as sym_key
33 from castellan.common.objects import x_509
34 from castellan.i18n import _
35 from castellan.key_manager import key_manager
36
37 DEFAULT_VAULT_URL = "http://127.0.0.1:8200"
38
39 vault_opts = [
40 cfg.StrOpt('root_token_id',
41 help='root token for vault'),
42 cfg.StrOpt('vault_url',
43 default=DEFAULT_VAULT_URL,
44 help='Use this endpoint to connect to Vault, for example: '
45 '"%s"' % DEFAULT_VAULT_URL),
46 cfg.StrOpt('ssl_ca_crt_file',
47 help='Absolute path to ca cert file'),
48 cfg.BoolOpt('use_ssl',
49 default=False,
50 help=_('SSL Enabled/Disabled')),
51 ]
52
53 VAULT_OPT_GROUP = 'vault'
54
55 _EXCEPTIONS_BY_CODE = [
56 requests.codes['internal_server_error'],
57 requests.codes['service_unavailable'],
58 requests.codes['request_timeout'],
59 requests.codes['gateway_timeout'],
60 requests.codes['precondition_failed'],
61 ]
62
63 LOG = logging.getLogger(__name__)
64
65
66 class VaultKeyManager(key_manager.KeyManager):
67 """Key Manager Interface that wraps the Vault REST API."""
68
69 _secret_type_dict = {
70 op_data.OpaqueData: 'opaque',
71 passphrase.Passphrase: 'passphrase',
72 pri_key.PrivateKey: 'private',
73 pub_key.PublicKey: 'public',
74 sym_key.SymmetricKey: 'symmetric',
75 x_509.X509: 'certificate'}
76
77 def __init__(self, configuration):
78 self._conf = configuration
79 self._conf.register_opts(vault_opts, group=VAULT_OPT_GROUP)
80 loading.register_session_conf_options(self._conf, VAULT_OPT_GROUP)
81 self._session = requests.Session()
82 self._root_token_id = self._conf.vault.root_token_id
83 self._vault_url = self._conf.vault.vault_url
84 if self._vault_url.startswith("https://"):
85 self._verify_server = self._conf.vault.ssl_ca_crt_file or True
86 else:
87 self._verify_server = False
88
89 def _get_url(self):
90 if not self._vault_url.endswith('/'):
91 self._vault_url += '/'
92 return self._vault_url
93
94 def create_key_pair(self, context, algorithm, length,
95 expiration=None, name=None):
96 """Creates an asymmetric key pair."""
97 raise NotImplementedError(
98 "VaultKeyManager does not support asymmetric keys")
99
100 def _store_key_value(self, key_id, value):
101
102 type_value = self._secret_type_dict.get(type(value))
103 if type_value is None:
104 raise exception.KeyManagerError(
105 "Unknown type for value : %r" % value)
106
107 headers = {'X-Vault-Token': self._root_token_id}
108 try:
109 resource_url = self._get_url() + 'v1/secret/' + key_id
110 record = {
111 'type': type_value,
112 'value': binascii.hexlify(value.get_encoded()).decode('utf-8'),
113 'algorithm': (value.algorithm if hasattr(value, 'algorithm')
114 else None),
115 'bit_length': (value.bit_length if hasattr(value, 'bit_length')
116 else None),
117 'name': value.name,
118 'created': value.created
119 }
120 resp = self._session.post(resource_url,
121 verify=self._verify_server,
122 json=record,
123 headers=headers)
124 except requests.exceptions.Timeout as ex:
125 raise exception.KeyManagerError(six.text_type(ex))
126 except requests.exceptions.ConnectionError as ex:
127 raise exception.KeyManagerError(six.text_type(ex))
128 except Exception as ex:
129 raise exception.KeyManagerError(six.text_type(ex))
130
131 if resp.status_code in _EXCEPTIONS_BY_CODE:
132 raise exception.KeyManagerError(resp.reason)
133 if resp.status_code == requests.codes['forbidden']:
134 raise exception.Forbidden()
135
136 return key_id
137
138 def create_key(self, context, algorithm, length, name=None, **kwargs):
139 """Creates a symmetric key."""
140
141 # Confirm context is provided, if not raise forbidden
142 if not context:
143 msg = _("User is not authorized to use key manager.")
144 raise exception.Forbidden(msg)
145
146 key_id = uuid.uuid4().hex
147 key_value = os.urandom(length or 32)
148 key = sym_key.SymmetricKey(algorithm,
149 length or 32,
150 key_value,
151 key_id,
152 name or int(time.time()))
153 return self._store_key_value(key_id, key)
154
155 def store(self, context, key_value, **kwargs):
156 """Stores (i.e., registers) a key with the key manager."""
157
158 # Confirm context is provided, if not raise forbidden
159 if not context:
160 msg = _("User is not authorized to use key manager.")
161 raise exception.Forbidden(msg)
162
163 key_id = uuid.uuid4().hex
164 return self._store_key_value(key_id, key_value)
165
166 def get(self, context, key_id, metadata_only=False):
167 """Retrieves the key identified by the specified id."""
168
169 # Confirm context is provided, if not raise forbidden
170 if not context:
171 msg = _("User is not authorized to use key manager.")
172 raise exception.Forbidden(msg)
173
174 if not key_id:
175 raise exception.KeyManagerError('key identifier not provided')
176
177 headers = {'X-Vault-Token': self._root_token_id}
178 try:
179 resource_url = self._get_url() + 'v1/secret/' + key_id
180 resp = self._session.get(resource_url,
181 verify=self._verify_server,
182 headers=headers)
183 except requests.exceptions.Timeout as ex:
184 raise exception.KeyManagerError(six.text_type(ex))
185 except requests.exceptions.ConnectionError as ex:
186 raise exception.KeyManagerError(six.text_type(ex))
187 except Exception as ex:
188 raise exception.KeyManagerError(six.text_type(ex))
189
190 if resp.status_code in _EXCEPTIONS_BY_CODE:
191 raise exception.KeyManagerError(resp.reason)
192 if resp.status_code == requests.codes['forbidden']:
193 raise exception.Forbidden()
194 if resp.status_code == requests.codes['not_found']:
195 raise exception.ManagedObjectNotFoundError(uuid=key_id)
196
197 record = resp.json()['data']
198 key = None if metadata_only else binascii.unhexlify(record['value'])
199
200 clazz = None
201 for type_clazz, type_name in self._secret_type_dict.items():
202 if type_name == record['type']:
203 clazz = type_clazz
204
205 if clazz is None:
206 raise exception.KeyManagerError(
207 "Unknown type : %r" % record['type'])
208
209 if hasattr(clazz, 'algorithm') and hasattr(clazz, 'bit_length'):
210 return clazz(record['algorithm'],
211 record['bit_length'],
212 key,
213 record['name'],
214 record['created'],
215 key_id)
216 else:
217 return clazz(key,
218 record['name'],
219 record['created'],
220 key_id)
221
222 def delete(self, context, key_id):
223 """Represents deleting the key."""
224
225 # Confirm context is provided, if not raise forbidden
226 if not context:
227 msg = _("User is not authorized to use key manager.")
228 raise exception.Forbidden(msg)
229
230 if not key_id:
231 raise exception.KeyManagerError('key identifier not provided')
232
233 headers = {'X-Vault-Token': self._root_token_id}
234 try:
235 resource_url = self._get_url() + 'v1/secret/' + key_id
236 resp = self._session.delete(resource_url,
237 verify=self._verify_server,
238 headers=headers)
239 except requests.exceptions.Timeout as ex:
240 raise exception.KeyManagerError(six.text_type(ex))
241 except requests.exceptions.ConnectionError as ex:
242 raise exception.KeyManagerError(six.text_type(ex))
243 except Exception as ex:
244 raise exception.KeyManagerError(six.text_type(ex))
245
246 if resp.status_code in _EXCEPTIONS_BY_CODE:
247 raise exception.KeyManagerError(resp.reason)
248 if resp.status_code == requests.codes['forbidden']:
249 raise exception.Forbidden()
250 if resp.status_code == requests.codes['not_found']:
251 raise exception.ManagedObjectNotFoundError(uuid=key_id)
252
253 def list(self, context, object_type=None, metadata_only=False):
254 """Lists the managed objects given the criteria."""
255
256 # Confirm context is provided, if not raise forbidden
257 if not context:
258 msg = _("User is not authorized to use key manager.")
259 raise exception.Forbidden(msg)
260
261 if object_type and object_type not in self._secret_type_dict:
262 msg = _("Invalid secret type: %s") % object_type
263 raise exception.KeyManagerError(reason=msg)
264
265 headers = {'X-Vault-Token': self._root_token_id}
266 try:
267 resource_url = self._get_url() + 'v1/secret/?list=true'
268 resp = self._session.get(resource_url,
269 verify=self._verify_server,
270 headers=headers)
271 keys = resp.json()['data']['keys']
272 except requests.exceptions.Timeout as ex:
273 raise exception.KeyManagerError(six.text_type(ex))
274 except requests.exceptions.ConnectionError as ex:
275 raise exception.KeyManagerError(six.text_type(ex))
276 except Exception as ex:
277 raise exception.KeyManagerError(six.text_type(ex))
278
279 if resp.status_code in _EXCEPTIONS_BY_CODE:
280 raise exception.KeyManagerError(resp.reason)
281 if resp.status_code == requests.codes['forbidden']:
282 raise exception.Forbidden()
283 if resp.status_code == requests.codes['not_found']:
284 keys = []
285
286 objects = []
287 for obj_id in keys:
288 try:
289 obj = self.get(context, obj_id, metadata_only=metadata_only)
290 if object_type is None or isinstance(obj, object_type):
291 objects.append(obj)
292 except exception.ManagedObjectNotFoundError as e:
293 LOG.warning(_("Error occurred while retrieving object "
294 "metadata, not adding it to the list: %s"), e)
295 pass
296 return objects
1919 from castellan.key_manager import barbican_key_manager as bkm
2020 except ImportError:
2121 bkm = None
22
23 try:
24 from castellan.key_manager import vault_key_manager as vkm
25 except ImportError:
26 vkm = None
27
2228 from castellan.common import utils
2329
2430 _DEFAULT_LOG_LEVELS = ['castellan=WARN']
2935 '%(message)s')
3036
3137
32 def set_defaults(conf, api_class=None, barbican_endpoint=None,
38 def set_defaults(conf, backend=None, barbican_endpoint=None,
3339 barbican_api_version=None, auth_endpoint=None,
34 retry_delay=None, number_of_retries=None, verify_ssl=None):
40 retry_delay=None, number_of_retries=None, verify_ssl=None,
41 api_class=None, vault_root_token_id=None, vault_url=None,
42 vault_ssl_ca_crt_file=None, vault_use_ssl=None):
3543 """Set defaults for configuration values.
3644
3745 Overrides the default options values.
4351 :param retry_delay: Use this attribute to set retry delay.
4452 :param number_of_retries: Use this attribute to set number of retries.
4553 :param verify_ssl: Use this to specify if ssl should be verified.
54 :param vault_root_token_id: Use this for the root token id for vault.
55 :param vault_url: Use this for the url for vault.
56 :param vault_use_ssl: Use this to force vault driver to use ssl.
57 :param vault_ssl_ca_crt_file: Use this for the CA file for vault.
4658 """
4759 conf.register_opts(km.key_manager_opts, group='key_manager')
4860 if bkm:
4961 conf.register_opts(bkm.barbican_opts, group=bkm.BARBICAN_OPT_GROUP)
62 if vkm:
63 conf.register_opts(vkm.vault_opts, group=vkm.VAULT_OPT_GROUP)
5064
51 if api_class is not None:
52 conf.set_default('api_class', api_class, group='key_manager')
65 # Use the new backend option if set or fall back to the older api_class
66 default_backend = backend or api_class
67 if default_backend is not None:
68 conf.set_default('backend', default_backend, group='key_manager')
5369
5470 if bkm is not None:
5571 if barbican_endpoint is not None:
7086 if verify_ssl is not None:
7187 conf.set_default('verify_ssl', verify_ssl,
7288 group=bkm.BARBICAN_OPT_GROUP)
89
90 if vkm is not None:
91 if vault_root_token_id is not None:
92 conf.set_default('root_token_id', vault_root_token_id,
93 group=vkm.VAULT_OPT_GROUP)
94 if vault_url is not None:
95 conf.set_default('vault_url', vault_url,
96 group=vkm.VAULT_OPT_GROUP)
97 if vault_ssl_ca_crt_file is not None:
98 conf.set_default('ssl_ca_crt_file', vault_ssl_ca_crt_file,
99 group=vkm.VAULT_OPT_GROUP)
100 if vault_use_ssl is not None:
101 conf.set_default('use_ssl', vault_use_ssl,
102 group=vkm.VAULT_OPT_GROUP)
73103
74104
75105 def enable_logging(conf=None, app_name='castellan'):
105135
106136 if bkm is not None:
107137 opts.append((bkm.BARBICAN_OPT_GROUP, bkm.barbican_opts))
138 if vkm is not None:
139 opts.append((vkm.VAULT_OPT_GROUP, vkm.vault_opts))
108140 return opts
1111 sudo /usr/os-testr-env/bin/subunit2html $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html
1212 sudo gzip -9 $BASE/logs/testrepository.subunit
1313 sudo gzip -9 $BASE/logs/testr_results.html
14 sudo chown jenkins:jenkins $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz
14 sudo chown $USER:$USER $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz
1515 sudo chmod a+r $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz
1616 fi
1717 }
2323 sudo chown -R $owner:stack $CASTELLAN_DIR
2424
2525 testenv=functional
26
27 sudo -H -u $owner tox -e genconfig
28
29 if [ ! -d /etc/castellan ]; then
30 sudo mkdir /etc/castellan
31 fi
32
33 sudo cp $CASTELLAN_DIR/etc/castellan/castellan-functional.conf.sample /etc/castellan/castellan-functional.conf
3426
3527 # Run tests
3628 echo "Running Castellan $testenv test suite"
2525 from oslo_context import context
2626 from oslo_utils import uuidutils
2727 from oslotest import base
28 from testtools import testcase
2829
2930 from castellan.common.credentials import keystone_password
3031 from castellan.common.credentials import keystone_token
4950
5051 def setUp(self):
5152 super(BarbicanKeyManagerTestCase, self).setUp()
52 self.ctxt = self.get_context()
53 try:
54 self.ctxt = self.get_context()
55 self.key_mgr._get_barbican_client(self.ctxt)
56 except Exception as e:
57 # When we run functional-vault target, This test class needs
58 # to be skipped as barbican is not running
59 raise testcase.TestSkipped(str(e))
5360
5461 def tearDown(self):
5562 super(BarbicanKeyManagerTestCase, self).tearDown()
128135 base.BaseTestCase):
129136
130137 def get_context(self):
138 auth_url = CONF.identity.auth_url
131139 username = CONF.identity.username
132140 password = CONF.identity.password
133141 project_name = CONF.identity.project_name
135143 project_domain_name = CONF.identity.project_domain_name
136144
137145 ctxt = keystone_password.KeystonePassword(
138 username=username, password=password,
146 auth_url=auth_url, username=username, password=password,
139147 project_name=project_name,
140148 user_domain_name=user_domain_name,
141149 project_domain_name=project_domain_name)
164172
165173 return keystone_token.KeystoneToken(
166174 token=auth.get_token(sess),
175 auth_url=auth_url,
167176 project_id=auth.get_project_id(sess))
136136 self.assertEqual(managed_object.get_encoded(),
137137 retrieved_object.get_encoded())
138138 self.assertFalse(managed_object.is_metadata_only())
139 self.assertFalse(retrieved_object.is_metadata_only())
140 self.assertIsNotNone(retrieved_object.id)
139141
140142 @utils.parameterized_dataset({
141143 'symmetric_key': [_get_test_symmetric_key()],
154156 metadata_only=True)
155157 self.assertFalse(managed_object.is_metadata_only())
156158 self.assertTrue(retrieved_object.is_metadata_only())
159 self.assertIsNotNone(retrieved_object.id)
157160
158161 @utils.parameterized_dataset({
159162 'symmetric_key': [_get_test_symmetric_key()],
170173 retrieved_object = self.key_mgr.get(self.ctxt, uuid)
171174 self.assertEqual(managed_object.get_encoded(),
172175 retrieved_object.get_encoded())
176 self.assertIsNotNone(retrieved_object.id)
177
178 @utils.parameterized_dataset({
179 'symmetric_key': [_get_test_symmetric_key()],
180 'public_key': [_get_test_public_key()],
181 'private_key': [_get_test_private_key()],
182 'certificate': [_get_test_certificate()],
183 'passphrase': [_get_test_passphrase()],
184 'opaque_data': [_get_test_opaque_data()],
185 })
186 def test_list(self, managed_object):
187 uuid = self.key_mgr.store(self.ctxt, managed_object)
188 self.addCleanup(self.key_mgr.delete, self.ctxt, uuid)
189
190 # the list command may return more objects than the one we just
191 # created if older objects were not cleaned up, so we will simply
192 # check if the object we created is in the list
193 retrieved_objects = self.key_mgr.list(self.ctxt)
194 self.assertTrue(managed_object in retrieved_objects)
195 for retrieved_object in retrieved_objects:
196 self.assertFalse(retrieved_object.is_metadata_only())
197 self.assertIsNotNone(retrieved_object.id)
198
199 @utils.parameterized_dataset({
200 'symmetric_key': [_get_test_symmetric_key()],
201 'public_key': [_get_test_public_key()],
202 'private_key': [_get_test_private_key()],
203 'certificate': [_get_test_certificate()],
204 'passphrase': [_get_test_passphrase()],
205 'opaque_data': [_get_test_opaque_data()],
206 })
207 def test_list_metadata_only(self, managed_object):
208 uuid = self.key_mgr.store(self.ctxt, managed_object)
209 self.addCleanup(self.key_mgr.delete, self.ctxt, uuid)
210
211 expected_obj = self.key_mgr.get(self.ctxt, uuid, metadata_only=True)
212
213 # the list command may return more objects than the one we just
214 # created if older objects were not cleaned up, so we will simply
215 # check if the object we created is in the list
216 retrieved_objects = self.key_mgr.list(self.ctxt, metadata_only=True)
217 self.assertTrue(expected_obj in retrieved_objects)
218 for retrieved_object in retrieved_objects:
219 self.assertTrue(retrieved_object.is_metadata_only())
220 self.assertIsNotNone(retrieved_object.id)
221
222 @utils.parameterized_dataset({
223 'query_by_object_type': {
224 'object_1': _get_test_symmetric_key(),
225 'object_2': _get_test_public_key(),
226 'query_dict': dict(object_type=symmetric_key.SymmetricKey)
227 },
228 })
229 def test_list_with_filter(self, object_1, object_2, query_dict):
230 uuid1 = self.key_mgr.store(self.ctxt, object_1)
231 uuid2 = self.key_mgr.store(self.ctxt, object_2)
232 self.addCleanup(self.key_mgr.delete, self.ctxt, uuid1)
233 self.addCleanup(self.key_mgr.delete, self.ctxt, uuid2)
234
235 # the list command may return more objects than the one we just
236 # created if older objects were not cleaned up, so we will simply
237 # check that the returned objects have the expected type
238 retrieved_objects = self.key_mgr.list(self.ctxt, **query_dict)
239 for retrieved_object in retrieved_objects:
240 self.assertEqual(type(object_1), type(retrieved_object))
241 self.assertIsNotNone(retrieved_object.id)
242 self.assertTrue(object_1 in retrieved_objects)
0 # Licensed under the Apache License, Version 2.0 (the "License"); you may
1 # not use this file except in compliance with the License. You may obtain
2 # a copy of the License at
3 #
4 # http://www.apache.org/licenses/LICENSE-2.0
5 #
6 # Unless required by applicable law or agreed to in writing, software
7 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
8 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
9 # License for the specific language governing permissions and limitations
10 # under the License.
11
12 """
13 Functional test cases for the Vault key manager.
14
15 Note: This requires local running instance of Vault.
16 """
17 import abc
18 import os
19
20 from oslo_config import cfg
21 from oslo_context import context
22 from oslo_utils import uuidutils
23 from oslotest import base
24 from testtools import testcase
25
26 from castellan.common import exception
27 from castellan.key_manager import vault_key_manager
28 from castellan.tests.functional import config
29 from castellan.tests.functional.key_manager import test_key_manager
30
31 CONF = config.get_config()
32
33
34 class VaultKeyManagerTestCase(test_key_manager.KeyManagerTestCase):
35 def _create_key_manager(self):
36 key_mgr = vault_key_manager.VaultKeyManager(cfg.CONF)
37
38 if ('VAULT_TEST_URL' not in os.environ or
39 'VAULT_TEST_ROOT_TOKEN' not in os.environ):
40 raise testcase.TestSkipped('Missing Vault setup information')
41
42 key_mgr._root_token_id = os.environ['VAULT_TEST_ROOT_TOKEN']
43 key_mgr._vault_url = os.environ['VAULT_TEST_URL']
44 return key_mgr
45
46 @abc.abstractmethod
47 def get_context(self):
48 """Retrieves Context for Authentication"""
49 return
50
51 def setUp(self):
52 super(VaultKeyManagerTestCase, self).setUp()
53 self.ctxt = self.get_context()
54
55 def tearDown(self):
56 super(VaultKeyManagerTestCase, self).tearDown()
57
58 def test_create_key_pair(self):
59 self.assertRaises(NotImplementedError,
60 self.key_mgr.create_key_pair, None, None, None)
61
62 def test_create_null_context(self):
63 self.assertRaises(exception.Forbidden,
64 self.key_mgr.create_key, None, 'AES', 256)
65
66 def test_create_key_pair_null_context(self):
67 self.assertRaises(NotImplementedError,
68 self.key_mgr.create_key_pair, None, 'RSA', 2048)
69
70 def test_delete_null_context(self):
71 key_uuid = self._get_valid_object_uuid(
72 test_key_manager._get_test_symmetric_key())
73 self.addCleanup(self.key_mgr.delete, self.ctxt, key_uuid)
74 self.assertRaises(exception.Forbidden,
75 self.key_mgr.delete, None, key_uuid)
76
77 def test_delete_null_object(self):
78 self.assertRaises(exception.KeyManagerError,
79 self.key_mgr.delete, self.ctxt, None)
80
81 def test_get_null_context(self):
82 key_uuid = self._get_valid_object_uuid(
83 test_key_manager._get_test_symmetric_key())
84 self.addCleanup(self.key_mgr.delete, self.ctxt, key_uuid)
85 self.assertRaises(exception.Forbidden,
86 self.key_mgr.get, None, key_uuid)
87
88 def test_get_null_object(self):
89 self.assertRaises(exception.KeyManagerError,
90 self.key_mgr.get, self.ctxt, None)
91
92 def test_get_unknown_key(self):
93 bad_key_uuid = uuidutils.generate_uuid()
94 self.assertRaises(exception.ManagedObjectNotFoundError,
95 self.key_mgr.get, self.ctxt, bad_key_uuid)
96
97 def test_store_null_context(self):
98 key = test_key_manager._get_test_symmetric_key()
99
100 self.assertRaises(exception.Forbidden,
101 self.key_mgr.store, None, key)
102
103
104 class VaultKeyManagerOSLOContextTestCase(VaultKeyManagerTestCase,
105 base.BaseTestCase):
106 def get_context(self):
107 return context.get_admin_context()
162162 raise exception.Forbidden()
163163
164164 key_id = self._generate_key_id()
165 managed_object._id = key_id
165166 self.keys[key_id] = managed_object
166167
167168 return key_id
225226 random.shuffle(password)
226227
227228 return ''.join(password)
229
230 def list(self, context, object_type=None, metadata_only=False):
231 """Retrieves a list of managed objects that match the criteria.
232
233 A Forbidden exception is raised if the context is None.
234 If no search criteria is given, all objects are returned.
235 """
236 if context is None:
237 raise exception.Forbidden()
238
239 objects = []
240 for obj_id in self.keys:
241 obj = self.get(context, obj_id, metadata_only=metadata_only)
242 if type(obj) == object_type or object_type is None:
243 objects.append(obj)
244 return objects
1919
2020 from barbicanclient import exceptions as barbican_exceptions
2121 import mock
22 from oslo_config import cfg
2322 from oslo_utils import timeutils
2423
2524 from castellan.common import exception
3130 class BarbicanKeyManagerTestCase(test_key_manager.KeyManagerTestCase):
3231
3332 def _create_key_manager(self):
34 return barbican_key_manager.BarbicanKeyManager(cfg.CONF)
33 return barbican_key_manager.BarbicanKeyManager(self.conf)
3534
3635 def setUp(self):
3736 super(BarbicanKeyManagerTestCase, self).setUp()
7170 self.delete = self.mock_barbican.secrets.delete
7271 self.store = self.mock_barbican.secrets.store
7372 self.create = self.mock_barbican.secrets.create
73 self.list = self.mock_barbican.secrets.list
7474
7575 self.key_mgr._barbican_client = self.mock_barbican
7676 self.key_mgr._current_context = self.ctxt
77
78 def test_base_url_old_version(self):
79 version = "v1"
80 self.key_mgr.conf.barbican.barbican_api_version = version
81 endpoint = "http://localhost:9311"
82 base_url = self.key_mgr._create_base_url(mock.Mock(),
83 mock.Mock(),
84 endpoint)
85 self.assertEqual(endpoint + "/" + version, base_url)
86
87 def test_base_url_new_version(self):
88 version = "v1"
89 self.key_mgr.conf.barbican.barbican_api_version = version
90 endpoint = "http://localhost/key_manager"
91 base_url = self.key_mgr._create_base_url(mock.Mock(),
92 mock.Mock(),
93 endpoint)
94 self.assertEqual(endpoint + "/" + version, base_url)
7795
7896 def test_create_key(self):
7997 # Create order_ref_url and assign return value
188206 original_secret_metadata.bit_length = mock.sentinel.bit
189207 original_secret_metadata.secret_type = 'symmetric'
190208
209 key_id = "43ed09c3-e551-4c24-b612-e619abe9b534"
210 key_ref = ("http://localhost:9311/v1/secrets/" + key_id)
211 original_secret_metadata.secret_ref = key_ref
212
191213 created = timeutils.parse_isotime('2015-10-20 18:51:17+00:00')
192214 original_secret_metadata.created = created
193215 created_formatted = timeutils.parse_isotime(str(created))
203225 key = self.key_mgr.get(self.ctxt, self.key_id)
204226
205227 self.get.assert_called_once_with(self.secret_ref)
228 self.assertEqual(key_id, key.id)
206229 self.assertEqual(key_name, key.name)
207230 self.assertEqual(original_secret_data, key.get_encoded())
208231 self.assertEqual(created_posix, key.created)
347370 order_ref_url)
348371
349372 self.assertEqual(1, self.mock_barbican.orders.get.call_count)
373
374 def test_list_null_context(self):
375 self.key_mgr._barbican_client = None
376 self.assertRaises(exception.Forbidden,
377 self.key_mgr.list, None)
378
379 def test_list(self):
380 original_secret_metadata = mock.Mock()
381 original_secret_metadata.algorithm = mock.sentinel.alg
382 original_secret_metadata.bit_length = mock.sentinel.bit
383 original_secret_metadata.secret_type = 'symmetric'
384
385 key_id = "43ed09c3-e551-4c24-b612-e619abe9b534"
386 key_ref = ("http://localhost:9311/v1/secrets/" + key_id)
387 original_secret_metadata.secret_ref = key_ref
388
389 created = timeutils.parse_isotime('2015-10-20 18:51:17+00:00')
390 original_secret_metadata.created = created
391 created_formatted = timeutils.parse_isotime(str(created))
392 created_posix = calendar.timegm(created_formatted.timetuple())
393
394 key_name = 'my key'
395 original_secret_metadata.name = key_name
396
397 original_secret_data = b'test key'
398 original_secret_metadata.payload = original_secret_data
399
400 self.mock_barbican.secrets.list.return_value = (
401 [original_secret_metadata])
402
403 # check metadata_only = False
404 key_list = self.key_mgr.list(self.ctxt)
405 self.assertEqual(1, len(key_list))
406 key = key_list[0]
407
408 self.list.assert_called_once()
409 self.assertEqual(key_id, key.id)
410 self.assertEqual(key_name, key.name)
411 self.assertEqual(original_secret_data, key.get_encoded())
412 self.assertEqual(created_posix, key.created)
413
414 self.list.reset_mock()
415
416 # check metadata_only = True
417 key_list = self.key_mgr.list(self.ctxt, metadata_only=True)
418 self.assertEqual(1, len(key_list))
419 key = key_list[0]
420
421 self.list.assert_called_once()
422 self.assertEqual(key_name, key.name)
423 self.assertIsNone(key.get_encoded())
424 self.assertEqual(created_posix, key.created)
425
426 def test_list_with_error(self):
427 self.mock_barbican.secrets.list = mock.Mock(
428 side_effect=barbican_exceptions.HTTPClientError('test error'))
429 self.assertRaises(exception.KeyManagerError,
430 self.key_mgr.list, self.ctxt)
431
432 def test_list_with_invalid_object_type(self):
433 self.assertRaises(exception.KeyManagerError,
434 self.key_mgr.list, self.ctxt, "invalid_type")
1616 Test cases for the key manager.
1717 """
1818
19 from oslo_config import cfg
20 from oslo_config import fixture
21
22 from castellan import key_manager
23 from castellan.key_manager import barbican_key_manager
1924 from castellan.tests import base
25
26 CONF = cfg.CONF
2027
2128
2229 class KeyManagerTestCase(base.TestCase):
2734 def setUp(self):
2835 super(KeyManagerTestCase, self).setUp()
2936
37 self.conf = self.useFixture(fixture.Config()).conf
38
3039 self.key_mgr = self._create_key_manager()
40
41
42 class DefaultKeyManagerImplTestCase(KeyManagerTestCase):
43
44 def _create_key_manager(self):
45 return key_manager.API(self.conf)
46
47 def test_default_key_manager(self):
48 self.assertEqual("barbican", self.conf.key_manager.backend)
49 self.assertIsNotNone(self.key_mgr)
50 self.assertIsInstance(self.key_mgr,
51 barbican_key_manager.BarbicanKeyManager)
0 # Copyright 2017 Red Hat, Inc.
1 # All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
13 # under the License.
14
15 """
16 Test cases for the migration key manager.
17 """
18
19 import binascii
20 import mock
21
22 from oslo_config import cfg
23
24 from castellan.common import exception
25 from castellan.common.objects import symmetric_key as key
26 from castellan import key_manager
27 from castellan.key_manager import not_implemented_key_manager
28 from castellan.tests.unit.key_manager import test_key_manager
29
30 CONF = cfg.CONF
31
32
33 class ConfKeyManager(not_implemented_key_manager.NotImplementedKeyManager):
34 pass
35
36
37 class MigrationKeyManagerTestCase(test_key_manager.KeyManagerTestCase):
38
39 def _create_key_manager(self):
40 self.fixed_key = '1' * 64
41 try:
42 self.conf.register_opt(cfg.StrOpt('fixed_key'),
43 group='key_manager')
44 except cfg.DuplicateOptError:
45 pass
46 self.conf.set_override('fixed_key',
47 self.fixed_key,
48 group='key_manager')
49 return key_manager.API(self.conf)
50
51 def setUp(self):
52 super(MigrationKeyManagerTestCase, self).setUp()
53
54 # Create fake context (actual contents doesn't matter).
55 self.ctxt = mock.Mock()
56
57 fixed_key_bytes = bytes(binascii.unhexlify(self.fixed_key))
58 fixed_key_length = len(fixed_key_bytes) * 8
59 self.fixed_key_secret = key.SymmetricKey('AES',
60 fixed_key_length,
61 fixed_key_bytes)
62 self.fixed_key_id = '00000000-0000-0000-0000-000000000000'
63 self.other_key_id = "d152fa13-2b41-42ca-a934-6c21566c0f40"
64
65 def test_get_fixed_key(self):
66 self.assertEqual('MigrationKeyManager', type(self.key_mgr).__name__)
67 secret = self.key_mgr.get(self.ctxt, self.fixed_key_id)
68 self.assertEqual(self.fixed_key_secret, secret)
69
70 def test_get_fixed_key_fail_bad_context(self):
71 self.assertRaises(exception.Forbidden,
72 self.key_mgr.get,
73 context=None,
74 managed_object_id=self.fixed_key_id)
75
76 def test_delete_fixed_key(self):
77 self.key_mgr.delete(self.ctxt, self.fixed_key_id)
78 # Delete looks like it succeeded, but nothing actually happened.
79 secret = self.key_mgr.get(self.ctxt, self.fixed_key_id)
80 self.assertEqual(self.fixed_key_secret, secret)
81
82 def test_delete_fixed_key_fail_bad_context(self):
83 self.assertRaises(exception.Forbidden,
84 self.key_mgr.delete,
85 context=None,
86 managed_object_id=self.fixed_key_id)
87
88 def test_get_other_key(self):
89 # Request to get other_key_id should be passed on to the backend,
90 # who will throw an error because we don't have a valid context.
91 self.assertRaises(exception.KeyManagerError,
92 self.key_mgr.get,
93 context=self.ctxt,
94 managed_object_id=self.other_key_id)
95
96 def test_delete_other_key(self):
97 # Request to delete other_key_id should be passed on to the backend,
98 # who will throw an error because we don't have a valid context.
99 self.assertRaises(exception.KeyManagerError,
100 self.key_mgr.delete,
101 context=self.ctxt,
102 managed_object_id=self.other_key_id)
103
104 def test_no_fixed_key(self):
105 conf = self.conf
106 conf.set_override('fixed_key', None, group='key_manager')
107 key_mgr = key_manager.API(conf)
108 self.assertNotEqual('MigrationKeyManager', type(key_mgr).__name__)
109 self.assertRaises(exception.KeyManagerError,
110 key_mgr.get,
111 context=self.ctxt,
112 managed_object_id=self.fixed_key_id)
113
114 def test_using_conf_key_manager(self):
115 conf = self.conf
116 ckm_backend = 'castellan.tests.unit.key_manager.' \
117 'test_migration_key_manager.ConfKeyManager'
118 conf.set_override('backend', ckm_backend, group='key_manager')
119 key_mgr = key_manager.API(conf)
120 self.assertNotEqual('MigrationKeyManager', type(key_mgr).__name__)
121 self.assertRaises(NotImplementedError,
122 key_mgr.get,
123 context=self.ctxt,
124 managed_object_id=self.fixed_key_id)
5353
5454 self.context = context.RequestContext('fake', 'fake')
5555
56 def cleanUp(self):
57 super(MockKeyManagerTestCase, self).cleanUp()
58
59 self.key_mgr.keys = {}
60
5661 def test_create_key(self):
5762 key_id_1 = self.key_mgr.create_key(self.context)
5863 key_id_2 = self.key_mgr.create_key(self.context)
6469 key_id = self.key_mgr.create_key(self.context, length=length)
6570 key = self.key_mgr.get(self.context, key_id)
6671 self.assertEqual(length / 8, len(key.get_encoded()))
72 self.assertIsNotNone(key.id)
6773
6874 def test_create_key_with_name(self):
6975 name = 'my key'
7076 key_id = self.key_mgr.create_key(self.context, name=name)
7177 key = self.key_mgr.get(self.context, key_id)
7278 self.assertEqual(name, key.name)
79 self.assertIsNotNone(key.id)
7380
7481 def test_create_key_with_algorithm(self):
7582 algorithm = 'DES'
7683 key_id = self.key_mgr.create_key(self.context, algorithm=algorithm)
7784 key = self.key_mgr.get(self.context, key_id)
7885 self.assertEqual(algorithm, key.algorithm)
86 self.assertIsNotNone(key.id)
7987
8088 def test_create_key_null_context(self):
8189 self.assertRaises(exception.Forbidden,
8896 self.context, 'RSA', length, name=name)
8997
9098 private_key = self.key_mgr.get(self.context, private_key_uuid)
99 self.assertIsNotNone(private_key.id)
91100 public_key = self.key_mgr.get(self.context, public_key_uuid)
101 self.assertIsNotNone(public_key.id)
92102
93103 crypto_private_key = get_cryptography_private_key(private_key)
94104 crypto_public_key = get_cryptography_public_key(public_key)
147157 actual_key = self.key_mgr.get(self.context, key_id)
148158 self.assertEqual(_key, actual_key)
149159
160 self.assertIsNotNone(actual_key.id)
161
150162 def test_store_key_and_get_metadata(self):
151163 secret_key = bytes(b'0' * 64)
152164 _key = sym_key.SymmetricKey('AES', 64 * 8, secret_key)
158170 self.assertIsNone(actual_key.get_encoded())
159171 self.assertTrue(actual_key.is_metadata_only())
160172
173 self.assertIsNotNone(actual_key.id)
174
161175 def test_store_key_and_get_metadata_and_get_key(self):
162176 secret_key = bytes(b'0' * 64)
163177 _key = sym_key.SymmetricKey('AES', 64 * 8, secret_key)
175189 self.assertIsNotNone(actual_key.get_encoded())
176190 self.assertFalse(actual_key.is_metadata_only())
177191
192 self.assertIsNotNone(actual_key.id)
193
178194 def test_store_null_context(self):
179195 self.assertRaises(exception.Forbidden,
180196 self.key_mgr.store, None, None)
200216 def test_delete_unknown_key(self):
201217 self.assertRaises(KeyError, self.key_mgr.delete, self.context,
202218 None)
219
220 def test_list_null_context(self):
221 self.assertRaises(exception.Forbidden, self.key_mgr.list, None)
222
223 def test_list_keys(self):
224 key1 = sym_key.SymmetricKey('AES', 64 * 8, bytes(b'0' * 64))
225 self.key_mgr.store(self.context, key1)
226 key2 = sym_key.SymmetricKey('AES', 32 * 8, bytes(b'0' * 32))
227 self.key_mgr.store(self.context, key2)
228
229 keys = self.key_mgr.list(self.context)
230 self.assertEqual(2, len(keys))
231 self.assertTrue(key1 in keys)
232 self.assertTrue(key2 in keys)
233
234 for key in keys:
235 self.assertIsNotNone(key.id)
236
237 def test_list_keys_metadata_only(self):
238 key1 = sym_key.SymmetricKey('AES', 64 * 8, bytes(b'0' * 64))
239 self.key_mgr.store(self.context, key1)
240 key2 = sym_key.SymmetricKey('AES', 32 * 8, bytes(b'0' * 32))
241 self.key_mgr.store(self.context, key2)
242
243 keys = self.key_mgr.list(self.context, metadata_only=True)
244 self.assertEqual(2, len(keys))
245 bit_length_list = [key1.bit_length, key2.bit_length]
246 for key in keys:
247 self.assertTrue(key.is_metadata_only())
248 self.assertTrue(key.bit_length in bit_length_list)
249
250 for key in keys:
251 self.assertIsNotNone(key.id)
4545 self.assertRaises(NotImplementedError,
4646 self.key_mgr.get, None, None)
4747
48 def test_list(self):
49 self.assertRaises(NotImplementedError,
50 self.key_mgr.list, None)
51
4852 def test_delete(self):
4953 self.assertRaises(NotImplementedError,
5054 self.key_mgr.delete, None, None)
1414
1515 from oslo_config import cfg
1616
17 from castellan import key_manager
1718 from castellan.key_manager import barbican_key_manager as bkm
1819 from castellan import options
1920 from castellan.tests import base
21 from castellan.tests.unit.key_manager import mock_key_manager
2022
2123
2224 class TestOptions(base.TestCase):
2426 def test_set_defaults(self):
2527 conf = cfg.ConfigOpts()
2628
27 api_class = 'test.api.class'
28 options.set_defaults(conf, api_class=api_class)
29 self.assertEqual(api_class, conf.key_manager.api_class)
29 self.assertTrue(isinstance(key_manager.API(conf),
30 bkm.BarbicanKeyManager))
31
32 cls = mock_key_manager.MockKeyManager
33 backend = '%s.%s' % (cls.__module__, cls.__name__)
34 options.set_defaults(conf, backend=backend)
35 self.assertEqual(backend, conf.key_manager.backend)
36 self.assertIsInstance(key_manager.API(conf),
37 mock_key_manager.MockKeyManager)
3038
3139 barbican_endpoint = 'http://test-server.org:9311/'
3240 options.set_defaults(conf, barbican_endpoint=barbican_endpoint)
0 redirectmatch 301 ^/castellan/([^/]+)/usage.html$ ^/user/index.html
1 redirectmatch 301 ^/castellan/([^/]+)/testing.html$ ^/contributor/testing.html
2 redirectmatch 301 ^/castellan/([^/]+)/installation.html$ ^/install/index.html
6363 # html_last_updated_fmt = '%b %d, %Y'
6464 html_last_updated_fmt = '%Y-%m-%d %H:%M'
6565
66 # Add any paths that contain "extra" files, such as .htaccess or
67 # robots.txt.
68 html_extra_path = ['_extra']
69
6670 # Grouping the document tree into LaTeX files. List of tuples
6771 # (source start file, target name, title, author, documentclass
6872 # [howto/manual]).
3636
3737 # keystone token credential
3838 [key_manager]
39 auth_url = 'http://192.169.5.254:5000'
3940 auth_type = 'keystone_token'
4041 token = '5b4de0bb77064f289f7cc58e33bea8c7'
4142 project_id = 'a1e19934af81420d980a5d02b4afe9fb'
4243
4344 # keystone password credential
4445 [key_manager]
46 auth_url = 'http://192.169.5.254:5000'
4547 auth_type = 'keystone_password'
4648 username = 'admin'
4749 password = 'passw0rd1'
6365
6466 from castellan.common import utils
6567
66 CONF = <your_configuration>
67 context = utils.credential_factory(conf=CONF, context=None)
68 CONF = cfg.CONF
69 CONF(default_config_files=['~/castellan.conf'])
70 context = utils.credential_factory(conf=CONF)
6871
6972 Now you can go ahead and pass the context and use it for authentication.
7073
0 - hosts: all
1 roles:
2 - fetch-tox-output
3 - fetch-stestr-output
0 - hosts: all
1 roles:
2 - run-devstack
3 - role: bindep
4 bindep_profile: test
5 bindep_dir: "{{ zuul_work_dir }}"
6 - test-setup
7 - ensure-tox
0 - hosts: all
1 roles:
2 - tox
0 ---
1 features:
2 - |
3 Added a new provider for Vault (https://www.vaultproject.io/)
0 ---
1 deprecations:
2 - |
3 Config option barbican/auth_endpoint is unnecessary and deprecated in
4 favor of the more standard key_manager/auth_url.
0 ---
1 features:
2 - |
3 Enhance the key manager to handle requests containing the special (all
4 zeros) managed object ID associated with Cinder's and Nova's legacy
5 ConfKeyManager. The purpose of this feature is to help users migrate from
6 the ConfKeyManager to a modern key manager such as Barbican. The feature
7 works by ensuring the ConfKeyManager's all-zeros key ID continues to
8 function when Barbican or Vault is the key manager.
0 # -*- coding: utf-8 -*-
1 # Licensed under the Apache License, Version 2.0 (the "License");
2 # you may not use this file except in compliance with the License.
3 # You may obtain a copy of the License at
4 #
5 # http://www.apache.org/licenses/LICENSE-2.0
6 #
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS,
9 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
10 # implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 # This file is execfile()d with the current directory set to its
15 # containing dir.
16 #
17 # Note that not all possible configuration values are present in this
18 # autogenerated file.
19 #
20 # All configuration values have a default; values that are commented out
21 # serve to show the default.
22
23 # If extensions (or modules to document with autodoc) are in another directory,
24 # add these directories to sys.path here. If the directory is relative to the
25 # documentation root, use os.path.abspath to make it absolute, like shown here.
26 # sys.path.insert(0, os.path.abspath('.'))
27
28 # -- General configuration ------------------------------------------------
29
30 # If your documentation needs a minimal Sphinx version, state it here.
31 # needs_sphinx = '1.0'
32
33 # Add any Sphinx extension module names here, as strings. They can be
34 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
35 # ones.
36 extensions = [
37 'openstackdocstheme',
38 'reno.sphinxext',
39 ]
40
41 # Add any paths that contain templates here, relative to this directory.
42 templates_path = ['_templates']
43
44 # The suffix of source filenames.
45 source_suffix = '.rst'
46
47 # The encoding of source files.
48 # source_encoding = 'utf-8-sig'
49
50 # The master toctree document.
51 master_doc = 'index'
52
53 # General information about the project.
54 repository_name = 'openstack/castellan'
55 bug_project = 'castellan'
56 bug_tag = 'doc'
57 project = u'Castellan Release Notes'
58 copyright = u'2017, Castellan Developers'
59
60 # Release notes do not need a version number in the title, they
61 # cover multiple releases.
62 # The full version, including alpha/beta/rc tags.
63 release = ''
64 # The short X.Y version.
65 version = ''
66
67 # The language for content autogenerated by Sphinx. Refer to documentation
68 # for a list of supported languages.
69 # language = None
70
71 # There are two options for replacing |today|: either, you set today to some
72 # non-false value, then it is used:
73 # today = ''
74 # Else, today_fmt is used as the format for a strftime call.
75 # today_fmt = '%B %d, %Y'
76
77 # List of patterns, relative to source directory, that match files and
78 # directories to ignore when looking for source files.
79 exclude_patterns = []
80
81 # The reST default role (used for this markup: `text`) to use for all
82 # documents.
83 # default_role = None
84
85 # If true, '()' will be appended to :func: etc. cross-reference text.
86 # add_function_parentheses = True
87
88 # If true, the current module name will be prepended to all description
89 # unit titles (such as .. function::).
90 # add_module_names = True
91
92 # If true, sectionauthor and moduleauthor directives will be shown in the
93 # output. They are ignored by default.
94 # show_authors = False
95
96 # The name of the Pygments (syntax highlighting) style to use.
97 pygments_style = 'sphinx'
98
99 # A list of ignored prefixes for module index sorting.
100 # modindex_common_prefix = []
101
102 # If true, keep warnings as "system message" paragraphs in the built documents.
103 # keep_warnings = False
104
105
106 # -- Options for HTML output ----------------------------------------------
107
108 # The theme to use for HTML and HTML Help pages. See the documentation for
109 # a list of builtin themes.
110 html_theme = 'openstackdocs'
111
112 # Theme options are theme-specific and customize the look and feel of a theme
113 # further. For a list of options available for each theme, see the
114 # documentation.
115 # html_theme_options = {}
116
117 # Add any paths that contain custom themes here, relative to this directory.
118 # html_theme_path = []
119
120 # The name for this set of Sphinx documents. If None, it defaults to
121 # "<project> v<release> documentation".
122 # html_title = None
123
124 # A shorter title for the navigation bar. Default is the same as html_title.
125 # html_short_title = None
126
127 # The name of an image file (relative to this directory) to place at the top
128 # of the sidebar.
129 # html_logo = None
130
131 # The name of an image file (within the static path) to use as favicon of the
132 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
133 # pixels large.
134 # html_favicon = None
135
136 # Add any paths that contain custom static files (such as style sheets) here,
137 # relative to this directory. They are copied after the builtin static files,
138 # so a file named "default.css" will overwrite the builtin "default.css".
139 html_static_path = ['_static']
140
141 # Add any extra paths that contain custom files (such as robots.txt or
142 # .htaccess) here, relative to this directory. These files are copied
143 # directly to the root of the documentation.
144 # html_extra_path = []
145
146 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
147 # using the given strftime format.
148 html_last_updated_fmt = '%Y-%m-%d %H:%M'
149
150 # If true, SmartyPants will be used to convert quotes and dashes to
151 # typographically correct entities.
152 # html_use_smartypants = True
153
154 # Custom sidebar templates, maps document names to template names.
155 # html_sidebars = {}
156
157 # Additional templates that should be rendered to pages, maps page names to
158 # template names.
159 # html_additional_pages = {}
160
161 # If false, no module index is generated.
162 # html_domain_indices = True
163
164 # If false, no index is generated.
165 # html_use_index = True
166
167 # If true, the index is split into individual pages for each letter.
168 # html_split_index = False
169
170 # If true, links to the reST sources are added to the pages.
171 # html_show_sourcelink = True
172
173 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
174 # html_show_sphinx = True
175
176 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
177 # html_show_copyright = True
178
179 # If true, an OpenSearch description file will be output, and all pages will
180 # contain a <link> tag referring to it. The value of this option must be the
181 # base URL from which the finished HTML is served.
182 # html_use_opensearch = ''
183
184 # This is the file name suffix for HTML files (e.g. ".xhtml").
185 # html_file_suffix = None
186
187 # Output file base name for HTML help builder.
188 htmlhelp_basename = 'CastellanReleaseNotesdoc'
189
190
191 # -- Options for LaTeX output ---------------------------------------------
192
193 latex_elements = {
194 # The paper size ('letterpaper' or 'a4paper').
195 # 'papersize': 'letterpaper',
196
197 # The font size ('10pt', '11pt' or '12pt').
198 # 'pointsize': '10pt',
199
200 # Additional stuff for the LaTeX preamble.
201 # 'preamble': '',
202 }
203
204 # Grouping the document tree into LaTeX files. List of tuples
205 # (source start file, target name, title,
206 # author, documentclass [howto, manual, or own class]).
207 latex_documents = [
208 ('index', 'CastellanReleaseNotes.tex',
209 u'Castellan Release Notes Documentation',
210 u'Castellan Developers', 'manual'),
211 ]
212
213 # The name of an image file (relative to this directory) to place at the top of
214 # the title page.
215 # latex_logo = None
216
217 # For "manual" documents, if this is true, then toplevel headings are parts,
218 # not chapters.
219 # latex_use_parts = False
220
221 # If true, show page references after internal links.
222 # latex_show_pagerefs = False
223
224 # If true, show URL addresses after external links.
225 # latex_show_urls = False
226
227 # Documents to append as an appendix to all manuals.
228 # latex_appendices = []
229
230 # If false, no module index is generated.
231 # latex_domain_indices = True
232
233
234 # -- Options for manual page output ---------------------------------------
235
236 # One entry per manual page. List of tuples
237 # (source start file, name, description, authors, manual section).
238 man_pages = [
239 ('index', 'castellanreleasenotes',
240 u'Castellan Release Notes Documentation',
241 [u'Castellan Developers'], 1)
242 ]
243
244 # If true, show URL addresses after external links.
245 # man_show_urls = False
246
247
248 # -- Options for Texinfo output -------------------------------------------
249
250 # Grouping the document tree into Texinfo files. List of tuples
251 # (source start file, target name, title, author,
252 # dir menu entry, description, category)
253 texinfo_documents = [
254 ('index', 'CastellanReleaseNotes',
255 u'Castellan Release Notes Documentation',
256 u'Castellan Developers', 'CastellanReleaseNotes',
257 'One line description of project.',
258 'Miscellaneous'),
259 ]
260
261 # Documents to append as an appendix to all manuals.
262 # texinfo_appendices = []
263
264 # If false, no module index is generated.
265 # texinfo_domain_indices = True
266
267 # How to display URL addresses: 'footnote', 'no', or 'inline'.
268 # texinfo_show_urls = 'footnote'
269
270 # If true, do not generate a @detailmenu in the "Top" node's menu.
271 # texinfo_no_detailmenu = False
272
273 # -- Options for Internationalization output ------------------------------
274 locale_dirs = ['locale/']
0 =========================
1 Castellan Release Notes
2 =========================
3
4 .. toctree::
5 :maxdepth: 1
6
7 unreleased
8 pike
0 ===================================
1 Pike Series Release Notes
2 ===================================
3
4 .. release-notes::
5 :branch: stable/pike
0 ==============================
1 Current Series Release Notes
2 ==============================
3
4 .. release-notes::
33
44 pbr!=2.1.0,>=2.0.0 # Apache-2.0
55 Babel!=2.4.0,>=2.3.4 # BSD
6 cryptography>=1.6 # BSD/Apache-2.0
7 python-barbicanclient>=4.0.0 # Apache-2.0
8 oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
9 oslo.context>=2.14.0 # Apache-2.0
10 oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0
11 oslo.log>=3.22.0 # Apache-2.0
12 oslo.utils>=3.20.0 # Apache-2.0
13 keystoneauth1>=2.21.0 # Apache-2.0
6 cryptography!=2.0,>=1.9 # BSD/Apache-2.0
7 python-barbicanclient!=4.5.0,!=4.5.1,>=4.0.0 # Apache-2.0
8 oslo.config>=5.1.0 # Apache-2.0
9 oslo.context>=2.19.2 # Apache-2.0
10 oslo.i18n>=3.15.3 # Apache-2.0
11 oslo.log>=3.30.0 # Apache-2.0
12 oslo.utils>=3.33.0 # Apache-2.0
13 stevedore>=1.20.0 # Apache-2.0
14 keystoneauth1>=3.3.0 # Apache-2.0
2626 castellan.tests.functional.config = castellan.tests.functional.config:list_opts
2727 castellan.config = castellan.options:list_opts
2828
29 castellan.drivers =
30 barbican = castellan.key_manager.barbican_key_manager:BarbicanKeyManager
31 vault = castellan.key_manager.vault_key_manager:VaultKeyManager
32
2933 [build_sphinx]
3034 source-dir = doc/source
3135 build-dir = doc/build
33 hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
44
55 coverage!=4.4,>=4.0 # Apache-2.0
6 python-barbicanclient>=4.0.0 # Apache-2.0
7 python-subunit>=0.0.18 # Apache-2.0/BSD
6 python-barbicanclient!=4.5.0,!=4.5.1,>=4.0.0 # Apache-2.0
7 python-subunit>=1.0.0 # Apache-2.0/BSD
88 sphinx>=1.6.2 # BSD
9 openstackdocstheme>=1.11.0 # Apache-2.0
9 openstackdocstheme>=1.17.0 # Apache-2.0
1010 oslotest>=1.10.0 # Apache-2.0
1111 testrepository>=0.0.18 # Apache-2.0/BSD
1212 testscenarios>=0.4 # Apache-2.0/BSD
13 testtools>=1.4.0 # MIT
13 testtools>=2.2.0 # MIT
1414 bandit>=1.1.0 # Apache-2.0
15 reno>=2.5.0 # Apache-2.0
16 pifpaf>=0.10.0 # Apache-2.0
0 #!/bin/bash
1 set -eux
2 if [ -z "$(which vault)" ]; then
3 VAULT_VERSION=0.7.3
4 SUFFIX=zip
5 case `uname -s` in
6 Darwin)
7 OS=darwin
8 ;;
9 Linux)
10 OS=linux
11 ;;
12 *)
13 echo "Unsupported OS"
14 exit 1
15 esac
16 case `uname -m` in
17 x86_64)
18 MACHINE=amd64
19 ;;
20 *)
21 echo "Unsupported machine"
22 exit 1
23 esac
24 TARBALL_NAME=vault_${VAULT_VERSION}_${OS}_${MACHINE}
25 test ! -d "$TARBALL_NAME" && mkdir ${TARBALL_NAME} && wget https://releases.hashicorp.com/vault/${VAULT_VERSION}/${TARBALL_NAME}.${SUFFIX} && unzip -d ${TARBALL_NAME} ${TARBALL_NAME}.${SUFFIX} && rm ${TARBALL_NAME}.${SUFFIX}
26 export VAULT_CONFIG_PATH=$(pwd)/$TARBALL_NAME/vault.json
27 export PATH=$PATH:$(pwd)/$TARBALL_NAME
28 fi
29
30 $*
+0
-53
tools/tox_install.sh less more
0 #!/usr/bin/env bash
1
2 # [liujiong] This file is refer to tox_install.sh in neutron-lib.
3 # Library constraint file contains this library version pin that is in conflict
4 # with installing the library from source. We should replace the version pin in
5 # the constraints file before applying it for from-source installation.
6
7 ZUUL_CLONER=/usr/zuul-env/bin/zuul-cloner
8 BRANCH_NAME=master
9 LIB_NAME=castellan
10 requirements_installed=$(echo "import openstack_requirements" | python 2>/dev/null ; echo $?)
11
12 set -e
13
14 CONSTRAINTS_FILE=$1
15 shift
16
17 install_cmd="pip install"
18 mydir=$(mktemp -dt "$LIB_NAME-tox_install-XXXXXXX")
19 trap "rm -rf $mydir" EXIT
20 localfile=$mydir/upper-constraints.txt
21 if [[ $CONSTRAINTS_FILE != http* ]]; then
22 CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE
23 fi
24 curl $CONSTRAINTS_FILE -k -o $localfile
25 install_cmd="$install_cmd -c$localfile"
26
27 if [ $requirements_installed -eq 0 ]; then
28 echo "Requirements already installed; using existing package"
29 elif [ -x "$ZUUL_CLONER" ]; then
30 pushd $mydir
31 $ZUUL_CLONER --cache-dir \
32 /opt/git \
33 --branch $BRANCH_NAME \
34 git://git.openstack.org \
35 openstack/requirements
36 cd openstack/requirements
37 $install_cmd -e .
38 popd
39 else
40 if [ -z "$REQUIREMENTS_PIP_LOCATION" ]; then
41 REQUIREMENTS_PIP_LOCATION="git+https://git.openstack.org/openstack/requirements@$BRANCH_NAME#egg=requirements"
42 fi
43 $install_cmd -U -e ${REQUIREMENTS_PIP_LOCATION}
44 fi
45
46 # This is the main purpose of the script: Allow local installation of
47 # the current repo. It is listed in constraints file and thus any
48 # install will be constrained and we need to unconstrain it.
49 edit-constraints $localfile -- $LIB_NAME "-e file://$PWD#egg=$LIB_NAME"
50
51 $install_cmd -U $*
52 exit $?
44
55 [testenv]
66 usedevelop = True
7 install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
7 install_command = pip install {opts} {packages}
88 setenv =
99 VIRTUAL_ENV={envdir}
1010 OS_TEST_PATH=./castellan/tests/unit
11 deps = -r{toxinidir}/requirements.txt
11 deps =
12 -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt}
13 -r{toxinidir}/requirements.txt
1214 -r{toxinidir}/test-requirements.txt
1315 commands = python setup.py testr --slowest --testr-args='{posargs}'
1416
4345 [testenv:docs]
4446 commands = python setup.py build_sphinx
4547
48 [testenv:releasenotes]
49 commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
50
4651 [testenv:functional]
4752 usedevelop = True
4853 install_command = pip install -U {opts} {packages}
4954 setenv =
5055 VIRTUAL_ENV={envdir}
5156 OS_TEST_PATH=./castellan/tests/functional
52 deps = -r{toxinidir}/requirements.txt
53 -r{toxinidir}/test-requirements.txt
5457 commands = python setup.py testr --slowest --testr-args='{posargs}'
58
59 [testenv:functional-vault]
60 passenv = HOME
61 usedevelop = True
62 install_command = pip install -U {opts} {packages}
63 setenv =
64 VIRTUAL_ENV={envdir}
65 OS_TEST_PATH=./castellan/tests/functional
66 commands =
67 {toxinidir}/tools/setup-vault-env.sh pifpaf -e VAULT_TEST run vault -- python setup.py testr --slowest --testr-args='{posargs}'
5568
5669 [testenv:genconfig]
5770 commands =