"Fossies" - the Fresh Open Source Software Archive

Member "barbican-12.0.0/barbican/model/repositories.py" (14 Apr 2021, 100934 Bytes) of package /linux/misc/openstack/barbican-12.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

    1 # Copyright (c) 2013-2014 Rackspace, Inc.
    2 #
    3 # Licensed under the Apache License, Version 2.0 (the "License");
    4 # you may not use this file except in compliance with the License.
    5 # You may obtain 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,
   11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
   12 # implied.
   13 # See the License for the specific language governing permissions and
   14 # limitations under the License.
   15 
   16 """
   17 Defines interface for DB access that Resource controllers may reference
   18 
   19 TODO: The top part of this file was 'borrowed' from Glance, but seems
   20 quite intense for sqlalchemy, and maybe could be simplified.
   21 """
   22 
   23 import logging
   24 import re
   25 import sys
   26 import time
   27 
   28 from oslo_db import exception as db_exc
   29 from oslo_db.sqlalchemy import session
   30 from oslo_utils import timeutils
   31 from oslo_utils import uuidutils
   32 import sqlalchemy
   33 from sqlalchemy import func as sa_func
   34 from sqlalchemy import or_
   35 import sqlalchemy.orm as sa_orm
   36 
   37 from barbican.common import config
   38 from barbican.common import exception
   39 from barbican.common import utils
   40 from barbican import i18n as u
   41 from barbican.model.migration import commands
   42 from barbican.model import models
   43 
   44 LOG = utils.getLogger(__name__)
   45 
   46 
   47 _ENGINE = None
   48 _SESSION_FACTORY = None
   49 BASE = models.BASE
   50 sa_logger = None
   51 
   52 # Singleton repository references, instantiated via get_xxxx_repository()
   53 #   functions below.  Please keep this list in alphabetical order.
   54 _CA_REPOSITORY = None
   55 _CONTAINER_ACL_USER_REPOSITORY = None
   56 _CONTAINER_ACL_REPOSITORY = None
   57 _CONTAINER_CONSUMER_REPOSITORY = None
   58 _CONTAINER_REPOSITORY = None
   59 _CONTAINER_SECRET_REPOSITORY = None
   60 _ENCRYPTED_DATUM_REPOSITORY = None
   61 _KEK_DATUM_REPOSITORY = None
   62 _ORDER_PLUGIN_META_REPOSITORY = None
   63 _ORDER_BARBICAN_META_REPOSITORY = None
   64 _ORDER_REPOSITORY = None
   65 _ORDER_RETRY_TASK_REPOSITORY = None
   66 _PREFERRED_CA_REPOSITORY = None
   67 _PROJECT_REPOSITORY = None
   68 _PROJECT_CA_REPOSITORY = None
   69 _PROJECT_QUOTAS_REPOSITORY = None
   70 _SECRET_ACL_USER_REPOSITORY = None
   71 _SECRET_ACL_REPOSITORY = None
   72 _SECRET_META_REPOSITORY = None
   73 _SECRET_USER_META_REPOSITORY = None
   74 _SECRET_REPOSITORY = None
   75 _TRANSPORT_KEY_REPOSITORY = None
   76 _SECRET_STORES_REPOSITORY = None
   77 _PROJECT_SECRET_STORE_REPOSITORY = None
   78 _SECRET_CONSUMER_REPOSITORY = None
   79 
   80 
   81 CONF = config.CONF
   82 
   83 
   84 def hard_reset():
   85     """Performs a hard reset of database resources, used for unit testing."""
   86     # TODO(jvrbanac): Remove this as soon as we improve our unit testing
   87     # to not require this.
   88     global _ENGINE, _SESSION_FACTORY
   89     if _ENGINE:
   90         _ENGINE.dispose()
   91     _ENGINE = None
   92     _SESSION_FACTORY = None
   93 
   94     # Make sure we reinitialize the engine and session factory
   95     setup_database_engine_and_factory()
   96 
   97 
   98 def setup_database_engine_and_factory(initialize_secret_stores=False):
   99     global sa_logger, _SESSION_FACTORY, _ENGINE
  100 
  101     LOG.info('Setting up database engine and session factory')
  102     if CONF.debug:
  103         sa_logger = logging.getLogger('sqlalchemy.engine')
  104         sa_logger.setLevel(logging.DEBUG)
  105     if CONF.sql_pool_logging:
  106         pool_logger = logging.getLogger('sqlalchemy.pool')
  107         pool_logger.setLevel(logging.DEBUG)
  108 
  109     _ENGINE = _get_engine(_ENGINE)
  110 
  111     # Utilize SQLAlchemy's scoped_session to ensure that we only have one
  112     # session instance per thread.
  113     session_maker = sa_orm.sessionmaker(bind=_ENGINE)
  114     _SESSION_FACTORY = sqlalchemy.orm.scoped_session(session_maker)
  115     if initialize_secret_stores:
  116         _initialize_secret_stores_data()
  117 
  118 
  119 def start():
  120     """Start for read-write requests placeholder
  121 
  122     Typically performed at the start of a request cycle, say for POST or PUT
  123     requests.
  124     """
  125     pass
  126 
  127 
  128 def start_read_only():
  129     """Start for read-only requests placeholder
  130 
  131     Typically performed at the start of a request cycle, say for GET or HEAD
  132     requests.
  133     """
  134     pass
  135 
  136 
  137 def commit():
  138     """Commit session state so far to the database.
  139 
  140     Typically performed at the end of a request cycle.
  141     """
  142     get_session().commit()
  143 
  144 
  145 def rollback():
  146     """Rollback session state so far.
  147 
  148     Typically performed when the request cycle raises an Exception.
  149     """
  150     get_session().rollback()
  151 
  152 
  153 def clear():
  154     """Dispose of this session, releases db resources.
  155 
  156     Typically performed at the end of a request cycle, after a
  157     commit() or rollback().
  158     """
  159     if _SESSION_FACTORY:  # not initialized in some unit test
  160         _SESSION_FACTORY.remove()
  161 
  162 
  163 def get_session():
  164     """Helper method to grab session."""
  165     return _SESSION_FACTORY()
  166 
  167 
  168 def _get_engine(engine):
  169     if not engine:
  170         connection = CONF.sql_connection
  171         if not connection:
  172             raise exception.BarbicanException(
  173                 u._('No SQL connection configured'))
  174 
  175     # TODO(jfwood):
  176     # connection_dict = sqlalchemy.engine.url.make_url(_CONNECTION)
  177 
  178         engine_args = {
  179             'idle_timeout': CONF.sql_idle_timeout}
  180         if CONF.sql_pool_size:
  181             engine_args['max_pool_size'] = CONF.sql_pool_size
  182         if CONF.sql_pool_max_overflow:
  183             engine_args['max_overflow'] = CONF.sql_pool_max_overflow
  184 
  185         db_connection = None
  186         try:
  187             engine = _create_engine(connection, **engine_args)
  188             db_connection = engine.connect()
  189         except Exception as err:
  190             msg = u._("Error configuring registry database with supplied "
  191                       "sql_connection. Got error: {error}").format(error=err)
  192             LOG.exception(msg)
  193             raise exception.BarbicanException(msg)
  194         finally:
  195             if db_connection:
  196                 db_connection.close()
  197 
  198         if CONF.db_auto_create:
  199             meta = sqlalchemy.MetaData()
  200             meta.reflect(bind=engine)
  201             tables = meta.tables
  202 
  203             _auto_generate_tables(engine, tables)
  204         else:
  205             LOG.info('Not auto-creating barbican registry DB')
  206 
  207     return engine
  208 
  209 
  210 def model_query(model, *args, **kwargs):
  211     """Query helper for simpler session usage."""
  212     session = kwargs.get('session')
  213     query = session.query(model, *args)
  214     return query
  215 
  216 
  217 def _initialize_secret_stores_data():
  218     """Initializes secret stores data in database.
  219 
  220     This logic is executed only when database engine and factory is built.
  221     Secret store get_manager internally reads secret store plugin configuration
  222     from service configuration and saves it in secret_stores table in database.
  223     """
  224     if utils.is_multiple_backends_enabled():
  225         from barbican.plugin.interface import secret_store
  226         secret_store.get_manager()
  227 
  228 
  229 def is_db_connection_error(args):
  230     """Return True if error in connecting to db."""
  231     # NOTE(adam_g): This is currently MySQL specific and needs to be extended
  232     #               to support Postgres and others.
  233     conn_err_codes = ('2002', '2003', '2006')
  234     for err_code in conn_err_codes:
  235         if args.find(err_code) != -1:
  236             return True
  237     return False
  238 
  239 
  240 def _create_engine(connection, **engine_args):
  241     LOG.debug('Sql connection: please check "sql_connection" property in '
  242               'barbican configuration file; Args: %s', engine_args)
  243 
  244     engine = session.create_engine(connection, **engine_args)
  245 
  246     # TODO(jfwood): if 'mysql' in connection_dict.drivername:
  247     # TODO(jfwood): sqlalchemy.event.listen(_ENGINE, 'checkout',
  248     # TODO(jfwood):                         ping_listener)
  249 
  250     # Wrap the engine's connect method with a retry decorator.
  251     engine.connect = wrap_db_error(engine.connect)
  252 
  253     return engine
  254 
  255 
  256 def _auto_generate_tables(engine, tables):
  257     if tables and 'alembic_version' in tables:
  258         # Upgrade the database to the latest version.
  259         LOG.info('Updating schema to latest version')
  260         commands.upgrade()
  261     else:
  262         # Create database tables from our models.
  263         LOG.info('Auto-creating barbican registry DB')
  264         models.BASE.metadata.create_all(engine)
  265 
  266         # Sync the alembic version 'head' with current models.
  267         commands.stamp()
  268 
  269 
  270 def wrap_db_error(f):
  271     """Retry DB connection. Copied from nova and modified."""
  272     def _wrap(*args, **kwargs):
  273         try:
  274             return f(*args, **kwargs)
  275         except sqlalchemy.exc.OperationalError as e:
  276             if not is_db_connection_error(e.args[0]):
  277                 raise
  278 
  279             remaining_attempts = CONF.sql_max_retries
  280             while True:
  281                 LOG.warning('SQL connection failed. %d attempts left.',
  282                             remaining_attempts)
  283                 remaining_attempts -= 1
  284                 time.sleep(CONF.sql_retry_interval)
  285                 try:
  286                     return f(*args, **kwargs)
  287                 except sqlalchemy.exc.OperationalError as e:
  288                     if (remaining_attempts <= 0 or not
  289                             is_db_connection_error(e.args[0])):
  290                         raise
  291                 except sqlalchemy.exc.DBAPIError:
  292                     raise
  293         except sqlalchemy.exc.DBAPIError:
  294             raise
  295     _wrap.__name__ = f.__name__
  296     return _wrap
  297 
  298 
  299 def clean_paging_values(offset_arg=0, limit_arg=CONF.default_limit_paging):
  300     """Cleans and safely limits raw paging offset/limit values."""
  301     offset_arg = offset_arg or 0
  302     limit_arg = limit_arg or CONF.default_limit_paging
  303 
  304     try:
  305         offset = int(offset_arg)
  306         if offset < 0:
  307             offset = 0
  308         if offset > sys.maxsize:
  309             offset = 0
  310     except ValueError:
  311         offset = 0
  312 
  313     try:
  314         limit = int(limit_arg)
  315         if limit < 1:
  316             limit = 1
  317         if limit > CONF.max_limit_paging:
  318             limit = CONF.max_limit_paging
  319     except ValueError:
  320         limit = CONF.default_limit_paging
  321 
  322     LOG.debug("Clean paging values limit=%(limit)s, offset=%(offset)s" %
  323               {'limit': limit,
  324                'offset': offset})
  325 
  326     return offset, limit
  327 
  328 
  329 def delete_all_project_resources(project_id):
  330     """Logic to cleanup all project resources.
  331 
  332     This cleanup uses same alchemy session to perform all db operations as a
  333     transaction and will commit only when all db operations are performed
  334     without error.
  335     """
  336     session = get_session()
  337 
  338     container_repo = get_container_repository()
  339     container_repo.delete_project_entities(
  340         project_id, suppress_exception=False, session=session)
  341     # secret children SecretStoreMetadatum, EncryptedDatum
  342     # and container_secrets are deleted as part of secret delete
  343     secret_repo = get_secret_repository()
  344     secret_repo.delete_project_entities(
  345         project_id, suppress_exception=False, session=session)
  346     kek_repo = get_kek_datum_repository()
  347     kek_repo.delete_project_entities(
  348         project_id, suppress_exception=False, session=session)
  349     project_repo = get_project_repository()
  350     project_repo.delete_project_entities(
  351         project_id, suppress_exception=False, session=session)
  352 
  353 
  354 class BaseRepo(object):
  355     """Base repository for the barbican entities.
  356 
  357     This class provides template methods that allow sub-classes to hook
  358     specific functionality as needed. Clients access instances of this class
  359     via singletons, therefore implementations should be stateless aside from
  360     configuration.
  361     """
  362 
  363     def get_session(self, session=None):
  364         LOG.debug("Getting session...")
  365         return session or get_session()
  366 
  367     def get(self, entity_id, external_project_id=None,
  368             force_show_deleted=False,
  369             suppress_exception=False, session=None):
  370         """Get an entity or raise if it does not exist."""
  371         session = self.get_session(session)
  372 
  373         try:
  374             query = self._do_build_get_query(entity_id,
  375                                              external_project_id,
  376                                              session)
  377 
  378             # filter out deleted entities if requested
  379             if not force_show_deleted:
  380                 query = query.filter_by(deleted=False)
  381 
  382             entity = query.one()
  383 
  384         except sa_orm.exc.NoResultFound:
  385             LOG.exception("Not found for %s", entity_id)
  386             entity = None
  387             if not suppress_exception:
  388                 _raise_entity_not_found(self._do_entity_name(), entity_id)
  389 
  390         return entity
  391 
  392     def create_from(self, entity, session=None):
  393         """Sub-class hook: create from entity."""
  394         if not entity:
  395             msg = u._(
  396                 "Must supply non-None {entity_name}."
  397             ).format(entity_name=self._do_entity_name())
  398             raise exception.Invalid(msg)
  399 
  400         if entity.id:
  401             msg = u._(
  402                 "Must supply {entity_name} with id=None (i.e. new entity)."
  403             ).format(entity_name=self._do_entity_name())
  404             raise exception.Invalid(msg)
  405 
  406         LOG.debug("Begin create from...")
  407         session = self.get_session(session)
  408         start = time.time()  # DEBUG
  409 
  410         # Validate the attributes before we go any further. From my
  411         # (unknown Glance developer) investigation, the @validates
  412         # decorator does not validate
  413         # on new records, only on existing records, which is, well,
  414         # idiotic.
  415         self._do_validate(entity.to_dict())
  416 
  417         try:
  418             LOG.debug("Saving entity...")
  419             entity.save(session=session)
  420         except db_exc.DBDuplicateEntry as e:
  421             session.rollback()
  422             LOG.exception('Problem saving entity for create')
  423             error_msg = re.sub('[()]', '', str(e.args))
  424             raise exception.ConstraintCheck(error=error_msg)
  425 
  426         LOG.debug('Elapsed repo '
  427                   'create secret:%s', (time.time() - start))  # DEBUG
  428 
  429         return entity
  430 
  431     def update_from(self, model_class, entity_id, values, session=None):
  432         if id in values:
  433             raise Exception('Cannot update id')
  434         LOG.debug('Begin update from ...')
  435         session = self.get_session(session=session)
  436 
  437         query = session.query(model_class)
  438         query = query.filter_by(id=entity_id)
  439         try:
  440             LOG.debug('Updating value ...')
  441             entity = query.one()
  442         except sa_orm.exc.NoResultFound:
  443             raise exception.NotFound('DB Entity with id {0} not '
  444                                      'found'.format(entity_id))
  445         self._update_values(entity, values)
  446         entity.save()
  447 
  448     def save(self, entity):
  449         """Saves the state of the entity."""
  450         entity.updated_at = timeutils.utcnow()
  451 
  452         # Validate the attributes before we go any further. From my
  453         # (unknown Glance developer) investigation, the @validates
  454         # decorator does not validate
  455         # on new records, only on existing records, which is, well,
  456         # idiotic.
  457         self._do_validate(entity.to_dict())
  458 
  459         entity.save()
  460 
  461     def delete_entity_by_id(self, entity_id, external_project_id,
  462                             session=None):
  463         """Remove the entity by its ID."""
  464 
  465         session = self.get_session(session)
  466 
  467         entity = self.get(entity_id=entity_id,
  468                           external_project_id=external_project_id,
  469                           session=session)
  470 
  471         entity.delete(session=session)
  472 
  473     def _do_entity_name(self):
  474         """Sub-class hook: return entity name, such as for debugging."""
  475         return "Entity"
  476 
  477     def _do_build_get_query(self, entity_id, external_project_id, session):
  478         """Sub-class hook: build a retrieve query."""
  479         return None
  480 
  481     def _do_convert_values(self, values):
  482         """Sub-class hook: convert text-based values to target types
  483 
  484         This is specifically for database values.
  485         """
  486         pass
  487 
  488     def _do_validate(self, values):
  489         """Sub-class hook: validate values.
  490 
  491         Validates the incoming data and raises an Invalid exception
  492         if anything is out of order.
  493 
  494         :param values: Mapping of entity metadata to check
  495         """
  496         status = values.get('status', None)
  497         if not status:
  498             # TODO(jfwood): I18n this!
  499             msg = u._("{entity_name} status is required.").format(
  500                 entity_name=self._do_entity_name())
  501             raise exception.Invalid(msg)
  502 
  503         if not models.States.is_valid(status):
  504             msg = u._("Invalid status '{status}' for {entity_name}.").format(
  505                 status=status, entity_name=self._do_entity_name())
  506             raise exception.Invalid(msg)
  507 
  508         return values
  509 
  510     def _update_values(self, entity_ref, values):
  511         for k in values:
  512             if getattr(entity_ref, k) != values[k]:
  513                 setattr(entity_ref, k, values[k])
  514 
  515     def _build_get_project_entities_query(self, project_id, session):
  516         """Sub-class hook: build a query to retrieve entities for a project.
  517 
  518         :param project_id: id of barbican project entity
  519         :param session: existing db session reference.
  520         :returns: A query object for getting all project related entities
  521 
  522         This will filter deleted entities if there.
  523         """
  524         msg = u._(
  525             "{entity_name} is missing query build method for get "
  526             "project entities.").format(
  527                 entity_name=self._do_entity_name())
  528         raise NotImplementedError(msg)
  529 
  530     def get_project_entities(self, project_id, session=None):
  531         """Gets entities associated with a given project.
  532 
  533         :param project_id: id of barbican project entity
  534         :param session: existing db session reference. If None, gets session.
  535         :returns: list of matching entities found otherwise returns empty list
  536                   if no entity exists for a given project.
  537 
  538         Sub-class should implement `_build_get_project_entities_query` function
  539         to delete related entities otherwise it would raise NotImplementedError
  540         on its usage.
  541         """
  542 
  543         session = self.get_session(session)
  544         query = self._build_get_project_entities_query(project_id, session)
  545         if query:
  546             return query.all()
  547         else:
  548             return []
  549 
  550     def get_count(self, project_id, session=None):
  551         """Gets count of entities associated with a given project
  552 
  553         :param project_id: id of barbican project entity
  554         :param session: existing db session reference. If None, gets session.
  555         :return: an number 0 or greater
  556 
  557         Sub-class should implement `_build_get_project_entities_query` function
  558         to delete related entities otherwise it would raise NotImplementedError
  559         on its usage.
  560         """
  561         session = self.get_session(session)
  562         query = self._build_get_project_entities_query(project_id, session)
  563         if query:
  564             return query.count()
  565         else:
  566             return 0
  567 
  568     def delete_project_entities(self, project_id,
  569                                 suppress_exception=False,
  570                                 session=None):
  571         """Deletes entities for a given project.
  572 
  573         :param project_id: id of barbican project entity
  574         :param suppress_exception: Pass True if want to suppress exception
  575         :param session: existing db session reference. If None, gets session.
  576 
  577         Sub-class should implement `_build_get_project_entities_query` function
  578         to delete related entities otherwise it would raise NotImplementedError
  579         on its usage.
  580         """
  581         session = self.get_session(session)
  582         query = self._build_get_project_entities_query(project_id,
  583                                                        session=session)
  584         try:
  585             # query cannot be None as related repo class is expected to
  586             # implement it otherwise error is raised in build query call
  587             for entity in query:
  588                 # Its a soft delete so its more like entity update
  589                 entity.delete(session=session)
  590         except sqlalchemy.exc.SQLAlchemyError:
  591             LOG.exception('Problem finding project related entity to delete')
  592             if not suppress_exception:
  593                 raise exception.BarbicanException(u._('Error deleting project '
  594                                                       'entities for '
  595                                                       'project_id=%s'),
  596                                                   project_id)
  597 
  598 
  599 class ProjectRepo(BaseRepo):
  600     """Repository for the Project entity."""
  601 
  602     def _do_entity_name(self):
  603         """Sub-class hook: return entity name, such as for debugging."""
  604         return "Project"
  605 
  606     def _do_build_get_query(self, entity_id, external_project_id, session):
  607         """Sub-class hook: build a retrieve query."""
  608         return session.query(models.Project).filter_by(id=entity_id)
  609 
  610     def _do_validate(self, values):
  611         """Sub-class hook: validate values."""
  612         pass
  613 
  614     def find_by_external_project_id(self, external_project_id,
  615                                     suppress_exception=False, session=None):
  616         session = self.get_session(session)
  617 
  618         try:
  619             query = session.query(models.Project)
  620             query = query.filter_by(external_id=external_project_id)
  621 
  622             entity = query.one()
  623 
  624         except sa_orm.exc.NoResultFound:
  625             entity = None
  626             if not suppress_exception:
  627                 LOG.exception("Problem getting Project %s",
  628                               external_project_id)
  629                 raise exception.NotFound(u._(
  630                     "No {entity_name} found with keystone-ID {id}").format(
  631                         entity_name=self._do_entity_name(),
  632                         id=external_project_id))
  633 
  634         return entity
  635 
  636     def _build_get_project_entities_query(self, project_id, session):
  637         """Builds query for retrieving project for given id."""
  638         query = session.query(models.Project)
  639         return query.filter_by(id=project_id).filter_by(deleted=False)
  640 
  641 
  642 class SecretRepo(BaseRepo):
  643     """Repository for the Secret entity."""
  644 
  645     def get_secret_list(self, external_project_id,
  646                         offset_arg=None, limit_arg=None,
  647                         name=None, alg=None, mode=None,
  648                         bits=0, secret_type=None, suppress_exception=False,
  649                         session=None, acl_only=None, user_id=None,
  650                         created=None, updated=None, expiration=None,
  651                         sort=None):
  652         """Returns a list of secrets
  653 
  654         The list is scoped to secrets that are associated with the
  655         external_project_id (e.g. Keystone Project ID), and filtered
  656         using any provided filters.
  657         """
  658         offset, limit = clean_paging_values(offset_arg, limit_arg)
  659 
  660         session = self.get_session(session)
  661         utcnow = timeutils.utcnow()
  662 
  663         query = session.query(models.Secret)
  664         query = query.filter_by(deleted=False)
  665 
  666         query = query.filter(or_(models.Secret.expiration.is_(None),
  667                                  models.Secret.expiration > utcnow))
  668 
  669         if name:
  670             query = query.filter(models.Secret.name.like(name))
  671         if alg:
  672             query = query.filter(models.Secret.algorithm.like(alg))
  673         if mode:
  674             query = query.filter(models.Secret.mode.like(mode))
  675         if bits > 0:
  676             query = query.filter(models.Secret.bit_length == bits)
  677         if secret_type:
  678             query = query.filter(models.Secret.secret_type == secret_type)
  679         if created:
  680             query = self._build_date_filter_query(query, 'created_at', created)
  681         if updated:
  682             query = self._build_date_filter_query(query, 'updated_at', updated)
  683         if expiration:
  684             query = self._build_date_filter_query(
  685                 query, 'expiration', expiration
  686             )
  687         else:
  688             query = query.filter(or_(models.Secret.expiration.is_(None),
  689                                      models.Secret.expiration > utcnow))
  690         if sort:
  691             query = self._build_sort_filter_query(query, sort)
  692 
  693         if acl_only and acl_only.lower() == 'true' and user_id:
  694             query = query.join(models.SecretACL)
  695             query = query.join(models.SecretACLUser)
  696             query = query.filter(models.SecretACLUser.user_id == user_id)
  697         else:
  698             query = query.join(models.Project)
  699             query = query.filter(
  700                 models.Project.external_id == external_project_id)
  701 
  702         total = query.count()
  703         end_offset = offset + limit
  704 
  705         LOG.debug('Retrieving from %s to %s', offset, end_offset)
  706 
  707         query = query.limit(limit).offset(offset)
  708         entities = query.all()
  709 
  710         LOG.debug('Number entities retrieved: %s out of %s',
  711                   len(entities), total)
  712 
  713         if total <= 0 and not suppress_exception:
  714             _raise_no_entities_found(self._do_entity_name())
  715 
  716         return entities, offset, limit, total
  717 
  718     def _do_entity_name(self):
  719         """Sub-class hook: return entity name, such as for debugging."""
  720         return "Secret"
  721 
  722     def _do_build_get_query(self, entity_id, external_project_id, session):
  723         """Sub-class hook: build a retrieve query."""
  724         utcnow = timeutils.utcnow()
  725 
  726         expiration_filter = or_(models.Secret.expiration.is_(None),
  727                                 models.Secret.expiration > utcnow)
  728 
  729         query = session.query(models.Secret)
  730         query = query.filter_by(id=entity_id, deleted=False)
  731         query = query.filter(expiration_filter)
  732         query = query.join(models.Project)
  733         query = query.filter(models.Project.external_id == external_project_id)
  734         return query
  735 
  736     def _do_validate(self, values):
  737         """Sub-class hook: validate values."""
  738         pass
  739 
  740     def _build_get_project_entities_query(self, project_id, session):
  741         """Builds query for retrieving Secrets associated with a given project
  742 
  743         :param project_id: id of barbican project entity
  744         :param session: existing db session reference.
  745         """
  746 
  747         utcnow = timeutils.utcnow()
  748         expiration_filter = or_(models.Secret.expiration.is_(None),
  749                                 models.Secret.expiration > utcnow)
  750 
  751         query = session.query(models.Secret).filter_by(deleted=False)
  752         query = query.filter(models.Secret.project_id == project_id)
  753         query = query.filter(expiration_filter)
  754 
  755         return query
  756 
  757     def _build_date_filter_query(self, query, attribute, date_filters):
  758         """Parses date_filters to apply each filter to the given query
  759 
  760         :param query: query object to apply filters to
  761         :param attribute: name of the model attribute to be filtered
  762         :param date_filters: comma separated string of date filters to apply
  763         """
  764         parse = timeutils.parse_isotime
  765         for filter in date_filters.split(','):
  766             if filter.startswith('lte:'):
  767                 isotime = filter[4:]
  768                 query = query.filter(or_(
  769                     getattr(models.Secret, attribute) < parse(isotime),
  770                     getattr(models.Secret, attribute) == parse(isotime))
  771                 )
  772             elif filter.startswith('lt:'):
  773                 isotime = filter[3:]
  774                 query = query.filter(
  775                     getattr(models.Secret, attribute) < parse(isotime)
  776                 )
  777             elif filter.startswith('gte:'):
  778                 isotime = filter[4:]
  779                 query = query.filter(or_(
  780                     getattr(models.Secret, attribute) > parse(isotime),
  781                     getattr(models.Secret, attribute) == parse(isotime))
  782                 )
  783             elif filter.startswith('gt:'):
  784                 isotime = filter[3:]
  785                 query = query.filter(
  786                     getattr(models.Secret, attribute) > parse(isotime)
  787                 )
  788             else:
  789                 query = query.filter(
  790                     getattr(models.Secret, attribute) == parse(filter)
  791                 )
  792         return query
  793 
  794     def _build_sort_filter_query(self, query, sort_filters):
  795         """Parses sort_filters to order the query"""
  796         key_to_column_map = {
  797             'created': 'created_at',
  798             'updated': 'updated_at'
  799         }
  800         ordering = list()
  801         for sort in sort_filters.split(','):
  802             if ':' in sort:
  803                 key, direction = sort.split(':')
  804             else:
  805                 key, direction = sort, 'asc'
  806             ordering.append(
  807                 getattr(
  808                     getattr(models.Secret, key_to_column_map.get(key, key)),
  809                     direction
  810                 )()
  811             )
  812         return query.order_by(*ordering)
  813 
  814     def get_secret_by_id(self, entity_id, suppress_exception=False,
  815                          session=None):
  816         """Gets secret by its entity id without project id check."""
  817         session = self.get_session(session)
  818         try:
  819             utcnow = timeutils.utcnow()
  820             expiration_filter = or_(models.Secret.expiration.is_(None),
  821                                     models.Secret.expiration > utcnow)
  822 
  823             query = session.query(models.Secret)
  824             query = query.filter_by(id=entity_id, deleted=False)
  825             query = query.filter(expiration_filter)
  826             entity = query.one()
  827         except sa_orm.exc.NoResultFound:
  828             entity = None
  829             if not suppress_exception:
  830                 LOG.exception("Problem getting secret %s",
  831                               entity_id)
  832                 raise exception.NotFound(u._(
  833                     "No secret found with secret-ID {id}").format(
  834                         entity_name=self._do_entity_name(),
  835                         id=entity_id))
  836         return entity
  837 
  838 
  839 class EncryptedDatumRepo(BaseRepo):
  840     """Repository for the EncryptedDatum entity
  841 
  842     Stores encrypted information on behalf of a Secret.
  843     """
  844 
  845     def _do_entity_name(self):
  846         """Sub-class hook: return entity name, such as for debugging."""
  847         return "EncryptedDatum"
  848 
  849     def _do_build_get_query(self, entity_id, external_project_id, session):
  850         """Sub-class hook: build a retrieve query."""
  851         return session.query(models.EncryptedDatum).filter_by(id=entity_id)
  852 
  853     def _do_validate(self, values):
  854         """Sub-class hook: validate values."""
  855         pass
  856 
  857 
  858 class SecretStoreMetadatumRepo(BaseRepo):
  859     """Repository for the SecretStoreMetadatum entity
  860 
  861     Stores key/value information on behalf of a Secret.
  862     """
  863 
  864     def save(self, metadata, secret_model):
  865         """Saves the specified metadata for the secret.
  866 
  867         :raises NotFound if entity does not exist.
  868         """
  869         now = timeutils.utcnow()
  870 
  871         for k, v in metadata.items():
  872             meta_model = models.SecretStoreMetadatum(k, v)
  873             meta_model.updated_at = now
  874             meta_model.secret = secret_model
  875             meta_model.save()
  876 
  877     def get_metadata_for_secret(self, secret_id):
  878         """Returns a dict of SecretStoreMetadatum instances."""
  879 
  880         session = get_session()
  881 
  882         query = session.query(models.SecretStoreMetadatum)
  883         query = query.filter_by(deleted=False)
  884 
  885         query = query.filter(
  886             models.SecretStoreMetadatum.secret_id == secret_id)
  887 
  888         metadata = query.all()
  889         return {m.key: m.value for m in metadata}
  890 
  891     def _do_entity_name(self):
  892         """Sub-class hook: return entity name, such as for debugging."""
  893         return "SecretStoreMetadatum"
  894 
  895     def _do_build_get_query(self, entity_id, external_project_id, session):
  896         """Sub-class hook: build a retrieve query."""
  897         query = session.query(models.SecretStoreMetadatum)
  898         return query.filter_by(id=entity_id)
  899 
  900     def _do_validate(self, values):
  901         """Sub-class hook: validate values."""
  902         pass
  903 
  904 
  905 class SecretUserMetadatumRepo(BaseRepo):
  906     """Repository for the SecretUserMetadatum entity
  907 
  908     Stores key/value information on behalf of a Secret.
  909     """
  910 
  911     def create_replace_user_metadata(self, secret_id, metadata):
  912         """Creates or replaces the specified metadata for the secret."""
  913         now = timeutils.utcnow()
  914 
  915         session = get_session()
  916         query = session.query(models.SecretUserMetadatum)
  917         query = query.filter_by(secret_id=secret_id)
  918         query.delete()
  919 
  920         for k, v in metadata.items():
  921             meta_model = models.SecretUserMetadatum(k, v)
  922             meta_model.secret_id = secret_id
  923             meta_model.updated_at = now
  924             meta_model.save(session=session)
  925 
  926     def get_metadata_for_secret(self, secret_id):
  927         """Returns a dict of SecretUserMetadatum instances."""
  928         session = get_session()
  929 
  930         query = session.query(models.SecretUserMetadatum)
  931         query = query.filter_by(deleted=False)
  932 
  933         query = query.filter(
  934             models.SecretUserMetadatum.secret_id == secret_id)
  935 
  936         metadata = query.all()
  937         return {m.key: m.value for m in metadata}
  938 
  939     def create_replace_user_metadatum(self, secret_id, key, value):
  940         now = timeutils.utcnow()
  941 
  942         session = get_session()
  943         query = session.query(models.SecretUserMetadatum)
  944         query = query.filter_by(secret_id=secret_id)
  945         query = query.filter_by(key=key)
  946         query.delete()
  947 
  948         meta_model = models.SecretUserMetadatum(key, value)
  949         meta_model.secret_id = secret_id
  950         meta_model.updated_at = now
  951         meta_model.save(session=session)
  952 
  953     def delete_metadatum(self, secret_id, key):
  954         """Removes a key from a SecretUserMetadatum instances."""
  955         session = get_session()
  956 
  957         query = session.query(models.SecretUserMetadatum)
  958         query = query.filter_by(secret_id=secret_id)
  959         query = query.filter_by(key=key)
  960         query.delete()
  961 
  962     def _do_entity_name(self):
  963         """Sub-class hook: return entity name, such as for debugging."""
  964         return "SecretUserMetadatum"
  965 
  966     def _do_build_get_query(self, entity_id, external_project_id, session):
  967         """Sub-class hook: build a retrieve query."""
  968         query = session.query(models.SecretUserMetadatum)
  969         return query.filter_by(id=entity_id)
  970 
  971     def _do_validate(self, values):
  972         """Sub-class hook: validate values."""
  973         pass
  974 
  975 
  976 class KEKDatumRepo(BaseRepo):
  977     """Repository for the KEKDatum entity
  978 
  979     Stores key encryption key (KEK) metadata used by crypto plugins to
  980     encrypt/decrypt secrets.
  981     """
  982 
  983     def find_or_create_kek_datum(self, project,
  984                                  plugin_name,
  985                                  suppress_exception=False,
  986                                  session=None):
  987         """Find or create a KEK datum instance."""
  988         if not plugin_name:
  989             raise exception.BarbicanException(
  990                 u._('Tried to register crypto plugin with null or empty '
  991                     'name.'))
  992 
  993         kek_datum = None
  994 
  995         session = self.get_session(session)
  996 
  997         query = session.query(models.KEKDatum)
  998         query = query.filter_by(project_id=project.id,
  999                                 plugin_name=plugin_name,
 1000                                 active=True,
 1001                                 deleted=False)
 1002 
 1003         query = query.order_by(models.KEKDatum.created_at)
 1004 
 1005         kek_datums = query.all()
 1006 
 1007         if not kek_datums:
 1008             kek_datum = models.KEKDatum()
 1009 
 1010             kek_datum.kek_label = "project-{0}-key-{1}".format(
 1011                 project.external_id, uuidutils.generate_uuid())
 1012             kek_datum.project_id = project.id
 1013             kek_datum.plugin_name = plugin_name
 1014             kek_datum.status = models.States.ACTIVE
 1015 
 1016             self.save(kek_datum)
 1017         else:
 1018             kek_datum = kek_datums.pop()
 1019 
 1020             # (alee)  There should be only one active KEKDatum.
 1021             # Due to a race condition with many threads or
 1022             # many barbican processes, its possible to have
 1023             # multiple active KEKDatum.  The code below makes
 1024             # all the extra KEKDatum inactive
 1025             # See LP#1726378
 1026             for kd in kek_datums:
 1027                 LOG.debug(
 1028                     "Multiple active KEKDatum found for %s."
 1029                     "Setting %s to be inactive.",
 1030                     project.external_id,
 1031                     kd.kek_label)
 1032                 kd.active = False
 1033                 self.save(kd)
 1034 
 1035         return kek_datum
 1036 
 1037     def _do_entity_name(self):
 1038         """Sub-class hook: return entity name, such as for debugging."""
 1039         return "KEKDatum"
 1040 
 1041     def _do_build_get_query(self, entity_id, external_project_id, session):
 1042         """Sub-class hook: build a retrieve query."""
 1043         return session.query(models.KEKDatum).filter_by(id=entity_id)
 1044 
 1045     def _do_validate(self, values):
 1046         """Sub-class hook: validate values."""
 1047         pass
 1048 
 1049     def _build_get_project_entities_query(self, project_id, session):
 1050         """Builds query for retrieving KEK Datum instance(s).
 1051 
 1052         The returned KEK Datum instance(s) are related to a given project.
 1053 
 1054         :param project_id: id of barbican project entity
 1055         :param session: existing db session reference.
 1056         """
 1057         return session.query(models.KEKDatum).filter_by(
 1058             project_id=project_id).filter_by(deleted=False)
 1059 
 1060 
 1061 class OrderRepo(BaseRepo):
 1062     """Repository for the Order entity."""
 1063 
 1064     def get_by_create_date(self, external_project_id, offset_arg=None,
 1065                            limit_arg=None, meta_arg=None,
 1066                            suppress_exception=False, session=None):
 1067         """Returns a list of orders
 1068 
 1069         The list is ordered by the date they were created at and paged
 1070         based on the offset and limit fields.
 1071 
 1072         :param external_project_id: The keystone id for the project.
 1073         :param offset_arg: The entity number where the query result should
 1074                            start.
 1075         :param limit_arg: The maximum amount of entities in the result set.
 1076         :param meta_arg: Optional meta field used to filter results.
 1077         :param suppress_exception: Whether NoResultFound exceptions should be
 1078                                    suppressed.
 1079         :param session: SQLAlchemy session object.
 1080 
 1081         :returns: Tuple consisting of (list_of_entities, offset, limit, total).
 1082         """
 1083 
 1084         offset, limit = clean_paging_values(offset_arg, limit_arg)
 1085 
 1086         session = self.get_session(session)
 1087 
 1088         query = session.query(models.Order)
 1089         query = query.order_by(models.Order.created_at)
 1090         query = query.filter_by(deleted=False)
 1091 
 1092         if meta_arg:
 1093             query = query.filter(models.Order.meta.contains(meta_arg))
 1094 
 1095         query = query.join(models.Project, models.Order.project)
 1096         query = query.filter(models.Project.external_id == external_project_id)
 1097 
 1098         start = offset
 1099         end = offset + limit
 1100         LOG.debug('Retrieving from %s to %s', start, end)
 1101         total = query.count()
 1102         entities = query.offset(start).limit(limit).all()
 1103         LOG.debug('Number entities retrieved: %s out of %s',
 1104                   len(entities), total
 1105                   )
 1106 
 1107         if total <= 0 and not suppress_exception:
 1108             _raise_no_entities_found(self._do_entity_name())
 1109 
 1110         return entities, offset, limit, total
 1111 
 1112     def _do_entity_name(self):
 1113         """Sub-class hook: return entity name, such as for debugging."""
 1114         return "Order"
 1115 
 1116     def _do_build_get_query(self, entity_id, external_project_id, session):
 1117         """Sub-class hook: build a retrieve query."""
 1118         query = session.query(models.Order)
 1119         query = query.filter_by(id=entity_id, deleted=False)
 1120         query = query.join(models.Project, models.Order.project)
 1121         query = query.filter(models.Project.external_id == external_project_id)
 1122         return query
 1123 
 1124     def _do_validate(self, values):
 1125         """Sub-class hook: validate values."""
 1126         pass
 1127 
 1128     def _build_get_project_entities_query(self, project_id, session):
 1129         """Builds query for retrieving orders related to given project.
 1130 
 1131         :param project_id: id of barbican project entity
 1132         :param session: existing db session reference.
 1133         """
 1134         return session.query(models.Order).filter_by(
 1135             project_id=project_id).filter_by(deleted=False)
 1136 
 1137 
 1138 class OrderPluginMetadatumRepo(BaseRepo):
 1139     """Repository for the OrderPluginMetadatum entity
 1140 
 1141     Stores key/value plugin information on behalf of an Order.
 1142     """
 1143 
 1144     def save(self, metadata, order_model):
 1145         """Saves the specified metadata for the order.
 1146 
 1147         :raises NotFound if entity does not exist.
 1148         """
 1149         now = timeutils.utcnow()
 1150         session = get_session()
 1151 
 1152         for k, v in metadata.items():
 1153             meta_model = models.OrderPluginMetadatum(k, v)
 1154             meta_model.updated_at = now
 1155             meta_model.order = order_model
 1156             meta_model.save(session=session)
 1157 
 1158     def get_metadata_for_order(self, order_id):
 1159         """Returns a dict of OrderPluginMetadatum instances."""
 1160 
 1161         session = get_session()
 1162 
 1163         try:
 1164             query = session.query(models.OrderPluginMetadatum)
 1165             query = query.filter_by(deleted=False)
 1166 
 1167             query = query.filter(
 1168                 models.OrderPluginMetadatum.order_id == order_id)
 1169 
 1170             metadata = query.all()
 1171 
 1172         except sa_orm.exc.NoResultFound:
 1173             metadata = {}
 1174 
 1175         return {m.key: m.value for m in metadata}
 1176 
 1177     def _do_entity_name(self):
 1178         """Sub-class hook: return entity name, such as for debugging."""
 1179         return "OrderPluginMetadatum"
 1180 
 1181     def _do_build_get_query(self, entity_id, external_project_id, session):
 1182         """Sub-class hook: build a retrieve query."""
 1183         query = session.query(models.OrderPluginMetadatum)
 1184         return query.filter_by(id=entity_id)
 1185 
 1186     def _do_validate(self, values):
 1187         """Sub-class hook: validate values."""
 1188         pass
 1189 
 1190 
 1191 class OrderBarbicanMetadatumRepo(BaseRepo):
 1192     """Repository for the OrderBarbicanMetadatum entity
 1193 
 1194     Stores key/value plugin information on behalf of a Order.
 1195     """
 1196 
 1197     def save(self, metadata, order_model):
 1198         """Saves the specified metadata for the order.
 1199 
 1200         :raises NotFound if entity does not exist.
 1201         """
 1202         now = timeutils.utcnow()
 1203         session = get_session()
 1204 
 1205         for k, v in metadata.items():
 1206             meta_model = models.OrderBarbicanMetadatum(k, v)
 1207             meta_model.updated_at = now
 1208             meta_model.order = order_model
 1209             meta_model.save(session=session)
 1210 
 1211     def get_metadata_for_order(self, order_id):
 1212         """Returns a dict of OrderBarbicanMetadatum instances."""
 1213 
 1214         session = get_session()
 1215 
 1216         try:
 1217             query = session.query(models.OrderBarbicanMetadatum)
 1218             query = query.filter_by(deleted=False)
 1219 
 1220             query = query.filter(
 1221                 models.OrderBarbicanMetadatum.order_id == order_id)
 1222 
 1223             metadata = query.all()
 1224 
 1225         except sa_orm.exc.NoResultFound:
 1226             metadata = {}
 1227 
 1228         return {m.key: m.value for m in metadata}
 1229 
 1230     def _do_entity_name(self):
 1231         """Sub-class hook: return entity name, such as for debugging."""
 1232         return "OrderBarbicanMetadatum"
 1233 
 1234     def _do_build_get_query(self, entity_id, external_project_id, session):
 1235         """Sub-class hook: build a retrieve query."""
 1236         query = session.query(models.OrderBarbicanMetadatum)
 1237         return query.filter_by(id=entity_id)
 1238 
 1239     def _do_validate(self, values):
 1240         """Sub-class hook: validate values."""
 1241         pass
 1242 
 1243 
 1244 class OrderRetryTaskRepo(BaseRepo):
 1245     """Repository for the OrderRetryTask entity."""
 1246 
 1247     def get_by_create_date(
 1248             self, only_at_or_before_this_date=None,
 1249             offset_arg=None, limit_arg=None,
 1250             suppress_exception=False,
 1251             session=None):
 1252         """Returns a list of order retry task entities
 1253 
 1254         The list is ordered by the date they were created at and paged
 1255         based on the offset and limit fields.
 1256 
 1257         :param only_at_or_before_this_date: If specified, only entities at or
 1258             before this date are returned.
 1259         :param offset_arg: The entity number where the query result should
 1260             start.
 1261         :param limit_arg: The maximum amount of entities in the result set.
 1262         :param suppress_exception: Whether NoResultFound exceptions should be
 1263             suppressed.
 1264         :param session: SQLAlchemy session object.
 1265 
 1266         :returns: Tuple consisting of (list_of_entities, offset, limit, total).
 1267         """
 1268 
 1269         offset, limit = clean_paging_values(offset_arg, limit_arg)
 1270 
 1271         session = self.get_session(session)
 1272 
 1273         query = session.query(models.OrderRetryTask)
 1274         query = query.order_by(models.OrderRetryTask.created_at)
 1275         query = query.filter_by(deleted=False)
 1276         if only_at_or_before_this_date:
 1277             query = query.filter(
 1278                 models.OrderRetryTask.retry_at <= only_at_or_before_this_date)
 1279 
 1280         start = offset
 1281         end = offset + limit
 1282         LOG.debug('Retrieving from %s to %s', start, end)
 1283         total = query.count()
 1284         entities = query.offset(start).limit(limit).all()
 1285         LOG.debug('Number entities retrieved: %s out of %s',
 1286                   len(entities), total
 1287                   )
 1288 
 1289         if total <= 0 and not suppress_exception:
 1290             _raise_no_entities_found(self._do_entity_name())
 1291 
 1292         return entities, offset, limit, total
 1293 
 1294     def _do_entity_name(self):
 1295         """Sub-class hook: return entity name, such as for debugging."""
 1296         return "OrderRetryTask"
 1297 
 1298     def _do_build_get_query(self, entity_id, external_project_id, session):
 1299         """Sub-class hook: build a retrieve query."""
 1300         query = session.query(models.OrderRetryTask)
 1301         query = query.filter_by(id=entity_id, deleted=False)
 1302         return query
 1303 
 1304     def _do_validate(self, values):
 1305         """Sub-class hook: validate values."""
 1306         pass
 1307 
 1308 
 1309 class ContainerRepo(BaseRepo):
 1310     """Repository for the Container entity."""
 1311 
 1312     def get_by_create_date(self, external_project_id, offset_arg=None,
 1313                            limit_arg=None, name_arg=None, type_arg=None,
 1314                            suppress_exception=False, session=None):
 1315         """Returns a list of containers
 1316 
 1317         The list is ordered by the date they were created at and paged
 1318         based on the offset and limit fields. The external_project_id is
 1319         external-to-Barbican value assigned to the project by Keystone.
 1320         """
 1321 
 1322         offset, limit = clean_paging_values(offset_arg, limit_arg)
 1323 
 1324         session = self.get_session(session)
 1325 
 1326         query = session.query(models.Container)
 1327         query = query.order_by(models.Container.created_at)
 1328         query = query.filter_by(deleted=False)
 1329 
 1330         if name_arg:
 1331             query = query.filter(models.Container.name.like(name_arg))
 1332 
 1333         if type_arg:
 1334             query = query.filter(models.Container.type == type_arg)
 1335 
 1336         query = query.join(models.Project, models.Container.project)
 1337         query = query.filter(models.Project.external_id == external_project_id)
 1338 
 1339         start = offset
 1340         end = offset + limit
 1341         LOG.debug('Retrieving from %s to %s', start, end)
 1342         total = query.count()
 1343         entities = query.offset(start).limit(limit).all()
 1344         LOG.debug('Number entities retrieved: %s out of %s',
 1345                   len(entities), total
 1346                   )
 1347 
 1348         if total <= 0 and not suppress_exception:
 1349             _raise_no_entities_found(self._do_entity_name())
 1350 
 1351         return entities, offset, limit, total
 1352 
 1353     def _do_entity_name(self):
 1354         """Sub-class hook: return entity name, such as for debugging."""
 1355         return "Container"
 1356 
 1357     def _do_build_get_query(self, entity_id, external_project_id, session):
 1358         """Sub-class hook: build a retrieve query."""
 1359         query = session.query(models.Container)
 1360         query = query.filter_by(id=entity_id, deleted=False)
 1361         query = query.join(models.Project, models.Container.project)
 1362         query = query.filter(models.Project.external_id == external_project_id)
 1363         return query
 1364 
 1365     def _do_validate(self, values):
 1366         """Sub-class hook: validate values."""
 1367         pass
 1368 
 1369     def _build_get_project_entities_query(self, project_id, session):
 1370         """Builds query for retrieving container related to given project.
 1371 
 1372         :param project_id: id of barbican project entity
 1373         :param session: existing db session reference.
 1374         """
 1375         return session.query(models.Container).filter_by(
 1376             deleted=False).filter_by(project_id=project_id)
 1377 
 1378     def get_container_by_id(self, entity_id, suppress_exception=False,
 1379                             session=None):
 1380         """Gets container by its entity id without project id check."""
 1381         session = self.get_session(session)
 1382         try:
 1383             query = session.query(models.Container)
 1384             query = query.filter_by(id=entity_id, deleted=False)
 1385             entity = query.one()
 1386         except sa_orm.exc.NoResultFound:
 1387             entity = None
 1388             if not suppress_exception:
 1389                 LOG.exception("Problem getting container %s", entity_id)
 1390                 raise exception.NotFound(u._(
 1391                     "No container found with container-ID {id}").format(
 1392                         entity_name=self._do_entity_name(),
 1393                         id=entity_id))
 1394         return entity
 1395 
 1396 
 1397 class ContainerSecretRepo(BaseRepo):
 1398     """Repository for the ContainerSecret entity."""
 1399     def _do_entity_name(self):
 1400         """Sub-class hook: return entity name, such as for debugging."""
 1401         return "ContainerSecret"
 1402 
 1403     def _do_build_get_query(self, entity_id, external_project_id, session):
 1404         """Sub-class hook: build a retrieve query."""
 1405         return session.query(models.ContainerSecret
 1406                              ).filter_by(id=entity_id)
 1407 
 1408     def _do_validate(self, values):
 1409         """Sub-class hook: validate values."""
 1410         pass
 1411 
 1412 
 1413 class ContainerConsumerRepo(BaseRepo):
 1414     """Repository for the Service entity."""
 1415 
 1416     def get_by_container_id(self, container_id,
 1417                             offset_arg=None, limit_arg=None,
 1418                             suppress_exception=False, session=None):
 1419         """Returns a list of Consumers
 1420 
 1421         The list is ordered by the date they were created at and paged
 1422         based on the offset and limit fields.
 1423         """
 1424 
 1425         offset, limit = clean_paging_values(offset_arg, limit_arg)
 1426 
 1427         session = self.get_session(session)
 1428 
 1429         query = session.query(models.ContainerConsumerMetadatum)
 1430         query = query.order_by(models.ContainerConsumerMetadatum.name)
 1431         query = query.filter_by(deleted=False)
 1432         query = query.filter(
 1433             models.ContainerConsumerMetadatum.container_id == container_id
 1434         )
 1435 
 1436         start = offset
 1437         end = offset + limit
 1438         LOG.debug('Retrieving from %s to %s', start, end)
 1439         total = query.count()
 1440         entities = query.offset(start).limit(limit).all()
 1441         LOG.debug('Number entities retrieved: %s out of %s',
 1442                   len(entities), total
 1443                   )
 1444 
 1445         if total <= 0 and not suppress_exception:
 1446             _raise_no_entities_found(self._do_entity_name())
 1447 
 1448         return entities, offset, limit, total
 1449 
 1450     def get_by_values(self, container_id, name, URL, suppress_exception=False,
 1451                       show_deleted=False, session=None):
 1452         session = self.get_session(session)
 1453 
 1454         try:
 1455             query = session.query(models.ContainerConsumerMetadatum)
 1456             query = query.filter_by(
 1457                 container_id=container_id,
 1458                 name=name,
 1459                 URL=URL)
 1460 
 1461             if not show_deleted:
 1462                 query.filter_by(deleted=False)
 1463             consumer = query.one()
 1464         except sa_orm.exc.NoResultFound:
 1465             consumer = None
 1466             if not suppress_exception:
 1467                 raise exception.NotFound(
 1468                     u._("Could not find {entity_name}").format(
 1469                         entity_name=self._do_entity_name()))
 1470 
 1471         return consumer
 1472 
 1473     def create_or_update_from(self, new_consumer, container, session=None):
 1474         session = self.get_session(session)
 1475         try:
 1476             container.updated_at = timeutils.utcnow()
 1477             container.consumers.append(new_consumer)
 1478             container.save(session=session)
 1479         except db_exc.DBDuplicateEntry:
 1480             session.rollback()  # We know consumer already exists.
 1481 
 1482             # This operation is idempotent, so log this and move on
 1483             LOG.debug("Consumer %s with URL %s already exists for "
 1484                       "container %s, continuing...", new_consumer.name,
 1485                       new_consumer.URL, new_consumer.container_id)
 1486             # Get the existing entry and reuse it by clearing the deleted flags
 1487             existing_consumer = self.get_by_values(
 1488                 new_consumer.container_id, new_consumer.name, new_consumer.URL,
 1489                 show_deleted=True)
 1490             existing_consumer.deleted = False
 1491             existing_consumer.deleted_at = None
 1492             # We are not concerned about timing here -- set only, no reads
 1493             existing_consumer.save()
 1494 
 1495     def _do_entity_name(self):
 1496         """Sub-class hook: return entity name, such as for debugging."""
 1497         return "ContainerConsumer"
 1498 
 1499     def _do_build_get_query(self, entity_id, external_project_id, session):
 1500         """Sub-class hook: build a retrieve query."""
 1501         query = session.query(models.ContainerConsumerMetadatum)
 1502         return query.filter_by(id=entity_id, deleted=False)
 1503 
 1504     def _do_validate(self, values):
 1505         """Sub-class hook: validate values."""
 1506         pass
 1507 
 1508     def _build_get_project_entities_query(self, project_id, session):
 1509         """Builds query for retrieving consumers associated with given project
 1510 
 1511         :param project_id: id of barbican project entity
 1512         :param session: existing db session reference.
 1513         """
 1514         query = session.query(
 1515             models.ContainerConsumerMetadatum).filter_by(deleted=False)
 1516         query = query.filter(
 1517             models.ContainerConsumerMetadatum.project_id == project_id)
 1518 
 1519         return query
 1520 
 1521 
 1522 class TransportKeyRepo(BaseRepo):
 1523     """Repository for the TransportKey entity
 1524 
 1525     Stores transport keys for wrapping the secret data to/from a
 1526     barbican client.
 1527     """
 1528 
 1529     def _do_entity_name(self):
 1530         """Sub-class hook: return entity name, such as for debugging."""
 1531         return "TransportKey"
 1532 
 1533     def get_by_create_date(self, plugin_name=None,
 1534                            offset_arg=None, limit_arg=None,
 1535                            suppress_exception=False, session=None):
 1536         """Returns a list of transport keys
 1537 
 1538         The list is ordered from latest created first. The search accepts
 1539         plugin_id as an optional parameter for the search.
 1540         """
 1541 
 1542         offset, limit = clean_paging_values(offset_arg, limit_arg)
 1543 
 1544         session = self.get_session(session)
 1545 
 1546         query = session.query(models.TransportKey)
 1547         query = query.order_by(models.TransportKey.created_at)
 1548         if plugin_name is not None:
 1549             query = session.query(models.TransportKey)
 1550             query = query.filter_by(deleted=False, plugin_name=plugin_name)
 1551         else:
 1552             query = query.filter_by(deleted=False)
 1553 
 1554         start = offset
 1555         end = offset + limit
 1556         LOG.debug('Retrieving from %s to %s', start, end)
 1557         total = query.count()
 1558         entities = query.offset(start).limit(limit).all()
 1559         LOG.debug('Number of entities retrieved: %s out of %s',
 1560                   len(entities), total)
 1561 
 1562         if total <= 0 and not suppress_exception:
 1563             _raise_no_entities_found(self._do_entity_name())
 1564 
 1565         return entities, offset, limit, total
 1566 
 1567     def get_latest_transport_key(self, plugin_name, suppress_exception=False,
 1568                                  session=None):
 1569         """Returns the latest transport key for a given plugin."""
 1570         entity, offset, limit, total = self.get_by_create_date(
 1571             plugin_name, offset_arg=0, limit_arg=1,
 1572             suppress_exception=suppress_exception, session=session)
 1573         return entity
 1574 
 1575     def _do_build_get_query(self, entity_id, external_project_id, session):
 1576         """Sub-class hook: build a retrieve query."""
 1577         return session.query(models.TransportKey).filter_by(id=entity_id)
 1578 
 1579     def _do_validate(self, values):
 1580         """Sub-class hook: validate values."""
 1581         pass
 1582 
 1583 
 1584 class CertificateAuthorityRepo(BaseRepo):
 1585     """Repository for the CertificateAuthority entity.
 1586 
 1587     CertificateAuthority entries are not soft delete. So there is no
 1588     need to have deleted=False filter in queries.
 1589     """
 1590 
 1591     def get_by_create_date(self, offset_arg=None, limit_arg=None,
 1592                            plugin_name=None, plugin_ca_id=None,
 1593                            suppress_exception=False, session=None,
 1594                            show_expired=False, project_id=None,
 1595                            restrict_to_project_cas=False):
 1596         """Returns a list of certificate authorities
 1597 
 1598         The returned certificate authorities are ordered by the date they
 1599         were created and paged based on the offset and limit fields.
 1600         """
 1601 
 1602         offset, limit = clean_paging_values(offset_arg, limit_arg)
 1603         session = self.get_session(session)
 1604 
 1605         if restrict_to_project_cas:
 1606             # get both subCAs which have been defined for your project
 1607             # (cas for which the ca.project_id == project_id) AND
 1608             # project_cas which are defined for your project
 1609             # (pca.project_id = project_id)
 1610             query1 = session.query(models.CertificateAuthority)
 1611             query1 = query1.filter(
 1612                 models.CertificateAuthority.project_id == project_id)
 1613 
 1614             query2 = session.query(models.CertificateAuthority)
 1615             query2 = query2.join(models.ProjectCertificateAuthority)
 1616             query2 = query2.filter(
 1617                 models.ProjectCertificateAuthority.project_id == project_id)
 1618 
 1619             query = query1.union(query2)
 1620         else:
 1621             # get both subcas that have been defined for your project
 1622             # (cas for which ca.project_id == project_id) AND
 1623             # all top-level CAs (ca.project_id == None)
 1624 
 1625             query = session.query(models.CertificateAuthority)
 1626             query = query.filter(or_(
 1627                 models.CertificateAuthority.project_id == project_id,
 1628                 models.CertificateAuthority.project_id.is_(None)
 1629             ))
 1630 
 1631         query = query.order_by(models.CertificateAuthority.created_at)
 1632         query = query.filter_by(deleted=False)
 1633 
 1634         if not show_expired:
 1635             utcnow = timeutils.utcnow()
 1636             query = query.filter(or_(
 1637                 models.CertificateAuthority.expiration.is_(None),
 1638                 models.CertificateAuthority.expiration > utcnow))
 1639 
 1640         if plugin_name:
 1641             query = query.filter(
 1642                 models.CertificateAuthority.plugin_name.like(plugin_name))
 1643         if plugin_ca_id:
 1644             query = query.filter(
 1645                 models.CertificateAuthority.plugin_ca_id.like(plugin_ca_id))
 1646 
 1647         start = offset
 1648         end = offset + limit
 1649         LOG.debug('Retrieving from %s to %s', start, end)
 1650         total = query.count()
 1651         entities = query.offset(start).limit(limit).all()
 1652         LOG.debug('Number entities retrieved: %s out of %s',
 1653                   len(entities), total
 1654                   )
 1655 
 1656         if total <= 0 and not suppress_exception:
 1657             _raise_no_entities_found(self._do_entity_name())
 1658 
 1659         return entities, offset, limit, total
 1660 
 1661     def update_entity(self, old_ca, parsed_ca_in, session=None):
 1662         """Updates CA entry and its sub-entries."""
 1663         parsed_ca = dict(parsed_ca_in)
 1664 
 1665         # these fields cannot be  modified
 1666         parsed_ca.pop('plugin_name', None)
 1667         parsed_ca.pop('plugin_ca_id', None)
 1668 
 1669         expiration = parsed_ca.pop('expiration', None)
 1670         expiration_iso = timeutils.parse_isotime(expiration.strip())
 1671         new_expiration = timeutils.normalize_time(expiration_iso)
 1672 
 1673         session = self.get_session(session)
 1674         query = session.query(models.CertificateAuthority).filter_by(
 1675             id=old_ca.id, deleted=False)
 1676         entity = query.one()
 1677 
 1678         entity.expiration = new_expiration
 1679 
 1680         for k, v in entity.ca_meta.items():
 1681             if k not in parsed_ca.keys():
 1682                 v.delete(session)
 1683 
 1684         for key in parsed_ca:
 1685             if key not in entity.ca_meta.keys():
 1686                 meta = models.CertificateAuthorityMetadatum(
 1687                     key, parsed_ca[key])
 1688                 entity.ca_meta[key] = meta
 1689             else:
 1690                 entity.ca_meta[key].value = parsed_ca[key]
 1691 
 1692         entity.save()
 1693         return entity
 1694 
 1695     def _do_entity_name(self):
 1696         """Sub-class hook: return entity name, such as for debugging."""
 1697         return "CertificateAuthority"
 1698 
 1699     def _do_build_get_query(self, entity_id, external_project_id, session):
 1700         """Sub-class hook: build a retrieve query."""
 1701         utcnow = timeutils.utcnow()
 1702 
 1703         # TODO(jfwood): Performance? Is the many-to-many join needed?
 1704         expiration_filter = or_(
 1705             models.CertificateAuthority.expiration.is_(None),
 1706             models.CertificateAuthority.expiration > utcnow)
 1707 
 1708         query = session.query(models.CertificateAuthority)
 1709         query = query.filter_by(id=entity_id, deleted=False)
 1710         query = query.filter(expiration_filter)
 1711 
 1712         return query
 1713 
 1714     def _do_validate(self, values):
 1715         """Sub-class hook: validate values."""
 1716         pass
 1717 
 1718     def _build_get_project_entities_query(self, project_id, session):
 1719         """Builds query for retrieving CA related to given project.
 1720 
 1721         :param project_id: id of barbican project entity
 1722         :param session: existing db session reference.
 1723         """
 1724         return session.query(models.CertificateAuthority).filter_by(
 1725             project_id=project_id).filter_by(deleted=False)
 1726 
 1727 
 1728 class CertificateAuthorityMetadatumRepo(BaseRepo):
 1729     """Repository for the CertificateAuthorityMetadatum entity
 1730 
 1731     Stores key/value information on behalf of a CA.
 1732     """
 1733 
 1734     def save(self, metadata, ca_model):
 1735         """Saves the specified metadata for the CA.
 1736 
 1737         :raises NotFound if entity does not exist.
 1738         """
 1739         now = timeutils.utcnow()
 1740         session = get_session()
 1741 
 1742         for k, v in metadata.items():
 1743             meta_model = models.CertificateAuthorityMetadatum(k, v)
 1744             meta_model.updated_at = now
 1745             meta_model.ca = ca_model
 1746             meta_model.save(session=session)
 1747 
 1748     def get_metadata_for_certificate_authority(self, ca_id):
 1749         """Returns a dict of CertificateAuthorityMetadatum instances."""
 1750 
 1751         session = get_session()
 1752 
 1753         try:
 1754             query = session.query(models.CertificateAuthorityMetadatum)
 1755             query = query.filter_by(deleted=False)
 1756 
 1757             query = query.filter(
 1758                 models.CertificateAuthorityMetadatum.ca_id == ca_id)
 1759 
 1760             metadata = query.all()
 1761 
 1762         except sa_orm.exc.NoResultFound:
 1763             metadata = dict()
 1764 
 1765         return {(m.key, m.value) for m in metadata}
 1766 
 1767     def _do_entity_name(self):
 1768         """Sub-class hook: return entity name, such as for debugging."""
 1769         return "CertificateAuthorityMetadatum"
 1770 
 1771     def _do_build_get_query(self, entity_id, external_project_id, session):
 1772         """Sub-class hook: build a retrieve query."""
 1773         query = session.query(models.CertificateAuthorityMetadatum)
 1774         return query.filter_by(id=entity_id)
 1775 
 1776     def _do_validate(self, values):
 1777         """Sub-class hook: validate values."""
 1778         pass
 1779 
 1780 
 1781 class ProjectCertificateAuthorityRepo(BaseRepo):
 1782     """Repository for the ProjectCertificateAuthority entity.
 1783 
 1784     ProjectCertificateAuthority entries are not soft delete. So there is no
 1785     need to have deleted=False filter in queries.
 1786     """
 1787 
 1788     def get_by_create_date(self, offset_arg=None, limit_arg=None,
 1789                            project_id=None, ca_id=None,
 1790                            suppress_exception=False, session=None):
 1791         """Returns a list of project CAs
 1792 
 1793         The returned project are ordered by the date they
 1794         were created and paged based on the offset and limit fields.
 1795         """
 1796 
 1797         offset, limit = clean_paging_values(offset_arg, limit_arg)
 1798 
 1799         session = self.get_session(session)
 1800 
 1801         query = session.query(models.ProjectCertificateAuthority)
 1802         query = query.order_by(models.ProjectCertificateAuthority.created_at)
 1803         query = query.filter_by(deleted=False)
 1804 
 1805         if project_id:
 1806             query = query.filter(
 1807                 models.ProjectCertificateAuthority.project_id.like(project_id))
 1808         if ca_id:
 1809             query = query.filter(
 1810                 models.ProjectCertificateAuthority.ca_id.like(ca_id))
 1811 
 1812         start = offset
 1813         end = offset + limit
 1814         LOG.debug('Retrieving from %s to %s', start, end)
 1815         total = query.count()
 1816         entities = query.offset(start).limit(limit).all()
 1817         LOG.debug('Number entities retrieved: %s out of %s',
 1818                   len(entities), total
 1819                   )
 1820 
 1821         if total <= 0 and not suppress_exception:
 1822             _raise_no_entities_found(self._do_entity_name())
 1823 
 1824         return entities, offset, limit, total
 1825 
 1826     def _do_entity_name(self):
 1827         """Sub-class hook: return entity name, such as for debugging."""
 1828         return "ProjectCertificateAuthority"
 1829 
 1830     def _do_build_get_query(self, entity_id, external_project_id, session):
 1831         """Sub-class hook: build a retrieve query."""
 1832         return session.query(models.ProjectCertificateAuthority).filter_by(
 1833             id=entity_id)
 1834 
 1835     def _do_validate(self, values):
 1836         """Sub-class hook: validate values."""
 1837         pass
 1838 
 1839     def _build_get_project_entities_query(self, project_id, session):
 1840         """Builds query for retrieving CA related to given project.
 1841 
 1842         :param project_id: id of barbican project entity
 1843         :param session: existing db session reference.
 1844         """
 1845         return session.query(models.ProjectCertificateAuthority).filter_by(
 1846             project_id=project_id)
 1847 
 1848 
 1849 class PreferredCertificateAuthorityRepo(BaseRepo):
 1850     """Repository for the PreferredCertificateAuthority entity.
 1851 
 1852     PreferredCertificateAuthority entries are not soft delete. So there is no
 1853     need to have deleted=False filter in queries.
 1854     """
 1855 
 1856     def get_by_create_date(self, offset_arg=None, limit_arg=None,
 1857                            project_id=None, ca_id=None,
 1858                            suppress_exception=False, session=None):
 1859         """Returns a list of preferred CAs
 1860 
 1861         The returned CAs are ordered by the date they
 1862         were created and paged based on the offset and limit fields.
 1863         """
 1864 
 1865         offset, limit = clean_paging_values(offset_arg, limit_arg)
 1866 
 1867         session = self.get_session(session)
 1868 
 1869         query = session.query(models.PreferredCertificateAuthority)
 1870         query = query.order_by(models.PreferredCertificateAuthority.created_at)
 1871 
 1872         if project_id:
 1873             query = query.filter(
 1874                 models.PreferredCertificateAuthority.project_id.like(
 1875                     project_id))
 1876         if ca_id:
 1877             query = query.filter(
 1878                 models.PreferredCertificateAuthority.ca_id.like(ca_id))
 1879 
 1880         start = offset
 1881         end = offset + limit
 1882         LOG.debug('Retrieving from %s to %s', start, end)
 1883         total = query.count()
 1884         entities = query.offset(start).limit(limit).all()
 1885         LOG.debug('Number entities retrieved: %s out of %s',
 1886                   len(entities), total
 1887                   )
 1888 
 1889         if total <= 0 and not suppress_exception:
 1890             _raise_no_entities_found(self._do_entity_name())
 1891 
 1892         return entities, offset, limit, total
 1893 
 1894     def create_or_update_by_project_id(self, project_id, ca_id, session=None):
 1895         """Create or update preferred CA for a project by project_id.
 1896 
 1897         :param project_id: ID of project whose preferred CA will be saved
 1898         :param ca_id: ID of preferred CA
 1899         :param session: SQLAlchemy session object.
 1900         :return: None
 1901         """
 1902         session = self.get_session(session)
 1903         query = session.query(models.PreferredCertificateAuthority)
 1904         query = query.filter_by(project_id=project_id)
 1905         try:
 1906             entity = query.one()
 1907         except sa_orm.exc.NoResultFound:
 1908             self.create_from(
 1909                 models.PreferredCertificateAuthority(project_id, ca_id),
 1910                 session=session)
 1911         else:
 1912             entity.ca_id = ca_id
 1913             entity.save(session)
 1914 
 1915     def _do_entity_name(self):
 1916         """Sub-class hook: return entity name, such as for debugging."""
 1917         return "PreferredCertificateAuthority"
 1918 
 1919     def _do_build_get_query(self, entity_id, external_project_id, session):
 1920         """Sub-class hook: build a retrieve query."""
 1921         return session.query(models.PreferredCertificateAuthority).filter_by(
 1922             id=entity_id)
 1923 
 1924     def _do_validate(self, values):
 1925         """Sub-class hook: validate values."""
 1926         pass
 1927 
 1928     def _build_get_project_entities_query(self, project_id, session):
 1929         """Builds query for retrieving preferred CA related to given project.
 1930 
 1931         :param project_id: id of barbican project entity
 1932         :param session: existing db session reference.
 1933         """
 1934         return session.query(models.PreferredCertificateAuthority).filter_by(
 1935             project_id=project_id)
 1936 
 1937 
 1938 class SecretACLRepo(BaseRepo):
 1939     """Repository for the SecretACL entity.
 1940 
 1941     There is no need for SecretACLUserRepo as none of logic access
 1942     SecretACLUser (ACL user data) directly. Its always derived from
 1943     SecretACL relationship.
 1944 
 1945     SecretACL and SecretACLUser data is not soft delete. So there is no need
 1946     to have deleted=False filter in queries.
 1947     """
 1948 
 1949     def _do_entity_name(self):
 1950         """Sub-class hook: return entity name, such as for debugging."""
 1951         return "SecretACL"
 1952 
 1953     def _do_build_get_query(self, entity_id, external_project_id, session):
 1954         """Sub-class hook: build a retrieve query."""
 1955         query = session.query(models.SecretACL)
 1956         query = query.filter_by(id=entity_id)
 1957         return query
 1958 
 1959     def _do_validate(self, values):
 1960         """Sub-class hook: validate values."""
 1961         pass
 1962 
 1963     def get_by_secret_id(self, secret_id, session=None):
 1964         """Return list of secret ACLs by secret id."""
 1965 
 1966         session = self.get_session(session)
 1967 
 1968         query = session.query(models.SecretACL)
 1969         query = query.filter_by(secret_id=secret_id)
 1970 
 1971         return query.all()
 1972 
 1973     def create_or_replace_from(self, secret, secret_acl, user_ids=None,
 1974                                session=None):
 1975         session = self.get_session(session)
 1976         secret.updated_at = timeutils.utcnow()
 1977         secret_acl.updated_at = timeutils.utcnow()
 1978         secret.secret_acls.append(secret_acl)
 1979         secret.save(session=session)
 1980 
 1981         self._create_or_replace_acl_users(secret_acl, user_ids,
 1982                                           session=session)
 1983 
 1984     def _create_or_replace_acl_users(self, secret_acl, user_ids, session=None):
 1985         """Creates or updates secret acl user based on input user_ids list.
 1986 
 1987         user_ids is expected to be list of ids (enforced by schema validation).
 1988         Input user ids should have complete list of acl users. It does not
 1989         apply partial update of user ids.
 1990 
 1991         If user_ids is None, no change is made in acl user data.
 1992         If user_ids list is not None, then following change is made.
 1993         For existing acl users, just update timestamp if user_id is present in
 1994         input user ids list. Otherwise, remove existing acl user entries.
 1995         Then add the remaining input user ids as new acl user db entries.
 1996         """
 1997         if user_ids is None:
 1998             return
 1999 
 2000         user_ids = set(user_ids)
 2001 
 2002         now = timeutils.utcnow()
 2003         session = self.get_session(session)
 2004         secret_acl.updated_at = now
 2005 
 2006         for acl_user in secret_acl.acl_users:
 2007             if acl_user.user_id in user_ids:  # input user_id already exists
 2008                 acl_user.updated_at = now
 2009                 user_ids.remove(acl_user.user_id)
 2010             else:
 2011                 acl_user.delete(session)
 2012 
 2013         for user_id in user_ids:
 2014             acl_user = models.SecretACLUser(secret_acl.id, user_id)
 2015             secret_acl.acl_users.append(acl_user)
 2016 
 2017         secret_acl.save(session=session)
 2018 
 2019     def get_count(self, secret_id, session=None):
 2020         """Gets count of existing secret ACL(s) for a given secret."""
 2021         session = self.get_session(session)
 2022         query = session.query(sa_func.count(models.SecretACL.id))
 2023         query = query.filter(models.SecretACL.secret_id == secret_id)
 2024         return query.scalar()
 2025 
 2026     def delete_acls_for_secret(self, secret, session=None):
 2027         session = self.get_session(session)
 2028 
 2029         for entity in secret.secret_acls:
 2030             entity.delete(session=session)
 2031 
 2032 
 2033 class SecretACLUserRepo(BaseRepo):
 2034     """Repository for the SecretACLUser entity."""
 2035 
 2036     def _do_entity_name(self):
 2037         """Sub-class hook: return entity name, such as for debugging."""
 2038         return "SecretACLUser"
 2039 
 2040     def _do_build_get_query(self, entity_id, external_project_id, session):
 2041         """Sub-class hook: build a retrieve query."""
 2042 
 2043         query = session.query(models.SecretACLUser)
 2044         query = query.filter_by(id=entity_id)
 2045 
 2046         return query
 2047 
 2048     def _do_validate(self, values):
 2049         """Sub-class hook: validate values."""
 2050         pass
 2051 
 2052 
 2053 class ContainerACLRepo(BaseRepo):
 2054     """Repository for the ContainerACL entity.
 2055 
 2056     There is no need for ContainerACLUserRepo as none of logic access
 2057     ContainerACLUser (ACL user data) directly. Its always derived from
 2058     ContainerACL relationship.
 2059 
 2060     ContainerACL and ContainerACLUser data is not soft delete. So there is no
 2061     need to have deleted=False filter in queries.
 2062     """
 2063 
 2064     def _do_entity_name(self):
 2065         """Sub-class hook: return entity name, such as for debugging."""
 2066         return "ContainerACL"
 2067 
 2068     def _do_build_get_query(self, entity_id, external_project_id, session):
 2069         """Sub-class hook: build a retrieve query."""
 2070 
 2071         query = session.query(models.ContainerACL)
 2072         query = query.filter_by(id=entity_id)
 2073 
 2074         return query
 2075 
 2076     def _do_validate(self, values):
 2077         """Sub-class hook: validate values."""
 2078         pass
 2079 
 2080     def get_by_container_id(self, container_id, session=None):
 2081         """Return list of container ACLs by container id."""
 2082 
 2083         session = self.get_session(session)
 2084         query = session.query(models.ContainerACL)
 2085         query = query.filter_by(container_id=container_id)
 2086         return query.all()
 2087 
 2088     def create_or_replace_from(self, container, container_acl,
 2089                                user_ids=None, session=None):
 2090         session = self.get_session(session)
 2091         container.updated_at = timeutils.utcnow()
 2092         container_acl.updated_at = timeutils.utcnow()
 2093         container.container_acls.append(container_acl)
 2094         container.save(session=session)
 2095 
 2096         self._create_or_replace_acl_users(container_acl, user_ids, session)
 2097 
 2098     def _create_or_replace_acl_users(self, container_acl, user_ids,
 2099                                      session=None):
 2100         """Creates or updates container acl user based on input user_ids list.
 2101 
 2102         user_ids is expected to be list of ids (enforced by schema validation).
 2103         Input user ids should have complete list of acl users. It does not
 2104         apply partial update of user ids.
 2105 
 2106         If user_ids is None, no change is made in acl user data.
 2107         If user_ids list is not None, then following change is made.
 2108         For existing acl users, just update timestamp if user_id is present in
 2109         input user ids list. Otherwise, remove existing acl user entries.
 2110         Then add the remaining input user ids as new acl user db entries.
 2111         """
 2112         if user_ids is None:
 2113             return
 2114 
 2115         user_ids = set(user_ids)
 2116 
 2117         now = timeutils.utcnow()
 2118         session = self.get_session(session)
 2119         container_acl.updated_at = now
 2120 
 2121         for acl_user in container_acl.acl_users:
 2122             if acl_user.user_id in user_ids:  # input user_id already exists
 2123                 acl_user.updated_at = now
 2124                 user_ids.remove(acl_user.user_id)
 2125             else:
 2126                 acl_user.delete(session)
 2127 
 2128         for user_id in user_ids:
 2129             acl_user = models.ContainerACLUser(container_acl.id, user_id)
 2130             container_acl.acl_users.append(acl_user)
 2131 
 2132         container_acl.save(session=session)
 2133 
 2134     def get_count(self, container_id, session=None):
 2135         """Gets count of existing container ACL(s) for a given container."""
 2136         session = self.get_session(session)
 2137 
 2138         query = session.query(sa_func.count(models.ContainerACL.id))
 2139         query = query.filter(models.ContainerACL.container_id == container_id)
 2140         return query.scalar()
 2141 
 2142     def delete_acls_for_container(self, container, session=None):
 2143         session = self.get_session(session)
 2144 
 2145         for entity in container.container_acls:
 2146             entity.delete(session=session)
 2147 
 2148 
 2149 class ContainerACLUserRepo(BaseRepo):
 2150     """Repository for ContainerACLUser entity."""
 2151     def _do_entity_name(self):
 2152         """Sub-class hook: return entity name, such as for debugging."""
 2153         return "ContainerACLUser"
 2154 
 2155     def _do_build_get_query(self, entity_id, external_project_id, session):
 2156         """Sub-class hook: build a retrieve query."""
 2157 
 2158         query = session.query(models.ContainerACLUser)
 2159         query = query.filter_by(id=entity_id)
 2160 
 2161         return query
 2162 
 2163     def _do_validate(self, values):
 2164         """Sub-class hook: validate values."""
 2165         pass
 2166 
 2167 
 2168 class ProjectQuotasRepo(BaseRepo):
 2169     """Repository for the ProjectQuotas entity."""
 2170     def _do_entity_name(self):
 2171         """Sub-class hook: return entity name, such as for debugging."""
 2172         return "ProjectQuotas"
 2173 
 2174     def _do_build_get_query(self, entity_id, external_project_id, session):
 2175         """Sub-class hook: build a retrieve query."""
 2176         return session.query(models.ProjectQuotas).filter_by(id=entity_id)
 2177 
 2178     def _do_validate(self, values):
 2179         """Sub-class hook: validate values."""
 2180         pass
 2181 
 2182     def get_by_create_date(self, offset_arg=None, limit_arg=None,
 2183                            suppress_exception=False, session=None):
 2184         """Returns a list of ProjectQuotas
 2185 
 2186         The list is ordered by the date they were created at and paged
 2187         based on the offset and limit fields.
 2188 
 2189         :param offset_arg: The entity number where the query result should
 2190                            start.
 2191         :param limit_arg: The maximum amount of entities in the result set.
 2192         :param suppress_exception: Whether NoResultFound exceptions should be
 2193                                    suppressed.
 2194         :param session: SQLAlchemy session object.
 2195         :raises NotFound: if no quota config is found for the project
 2196         :returns: Tuple consisting of (list_of_entities, offset, limit, total).
 2197         """
 2198 
 2199         offset, limit = clean_paging_values(offset_arg, limit_arg)
 2200 
 2201         session = self.get_session(session)
 2202 
 2203         query = session.query(models.ProjectQuotas)
 2204         query = query.order_by(models.ProjectQuotas.created_at)
 2205         query = query.join(models.Project, models.ProjectQuotas.project)
 2206 
 2207         start = offset
 2208         end = offset + limit
 2209         LOG.debug('Retrieving from %s to %s', start, end)
 2210         total = query.count()
 2211         entities = query.offset(start).limit(limit).all()
 2212         LOG.debug('Number entities retrieved: %s out of %s',
 2213                   len(entities), total)
 2214 
 2215         if total <= 0 and not suppress_exception:
 2216             _raise_no_entities_found(self._do_entity_name())
 2217 
 2218         return entities, offset, limit, total
 2219 
 2220     def create_or_update_by_project_id(self, project_id,
 2221                                        parsed_project_quotas,
 2222                                        session=None):
 2223         """Create or update Project Quotas config for a project by project_id.
 2224 
 2225         :param project_id: ID of project whose quota config will be saved
 2226         :param parsed_project_quotas: Python dict with quota definition
 2227         :param session: SQLAlchemy session object.
 2228         :return: None
 2229         """
 2230         session = self.get_session(session)
 2231         query = session.query(models.ProjectQuotas)
 2232         query = query.filter_by(project_id=project_id)
 2233         try:
 2234             entity = query.one()
 2235         except sa_orm.exc.NoResultFound:
 2236             self.create_from(
 2237                 models.ProjectQuotas(project_id,
 2238                                      parsed_project_quotas),
 2239                 session=session)
 2240         else:
 2241             self._update_values(entity, parsed_project_quotas)
 2242             entity.save(session)
 2243 
 2244     def get_by_external_project_id(self, external_project_id,
 2245                                    suppress_exception=False, session=None):
 2246         """Return configured Project Quotas for a project by project_id.
 2247 
 2248         :param external_project_id: external ID of project to get quotas for
 2249         :param suppress_exception: when True, NotFound is not raised
 2250         :param session: SQLAlchemy session object.
 2251         :raises NotFound: if no quota config is found for the project
 2252         :return: None or Python dict of project quotas for project
 2253         """
 2254         session = self.get_session(session)
 2255         query = session.query(models.ProjectQuotas)
 2256         query = query.join(models.Project, models.ProjectQuotas.project)
 2257         query = query.filter(models.Project.external_id == external_project_id)
 2258         try:
 2259             entity = query.one()
 2260         except sa_orm.exc.NoResultFound:
 2261             if suppress_exception:
 2262                 return None
 2263             else:
 2264                 _raise_no_entities_found(self._do_entity_name())
 2265         return entity
 2266 
 2267     def delete_by_external_project_id(self, external_project_id,
 2268                                       suppress_exception=False, session=None):
 2269         """Remove configured Project Quotas for a project by project_id.
 2270 
 2271         :param external_project_id: external ID of project to delete quotas
 2272         :param suppress_exception: when True, NotFound is not raised
 2273         :param session: SQLAlchemy session object.
 2274         :raises NotFound: if no quota config is found for the project
 2275         :return: None
 2276         """
 2277 
 2278         session = self.get_session(session)
 2279         query = session.query(models.ProjectQuotas)
 2280         query = query.join(models.Project, models.ProjectQuotas.project)
 2281         query = query.filter(models.Project.external_id == external_project_id)
 2282         try:
 2283             entity = query.one()
 2284         except sa_orm.exc.NoResultFound:
 2285             if suppress_exception:
 2286                 return
 2287             else:
 2288                 _raise_no_entities_found(self._do_entity_name())
 2289         entity.delete(session=session)
 2290 
 2291 
 2292 class SecretStoresRepo(BaseRepo):
 2293     """Repository for the SecretStores entity.
 2294 
 2295     SecretStores entries are not soft delete. So there is no
 2296     need to have deleted=False filter in queries.
 2297     """
 2298 
 2299     def get_all(self, session=None):
 2300         """Get list of available secret stores.
 2301 
 2302         Status value is not used while getting complete list as
 2303         we will just maintain ACTIVE ones. No other state is used and
 2304         needed here.
 2305         :param session: SQLAlchemy session object.
 2306         :return: None
 2307         """
 2308         session = self.get_session(session)
 2309         query = session.query(models.SecretStores)
 2310         query.order_by(models.SecretStores.created_at.asc())
 2311         return query.all()
 2312 
 2313     def _do_entity_name(self):
 2314         """Sub-class hook: return entity name, such as for debugging."""
 2315         return "SecretStores"
 2316 
 2317     def _do_build_get_query(self, entity_id, external_project_id, session):
 2318         """Sub-class hook: build a retrieve query."""
 2319         return session.query(models.SecretStores).filter_by(
 2320             id=entity_id)
 2321 
 2322     def _do_validate(self, values):
 2323         """Sub-class hook: validate values."""
 2324         pass
 2325 
 2326 
 2327 class ProjectSecretStoreRepo(BaseRepo):
 2328     """Repository for the ProjectSecretStore entity.
 2329 
 2330     ProjectSecretStore entries are not soft delete. So there is no
 2331     need to have deleted=False filter in queries.
 2332     """
 2333 
 2334     def get_secret_store_for_project(self, project_id, external_project_id,
 2335                                      suppress_exception=False, session=None):
 2336         """Returns preferred secret store for a project if set.
 2337 
 2338         :param project_id: ID of project whose preferred secret store is set
 2339         :param external_project_id: external ID of project whose preferred
 2340                secret store is set
 2341         :param suppress_exception: when True, NotFound is not raised
 2342         :param session: SQLAlchemy session object.
 2343 
 2344         Will return preferred secret store by external project id if provided
 2345         otherwise uses barbican project identifier to lookup.
 2346 
 2347         Throws exception in case no preferred secret store is defined and
 2348         supporess_exception=False. If suppress_exception is True, then returns
 2349         None for no preferred secret store for a project found.
 2350         """
 2351         session = self.get_session(session)
 2352         if external_project_id is None:
 2353             query = session.query(models.ProjectSecretStore).filter_by(
 2354                 project_id=project_id)
 2355         else:
 2356             query = session.query(models.ProjectSecretStore)
 2357             query = query.join(models.Project,
 2358                                models.ProjectSecretStore.project)
 2359             query = query.filter(models.Project.external_id ==
 2360                                  external_project_id)
 2361         try:
 2362             entity = query.one()
 2363         except sa_orm.exc.NoResultFound:
 2364             LOG.info("No preferred secret store found for project = %s",
 2365                      project_id)
 2366             entity = None
 2367             if not suppress_exception:
 2368                 _raise_entity_not_found(self._do_entity_name(), project_id)
 2369         return entity
 2370 
 2371     def create_or_update_for_project(self, project_id, secret_store_id,
 2372                                      session=None):
 2373         """Create or update preferred secret store for a project.
 2374 
 2375         :param project_id: ID of project whose preferred secret store is set
 2376         :param secret_store_id: ID of secret store
 2377         :param session: SQLAlchemy session object.
 2378         :return: None
 2379 
 2380         If preferred secret store is not set for given project, then create
 2381         new preferred secret store setting for that project. If secret store
 2382         setting for project is already there, then it updates with given secret
 2383         store id.
 2384         """
 2385         session = self.get_session(session)
 2386         try:
 2387             entity = self.get_secret_store_for_project(project_id, None,
 2388                                                        session=session)
 2389         except exception.NotFound:
 2390             entity = self.create_from(
 2391                 models.ProjectSecretStore(project_id, secret_store_id),
 2392                 session=session)
 2393         else:
 2394             entity.secret_store_id = secret_store_id
 2395             entity.save(session)
 2396         return entity
 2397 
 2398     def get_count_by_secret_store(self, secret_store_id, session=None):
 2399         """Gets count of projects mapped to a given secret store.
 2400 
 2401         :param secret_store_id: id of secret stores entity
 2402         :param session: existing db session reference. If None, gets session.
 2403         :return: an number 0 or greater
 2404 
 2405         This method is supposed to provide count of projects which are
 2406         currently set to use input secret store as their preferred store. This
 2407         is used when existing secret store configuration is removed and
 2408         validation is done to make sure that there are no projects using it as
 2409         preferred secret store.
 2410         """
 2411         session = self.get_session(session)
 2412         query = session.query(models.ProjectSecretStore).filter_by(
 2413             secret_store_id=secret_store_id)
 2414         return query.count()
 2415 
 2416     def _do_entity_name(self):
 2417         """Sub-class hook: return entity name, such as for debugging."""
 2418         return "ProjectSecretStore"
 2419 
 2420     def _do_build_get_query(self, entity_id, external_project_id, session):
 2421         """Sub-class hook: build a retrieve query."""
 2422         return session.query(models.ProjectSecretStore).filter_by(
 2423             id=entity_id)
 2424 
 2425     def _do_validate(self, values):
 2426         """Sub-class hook: validate values."""
 2427         pass
 2428 
 2429     def _build_get_project_entities_query(self, project_id, session):
 2430         """Builds query for getting preferred secret stores list for a project.
 2431 
 2432         :param project_id: id of barbican project entity
 2433         :param session: existing db session reference.
 2434         """
 2435         return session.query(models.ProjectSecretStore).filter_by(
 2436             project_id=project_id)
 2437 
 2438 
 2439 class SecretConsumerRepo(BaseRepo):
 2440     """Repository for the SecretConsumer entity."""
 2441 
 2442     def get_by_secret_id(self, secret_id,
 2443                          offset_arg=None, limit_arg=None,
 2444                          suppress_exception=False, session=None):
 2445         """Returns a list of SecretConsumers for a specific secret_id
 2446 
 2447         The list is ordered by the date they were created at and paged
 2448         based on the offset and limit fields.
 2449         """
 2450 
 2451         offset, limit = clean_paging_values(offset_arg, limit_arg)
 2452 
 2453         session = self.get_session(session)
 2454 
 2455         query = session.query(models.SecretConsumerMetadatum)
 2456         query = query.order_by(models.SecretConsumerMetadatum.created_at)
 2457         query = query.filter_by(deleted=False)
 2458         query = query.filter(
 2459             models.SecretConsumerMetadatum.secret_id == secret_id
 2460         )
 2461 
 2462         start = offset
 2463         end = offset + limit
 2464         LOG.debug('Retrieving from %s to %s', start, end)
 2465         total = query.count()
 2466         entities = query.offset(start).limit(limit).all()
 2467         LOG.debug('Number entities retrieved: %s out of %s',
 2468                   len(entities), total
 2469                   )
 2470 
 2471         if total <= 0 and not suppress_exception:
 2472             _raise_no_entities_found(self._do_entity_name())
 2473 
 2474         return entities, offset, limit, total
 2475 
 2476     def get_by_resource_id(self, resource_id,
 2477                            offset_arg=None, limit_arg=None,
 2478                            suppress_exception=False, session=None):
 2479         """Returns a list of SecretConsumers for a specific resource_id
 2480 
 2481         The list is ordered by the date they were created at and paged
 2482         based on the offset and limit fields.
 2483         """
 2484 
 2485         offset, limit = clean_paging_values(offset_arg, limit_arg)
 2486 
 2487         session = self.get_session(session)
 2488 
 2489         query = session.query(models.SecretConsumerMetadatum)
 2490         query = query.order_by(models.SecretConsumerMetadatum.created_at)
 2491         query = query.filter_by(deleted=False)
 2492         query = query.filter(
 2493             models.SecretConsumerMetadatum.resource_id == resource_id
 2494         )
 2495 
 2496         start = offset
 2497         end = offset + limit
 2498         LOG.debug('Retrieving from %s to %s', start, end)
 2499         total = query.count()
 2500         entities = query.offset(start).limit(limit).all()
 2501         LOG.debug('Number entities retrieved: %s out of %s',
 2502                   len(entities), total
 2503                   )
 2504 
 2505         if total <= 0 and not suppress_exception:
 2506             _raise_no_entities_found(self._do_entity_name())
 2507 
 2508         return entities, offset, limit, total
 2509 
 2510     def get_by_values(self, secret_id, resource_id, suppress_exception=False,
 2511                       show_deleted=False, session=None):
 2512         session = self.get_session(session)
 2513 
 2514         try:
 2515             query = session.query(models.SecretConsumerMetadatum)
 2516             query = query.filter_by(
 2517                 secret_id=secret_id,
 2518                 resource_id=resource_id,
 2519             )
 2520 
 2521             if not show_deleted:
 2522                 query.filter_by(deleted=False)
 2523             consumer = query.one()
 2524         except sa_orm.exc.NoResultFound:
 2525             consumer = None
 2526             if not suppress_exception:
 2527                 raise exception.NotFound(
 2528                     u._("Could not find {entity_name}").format(
 2529                         entity_name=self._do_entity_name()))
 2530 
 2531         return consumer
 2532 
 2533     def create_or_update_from(self, new_consumer, secret, session=None):
 2534         session = self.get_session(session)
 2535         try:
 2536             secret.updated_at = timeutils.utcnow()
 2537             secret.consumers.append(new_consumer)
 2538             secret.save(session=session)
 2539         except db_exc.DBDuplicateEntry:
 2540             session.rollback()  # We know consumer already exists.
 2541 
 2542             # This operation is idempotent, so log this and move on
 2543             LOG.debug(
 2544                 "Consumer with resource_id %s already exists for secret %s...",
 2545                 new_consumer.resource_id, new_consumer.secret_id
 2546             )
 2547             # Get the existing entry and reuse it by clearing the deleted flags
 2548             existing_consumer = self.get_by_values(
 2549                 new_consumer.secret_id,
 2550                 new_consumer.resource_id,
 2551                 show_deleted=True
 2552             )
 2553             existing_consumer.deleted = False
 2554             existing_consumer.deleted_at = None
 2555             # We are not concerned about timing here -- set only, no reads
 2556             existing_consumer.save()
 2557 
 2558     def _do_entity_name(self):
 2559         """Sub-class hook: return entity name, such as for debugging."""
 2560         return "SecretConsumer"
 2561 
 2562     def _do_build_get_query(self, entity_id, external_project_id, session):
 2563         """Sub-class hook: build a retrieve query."""
 2564         query = session.query(models.SecretConsumerMetadatum)
 2565         return query.filter_by(id=entity_id, deleted=False)
 2566 
 2567     def _do_validate(self, values):
 2568         """Sub-class hook: validate values."""
 2569         pass
 2570 
 2571     def _build_get_project_entities_query(self, project_id, session):
 2572         """Builds query for retrieving consumers associated with given project
 2573 
 2574         :param project_id: id of barbican project entity
 2575         :param session: existing db session reference.
 2576         """
 2577         query = session.query(
 2578             models.SecretConsumerMetadatum).filter_by(deleted=False)
 2579         query = query.filter(
 2580             models.SecretConsumerMetadatum.project_id == project_id)
 2581 
 2582         return query
 2583 
 2584 
 2585 def get_ca_repository():
 2586     """Returns a singleton Secret repository instance."""
 2587     global _CA_REPOSITORY
 2588     return _get_repository(_CA_REPOSITORY, CertificateAuthorityRepo)
 2589 
 2590 
 2591 def get_container_acl_repository():
 2592     """Returns a singleton Container ACL repository instance."""
 2593     global _CONTAINER_ACL_REPOSITORY
 2594     return _get_repository(_CONTAINER_ACL_REPOSITORY, ContainerACLRepo)
 2595 
 2596 
 2597 def get_container_consumer_repository():
 2598     """Returns a singleton Container Consumer repository instance."""
 2599     global _CONTAINER_CONSUMER_REPOSITORY
 2600     return _get_repository(_CONTAINER_CONSUMER_REPOSITORY,
 2601                            ContainerConsumerRepo)
 2602 
 2603 
 2604 def get_container_repository():
 2605     """Returns a singleton Container repository instance."""
 2606     global _CONTAINER_REPOSITORY
 2607     return _get_repository(_CONTAINER_REPOSITORY, ContainerRepo)
 2608 
 2609 
 2610 def get_container_secret_repository():
 2611     """Returns a singleton Container-Secret repository instance."""
 2612     global _CONTAINER_SECRET_REPOSITORY
 2613     return _get_repository(_CONTAINER_SECRET_REPOSITORY, ContainerSecretRepo)
 2614 
 2615 
 2616 def get_container_acl_user_repository():
 2617     """Returns a singleton Container-ACL-User repository instance."""
 2618     global _CONTAINER_ACL_USER_REPOSITORY
 2619     return _get_repository(_CONTAINER_ACL_USER_REPOSITORY,
 2620                            ContainerACLUserRepo)
 2621 
 2622 
 2623 def get_encrypted_datum_repository():
 2624     """Returns a singleton Encrypted Datum repository instance."""
 2625     global _ENCRYPTED_DATUM_REPOSITORY
 2626     return _get_repository(_ENCRYPTED_DATUM_REPOSITORY, EncryptedDatumRepo)
 2627 
 2628 
 2629 def get_kek_datum_repository():
 2630     """Returns a singleton KEK Datum repository instance."""
 2631     global _KEK_DATUM_REPOSITORY
 2632     return _get_repository(_KEK_DATUM_REPOSITORY, KEKDatumRepo)
 2633 
 2634 
 2635 def get_order_plugin_meta_repository():
 2636     """Returns a singleton Order-Plugin meta repository instance."""
 2637     global _ORDER_PLUGIN_META_REPOSITORY
 2638     return _get_repository(_ORDER_PLUGIN_META_REPOSITORY,
 2639                            OrderPluginMetadatumRepo)
 2640 
 2641 
 2642 def get_order_barbican_meta_repository():
 2643     """Returns a singleton Order-Barbican meta repository instance."""
 2644     global _ORDER_BARBICAN_META_REPOSITORY
 2645     return _get_repository(_ORDER_BARBICAN_META_REPOSITORY,
 2646                            OrderBarbicanMetadatumRepo)
 2647 
 2648 
 2649 def get_order_repository():
 2650     """Returns a singleton Order repository instance."""
 2651     global _ORDER_REPOSITORY
 2652     return _get_repository(_ORDER_REPOSITORY, OrderRepo)
 2653 
 2654 
 2655 def get_order_retry_tasks_repository():
 2656     """Returns a singleton OrderRetryTask repository instance."""
 2657     global _ORDER_RETRY_TASK_REPOSITORY
 2658     return _get_repository(_ORDER_RETRY_TASK_REPOSITORY, OrderRetryTaskRepo)
 2659 
 2660 
 2661 def get_preferred_ca_repository():
 2662     """Returns a singleton Secret repository instance."""
 2663     global _PREFERRED_CA_REPOSITORY
 2664     return _get_repository(_PREFERRED_CA_REPOSITORY,
 2665                            PreferredCertificateAuthorityRepo)
 2666 
 2667 
 2668 def get_project_repository():
 2669     """Returns a singleton Project repository instance."""
 2670     global _PROJECT_REPOSITORY
 2671     return _get_repository(_PROJECT_REPOSITORY, ProjectRepo)
 2672 
 2673 
 2674 def get_project_ca_repository():
 2675     """Returns a singleton Secret repository instance."""
 2676     global _PROJECT_CA_REPOSITORY
 2677     return _get_repository(_PROJECT_CA_REPOSITORY,
 2678                            ProjectCertificateAuthorityRepo)
 2679 
 2680 
 2681 def get_project_quotas_repository():
 2682     """Returns a singleton Project Quotas repository instance."""
 2683     global _PROJECT_QUOTAS_REPOSITORY
 2684     return _get_repository(_PROJECT_QUOTAS_REPOSITORY,
 2685                            ProjectQuotasRepo)
 2686 
 2687 
 2688 def get_secret_acl_repository():
 2689     """Returns a singleton Secret ACL repository instance."""
 2690     global _SECRET_ACL_REPOSITORY
 2691     return _get_repository(_SECRET_ACL_REPOSITORY, SecretACLRepo)
 2692 
 2693 
 2694 def get_secret_acl_user_repository():
 2695     """Returns a singleton Secret-ACL-User repository instance."""
 2696     global _SECRET_ACL_USER_REPOSITORY
 2697     return _get_repository(_SECRET_ACL_USER_REPOSITORY, SecretACLUserRepo)
 2698 
 2699 
 2700 def get_secret_meta_repository():
 2701     """Returns a singleton Secret meta repository instance."""
 2702     global _SECRET_META_REPOSITORY
 2703     return _get_repository(_SECRET_META_REPOSITORY, SecretStoreMetadatumRepo)
 2704 
 2705 
 2706 def get_secret_user_meta_repository():
 2707     """Returns a singleton Secret user meta repository instance."""
 2708     global _SECRET_USER_META_REPOSITORY
 2709     return _get_repository(_SECRET_USER_META_REPOSITORY,
 2710                            SecretUserMetadatumRepo)
 2711 
 2712 
 2713 def get_secret_repository():
 2714     """Returns a singleton Secret repository instance."""
 2715     global _SECRET_REPOSITORY
 2716     return _get_repository(_SECRET_REPOSITORY, SecretRepo)
 2717 
 2718 
 2719 def get_transport_key_repository():
 2720     """Returns a singleton Transport Key repository instance."""
 2721     global _TRANSPORT_KEY_REPOSITORY
 2722     return _get_repository(_TRANSPORT_KEY_REPOSITORY, TransportKeyRepo)
 2723 
 2724 
 2725 def get_secret_stores_repository():
 2726     """Returns a singleton Secret Stores repository instance."""
 2727     global _SECRET_STORES_REPOSITORY
 2728     return _get_repository(_SECRET_STORES_REPOSITORY, SecretStoresRepo)
 2729 
 2730 
 2731 def get_project_secret_store_repository():
 2732     """Returns a singleton Project Secret Store repository instance."""
 2733     global _PROJECT_SECRET_STORE_REPOSITORY
 2734     return _get_repository(_PROJECT_SECRET_STORE_REPOSITORY,
 2735                            ProjectSecretStoreRepo)
 2736 
 2737 
 2738 def get_secret_consumer_repository():
 2739     """Returns a singleton Secret Consumer repository instance."""
 2740     global _SECRET_CONSUMER_REPOSITORY
 2741     return _get_repository(_SECRET_CONSUMER_REPOSITORY,
 2742                            SecretConsumerRepo)
 2743 
 2744 
 2745 def _get_repository(global_ref, repo_class):
 2746     if not global_ref:
 2747         global_ref = repo_class()
 2748     return global_ref
 2749 
 2750 
 2751 def _raise_entity_not_found(entity_name, entity_id):
 2752     raise exception.NotFound(u._("No {entity} found with ID {id}").format(
 2753         entity=entity_name,
 2754         id=entity_id))
 2755 
 2756 
 2757 def _raise_entity_id_not_found(entity_id):
 2758     raise exception.NotFound(u._("Entity ID {entity_id} not "
 2759                                  "found").format(entity_id=entity_id))
 2760 
 2761 
 2762 def _raise_no_entities_found(entity_name):
 2763     raise exception.NotFound(
 2764         u._("No entities of type {entity_name} found").format(
 2765             entity_name=entity_name))