"Fossies" - the Fresh Open Source Software Archive

Member "barbican-12.0.0/barbican/api/controllers/containers.py" (14 Apr 2021, 12936 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. For more information about "containers.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 11.0.0_vs_12.0.0.

    1 #  Licensed under the Apache License, Version 2.0 (the "License"); you may
    2 #  not use this file except in compliance with the License. You may obtain
    3 #  a copy of the License at
    4 #
    5 #       http://www.apache.org/licenses/LICENSE-2.0
    6 #
    7 #  Unless required by applicable law or agreed to in writing, software
    8 #  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    9 #  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   10 #  License for the specific language governing permissions and limitations
   11 #  under the License.
   12 
   13 import pecan
   14 
   15 from barbican import api
   16 from barbican.api import controllers
   17 from barbican.api.controllers import acls
   18 from barbican.api.controllers import consumers
   19 from barbican.common import exception
   20 from barbican.common import hrefs
   21 from barbican.common import quota
   22 from barbican.common import resources as res
   23 from barbican.common import utils
   24 from barbican.common import validators
   25 from barbican import i18n as u
   26 from barbican.model import models
   27 from barbican.model import repositories as repo
   28 
   29 LOG = utils.getLogger(__name__)
   30 
   31 CONTAINER_GET = 'container:get'
   32 
   33 
   34 def container_not_found():
   35     """Throw exception indicating container not found."""
   36     pecan.abort(404, u._('Secrets container not found.'))
   37 
   38 
   39 def invalid_container_id():
   40     """Throw exception indicating container id is invalid."""
   41     pecan.abort(404, u._('Not Found. Provided container id is invalid.'))
   42 
   43 
   44 class ContainerController(controllers.ACLMixin):
   45     """Handles Container entity retrieval and deletion requests."""
   46 
   47     def __init__(self, container):
   48         self.container = container
   49         self.container_id = container.id
   50         self.consumer_repo = repo.get_container_consumer_repository()
   51         self.container_repo = repo.get_container_repository()
   52         self.validator = validators.ContainerValidator()
   53         self.consumers = consumers.ContainerConsumersController(
   54             self.container_id)
   55         self.acl = acls.ContainerACLsController(self.container)
   56 
   57     def get_acl_tuple(self, req, **kwargs):
   58         d = self.get_acl_dict_for_user(req, self.container.container_acls)
   59         d['project_id'] = self.container.project.external_id
   60         d['creator_id'] = self.container.creator_id
   61         return 'container', d
   62 
   63     @pecan.expose(generic=True, template='json')
   64     def index(self, **kwargs):
   65         pecan.abort(405)  # HTTP 405 Method Not Allowed as default
   66 
   67     @index.when(method='GET', template='json')
   68     @controllers.handle_exceptions(u._('Container retrieval'))
   69     @controllers.enforce_rbac(CONTAINER_GET)
   70     def on_get(self, external_project_id):
   71         dict_fields = self.container.to_dict_fields()
   72 
   73         for secret_ref in dict_fields['secret_refs']:
   74             hrefs.convert_to_hrefs(secret_ref)
   75 
   76         LOG.info('Retrieved container for project: %s',
   77                  external_project_id)
   78         return hrefs.convert_to_hrefs(
   79             hrefs.convert_to_hrefs(dict_fields)
   80         )
   81 
   82     @index.when(method='DELETE')
   83     @utils.allow_all_content_types
   84     @controllers.handle_exceptions(u._('Container deletion'))
   85     @controllers.enforce_rbac('container:delete')
   86     def on_delete(self, external_project_id, **kwargs):
   87         container_consumers = self.consumer_repo.get_by_container_id(
   88             self.container_id,
   89             suppress_exception=True
   90         )
   91         try:
   92             self.container_repo.delete_entity_by_id(
   93                 entity_id=self.container_id,
   94                 external_project_id=external_project_id
   95             )
   96         except exception.NotFound:
   97             LOG.exception('Problem deleting container')
   98             container_not_found()
   99 
  100         LOG.info('Deleted container for project: %s',
  101                  external_project_id)
  102 
  103         for consumer in container_consumers[0]:
  104             try:
  105                 self.consumer_repo.delete_entity_by_id(
  106                     consumer.id, external_project_id)
  107             except exception.NotFound:  # nosec
  108                 pass
  109 
  110 
  111 class ContainersController(controllers.ACLMixin):
  112     """Handles Container creation requests."""
  113 
  114     def __init__(self):
  115         self.consumer_repo = repo.get_container_consumer_repository()
  116         self.container_repo = repo.get_container_repository()
  117         self.secret_repo = repo.get_secret_repository()
  118         self.validator = validators.ContainerValidator()
  119         self.quota_enforcer = quota.QuotaEnforcer('containers',
  120                                                   self.container_repo)
  121 
  122     @pecan.expose()
  123     def _lookup(self, container_id, *remainder):
  124         if not utils.validate_id_is_uuid(container_id):
  125             invalid_container_id()
  126         container = self.container_repo.get_container_by_id(
  127             entity_id=container_id, suppress_exception=True)
  128         if not container:
  129             container_not_found()
  130 
  131         if len(remainder) > 0 and remainder[0] == 'secrets':
  132             return ContainersSecretsController(container), ()
  133 
  134         return ContainerController(container), remainder
  135 
  136     @pecan.expose(generic=True, template='json')
  137     def index(self, **kwargs):
  138         pecan.abort(405)  # HTTP 405 Method Not Allowed as default
  139 
  140     @index.when(method='GET', template='json')
  141     @controllers.handle_exceptions(u._('Containers(s) retrieval'))
  142     @controllers.enforce_rbac('containers:get')
  143     def on_get(self, project_id, **kw):
  144         LOG.debug('Start containers on_get for project-ID %s:', project_id)
  145 
  146         result = self.container_repo.get_by_create_date(
  147             project_id,
  148             offset_arg=kw.get('offset', 0),
  149             limit_arg=kw.get('limit', None),
  150             name_arg=kw.get('name', None),
  151             type_arg=kw.get('type', None),
  152             suppress_exception=True
  153         )
  154 
  155         containers, offset, limit, total = result
  156 
  157         if not containers:
  158             resp_ctrs_overall = {'containers': [], 'total': total}
  159         else:
  160             resp_ctrs = [
  161                 hrefs.convert_to_hrefs(c.to_dict_fields())
  162                 for c in containers
  163             ]
  164 
  165             for ctr in resp_ctrs:
  166                 for secret_ref in ctr.get('secret_refs', []):
  167                     hrefs.convert_to_hrefs(secret_ref)
  168 
  169             resp_ctrs_overall = hrefs.add_nav_hrefs(
  170                 'containers',
  171                 offset,
  172                 limit,
  173                 total,
  174                 {'containers': resp_ctrs}
  175             )
  176             resp_ctrs_overall.update({'total': total})
  177 
  178         LOG.info('Retrieved container list for project: %s', project_id)
  179         return resp_ctrs_overall
  180 
  181     @index.when(method='POST', template='json')
  182     @controllers.handle_exceptions(u._('Container creation'))
  183     @controllers.enforce_rbac('containers:post')
  184     @controllers.enforce_content_types(['application/json'])
  185     def on_post(self, external_project_id, **kwargs):
  186 
  187         project = res.get_or_create_project(external_project_id)
  188 
  189         data = api.load_body(pecan.request, validator=self.validator)
  190         ctxt = controllers._get_barbican_context(pecan.request)
  191         if ctxt:  # in authenticated pipleline case, always use auth token user
  192             data['creator_id'] = ctxt.user
  193 
  194         self.quota_enforcer.enforce(project)
  195 
  196         LOG.debug('Start on_post...%s', data)
  197 
  198         new_container = models.Container(data)
  199         new_container.project_id = project.id
  200 
  201         # TODO(hgedikli): performance optimizations
  202         for secret_ref in new_container.container_secrets:
  203             secret = self.secret_repo.get(
  204                 entity_id=secret_ref.secret_id,
  205                 external_project_id=external_project_id,
  206                 suppress_exception=True)
  207             if not secret:
  208                 # This only partially localizes the error message and
  209                 # doesn't localize secret_ref.name.
  210                 pecan.abort(
  211                     404,
  212                     u._("Secret provided for '{secret_name}' doesn't "
  213                         "exist.").format(secret_name=secret_ref.name)
  214                 )
  215 
  216         self.container_repo.create_from(new_container)
  217 
  218         url = hrefs.convert_container_to_href(new_container.id)
  219 
  220         pecan.response.status = 201
  221         pecan.response.headers['Location'] = url
  222         LOG.info('Created a container for project: %s',
  223                  external_project_id)
  224 
  225         return {'container_ref': url}
  226 
  227 
  228 class ContainersSecretsController(controllers.ACLMixin):
  229     """Handles ContainerSecret creation and deletion requests."""
  230 
  231     def __init__(self, container):
  232         LOG.debug('=== Creating ContainerSecretsController ===')
  233         self.container = container
  234         self.container_secret_repo = repo.get_container_secret_repository()
  235         self.secret_repo = repo.get_secret_repository()
  236         self.validator = validators.ContainerSecretValidator()
  237 
  238     def get_acl_tuple(self, req, **kwargs):
  239         acl = self.get_acl_dict_for_user(req, self.container.container_acls)
  240         acl['project_id'] = self.container.project.external_id
  241         acl['creator_id'] = self.container.creator_id
  242         return ('container', acl)
  243 
  244     @pecan.expose(generic=True)
  245     def index(self, **kwargs):
  246         pecan.abort(405)  # HTTP 405 Method Not Allowed as default
  247 
  248     @index.when(method='POST', template='json')
  249     @controllers.handle_exceptions(u._('Container Secret creation'))
  250     @controllers.enforce_rbac('container_secret:post')
  251     @controllers.enforce_content_types(['application/json'])
  252     def on_post(self, external_project_id, **kwargs):
  253         """Handles adding an existing secret to an existing container."""
  254 
  255         if self.container.type != 'generic':
  256             pecan.abort(400, u._("Only 'generic' containers can be modified."))
  257 
  258         data = api.load_body(pecan.request, validator=self.validator)
  259 
  260         name = data.get('name')
  261         secret_ref = data.get('secret_ref')
  262         secret_id = hrefs.get_secret_id_from_ref(secret_ref)
  263 
  264         secret = self.secret_repo.get(
  265             entity_id=secret_id,
  266             external_project_id=external_project_id,
  267             suppress_exception=True)
  268         if not secret:
  269             pecan.abort(404, u._("Secret provided doesn't exist."))
  270 
  271         found_container_secrets = list(
  272             filter(lambda cs: cs.secret_id == secret_id and cs.name == name,
  273                    self.container.container_secrets)
  274         )
  275 
  276         if found_container_secrets:
  277             pecan.abort(409, u._('Conflict. A secret with that name and ID is '
  278                                  'already stored in this container. The same '
  279                                  'secret can exist in a container as long as '
  280                                  'the name is unique.'))
  281 
  282         LOG.debug('Start container secret on_post...%s', secret_ref)
  283         new_container_secret = models.ContainerSecret()
  284         new_container_secret.container_id = self.container.id
  285         new_container_secret.name = name
  286         new_container_secret.secret_id = secret_id
  287         self.container_secret_repo.save(new_container_secret)
  288 
  289         url = hrefs.convert_container_to_href(self.container.id)
  290         LOG.debug('URI to container is %s', url)
  291 
  292         pecan.response.status = 201
  293         pecan.response.headers['Location'] = url
  294         LOG.info('Created a container secret for project: %s',
  295                  external_project_id)
  296 
  297         return {'container_ref': url}
  298 
  299     @index.when(method='DELETE')
  300     @utils.allow_all_content_types
  301     @controllers.handle_exceptions(u._('Container Secret deletion'))
  302     @controllers.enforce_rbac('container_secret:delete')
  303     def on_delete(self, external_project_id, **kwargs):
  304         """Handles removing a secret reference from an existing container."""
  305 
  306         data = api.load_body(pecan.request, validator=self.validator)
  307 
  308         name = data.get('name')
  309         secret_ref = data.get('secret_ref')
  310         secret_id = hrefs.get_secret_id_from_ref(secret_ref)
  311 
  312         secret = self.secret_repo.get(
  313             entity_id=secret_id,
  314             external_project_id=external_project_id,
  315             suppress_exception=True)
  316         if not secret:
  317             pecan.abort(404, u._("Secret '{secret_name}' with reference "
  318                                  "'{secret_ref}' doesn't exist.").format(
  319                                      secret_name=name, secret_ref=secret_ref))
  320 
  321         found_container_secrets = list(
  322             filter(lambda cs: cs.secret_id == secret_id and cs.name == name,
  323                    self.container.container_secrets)
  324         )
  325 
  326         if not found_container_secrets:
  327             pecan.abort(404, u._('Secret provided is not in the container'))
  328 
  329         for container_secret in found_container_secrets:
  330             self.container_secret_repo.delete_entity_by_id(
  331                 container_secret.id, external_project_id)
  332 
  333         pecan.response.status = 204
  334         LOG.info('Deleted container secret for project: %s',
  335                  external_project_id)