def _get_applicable_plugins_for_type(manager, project_id, existing_plugin_name,
                                     plugin_type_field):

    plugins = []
    plugin_dict = {ext.name: ext.obj for ext in manager.extensions if ext.obj}
    if utils.is_multiple_backends_enabled() and existing_plugin_name is None:
        proj_store_repo = db_repos.get_project_secret_store_repository()
        plugin_store = proj_store_repo.get_secret_store_for_project(
            project_id, None, suppress_exception=True)

        # If project specific store is not set, then use global default one.
        if not plugin_store:
            if manager.global_default_store_dict is None:
                # Need to cache data as dict instead of db object to be usable
                # across various request sqlalchemy sessions
                store_dict = get_global_default_secret_store().to_dict_fields()
                manager.global_default_store_dict = store_dict
            secret_store_data = manager.global_default_store_dict
        else:
            secret_store_data = plugin_store.secret_store.to_dict_fields()

        applicable_plugin_name = secret_store_data[plugin_type_field]
        if applicable_plugin_name in plugin_dict:
            plugins = [plugin_dict.get(applicable_plugin_name)]
        elif applicable_plugin_name:  # applicable_plugin_name has value
            raise exception.MultipleStorePreferredPluginMissing(
                applicable_plugin_name)
    else:
        plugins = plugin_dict.values()

    return plugins
Exemple #2
0
def _get_applicable_plugins_for_type(manager, project_id, existing_plugin_name,
                                     plugin_type_field):

    plugins = []
    plugin_dict = {ext.name: ext.obj for ext in manager.extensions if ext.obj}
    if utils.is_multiple_backends_enabled() and existing_plugin_name is None:
        proj_store_repo = db_repos.get_project_secret_store_repository()
        plugin_store = proj_store_repo.get_secret_store_for_project(
            project_id, None, suppress_exception=True)

        # If project specific store is not set, then use global default one.
        if not plugin_store:
            if manager.global_default_store_dict is None:
                # Need to cache data as dict instead of db object to be usable
                # across various request sqlalchemy sessions
                store_dict = get_global_default_secret_store().to_dict_fields()
                manager.global_default_store_dict = store_dict
            secret_store_data = manager.global_default_store_dict
        else:
            secret_store_data = plugin_store.secret_store.to_dict_fields()

        applicable_plugin_name = secret_store_data[plugin_type_field]
        if applicable_plugin_name in plugin_dict:
            plugins = [plugin_dict.get(applicable_plugin_name)]
        elif applicable_plugin_name:  # applicable_plugin_name has value
            raise exception.MultipleStorePreferredPluginMissing(
                applicable_plugin_name)
    else:
        plugins = plugin_dict.values()

    return plugins
Exemple #3
0
    def _lookup(self, secret_store_id, *remainder):
        if not utils.is_multiple_backends_enabled():
            _multiple_backends_not_enabled()

        secret_store = self.secret_stores_repo.get(entity_id=secret_store_id,
                                                   suppress_exception=True)
        if not secret_store:
            _secret_store_not_found()
        return SecretStoreController(secret_store), remainder
Exemple #4
0
    def _lookup(self, secret_store_id, *remainder):
        if not utils.is_multiple_backends_enabled():
            _multiple_backends_not_enabled()

        secret_store = self.secret_stores_repo.get(entity_id=secret_store_id,
                                                   suppress_exception=True)
        if not secret_store:
            _secret_store_not_found()
        return SecretStoreController(secret_store), remainder
Exemple #5
0
    def get_global_default(self, external_project_id, **kw):
        LOG.debug('Start secret-stores get global default secret store')

        if not utils.is_multiple_backends_enabled():
            _multiple_backends_not_enabled()

        res.get_or_create_project(external_project_id)

        store = multiple_backends.get_global_default_secret_store()

        return convert_secret_store_to_response_format(store)
Exemple #6
0
    def get_global_default(self, external_project_id, **kw):
        LOG.debug('Start secret-stores get global default secret store')

        if not utils.is_multiple_backends_enabled():
            _multiple_backends_not_enabled()

        res.get_or_create_project(external_project_id)

        store = multiple_backends.get_global_default_secret_store()

        return convert_secret_store_to_response_format(store)
Exemple #7
0
    def get_preferred(self, external_project_id, **kw):
        LOG.debug('Start secret-stores get preferred secret store')

        if not utils.is_multiple_backends_enabled():
            _multiple_backends_not_enabled()

        project = res.get_or_create_project(external_project_id)

        project_store = self.proj_store_repo.get_secret_store_for_project(
            project.id, None, suppress_exception=True)

        if project_store is None:
            _preferred_secret_store_not_found()

        return convert_secret_store_to_response_format(
            project_store.secret_store)
Exemple #8
0
    def get_preferred(self, external_project_id, **kw):
        LOG.debug('Start secret-stores get preferred secret store')

        if not utils.is_multiple_backends_enabled():
            _multiple_backends_not_enabled()

        project = res.get_or_create_project(external_project_id)

        project_store = self.proj_store_repo.get_secret_store_for_project(
            project.id, None, suppress_exception=True)

        if project_store is None:
            _preferred_secret_store_not_found()

        return convert_secret_store_to_response_format(
            project_store.secret_store)
Exemple #9
0
    def _get_internal_plugin_names(self, crypto_conf):
        """Gets plugin names used for loading via stevedore.

        When multiple secret store support is enabled, then crypto plugin names
        are read via updated configuration structure. If not enabled, then it
        reads MultiStr property in 'crypto' config section.
        """
        # to cache default global secret store value on first use
        self.global_default_store_dict = None
        if utils.is_multiple_backends_enabled():
            parsed_stores = multiple_backends.read_multiple_backends_config()
            plugin_names = [store.crypto_plugin for store in parsed_stores
                            if store.crypto_plugin]
        else:
            plugin_names = crypto_conf.crypto.enabled_crypto_plugins
        return plugin_names
Exemple #10
0
    def _get_internal_plugin_names(self, crypto_conf):
        """Gets plugin names used for loading via stevedore.

        When multiple secret store support is enabled, then crypto plugin names
        are read via updated configuration structure. If not enabled, then it
        reads MultiStr property in 'crypto' config section.
        """
        # to cache default global secret store value on first use
        self.global_default_store_dict = None
        if utils.is_multiple_backends_enabled():
            parsed_stores = multiple_backends.read_multiple_backends_config()
            plugin_names = [store.crypto_plugin for store in parsed_stores
                            if store.crypto_plugin]
        else:
            plugin_names = crypto_conf.crypto.enabled_crypto_plugins
        return plugin_names
Exemple #11
0
    def on_get(self, external_project_id, **kw):
        LOG.debug('Start SecretStoresController on_get: listing secret '
                  'stores')
        if not utils.is_multiple_backends_enabled():
            _multiple_backends_not_enabled()

        res.get_or_create_project(external_project_id)

        secret_stores = self.secret_stores_repo.get_all()

        resp_list = []
        for store in secret_stores:
            item = convert_secret_store_to_response_format(store)
            resp_list.append(item)

        resp = {'secret_stores': resp_list}

        return resp
Exemple #12
0
    def on_get(self, external_project_id, **kw):
        LOG.debug('Start SecretStoresController on_get: listing secret '
                  'stores')
        if not utils.is_multiple_backends_enabled():
            _multiple_backends_not_enabled()

        res.get_or_create_project(external_project_id)

        secret_stores = self.secret_stores_repo.get_all()

        resp_list = []
        for store in secret_stores:
            item = convert_secret_store_to_response_format(store)
            resp_list.append(item)

        resp = {'secret_stores': resp_list}

        return resp
def read_multiple_backends_config():
    """Reads and validates multiple backend related configuration.

    Multiple backends configuration is read only when multiple secret store
    flag is enabled.
    Configuration is validated to make sure that section specific to
    provided suffix exists in service configuration. Also validated that only
    one of section has global_default = True and its not missing.
    """
    conf = config.get_module_config('secretstore')

    parsed_stores = None
    if utils.is_multiple_backends_enabled():
        suffix_list = conf.secretstore.stores_lookup_suffix
        if not suffix_list:
            raise exception.MultipleSecretStoreLookupFailed()

        def register_options_dynamically(conf, group_name):
            store_opt_group = cfg.OptGroup(
                name=group_name, title='Plugins needed for this backend')
            store_opts = [
                cfg.StrOpt('secret_store_plugin',
                           help=u._('Internal name used to identify'
                                    'secretstore_plugin')
                           ),
                cfg.StrOpt('crypto_plugin',
                           help=u._('Internal name used to identify '
                                    'crypto_plugin.')
                           ),
                cfg.BoolOpt('global_default',
                            default=False,
                            help=u._('Flag to indicate if this plugin is '
                                     'global default plugin for deployment. '
                                     'Default is False.')
                            ),
                ]
            conf.register_group(store_opt_group)
            conf.register_opts(store_opts, group=store_opt_group)

        group_names = []
        # construct group names using those suffix and dynamically register
        # oslo config options under that group name
        for suffix in suffix_list:
            group_name = LOOKUP_PLUGINS_PREFIX + suffix
            register_options_dynamically(conf, group_name)
            group_names.append(group_name)

        store_conf = collections.namedtuple('store_conf', ['store_plugin',
                                                           'crypto_plugin',
                                                           'global_default'])
        parsed_stores = []
        global_default_count = 0
        # Section related to group names based of suffix list are always found
        # as we are dynamically registering group and its options.
        for group_name in group_names:
            conf_section = getattr(conf, group_name)
            if conf_section.global_default:
                global_default_count += 1

            store_plugin = conf_section.secret_store_plugin
            if not store_plugin:
                raise exception.MultipleStorePluginValueMissing(conf_section)

            parsed_stores.append(store_conf(store_plugin,
                                            conf_section.crypto_plugin,
                                            conf_section.global_default))

        if global_default_count != 1:
            raise exception.MultipleStoreIncorrectGlobalDefault(
                global_default_count)

    return parsed_stores
def sync_secret_stores(secretstore_manager, crypto_manager=None):
    """Synchronize secret store plugin names between service conf and database

    This method reads secret and crypto store plugin name from service
    configuration and then synchronizes corresponding data maintained in
    database SecretStores table.

    Any new plugin name(s) added in service configuration is added as a new
    entry in SecretStores table. If global_default value is changed for
    existing plugins, then global_default flag is updated to reflect that
    change in database. If plugin name is removed from service configuration,
    then removal is possible as long as respective plugin names are NOT set as
    preferred secret store for a project. If it is used and plugin name is
    removed, then error is raised. This logic is intended to be invoked at
    server startup so any error raised here will result in critical failure.
    """
    if not utils.is_multiple_backends_enabled():
        return

    # doing local import to avoid circular dependency between manager and
    # current utils module
    from barbican.plugin.crypto import manager as cm

    secret_stores_repo = db_repos.get_secret_stores_repository()
    proj_store_repo = db_repos.get_project_secret_store_repository()
    if crypto_manager is None:
        crypto_manager = cm.get_manager()

    def get_friendly_name_dict(ext_manager):
        """Returns dict of plugin internal name and friendly name entries."""
        names_dict = {}
        for ext in ext_manager.extensions:
            if ext.obj and hasattr(ext.obj, 'get_plugin_name'):
                names_dict[ext.name] = ext.obj.get_plugin_name()
        return names_dict

    ss_friendly_names = get_friendly_name_dict(secretstore_manager)
    crypto_friendly_names = get_friendly_name_dict(crypto_manager)
    # get existing secret stores data from database
    db_stores = secret_stores_repo.get_all()

    # read secret store data from service configuration
    conf_stores = []
    for parsed_store in secretstore_manager.parsed_stores:
        crypto_plugin = parsed_store.crypto_plugin
        if not crypto_plugin:
            crypto_plugin = None

        if crypto_plugin:
            friendly_name = crypto_friendly_names.get(crypto_plugin)
        else:
            friendly_name = ss_friendly_names.get(parsed_store.store_plugin)

        conf_stores.append(db_models.SecretStores(
            name=friendly_name, store_plugin=parsed_store.store_plugin,
            crypto_plugin=crypto_plugin,
            global_default=parsed_store.global_default))

    if db_stores:
        def fn_match(lh_store, rh_store):
            return (lh_store.store_plugin == rh_store.store_plugin and
                    lh_store.crypto_plugin == rh_store.crypto_plugin)

        for conf_store in conf_stores:
            # find existing db entry for plugin using conf based plugin names
            db_store_match = next((db_store for db_store in db_stores if
                                   fn_match(conf_store, db_store)), None)
            if db_store_match:
                # update existing db entry if global default is changed now
                if db_store_match.global_default != conf_store.global_default:
                    db_store_match.global_default = conf_store.global_default
                    # persist flag change.
                    db_store_match.save()
                # remove matches store from local list after processing
                db_stores.remove(db_store_match)
            else:  # new conf entry as no match found in existing entries
                secret_stores_repo.create_from(conf_store)

        # entries still present in db list are no longer configured in service
        # configuration, so try to remove them provided there is no project
        # is using it as preferred secret store.
        for db_store in db_stores:
            if proj_store_repo.get_count_by_secret_store(db_store.id) == 0:
                secret_stores_repo.delete_entity_by_id(db_store.id, None)
            else:
                raise exception.MultipleStorePluginStillInUse(db_store.name)
    else:  # initial setup case when there is no secret stores data in db
        for conf_store in conf_stores:
            secret_stores_repo.create_from(conf_store)
Exemple #15
0
def read_multiple_backends_config():
    """Reads and validates multiple backend related configuration.

    Multiple backends configuration is read only when multiple secret store
    flag is enabled.
    Configuration is validated to make sure that section specific to
    provided suffix exists in service configuration. Also validated that only
    one of section has global_default = True and its not missing.
    """
    conf = config.get_module_config('secretstore')

    parsed_stores = None
    if utils.is_multiple_backends_enabled():
        suffix_list = conf.secretstore.stores_lookup_suffix
        if not suffix_list:
            raise exception.MultipleSecretStoreLookupFailed()

        def register_options_dynamically(conf, group_name):
            store_opt_group = cfg.OptGroup(
                name=group_name, title='Plugins needed for this backend')
            store_opts = [
                cfg.StrOpt('secret_store_plugin',
                           default=None,
                           help=u._('Internal name used to identify'
                                    'secretstore_plugin')),
                cfg.StrOpt('crypto_plugin',
                           default=None,
                           help=u._('Internal name used to identify '
                                    'crypto_plugin.')),
                cfg.BoolOpt('global_default',
                            default=False,
                            help=u._('Flag to indicate if this plugin is '
                                     'global default plugin for deployment. '
                                     'Default is False.')),
            ]
            conf.register_group(store_opt_group)
            conf.register_opts(store_opts, group=store_opt_group)

        group_names = []
        # construct group names using those suffix and dynamically register
        # oslo config options under that group name
        for suffix in suffix_list:
            group_name = LOOKUP_PLUGINS_PREFIX + suffix
            register_options_dynamically(conf, group_name)
            group_names.append(group_name)

        store_conf = collections.namedtuple(
            'store_conf', ['store_plugin', 'crypto_plugin', 'global_default'])
        parsed_stores = []
        global_default_count = 0
        # Section related to group names based of suffix list are always found
        # as we are dynamically registering group and its options.
        for group_name in group_names:
            conf_section = getattr(conf, group_name)
            if conf_section.global_default:
                global_default_count += 1

            store_plugin = conf_section.secret_store_plugin
            if not store_plugin:
                raise exception.MultipleStorePluginValueMissing(conf_section)

            parsed_stores.append(
                store_conf(store_plugin, conf_section.crypto_plugin,
                           conf_section.global_default))

        if global_default_count != 1:
            raise exception.MultipleStoreIncorrectGlobalDefault(
                global_default_count)

    return parsed_stores
Exemple #16
0
def sync_secret_stores(secretstore_manager, crypto_manager=None):
    """Synchronize secret store plugin names between service conf and database

    This method reads secret and crypto store plugin name from service
    configuration and then synchronizes corresponding data maintained in
    database SecretStores table.

    Any new plugin name(s) added in service configuration is added as a new
    entry in SecretStores table. If global_default value is changed for
    existing plugins, then global_default flag is updated to reflect that
    change in database. If plugin name is removed from service configuration,
    then removal is possible as long as respective plugin names are NOT set as
    preferred secret store for a project. If it is used and plugin name is
    removed, then error is raised. This logic is intended to be invoked at
    server startup so any error raised here will result in critical failure.
    """
    if not utils.is_multiple_backends_enabled():
        return

    # doing local import to avoid circular dependency between manager and
    # current utils module
    from barbican.plugin.crypto import manager as cm

    secret_stores_repo = db_repos.get_secret_stores_repository()
    proj_store_repo = db_repos.get_project_secret_store_repository()
    if crypto_manager is None:
        crypto_manager = cm.get_manager()

    def get_friendly_name_dict(ext_manager):
        """Returns dict of plugin internal name and friendly name entries."""
        names_dict = {}
        for ext in ext_manager.extensions:
            if ext.obj and hasattr(ext.obj, 'get_plugin_name'):
                names_dict[ext.name] = ext.obj.get_plugin_name()
        return names_dict

    ss_friendly_names = get_friendly_name_dict(secretstore_manager)
    crypto_friendly_names = get_friendly_name_dict(crypto_manager)
    # get existing secret stores data from database
    db_stores = secret_stores_repo.get_all()

    # read secret store data from service configuration
    conf_stores = []
    for parsed_store in secretstore_manager.parsed_stores:
        crypto_plugin = parsed_store.crypto_plugin
        if not crypto_plugin:
            crypto_plugin = None

        if crypto_plugin:
            friendly_name = crypto_friendly_names.get(crypto_plugin)
        else:
            friendly_name = ss_friendly_names.get(parsed_store.store_plugin)

        conf_stores.append(
            db_models.SecretStores(name=friendly_name,
                                   store_plugin=parsed_store.store_plugin,
                                   crypto_plugin=crypto_plugin,
                                   global_default=parsed_store.global_default))

    if db_stores:

        def fn_match(lh_store, rh_store):
            return (lh_store.store_plugin == rh_store.store_plugin
                    and lh_store.crypto_plugin == rh_store.crypto_plugin)

        for conf_store in conf_stores:
            # find existing db entry for plugin using conf based plugin names
            db_store_match = next(
                (db_store
                 for db_store in db_stores if fn_match(conf_store, db_store)),
                None)
            if db_store_match:
                # update existing db entry if global default is changed now
                if db_store_match.global_default != conf_store.global_default:
                    db_store_match.global_default = conf_store.global_default
                    # persist flag change.
                    db_store_match.save()
                # remove matches store from local list after processing
                db_stores.remove(db_store_match)
            else:  # new conf entry as no match found in existing entries
                secret_stores_repo.create_from(conf_store)

        # entries still present in db list are no longer configured in service
        # configuration, so try to remove them provided there is no project
        # is using it as preferred secret store.
        for db_store in db_stores:
            if proj_store_repo.get_count_by_secret_store(db_store.id) == 0:
                secret_stores_repo.delete_entity_by_id(db_store.id, None)
            else:
                raise exception.MultipleStorePluginStillInUse(db_store.name)
    else:  # initial setup case when there is no secret stores data in db
        for conf_store in conf_stores:
            secret_stores_repo.create_from(conf_store)