def delete(self, context, key_id): """Represents deleting the key.""" # Confirm context is provided, if not raise forbidden if not context: msg = _("User is not authorized to use key manager.") raise exception.Forbidden(msg) if not key_id: raise exception.KeyManagerError('key identifier not provided') headers = {'X-Vault-Token': self._root_token_id} try: resource_url = self._get_url() + 'v1/secret/' + key_id resp = self._session.delete(resource_url, verify=self._verify_server, headers=headers) except requests.exceptions.Timeout as ex: raise exception.KeyManagerError(six.text_type(ex)) except requests.exceptions.ConnectionError as ex: raise exception.KeyManagerError(six.text_type(ex)) except Exception as ex: raise exception.KeyManagerError(six.text_type(ex)) if resp.status_code in _EXCEPTIONS_BY_CODE: raise exception.KeyManagerError(resp.reason) if resp.status_code == requests.codes['forbidden']: raise exception.Forbidden() if resp.status_code == requests.codes['not_found']: raise exception.ManagedObjectNotFoundError(uuid=key_id)
def get(self, context, key_id, metadata_only=False): """Retrieves the key identified by the specified id.""" # Confirm context is provided, if not raise forbidden if not context: msg = _("User is not authorized to use key manager.") raise exception.Forbidden(msg) if not key_id: raise exception.KeyManagerError('key identifier not provided') headers = {'X-Vault-Token': self._root_token_id} try: resource_url = self._get_url() + 'v1/secret/' + key_id resp = self._session.get(resource_url, verify=self._verify_server, headers=headers) except requests.exceptions.Timeout as ex: raise exception.KeyManagerError(six.text_type(ex)) except requests.exceptions.ConnectionError as ex: raise exception.KeyManagerError(six.text_type(ex)) except Exception as ex: raise exception.KeyManagerError(six.text_type(ex)) if resp.status_code in _EXCEPTIONS_BY_CODE: raise exception.KeyManagerError(resp.reason) if resp.status_code == requests.codes['forbidden']: raise exception.Forbidden() if resp.status_code == requests.codes['not_found']: raise exception.ManagedObjectNotFoundError(uuid=key_id) record = resp.json()['data'] key = None if metadata_only else binascii.unhexlify(record['value']) clazz = None for type_clazz, type_name in self._secret_type_dict.items(): if type_name == record['type']: clazz = type_clazz if clazz is None: raise exception.KeyManagerError("Unknown type : %r" % record['type']) if hasattr(clazz, 'algorithm') and hasattr(clazz, 'bit_length'): return clazz(record['algorithm'], record['bit_length'], key, record['name'], record['created'], key_id) else: return clazz(key, record['name'], record['created'], key_id)
def create_key_pair(self, context, algorithm, length, expiration=None, name=None): """Creates an asymmetric key pair. This implementation returns UUIDs for the created keys in the order: (private, public) Forbidden is raised if the context is None. """ if context is None: raise exception.Forbidden() if algorithm.lower() != 'rsa': msg = 'Invalid algorithm: {}, only RSA supported'.format(algorithm) raise ValueError(msg) valid_lengths = [2048, 3072, 4096] if length not in valid_lengths: msg = 'Invalid bit length: {}, only {} supported'.format( length, valid_lengths) raise ValueError(msg) private_key, public_key = self._generate_public_and_private_key( length, name) private_key_uuid = self.store(context, private_key) public_key_uuid = self.store(context, public_key) return private_key_uuid, public_key_uuid
def list(self, context, object_type=None, metadata_only=False): """Lists the managed objects given the criteria.""" # Confirm context is provided, if not raise forbidden if not context: msg = _("User is not authorized to use key manager.") raise exception.Forbidden(msg) if object_type and object_type not in self._secret_type_dict: msg = _("Invalid secret type: %s") % object_type raise exception.KeyManagerError(reason=msg) resp = self._do_http_request(self._session.get, self._get_resource_url()) if resp.status_code == requests.codes['not_found']: keys = [] else: keys = resp.json()['data']['keys'] objects = [] for obj_id in keys: try: obj = self.get(context, obj_id, metadata_only=metadata_only) if object_type is None or isinstance(obj, object_type): objects.append(obj) except exception.ManagedObjectNotFoundError as e: LOG.warning( _("Error occurred while retrieving object " "metadata, not adding it to the list: %s"), e) pass return objects
def test_generate_symmetric_key_raises_exception(self): key_spec = ss.KeySpec(ss.KeyAlgorithm.AES, 128) self.plugin.key_manager.create_key.side_effect = exception.Forbidden() self.assertRaises( ss.SecretGeneralException, self.plugin.generate_symmetric_key, key_spec )
def test_delete_secret_throws_exception(self): secret_metadata = {css.CastellanSecretStore.KEY_ID: key_ref1} self.plugin.key_manager.delete.side_effect = exception.Forbidden() self.assertRaises( ss.SecretGeneralException, self.plugin.delete_secret, secret_metadata )
def list(self, context, object_type=None, metadata_only=False): """Lists the managed objects given the criteria.""" # Confirm context is provided, if not raise forbidden if not context: msg = _("User is not authorized to use key manager.") raise exception.Forbidden(msg) if object_type and object_type not in self._secret_type_dict: msg = _("Invalid secret type: %s") % object_type raise exception.KeyManagerError(reason=msg) headers = {'X-Vault-Token': self._root_token_id} try: resource_url = self._get_url() + 'v1/secret/?list=true' resp = self._session.get(resource_url, verify=self._verify_server, headers=headers) keys = resp.json()['data']['keys'] except requests.exceptions.Timeout as ex: raise exception.KeyManagerError(six.text_type(ex)) except requests.exceptions.ConnectionError as ex: raise exception.KeyManagerError(six.text_type(ex)) except Exception as ex: raise exception.KeyManagerError(six.text_type(ex)) if resp.status_code in _EXCEPTIONS_BY_CODE: raise exception.KeyManagerError(resp.reason) if resp.status_code == requests.codes['forbidden']: raise exception.Forbidden() if resp.status_code == requests.codes['not_found']: keys = [] objects = [] for obj_id in keys: try: obj = self.get(context, obj_id, metadata_only=metadata_only) if object_type is None or isinstance(obj, object_type): objects.append(obj) except exception.ManagedObjectNotFoundError as e: LOG.warning( _("Error occurred while retrieving object " "metadata, not adding it to the list: %s"), e) pass return objects
def test_get_secret_throws_exception(self): secret_metadata = self.plugin._meta_dict(key_ref1, 256, 'AES') self.plugin.key_manager.get.side_effect = exception.Forbidden() self.assertRaises( ss.SecretGeneralException, self.plugin.get_secret, ss.SecretType.SYMMETRIC, secret_metadata )
def store(self, context, managed_object, **kwargs): """Stores (i.e., registers) a key with the key manager.""" if context is None: raise exception.Forbidden() key_id = self._generate_key_id() self.keys[key_id] = managed_object return key_id
def delete(self, context, managed_object_id, **kwargs): """Deletes the object identified by the specified id. A Forbidden exception is raised if the context is None and a KeyError is raised if the UUID is invalid. """ if context is None: raise exception.Forbidden() del self.keys[managed_object_id]
def delete(self, context, managed_object_id): if managed_object_id == self.fixed_key_id: LOG.debug("Not deleting key associated with" " fixed_key key ID") if context is None: raise exception.Forbidden() else: super(MigrationKeyManager, self).delete(context, managed_object_id)
def store(self, context, key_value, **kwargs): """Stores (i.e., registers) a key with the key manager.""" # Confirm context is provided, if not raise forbidden if not context: msg = _("User is not authorized to use key manager.") raise exception.Forbidden(msg) key_id = uuid.uuid4().hex return self._store_key_value(key_id, key_value)
def test_generate_asymmetric_throws_exception(self): key_spec = ss.KeySpec(ss.KeyAlgorithm.RSA, 2048) self.plugin.key_manager.create_key_pair.side_effect = ( exception.Forbidden() ) self.assertRaises( ss.SecretGeneralException, self.plugin.generate_asymmetric_key, key_spec )
def test_store_secret_raises_exception(self): payload = 'encrypt me!!' key_spec = mock.MagicMock() content_type = mock.MagicMock() transport_key = None secret_dto = ss.SecretDTO(ss.SecretType.SYMMETRIC, payload, key_spec, content_type, transport_key) self.plugin.key_manager.store.side_effect = exception.Forbidden() self.assertRaises(ss.SecretGeneralException, self.plugin.store_secret, secret_dto)
def get(self, context, managed_object_id, **kwargs): """Retrieves the key identified by the specified id. This implementation returns the key that is associated with the specified UUID. A Forbidden exception is raised if the specified context is None; a KeyError is raised if the UUID is invalid. """ if context is None: raise exception.Forbidden() return self.keys[managed_object_id]
def create_key(self, context, **kwargs): """Creates a symmetric key. This implementation returns a UUID for the created key. The algorithm for the key will always be AES. A Forbidden exception is raised if the specified context is None. """ if context is None: raise exception.Forbidden() key = self._generate_key(**kwargs) return self.store(context, key)
def create_key(self, context, algorithm, length, name=None, **kwargs): """Creates a symmetric key.""" # Confirm context is provided, if not raise forbidden if not context: msg = _("User is not authorized to use key manager.") raise exception.Forbidden(msg) key_id = uuid.uuid4().hex key_value = os.urandom(length or 32) key = sym_key.SymmetricKey(algorithm, length or 32, key_value, key_id, name or int(time.time())) return self._store_key_value(key_id, key)
def list(self, context, object_type=None, metadata_only=False): """Retrieves a list of managed objects that match the criteria. A Forbidden exception is raised if the context is None. If no search criteria is given, all objects are returned. """ if context is None: raise exception.Forbidden() objects = [] for obj_id in self.keys: obj = self.get(context, obj_id, metadata_only=metadata_only) if type(obj) == object_type or object_type is None: objects.append(obj) return objects
def delete(self, context, key_id): """Represents deleting the key.""" # Confirm context is provided, if not raise forbidden if not context: msg = _("User is not authorized to use key manager.") raise exception.Forbidden(msg) if not key_id: raise exception.KeyManagerError('key identifier not provided') resp = self._do_http_request(self._session.delete, self._get_resource_url(key_id)) if resp.status_code == requests.codes['not_found']: raise exception.ManagedObjectNotFoundError(uuid=key_id)
def get(self, context, managed_object_id): if managed_object_id == self.fixed_key_id: LOG.debug("Processing request for secret associated" " with fixed_key key ID") if context is None: raise exception.Forbidden() key_bytes = bytes(binascii.unhexlify(self.fixed_key)) secret = symmetric_key.SymmetricKey( 'AES', len(key_bytes) * 8, key_bytes) else: secret = super(MigrationKeyManager, self).get(context, managed_object_id) return secret
def _get_keystone_auth(self, context): if context.__class__.__name__ == 'KeystonePassword': return identity.Password( auth_url=context.auth_url, username=context.username, password=context.password, user_id=context.user_id, user_domain_id=context.user_domain_id, user_domain_name=context.user_domain_name, trust_id=context.trust_id, domain_id=context.domain_id, domain_name=context.domain_name, project_id=context.project_id, project_name=context.project_name, project_domain_id=context.project_domain_id, project_domain_name=context.project_domain_name, reauthenticate=context.reauthenticate) elif context.__class__.__name__ == 'KeystoneToken': return identity.Token( auth_url=context.auth_url, token=context.token, trust_id=context.trust_id, domain_id=context.domain_id, domain_name=context.domain_name, project_id=context.project_id, project_name=context.project_name, project_domain_id=context.project_domain_id, project_domain_name=context.project_domain_name, reauthenticate=context.reauthenticate) # this will be kept for oslo.context compatibility until # projects begin to use utils.credential_factory elif context.__class__.__name__ == 'RequestContext': if getattr(context, 'get_auth_plugin', None): return context.get_auth_plugin() else: return identity.Token( auth_url=self.conf.barbican.auth_endpoint, token=context.auth_token, project_id=context.project_id, project_name=context.project_name, project_domain_id=context.project_domain_id, project_domain_name=context.project_domain_name) else: msg = _("context must be of type KeystonePassword, " "KeystoneToken, or RequestContext.") LOG.error(msg) raise exception.Forbidden(reason=msg)
def get(self, context, managed_object_id, metadata_only=False, **kwargs): """Retrieves the key identified by the specified id. This implementation returns the key that is associated with the specified UUID. A Forbidden exception is raised if the specified context is None; a KeyError is raised if the UUID is invalid. """ if context is None: raise exception.Forbidden() obj = self.keys[managed_object_id] if metadata_only: if hasattr(obj, "_key"): obj._key = None if hasattr(obj, "_data"): obj._data = None if hasattr(obj, "_passphrase"): obj._passphrase = None return obj
def _do_http_request(self, method, resource, json=None): verify = self._verify_server headers = self._build_auth_headers() try: resp = method(resource, headers=headers, json=json, verify=verify) except requests.exceptions.Timeout as ex: raise exception.KeyManagerError(six.text_type(ex)) except requests.exceptions.ConnectionError as ex: raise exception.KeyManagerError(six.text_type(ex)) except Exception as ex: raise exception.KeyManagerError(six.text_type(ex)) if resp.status_code in _EXCEPTIONS_BY_CODE: raise exception.KeyManagerError(resp.reason) if resp.status_code == requests.codes['forbidden']: raise exception.Forbidden() return resp
def _get_barbican_client(self, context): """Creates a client to connect to the Barbican service. :param context: the user context for authentication :return: a Barbican Client object :raises Forbidden: if the context is None :raises KeyManagerError: if context is missing tenant or tenant is None or error occurs while creating client """ # Confirm context is provided, if not raise forbidden if not context: msg = u._("User is not authorized to use key manager.") LOG.error(msg) raise exception.Forbidden(msg) if not hasattr(context, 'tenant') or context.tenant is None: msg = u._("Unable to create Barbican Client without tenant " "attribute in context object.") LOG.error(msg) raise exception.KeyManagerError(reason=msg) if self._barbican_client and self._current_context == context: return self._barbican_client try: self._current_context = context auth = self._get_keystone_auth(context) sess = session.Session(auth=auth) self._barbican_endpoint = self._get_barbican_endpoint(auth, sess) self._barbican_client = barbican_client.Client( session=sess, endpoint=self._barbican_endpoint) except Exception as e: LOG.error(u._LE("Error creating Barbican client: %s"), e) raise exception.KeyManagerError(reason=e) self._base_url = self._create_base_url(auth, sess, self._barbican_endpoint) return self._barbican_client
def _get_keystone_auth(self, context): auth_url = self.conf.barbican.auth_endpoint if context.__class__.__name__ is 'KeystonePassword': return identity.v3.Password( auth_url=auth_url, username=context.username, password=context.password, user_id=context.user_id, user_domain_id=context.user_domain_id, user_domain_name=context.user_domain_name, trust_id=context.trust_id, domain_id=context.domain_id, domain_name=context.domain_name, project_id=context.project_id, project_name=context.project_name, project_domain_id=context.project_domain_id, project_domain_name=context.project_domain_name, reauthenticate=context.reauthenticate) elif context.__class__.__name__ is 'KeystoneToken': return identity.v3.Token( auth_url=auth_url, token=context.token, trust_id=context.trust_id, domain_id=context.domain_id, domain_name=context.domain_name, project_id=context.project_id, project_name=context.project_name, project_domain_id=context.project_domain_id, project_domain_name=context.project_domain_name, reauthenticate=context.reauthenticate) # this will be kept for oslo.context compatibility until # projects begin to use utils.credential_factory elif context.__class__.__name__ is 'RequestContext': return identity.v3.Token(auth_url=auth_url, token=context.auth_token, project_id=context.tenant) else: msg = "context must be of type KeystonePassword, KeystoneToken, " "or RequestContext." LOG.error(msg) raise exception.Forbidden(reason=msg)
def _store_key_value(self, key_id, value): type_value = self._secret_type_dict.get(type(value)) if type_value is None: raise exception.KeyManagerError("Unknown type for value : %r" % value) headers = {'X-Vault-Token': self._root_token_id} try: resource_url = self._get_url() + 'v1/secret/' + key_id record = { 'type': type_value, 'value': binascii.hexlify(value.get_encoded()).decode('utf-8'), 'algorithm': (value.algorithm if hasattr(value, 'algorithm') else None), 'bit_length': (value.bit_length if hasattr(value, 'bit_length') else None), 'name': value.name, 'created': value.created } resp = self._session.post(resource_url, verify=self._verify_server, json=record, headers=headers) except requests.exceptions.Timeout as ex: raise exception.KeyManagerError(six.text_type(ex)) except requests.exceptions.ConnectionError as ex: raise exception.KeyManagerError(six.text_type(ex)) except Exception as ex: raise exception.KeyManagerError(six.text_type(ex)) if resp.status_code in _EXCEPTIONS_BY_CODE: raise exception.KeyManagerError(resp.reason) if resp.status_code == requests.codes['forbidden']: raise exception.Forbidden() return key_id
def _get_barbican_client(self, context): """Creates a client to connect to the Barbican service. :param context: the user context for authentication :return: a Barbican Client object :raises Forbidden: if the context is None :raises KeyManagerError: if context is missing tenant or tenant is None or error occurs while creating client """ # Confirm context is provided, if not raise forbidden if not context: msg = _("User is not authorized to use key manager.") LOG.error(msg) raise exception.Forbidden(msg) if self._barbican_client and self._current_context == context: return self._barbican_client try: auth = self._get_keystone_auth(context) sess = session.Session(auth=auth, verify=self.conf.barbican.verify_ssl) self._barbican_endpoint = self._get_barbican_endpoint(auth, sess) self._barbican_client = barbican_client_import.Client( session=sess, endpoint=self._barbican_endpoint) self._current_context = context # TODO(pbourke): more fine grained exception handling - we are eating # tracebacks here except Exception as e: LOG.error("Error creating Barbican client: %s", e) raise exception.KeyManagerError(reason=e) self._base_url = self._create_base_url(auth, sess, self._barbican_endpoint) return self._barbican_client
def _build_auth_headers(self): if self._root_token_id: return {'X-Vault-Token': self._root_token_id} if self._approle_token_id: return {'X-Vault-Token': self._approle_token_id} if self._approle_role_id: params = { 'role_id': self._approle_role_id } if self._approle_secret_id: params['secret_id'] = self._approle_secret_id approle_login_url = '{}v1/auth/approle/login'.format( self._get_url() ) token_issue_utc = timeutils.utcnow() try: resp = self._session.post(url=approle_login_url, json=params, verify=self._verify_server) except requests.exceptions.Timeout as ex: raise exception.KeyManagerError(str(ex)) except requests.exceptions.ConnectionError as ex: raise exception.KeyManagerError(str(ex)) except Exception as ex: raise exception.KeyManagerError(str(ex)) if resp.status_code in _EXCEPTIONS_BY_CODE: raise exception.KeyManagerError(resp.reason) if resp.status_code == requests.codes['forbidden']: raise exception.Forbidden() resp = resp.json() self._cached_approle_token_id = resp['auth']['client_token'] self._approle_token_issue = token_issue_utc self._approle_token_ttl = resp['auth']['lease_duration'] return {'X-Vault-Token': self._approle_token_id} return {}
def create_key_pair(self, context, algorithm, length, expiration=None, name=None): """Creates an asymmetric key pair.""" # Confirm context is provided, if not raise forbidden if not context: msg = _("User is not authorized to use key manager.") raise exception.Forbidden(msg) if algorithm.lower() != 'rsa': raise NotImplementedError( "VaultKeyManager only implements rsa keys") priv_key = rsa.generate_private_key(public_exponent=65537, key_size=length, backend=default_backend()) private_key = pri_key.PrivateKey( 'RSA', length, priv_key.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption())) private_key_id = uuid.uuid4().hex private_id = self._store_key_value(private_key_id, private_key) # pub_key = priv_key.public_key() public_key = pub_key.PublicKey( 'RSA', length, priv_key.public_key().public_bytes( Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)) public_key_id = uuid.uuid4().hex public_id = self._store_key_value(public_key_id, public_key) return private_id, public_id
def get(self, context, key_id, metadata_only=False): """Retrieves the key identified by the specified id.""" # Confirm context is provided, if not raise forbidden if not context: msg = _("User is not authorized to use key manager.") raise exception.Forbidden(msg) if not key_id: raise exception.KeyManagerError('key identifier not provided') resp = self._do_http_request(self._session.get, self._get_resource_url(key_id)) if resp.status_code == requests.codes['not_found']: raise exception.ManagedObjectNotFoundError(uuid=key_id) record = resp.json()['data'] if self._get_api_version() != '1': record = record['data'] key = None if metadata_only else binascii.unhexlify(record['value']) clazz = None for type_clazz, type_name in self._secret_type_dict.items(): if type_name == record['type']: clazz = type_clazz if clazz is None: raise exception.KeyManagerError("Unknown type : %r" % record['type']) if hasattr(clazz, 'algorithm') and hasattr(clazz, 'bit_length'): return clazz(record['algorithm'], record['bit_length'], key, record['name'], record['created'], key_id) else: return clazz(key, record['name'], record['created'], key_id)