f0bb09dae2
With python 3.x, classes can use the metaclass= logic to not require usage of the six library. Subsequent patches will replace other six usages. Change-Id: I5f52283b520a3d4b5a7a112afa85d668f9ec4881
668 lines
25 KiB
Python
668 lines
25 KiB
Python
# Copyright (c) 2014 Johns Hopkins University Applied Physics Laboratory
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import abc
|
|
|
|
from oslo_config import cfg
|
|
from stevedore import named
|
|
|
|
from barbican.common import config
|
|
from barbican.common import exception
|
|
from barbican.common import utils
|
|
from barbican import i18n as u
|
|
from barbican.plugin.util import multiple_backends
|
|
from barbican.plugin.util import utils as plugin_utils
|
|
|
|
|
|
_SECRET_STORE = None
|
|
|
|
CONF = config.new_config()
|
|
DEFAULT_PLUGIN_NAMESPACE = 'barbican.secretstore.plugin'
|
|
DEFAULT_PLUGINS = ['store_crypto']
|
|
|
|
store_opt_group = cfg.OptGroup(name='secretstore',
|
|
title='Secret Store Plugin Options')
|
|
store_opts = [
|
|
cfg.StrOpt('namespace',
|
|
default=DEFAULT_PLUGIN_NAMESPACE,
|
|
help=u._('Extension namespace to search for plugins.')
|
|
),
|
|
cfg.MultiStrOpt('enabled_secretstore_plugins',
|
|
default=DEFAULT_PLUGINS,
|
|
help=u._('List of secret store plugins to load.')
|
|
),
|
|
cfg.BoolOpt('enable_multiple_secret_stores',
|
|
default=False,
|
|
help=u._('Flag to enable multiple secret store plugin'
|
|
' backend support. Default is False')
|
|
),
|
|
cfg.ListOpt('stores_lookup_suffix',
|
|
help=u._('List of suffix to use for looking up plugins which '
|
|
'are supported with multiple backend support.')
|
|
)
|
|
]
|
|
CONF.register_group(store_opt_group)
|
|
CONF.register_opts(store_opts, group=store_opt_group)
|
|
config.parse_args(CONF)
|
|
|
|
config.set_module_config("secretstore", CONF)
|
|
|
|
|
|
def list_opts():
|
|
yield store_opt_group, store_opts
|
|
|
|
|
|
class SecretStorePluginNotFound(exception.BarbicanHTTPException):
|
|
"""Raised when no plugins are installed."""
|
|
|
|
client_message = u._("No plugin was found that could support your request")
|
|
status_code = 400
|
|
|
|
def __init__(self, plugin_name=None):
|
|
if plugin_name:
|
|
message = u._('Secret store plugin "{name}"'
|
|
' not found.').format(name=plugin_name)
|
|
else:
|
|
message = u._("Secret store plugin not found.")
|
|
super(SecretStorePluginNotFound, self).__init__(message)
|
|
|
|
|
|
class SecretStoreSupportedPluginNotFound(exception.BarbicanHTTPException):
|
|
"""Raised when no secret store supported plugin is found."""
|
|
|
|
client_message = u._("Secret store supported plugin not found.")
|
|
status_code = 400
|
|
|
|
def __init__(self, key_spec):
|
|
message = u._("Could not find a secret store plugin for storing "
|
|
"secret with algorithm '{alg}' and bit-length "
|
|
"'{len}'.").format(alg=key_spec.alg,
|
|
len=key_spec.bit_length)
|
|
super(SecretStoreSupportedPluginNotFound, self).__init__(
|
|
message)
|
|
|
|
|
|
class SecretGenerateSupportedPluginNotFound(exception.BarbicanHTTPException):
|
|
"""Raised when no secret generate supported plugin is found."""
|
|
|
|
client_message = u._("Secret generate supported plugin not found.")
|
|
status_code = 400
|
|
|
|
def __init__(self, key_spec):
|
|
message = u._("Could not find a secret store plugin for generating "
|
|
"secret with algorithm '{alg}' and bit-length "
|
|
"'{len}'.").format(alg=key_spec.alg,
|
|
len=key_spec.bit_length)
|
|
super(SecretGenerateSupportedPluginNotFound, self).__init__(
|
|
message)
|
|
|
|
|
|
class SecretContentTypeNotSupportedException(exception.BarbicanHTTPException):
|
|
"""Raised when support for payload content type is not available."""
|
|
|
|
status_code = 400
|
|
|
|
def __init__(self, content_type):
|
|
super(SecretContentTypeNotSupportedException, self).__init__(
|
|
u._("A Content-Type of '{content_type}' for secrets is "
|
|
"not supported").format(
|
|
content_type=content_type)
|
|
)
|
|
self.content_type = content_type
|
|
self.client_message = u._(
|
|
"content-type of '{content_type}' not supported").format(
|
|
content_type=content_type)
|
|
|
|
|
|
class SecretContentEncodingNotSupportedException(
|
|
exception.BarbicanHTTPException):
|
|
"""Raised when support for payload content encoding is not available."""
|
|
status_code = 400
|
|
|
|
def __init__(self, content_encoding):
|
|
super(SecretContentEncodingNotSupportedException, self).__init__(
|
|
u._("Secret Content-Encoding of '{content_encoding}' "
|
|
"not supported").format(
|
|
content_encoding=content_encoding)
|
|
)
|
|
self.content_encoding = content_encoding
|
|
self.client_message = u._(
|
|
"content-encoding of '{content_encoding}' not supported").format(
|
|
content_encoding=content_encoding)
|
|
|
|
|
|
class SecretNoPayloadProvidedException(exception.BarbicanException):
|
|
"""Raised when secret information is not provided."""
|
|
def __init__(self):
|
|
super(SecretNoPayloadProvidedException, self).__init__(
|
|
u._('No secret information provided to encrypt.')
|
|
)
|
|
|
|
|
|
class SecretContentEncodingMustBeBase64(exception.BarbicanHTTPException):
|
|
"""Raised when encoding must be base64."""
|
|
|
|
client_message = u._("Text-based binary secret payloads must "
|
|
"specify a content-encoding of 'base64'")
|
|
status_code = 400
|
|
|
|
def __init__(self):
|
|
super(SecretContentEncodingMustBeBase64, self).__init__(
|
|
u._("Encoding type must be 'base64' for text-based payloads.")
|
|
)
|
|
|
|
|
|
class SecretGeneralException(exception.BarbicanException):
|
|
"""Raised when a system fault has occurred."""
|
|
def __init__(self, reason=u._('Unknown')):
|
|
super(SecretGeneralException, self).__init__(
|
|
u._('Problem seen during crypto processing - '
|
|
'Reason: {reason}').format(reason=reason)
|
|
)
|
|
self.reason = reason
|
|
|
|
|
|
class SecretPayloadDecodingError(exception.BarbicanHTTPException):
|
|
"""Raised when payload could not be decoded."""
|
|
|
|
client_message = u._("Problem decoding payload")
|
|
status_code = 400
|
|
|
|
def __init__(self):
|
|
super(SecretPayloadDecodingError, self).__init__(
|
|
u._("Problem decoding payload")
|
|
)
|
|
|
|
|
|
class SecretAcceptNotSupportedException(exception.BarbicanHTTPException):
|
|
"""Raised when requested decrypted content-type is not available."""
|
|
|
|
client_message = u._("Wrong payload content-type")
|
|
status_code = 406
|
|
|
|
def __init__(self, accept):
|
|
super(SecretAcceptNotSupportedException, self).__init__(
|
|
u._("Secret Accept of '{accept}' not supported").format(
|
|
accept=accept)
|
|
)
|
|
self.accept = accept
|
|
|
|
|
|
class SecretNotFoundException(exception.BarbicanHTTPException):
|
|
"""Raised when secret information could not be located."""
|
|
|
|
client_message = u._("Secret not found.")
|
|
status_code = 404
|
|
|
|
def __init__(self):
|
|
super(SecretNotFoundException, self).__init__(
|
|
u._('No secret information found'))
|
|
|
|
|
|
class SecretAlgorithmNotSupportedException(exception.BarbicanHTTPException):
|
|
"""Raised when support for an algorithm is not available."""
|
|
|
|
client_message = u._("Requested algorithm is not supported")
|
|
status_code = 400
|
|
|
|
def __init__(self, algorithm):
|
|
super(SecretAlgorithmNotSupportedException, self).__init__(
|
|
u._("Secret algorithm of '{algorithm}' not supported").format(
|
|
algorithm=algorithm)
|
|
)
|
|
self.algorithm = algorithm
|
|
|
|
|
|
class GeneratePassphraseNotSupportedException(exception.BarbicanHTTPException):
|
|
"""Raised when generating keys encrypted by passphrase is not supported."""
|
|
|
|
client_message = (
|
|
u._("Generating keys encrypted with passphrases is not supported")
|
|
)
|
|
status_code = 400
|
|
|
|
def __init__(self):
|
|
super(GeneratePassphraseNotSupportedException, self).__init__(
|
|
self.client_message
|
|
)
|
|
|
|
|
|
class SecretStorePluginsNotConfigured(exception.BarbicanException):
|
|
"""Raised when there are no secret store plugins configured."""
|
|
def __init__(self):
|
|
super(SecretStorePluginsNotConfigured, self).__init__(
|
|
u._('No secret store plugins have been configured')
|
|
)
|
|
|
|
|
|
class StorePluginNotAvailableOrMisconfigured(exception.BarbicanException):
|
|
"""Raised when a plugin that was previously used can not be found."""
|
|
def __init__(self, plugin_name):
|
|
super(StorePluginNotAvailableOrMisconfigured, self).__init__(
|
|
u._("The requested Store Plugin {plugin_name} is not "
|
|
"currently available. This is probably a server "
|
|
"misconfiguration.").format(
|
|
plugin_name=plugin_name)
|
|
)
|
|
self.plugin_name = plugin_name
|
|
|
|
|
|
class SecretType(object):
|
|
|
|
"""Constant to define the symmetric key type.
|
|
|
|
Used by getSecret to retrieve a symmetric key.
|
|
"""
|
|
SYMMETRIC = "symmetric"
|
|
"""Constant to define the public key type. Used by getSecret to retrieve a
|
|
public key.
|
|
"""
|
|
PUBLIC = "public"
|
|
"""Constant to define the private key type. Used by getSecret to retrieve a
|
|
private key.
|
|
"""
|
|
PRIVATE = "private"
|
|
"""Constant to define the passphrase type. Used by getSecret to retrieve a
|
|
passphrase."""
|
|
PASSPHRASE = "passphrase" # nosec
|
|
"""Constant to define the certificate type. Used by getSecret to retrieve a
|
|
certificate."""
|
|
CERTIFICATE = "certificate"
|
|
"""Constant to define the opaque date type. Used by getSecret to retrieve
|
|
opaque data. Opaque data can be any kind of data. This data type signals to
|
|
Barbican to just store the information and do not worry about the format or
|
|
encoding. This is the default type if no type is specified by the user."""
|
|
OPAQUE = utils.SECRET_TYPE_OPAQUE
|
|
|
|
|
|
class KeyAlgorithm(object):
|
|
|
|
"""Constant for the Diffie Hellman algorithm."""
|
|
DIFFIE_HELLMAN = "diffie_hellman"
|
|
"""Constant for the DSA algorithm."""
|
|
DSA = "dsa"
|
|
"""Constant for the RSA algorithm."""
|
|
RSA = "rsa"
|
|
"""Constant for the Elliptic Curve algorithm."""
|
|
EC = "ec"
|
|
"""Constant for the HMACSHA1 algorithm."""
|
|
HMACSHA1 = "hmacsha1"
|
|
"""Constant for the HMACSHA256 algorithm."""
|
|
HMACSHA256 = "hmacsha256"
|
|
"""Constant for the HMACSHA384 algorithm."""
|
|
HMACSHA384 = "hmacsha384"
|
|
"""Constant for the HMACSHA512 algorithm."""
|
|
HMACSHA512 = "hmacsha512"
|
|
"""List of asymmetric algorithms"""
|
|
ASYMMETRIC_ALGORITHMS = [DIFFIE_HELLMAN, DSA, RSA, EC]
|
|
"""Constant for the AES algorithm."""
|
|
AES = "aes"
|
|
"""Constant for the DES algorithm."""
|
|
DES = "des"
|
|
"""Constant for the DESede (triple-DES) algorithm."""
|
|
DESEDE = "desede"
|
|
"""List of symmetric algorithms"""
|
|
SYMMETRIC_ALGORITHMS = [AES, DES, DESEDE, HMACSHA1,
|
|
HMACSHA256, HMACSHA384, HMACSHA512]
|
|
|
|
|
|
class KeySpec(object):
|
|
"""This object specifies the algorithm and bit length for a key."""
|
|
|
|
def __init__(self, alg=None, bit_length=None, mode=None, passphrase=None):
|
|
"""Creates a new KeySpec.
|
|
|
|
:param alg:algorithm for the key
|
|
:param bit_length:bit length of the key
|
|
:param mode:algorithm mode for the key
|
|
:param passphrase:passphrase for the private_key
|
|
"""
|
|
self.alg = alg
|
|
self.bit_length = bit_length
|
|
self.mode = mode # TODO(john-wood-w) Paul, is 'mode' required?
|
|
self.passphrase = passphrase
|
|
|
|
|
|
class SecretDTO(object):
|
|
"""This object is a secret data transfer object (DTO).
|
|
|
|
This object encapsulates a key and attributes about the key. The attributes
|
|
include a KeySpec that contains the algorithm and bit length. The
|
|
attributes also include information on the encoding of the key.
|
|
"""
|
|
|
|
# TODO(john-wood-w) Remove 'content_type' once secret normalization work is
|
|
# completed.
|
|
def __init__(self, type, secret, key_spec, content_type,
|
|
transport_key=None):
|
|
"""Creates a new SecretDTO.
|
|
|
|
The secret is stored in the secret parameter. In the future this
|
|
DTO may include compression and key wrapping information.
|
|
|
|
:param type: SecretType for secret
|
|
:param secret: secret, as a base64-encoded string
|
|
:param key_spec: KeySpec key specifications
|
|
:param content_type: Content type of the secret, one of MIME
|
|
types such as 'text/plain' or 'application/octet-stream'
|
|
:param transport_key: presence of this parameter indicates that the
|
|
secret has been encrypted using a transport key. The transport
|
|
key is a base64 encoded x509 transport certificate.
|
|
"""
|
|
self.type = type or SecretType.OPAQUE
|
|
self.secret = secret
|
|
self.key_spec = key_spec
|
|
self.content_type = content_type
|
|
self.transport_key = transport_key
|
|
|
|
|
|
class AsymmetricKeyMetadataDTO(object):
|
|
"""This DTO encapsulates metadata(s) for asymmetric key components.
|
|
|
|
These components are private_key_meta, public_key_meta and passphrase_meta.
|
|
"""
|
|
|
|
def __init__(self, private_key_meta=None,
|
|
public_key_meta=None,
|
|
passphrase_meta=None):
|
|
"""Constructor for AsymmetricKeyMetadataDTO
|
|
|
|
:param private_key_meta: private key metadata
|
|
:param public_key_meta: public key metadata
|
|
:param passphrase_meta: passphrase key metadata
|
|
"""
|
|
self.private_key_meta = private_key_meta
|
|
self.public_key_meta = public_key_meta
|
|
self.passphrase_meta = passphrase_meta
|
|
|
|
|
|
class SecretStoreBase(object, metaclass=abc.ABCMeta):
|
|
|
|
@abc.abstractmethod
|
|
def get_plugin_name(self):
|
|
"""Gets user friendly plugin name.
|
|
|
|
This plugin name is expected to be read from config file.
|
|
There will be a default defined for plugin name which can be customized
|
|
in specific deployment if needed.
|
|
|
|
This name needs to be unique across a deployment.
|
|
"""
|
|
raise NotImplementedError # pragma: no cover
|
|
|
|
@abc.abstractmethod
|
|
def generate_symmetric_key(self, key_spec):
|
|
"""Generate a new symmetric key and store it.
|
|
|
|
Generates a new symmetric key and stores it in the secret store.
|
|
A dictionary is returned that contains metadata about the newly created
|
|
symmetric key. The dictionary of metadata is stored by Barbican and
|
|
passed into other methods to aid the plugins. This can be useful for
|
|
plugins that generate a unique ID in the external data store and use it
|
|
to retrieve the key in the future. The returned dictionary may be empty
|
|
if the SecretStore does not require it.
|
|
|
|
:param key_spec: KeySpec that contains details on the type of key to
|
|
generate
|
|
:returns: an optional dictionary containing metadata about the key
|
|
"""
|
|
raise NotImplementedError # pragma: no cover
|
|
|
|
@abc.abstractmethod
|
|
def generate_asymmetric_key(self, key_spec):
|
|
"""Generate a new asymmetric key pair and store it.
|
|
|
|
Generates a new asymmetric key pair and stores it in the secret
|
|
store. An object of type AsymmetricKeyMetadataDTO will be returned
|
|
containing attributes of metadata for newly created key pairs.
|
|
The metadata is stored by Barbican and passed into other methods
|
|
to aid the plugins. This can be useful for plugins that generate
|
|
a unique ID in the external data store and use it to retrieve the
|
|
key pairs in the future.
|
|
|
|
:param key_spec: KeySpec that contains details on the type of key to
|
|
generate
|
|
:returns: An object of type AsymmetricKeyMetadataDTO containing
|
|
metadata about the key pair.
|
|
"""
|
|
raise NotImplementedError # pragma: no cover
|
|
|
|
@abc.abstractmethod
|
|
def store_secret(self, secret_dto):
|
|
"""Stores a key.
|
|
|
|
The SecretDTO contains the bytes of the secret and properties of the
|
|
secret. The SecretStore retrieves the secret bytes, stores them, and
|
|
returns a dictionary of metadata about the secret. This can be
|
|
useful for plugins that generate a unique ID in the external data
|
|
store and use it to retrieve the secret in the future. The returned
|
|
dictionary may be empty if the SecretStore does not require it.
|
|
|
|
:param secret_dto: SecretDTO for secret
|
|
:returns: an optional dictionary containing metadata about the secret
|
|
"""
|
|
raise NotImplementedError # pragma: no cover
|
|
|
|
@abc.abstractmethod
|
|
def get_secret(self, secret_type, secret_metadata):
|
|
"""Retrieves a secret from the secret store.
|
|
|
|
Retrieves a secret from the secret store and returns a SecretDTO that
|
|
contains the secret.
|
|
|
|
The secret_metadata parameter is the metadata returned from one of the
|
|
generate or store methods. This data is used by the plugins to retrieve
|
|
the key.
|
|
|
|
The secret_type parameter may be useful for secret stores to know the
|
|
expected format of the secret. For instance if the type is
|
|
SecretDTO.PRIVATE then a PKCS8 structure is returned. This way secret
|
|
stores do not need to manage the secret type on their own.
|
|
|
|
:param secret_type: secret type
|
|
:param secret_metadata: secret metadata
|
|
:returns: SecretDTO that contains secret
|
|
"""
|
|
raise NotImplementedError # pragma: no cover
|
|
|
|
@abc.abstractmethod
|
|
def generate_supports(self, key_spec):
|
|
"""Returns a boolean indicating if the secret type is supported.
|
|
|
|
This checks if the algorithm and bit length are supported by the
|
|
generate methods. This is useful to call before calling
|
|
generate_symmetric_key or generate_asymetric_key to see if the key type
|
|
is supported before trying to generate it.
|
|
|
|
:param key_spec: KeySpec that contains details on the algorithm and bit
|
|
length
|
|
:returns: boolean indicating if the algorithm is supported
|
|
"""
|
|
raise NotImplementedError # pragma: no cover
|
|
|
|
@abc.abstractmethod
|
|
def delete_secret(self, secret_metadata):
|
|
"""Deletes a secret from the secret store.
|
|
|
|
Deletes a secret from a secret store. It can no longer be referenced
|
|
after this call.
|
|
|
|
:param secret_metadata: secret_metadata
|
|
"""
|
|
raise NotImplementedError # pragma: no cover
|
|
|
|
@abc.abstractmethod
|
|
def store_secret_supports(self, key_spec):
|
|
"""Returns a boolean indicating if the secret can be stored.
|
|
|
|
Checks if the secret store can store the secret, give the attributes
|
|
of the secret in the KeySpec. For example, some plugins may need to
|
|
know the attributes in order to store the secret, but other plugins
|
|
may be able to store the secret as a blob if no attributes are given.
|
|
|
|
:param key_spec: KeySpec for the secret
|
|
:returns: a boolean indicating if the secret can be stored
|
|
"""
|
|
raise NotImplementedError # pragma: no cover
|
|
|
|
def get_transport_key(self):
|
|
"""Gets a transport key.
|
|
|
|
Returns the current valid transport key associated with this plugin.
|
|
The transport key is expected to be a base64 encoded x509 certificate
|
|
containing a public key. Admins are responsible for deleting old keys
|
|
from the database using the DELETE method on the TransportKey resource.
|
|
|
|
By default, returns None. Plugins that support transport key
|
|
wrapping should override this method.
|
|
"""
|
|
return None
|
|
|
|
def is_transport_key_current(self, transport_key):
|
|
"""Determines if the provided transport key is the current valid key
|
|
|
|
Returns true if the transport key is the current valid transport key.
|
|
If the key is not valid, then barbican core will request a new
|
|
transport key from the plugin.
|
|
|
|
Returns False by default. Plugins that support transport key wrapping
|
|
should override this method.
|
|
"""
|
|
return False
|
|
|
|
|
|
def _enforce_extensions_configured(plugin_related_function):
|
|
def _check_plugins_configured(self, *args, **kwargs):
|
|
if not self.extensions:
|
|
raise SecretStorePluginsNotConfigured()
|
|
return plugin_related_function(self, *args, **kwargs)
|
|
return _check_plugins_configured
|
|
|
|
|
|
class SecretStorePluginManager(named.NamedExtensionManager):
|
|
def __init__(self, conf=CONF, invoke_args=(), invoke_kwargs={}):
|
|
ss_conf = config.get_module_config('secretstore')
|
|
plugin_names = self._get_internal_plugin_names(ss_conf)
|
|
|
|
super(SecretStorePluginManager, self).__init__(
|
|
ss_conf.secretstore.namespace,
|
|
plugin_names,
|
|
invoke_on_load=False, # Defer creating plugins to utility below.
|
|
invoke_args=invoke_args,
|
|
invoke_kwds=invoke_kwargs,
|
|
name_order=True # extensions sorted as per order of plugin names
|
|
)
|
|
|
|
plugin_utils.instantiate_plugins(self, invoke_args, invoke_kwargs)
|
|
|
|
multiple_backends.sync_secret_stores(self)
|
|
|
|
@_enforce_extensions_configured
|
|
def get_plugin_store(self, key_spec, plugin_name=None,
|
|
transport_key_needed=False, project_id=None):
|
|
"""Gets a secret store plugin.
|
|
|
|
:param: plugin_name: set to plugin_name to get specific plugin
|
|
:param: key_spec: KeySpec of key that will be stored
|
|
:param: transport_key_needed: set to True if a transport
|
|
key is required.
|
|
:returns: SecretStoreBase plugin implementation
|
|
"""
|
|
active_plugins = multiple_backends.get_applicable_store_plugins(
|
|
self, project_id=project_id, existing_plugin_name=plugin_name)
|
|
|
|
if plugin_name is not None:
|
|
for plugin in active_plugins:
|
|
if utils.generate_fullname_for(plugin) == plugin_name:
|
|
return plugin
|
|
raise SecretStorePluginNotFound(plugin_name)
|
|
|
|
if not transport_key_needed:
|
|
for plugin in active_plugins:
|
|
if plugin.store_secret_supports(key_spec):
|
|
return plugin
|
|
|
|
else:
|
|
for plugin in active_plugins:
|
|
if (plugin.get_transport_key() is not None and
|
|
plugin.store_secret_supports(key_spec)):
|
|
return plugin
|
|
|
|
raise SecretStoreSupportedPluginNotFound(key_spec)
|
|
|
|
@_enforce_extensions_configured
|
|
def get_plugin_retrieve_delete(self, plugin_name):
|
|
"""Gets a secret retrieve/delete plugin.
|
|
|
|
If this function is being called, it is because we are trying to
|
|
retrieve or delete an already stored secret. Thus, the plugin name is
|
|
actually gotten from the plugin metadata that has already been stored
|
|
in the database. So, in this case, if this plugin is not available,
|
|
this might be due to a server misconfiguration.
|
|
|
|
:returns: SecretStoreBase plugin implementation
|
|
:raises: StorePluginNotAvailableOrMisconfigured: If the plugin wasn't
|
|
found it's because the plugin parameters were not properly
|
|
configured on the database side.
|
|
"""
|
|
|
|
for plugin in plugin_utils.get_active_plugins(self):
|
|
if utils.generate_fullname_for(plugin) == plugin_name:
|
|
return plugin
|
|
raise StorePluginNotAvailableOrMisconfigured(plugin_name)
|
|
|
|
@_enforce_extensions_configured
|
|
def get_plugin_generate(self, key_spec, project_id=None):
|
|
"""Gets a secret generate plugin.
|
|
|
|
:param key_spec: KeySpec that contains details on the type of key to
|
|
generate
|
|
:returns: SecretStoreBase plugin implementation
|
|
"""
|
|
|
|
active_plugins = multiple_backends.get_applicable_store_plugins(
|
|
self, project_id=project_id, existing_plugin_name=None)
|
|
|
|
for plugin in active_plugins:
|
|
if plugin.generate_supports(key_spec):
|
|
return plugin
|
|
raise SecretGenerateSupportedPluginNotFound(key_spec)
|
|
|
|
def _get_internal_plugin_names(self, secretstore_conf):
|
|
"""Gets plugin names used for loading via stevedore.
|
|
|
|
When multiple secret store support is enabled, then secret store plugin
|
|
names are read via updated configuration structure. If not enabled,
|
|
then it reads MultiStr property in 'secretstore' config section.
|
|
"""
|
|
# to cache default global secret store value on first use
|
|
self.global_default_store_dict = None
|
|
if utils.is_multiple_backends_enabled():
|
|
self.parsed_stores = multiple_backends.\
|
|
read_multiple_backends_config()
|
|
plugin_names = [store.store_plugin for store in self.parsed_stores
|
|
if store.store_plugin]
|
|
else:
|
|
plugin_names = secretstore_conf.secretstore.\
|
|
enabled_secretstore_plugins
|
|
return plugin_names
|
|
|
|
|
|
def get_manager():
|
|
global _SECRET_STORE
|
|
if not _SECRET_STORE:
|
|
_SECRET_STORE = SecretStorePluginManager()
|
|
return _SECRET_STORE
|