glance/glance/api/v2/policy.py
Dan Smith ba37ea3227 Check get_image(s) in the API
This includes a change to catch Forbidden and convert to NotFound.
The previous Forbidden handler was not only correct (it shoud hide
the permissions error with "not found") but it was actually dead code,
since the DB was performing its own checks and would never raise
Forbidden.

This also includes a change of the default policy for get_images
to include the other states, like get_image does. I think this was
just an oversight in the original RBAC patches, which didn't matter
because they weren't really being honored strictly.

Partially implements: blueprint policy-refactor

Change-Id: I70100cd7f01da803e9740cea1f7ce7ae18ad6919
2021-08-04 07:56:05 -07:00

113 lines
4.1 KiB
Python

# Copyright 2021 Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
import webob.exc
from glance.api import policy
from glance.common import exception
LOG = logging.getLogger(__name__)
class APIPolicyBase(object):
def __init__(self, context, target=None, enforcer=None):
self._context = context
self._target = target or {}
self.enforcer = enforcer or policy.Enforcer()
def _enforce(self, rule_name):
try:
self.enforcer.enforce(self._context, rule_name, self._target)
except exception.Forbidden as e:
raise webob.exc.HTTPForbidden(explanation=str(e))
def check(self, name, *args):
"""Perform a soft check of a named policy.
This is used when you need to check if a policy is allowed for the
given resource, without needing to catch an exception. If the policy
check requires args, those are accepted here as well.
:param name: Policy name to check
:returns: bool indicating if the policy is allowed.
"""
try:
getattr(self, name)(*args)
return True
except webob.exc.HTTPForbidden:
return False
class ImageAPIPolicy(APIPolicyBase):
def __init__(self, context, image, enforcer=None):
super(ImageAPIPolicy, self).__init__(context,
policy.ImageTarget(image),
enforcer)
self._image = image
def _enforce(self, rule_name):
"""Translate Forbidden->NotFound for images."""
try:
super(ImageAPIPolicy, self)._enforce(rule_name)
except webob.exc.HTTPForbidden:
# If we are checking get_image, then Forbidden means the
# user cannot see this image, so raise NotFound. If we are
# checking anything else and get Forbidden, then raise
# NotFound in that case as well to avoid exposing images
# the user can not see, while preserving the Forbidden
# behavior for the ones they can see.
if rule_name == 'get_image' or not self.check('get_image'):
raise webob.exc.HTTPNotFound()
raise
def check(self, name, *args):
try:
return super(ImageAPIPolicy, self).check(name, *args)
except webob.exc.HTTPNotFound:
# NOTE(danms): Since our _enforce can raise NotFound, that
# too means a False check response.
return False
def _enforce_visibility(self, visibility):
# NOTE(danms): Use the existing enforcement routine for now,
# which shows that we're enforcing the same behavior. In the
# future, that should probably be moved here.
try:
policy._enforce_image_visibility(self.enforcer, self._context,
visibility, self._target)
except exception.Forbidden as e:
raise webob.exc.HTTPForbidden(explanation=str(e))
def update_property(self, name, value=None):
if name == 'visibility':
# NOTE(danms): Visibility changes have their own policy,
# so check that first, followed by the general
# modify_image policy below.
self._enforce_visibility(value)
self._enforce('modify_image')
def update_locations(self):
self._enforce('set_image_location')
def delete_locations(self):
self._enforce('delete_image_location')
def get_image(self):
self._enforce('get_image')
def get_images(self):
self._enforce('get_images')