예제 #1
0
 def show(self, request, id, **kwargs):
     """Returns detailed information about the requested entity."""
     try:
         # NOTE(salvatore-orlando): The following ensures that fields
         # which are needed for authZ policy validation are not stripped
         # away by the plugin before returning.
         field_list, added_fields = self._do_field_list(
             api_common.list_args(request, "fields"))
         parent_id = kwargs.get(self._parent_id_name)
         # Ensure policy engine is initialized
         policy.init()
         return {
             self._resource:
             self._view(request.context,
                        self._item(request,
                                   id,
                                   do_authz=True,
                                   field_list=field_list,
                                   parent_id=parent_id),
                        fields_to_strip=added_fields)
         }
     except oslo_policy.PolicyNotAuthorized:
         # To avoid giving away information, pretend that it
         # doesn't exist
         msg = _('The resource could not be found.')
         raise webob.exc.HTTPNotFound(msg)
예제 #2
0
 def _handle_action(request, id, **kwargs):
     arg_list = [request.context, id]
     # Ensure policy engine is initialized
     policy.init()
     # Fetch the resource and verify if the user can access it
     try:
         parent_id = kwargs.get(self._parent_id_name)
         resource = self._item(request,
                               id,
                               do_authz=True,
                               field_list=None,
                               parent_id=parent_id)
     except oslo_policy.PolicyNotAuthorized:
         msg = _('The resource could not be found.')
         raise webob.exc.HTTPNotFound(msg)
     body = kwargs.pop('body', None)
     # Explicit comparison with None to distinguish from {}
     if body is not None:
         arg_list.append(body)
     # It is ok to raise a 403 because accessibility to the
     # object was checked earlier in this method
     policy.enforce(request.context,
                    name,
                    resource,
                    pluralized=self._collection)
     ret_value = getattr(self._plugin, name)(*arg_list, **kwargs)
     # It is simply impossible to predict whether one of this
     # actions alters resource usage. For instance a tenant port
     # is created when a router interface is added. Therefore it is
     # important to mark as dirty resources whose counters have
     # been altered by this operation
     resource_registry.set_resources_dirty(request.context)
     return ret_value
예제 #3
0
    def _delete(self, request, id, **kwargs):
        action = self._plugin_handlers[self.DELETE]

        # Check authz
        policy.init()
        parent_id = kwargs.get(self._parent_id_name)
        obj = self._item(request, id, parent_id=parent_id)
        try:
            policy.enforce(request.context,
                           action,
                           obj,
                           pluralized=self._collection)
        except oslo_policy.PolicyNotAuthorized:
            # To avoid giving away information, pretend that it
            # doesn't exist
            msg = _('The resource could not be found.')
            raise webob.exc.HTTPNotFound(msg)

        obj_deleter = getattr(self._plugin, action)
        obj_deleter(request.context, id, **kwargs)
        # A delete operation usually alters resource usage, so mark affected
        # usage trackers as dirty
        resource_registry.set_resources_dirty(request.context)
        notifier_method = self._resource + '.delete.end'
        result = {self._resource: self._view(request.context, obj)}
        notifier_payload = {self._resource + '_id': id}
        notifier_payload.update(result)
        self._notifier.info(request.context, notifier_method, notifier_payload)
        registry.notify(self._resource,
                        events.BEFORE_RESPONSE,
                        self,
                        context=request.context,
                        data=result,
                        method_name=notifier_method,
                        action=action,
                        original={})
예제 #4
0
    def _update(self, request, id, body, **kwargs):
        body = Controller.prepare_request_body(request.context,
                                               body,
                                               False,
                                               self._resource,
                                               self._attr_info,
                                               allow_bulk=self._allow_bulk)
        action = self._plugin_handlers[self.UPDATE]
        # Load object to check authz
        # but pass only attributes in the original body and required
        # by the policy engine to the policy 'brain'
        field_list = [
            name for (name, value) in six.iteritems(self._attr_info)
            if (value.get('required_by_policy') or value.get('primary_key')
                or 'default' not in value)
        ]
        # Ensure policy engine is initialized
        policy.init()
        parent_id = kwargs.get(self._parent_id_name)
        orig_obj = self._item(request,
                              id,
                              field_list=field_list,
                              parent_id=parent_id)
        orig_object_copy = copy.copy(orig_obj)
        orig_obj.update(body[self._resource])
        # Make a list of attributes to be updated to inform the policy engine
        # which attributes are set explicitly so that it can distinguish them
        # from the ones that are set to their default values.
        orig_obj[n_const.ATTRIBUTES_TO_UPDATE] = body[self._resource].keys()
        try:
            policy.enforce(request.context,
                           action,
                           orig_obj,
                           pluralized=self._collection)
        except oslo_policy.PolicyNotAuthorized:
            with excutils.save_and_reraise_exception() as ctxt:
                # If a tenant is modifying its own object, it's safe to return
                # a 403. Otherwise, pretend that it doesn't exist to avoid
                # giving away information.
                orig_obj_tenant_id = orig_obj.get("tenant_id")
                if (request.context.tenant_id != orig_obj_tenant_id
                        or orig_obj_tenant_id is None):
                    ctxt.reraise = False
            msg = _('The resource could not be found.')
            raise webob.exc.HTTPNotFound(msg)

        obj_updater = getattr(self._plugin, action)
        kwargs = {self._resource: body}
        if parent_id:
            kwargs[self._parent_id_name] = parent_id
        obj = obj_updater(request.context, id, **kwargs)
        # Usually an update operation does not alter resource usage, but as
        # there might be side effects it might be worth checking for changes
        # in resource usage here as well (e.g: a tenant port is created when a
        # router interface is added)
        resource_registry.set_resources_dirty(request.context)

        result = {self._resource: self._view(request.context, obj)}
        notifier_method = self._resource + '.update.end'
        self._notifier.info(request.context, notifier_method, result)
        registry.notify(self._resource,
                        events.BEFORE_RESPONSE,
                        self,
                        context=request.context,
                        data=result,
                        method_name=notifier_method,
                        action=action,
                        original=orig_object_copy)
        return result
예제 #5
0
    def _create(self, request, body, **kwargs):
        """Creates a new instance of the requested entity."""
        parent_id = kwargs.get(self._parent_id_name)
        body = Controller.prepare_request_body(request.context,
                                               body,
                                               True,
                                               self._resource,
                                               self._attr_info,
                                               allow_bulk=self._allow_bulk)
        action = self._plugin_handlers[self.CREATE]
        # Check authz
        if self._collection in body:
            # Have to account for bulk create
            items = body[self._collection]
        else:
            items = [body]
        # Ensure policy engine is initialized
        policy.init()
        # Store requested resource amounts grouping them by tenant
        # This won't work with multiple resources. However because of the
        # current structure of this controller there will hardly be more than
        # one resource for which reservations are being made
        request_deltas = collections.defaultdict(int)
        for item in items:
            self._validate_network_tenant_ownership(request,
                                                    item[self._resource])
            policy.enforce(request.context,
                           action,
                           item[self._resource],
                           pluralized=self._collection)
            if 'tenant_id' not in item[self._resource]:
                # no tenant_id - no quota check
                continue
            tenant_id = item[self._resource]['tenant_id']
            request_deltas[tenant_id] += 1
        # Quota enforcement
        reservations = []
        try:
            for (tenant, delta) in request_deltas.items():
                reservation = quota.QUOTAS.make_reservation(
                    request.context, tenant, {self._resource: delta},
                    self._plugin)
                reservations.append(reservation)
        except n_exc.QuotaResourceUnknown as e:
            # We don't want to quota this resource
            LOG.debug(e)

        def notify(create_result):
            # Ensure usage trackers for all resources affected by this API
            # operation are marked as dirty
            with request.context.session.begin():
                # Commit the reservation(s)
                for reservation in reservations:
                    quota.QUOTAS.commit_reservation(request.context,
                                                    reservation.reservation_id)
                resource_registry.set_resources_dirty(request.context)

            notifier_method = self._resource + '.create.end'
            self._notifier.info(request.context, notifier_method,
                                create_result)
            registry.notify(self._resource,
                            events.BEFORE_RESPONSE,
                            self,
                            context=request.context,
                            data=create_result,
                            method_name=notifier_method,
                            collection=self._collection,
                            action=action,
                            original={})
            return create_result

        def do_create(body, bulk=False, emulated=False):
            kwargs = {self._parent_id_name: parent_id} if parent_id else {}
            if bulk and not emulated:
                obj_creator = getattr(self._plugin, "%s_bulk" % action)
            else:
                obj_creator = getattr(self._plugin, action)
            try:
                if emulated:
                    return self._emulate_bulk_create(obj_creator, request,
                                                     body, parent_id)
                else:
                    if self._collection in body:
                        # This is weird but fixing it requires changes to the
                        # plugin interface
                        kwargs.update({self._collection: body})
                    else:
                        kwargs.update({self._resource: body})
                    return obj_creator(request.context, **kwargs)
            except Exception:
                # In case of failure the plugin will always raise an
                # exception. Cancel the reservation
                with excutils.save_and_reraise_exception():
                    for reservation in reservations:
                        quota.QUOTAS.cancel_reservation(
                            request.context, reservation.reservation_id)

        if self._collection in body and self._native_bulk:
            # plugin does atomic bulk create operations
            objs = do_create(body, bulk=True)
            # Use first element of list to discriminate attributes which
            # should be removed because of authZ policies
            fields_to_strip = self._exclude_attributes_by_policy(
                request.context, objs[0])
            return notify({
                self._collection: [
                    self._filter_attributes(request.context,
                                            obj,
                                            fields_to_strip=fields_to_strip)
                    for obj in objs
                ]
            })
        else:
            if self._collection in body:
                # Emulate atomic bulk behavior
                objs = do_create(body, bulk=True, emulated=True)
                return notify({self._collection: objs})
            else:
                obj = do_create(body)
                return notify(
                    {self._resource: self._view(request.context, obj)})
예제 #6
0
 def index(self, request, **kwargs):
     """Returns a list of the requested entity."""
     parent_id = kwargs.get(self._parent_id_name)
     # Ensure policy engine is initialized
     policy.init()
     return self._items(request, True, parent_id)