diff --git a/glance/gateway.py b/glance/gateway.py index a1c13dec64..f5889aedfe 100644 --- a/glance/gateway.py +++ b/glance/gateway.py @@ -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( diff --git a/glance/tests/unit/test_gateway.py b/glance/tests/unit/test_gateway.py index 32d47fba6b..6521c0ab5a 100644 --- a/glance/tests/unit/test_gateway.py +++ b/glance/tests/unit/test_gateway.py @@ -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)