Refactor gateway get_repo auth layer

This makes us able to optionally not get the authorization layer on
top of the stack. The authorization layer makes everything on an
Image read-only based on hard-coded checks for "owner or admin".
If we are going to move to using policy to enforce modifications
to images, we need to discard this layer.

When the flag is True we also don't insert the policy layer in the
middle of the stack.

This patch cleans up the stacking to be a little more modular, and
adds a flag to control whether or not we get the authorization layer
on top or not.

Partially-Implements: blueprint policy-refactor

Change-Id: Ie8bea199175a0eb35428b1508af1c49111a87c37
This commit is contained in:
Dan Smith 2021-05-03 10:20:41 -07:00
parent 429f16124b
commit 8767489f7b
2 changed files with 56 additions and 18 deletions

View File

@ -67,27 +67,42 @@ class Gateway(object):
policy_member_factory, context)
return authorized_image_factory
def get_repo(self, context):
image_repo = glance.db.ImageRepo(context, self.db_api)
store_image_repo = glance.location.ImageRepoProxy(
image_repo, context, self.store_api, self.store_utils)
quota_image_repo = glance.quota.ImageRepoProxy(
store_image_repo, context, self.db_api, self.store_utils)
policy_image_repo = policy.ImageRepoProxy(
quota_image_repo, context, self.policy)
notifier_image_repo = glance.notifier.ImageRepoProxy(
policy_image_repo, context, self.notifier)
def get_repo(self, context, authorization_layer=True):
"""Get the layered ImageRepo model.
This is where we construct the "the onion" by layering
ImageRepo models on top of each other, starting with the DB at
the bottom.
NB: Code that has implemented policy checks fully above this
layer should pass authorization_layer=False to ensure that no
conflicts with old checks happen. Legacy code should continue
passing True until legacy checks are no longer needed.
:param context: The RequestContext
:param authorization_layer: Controls whether or not we add the legacy
glance.authorization and glance.policy
layers.
:returns: An ImageRepo-like object
"""
repo = glance.db.ImageRepo(context, self.db_api)
repo = glance.location.ImageRepoProxy(
repo, context, self.store_api, self.store_utils)
repo = glance.quota.ImageRepoProxy(
repo, context, self.db_api, self.store_utils)
if authorization_layer:
repo = policy.ImageRepoProxy(repo, context, self.policy)
repo = glance.notifier.ImageRepoProxy(
repo, context, self.notifier)
if property_utils.is_property_protection_enabled():
property_rules = property_utils.PropertyRules(self.policy)
pir = property_protections.ProtectedImageRepoProxy(
notifier_image_repo, context, property_rules)
authorized_image_repo = authorization.ImageRepoProxy(
pir, context)
else:
authorized_image_repo = authorization.ImageRepoProxy(
notifier_image_repo, context)
repo = property_protections.ProtectedImageRepoProxy(
repo, context, property_rules)
if authorization_layer:
repo = authorization.ImageRepoProxy(repo, context)
return authorized_image_repo
return repo
def get_member_repo(self, image, context):
image_member_repo = glance.db.ImageMemberRepo(

View File

@ -15,7 +15,10 @@
from unittest import mock
from glance.api import authorization
from glance.api import property_protections
from glance import gateway
from glance import notifier
import glance.tests.utils as test_utils
@ -67,3 +70,23 @@ class TestGateway(test_utils.BaseTestCase):
admin_repo=mock.sentinel.admin_repo)
_test()
@mock.patch('glance.api.policy.ImageRepoProxy')
def test_get_repo(self, mock_proxy):
repo = self.gateway.get_repo(self.context)
self.assertIsInstance(repo, authorization.ImageRepoProxy)
mock_proxy.assert_called_once_with(mock.ANY, mock.sentinel.context,
mock.ANY)
@mock.patch('glance.api.policy.ImageRepoProxy')
def test_get_repo_without_auth(self, mock_proxy):
repo = self.gateway.get_repo(self.context, authorization_layer=False)
self.assertIsInstance(repo, notifier.ImageRepoProxy)
mock_proxy.assert_not_called()
@mock.patch('glance.common.property_utils.PropertyRules._load_rules')
def test_get_repo_without_auth_with_pp(self, mock_load):
self.config(property_protection_file='foo')
repo = self.gateway.get_repo(self.context, authorization_layer=False)
self.assertIsInstance(repo,
property_protections.ProtectedImageRepoProxy)