diff --git a/.zuul.yaml b/.zuul.yaml index 04836e2fea..f04d00b387 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -289,6 +289,20 @@ glance_store: rbd_thin_provisioning: True +# TODO(pdeore): Remove this jobs once all the glance jobs will be tested +# with new RBAC in integrated way and we do not need this separate job. +- job: + name: tempest-integrated-storage-enforce-scope-new-defaults + parent: tempest-integrated-storage + description: | + This job runs the Tempest tests with scope and new defaults enabled + Glance services. + vars: + devstack_localrc: + NOVA_ENFORCE_SCOPE: true + CINDER_ENFORCE_SCOPE: true + GLANCE_ENFORCE_SCOPE: true + - project: templates: - check-requirements @@ -319,6 +333,8 @@ - ^\.zuul\.yaml$ - tempest-integrated-storage: irrelevant-files: *tempest-irrelevant-files + - tempest-integrated-storage-enforce-scope-new-defaults: + irrelevant-files: *tempest-irrelevant-files - tempest-integrated-storage-import: irrelevant-files: *tempest-irrelevant-files - tempest-integrated-storage-import-standalone: @@ -339,6 +355,8 @@ - openstack-tox-functional-py39 - tempest-integrated-storage: irrelevant-files: *tempest-irrelevant-files + - tempest-integrated-storage-enforce-scope-new-defaults: + irrelevant-files: *tempest-irrelevant-files - tempest-integrated-storage-import: irrelevant-files: *tempest-irrelevant-files - tempest-integrated-storage-import-standalone: diff --git a/glance/api/policy.py b/glance/api/policy.py index 605b8118c6..d92d4df6d7 100644 --- a/glance/api/policy.py +++ b/glance/api/policy.py @@ -34,11 +34,15 @@ CONF = cfg.CONF _ENFORCER = None -# TODO(gmann): Remove setting the default value of config policy_file -# once oslo_policy change the default value to 'policy.yaml'. -# https://github.com/openstack/oslo.policy/blob/a626ad12fe5a3abd49d70e3e5b95589d279ab578/oslo_policy/opts.py#L49 +# TODO(gmann): Remove overriding the default value of config options +# 'policy_file', 'enforce_scope', and 'enforce_new_defaults' once +# oslo_policy change their default value to what is overridden here. DEFAULT_POLICY_FILE = 'policy.yaml' -opts.set_defaults(cfg.CONF, DEFAULT_POLICY_FILE) +opts.set_defaults( + cfg.CONF, + DEFAULT_POLICY_FILE, + enforce_scope=True, + enforce_new_defaults=True) class Enforcer(policy.Enforcer): diff --git a/glance/common/config.py b/glance/common/config.py index 7891daccf0..cff15ab1d4 100644 --- a/glance/common/config.py +++ b/glance/common/config.py @@ -593,7 +593,7 @@ Related options: Related options: * [DEFAULT]/node_staging_uri""")), - cfg.BoolOpt('enforce_secure_rbac', default=False, + cfg.BoolOpt('enforce_secure_rbac', default=True, deprecated_for_removal=True, deprecated_reason=_(""" This option has been introduced to require operators to opt into enforcing diff --git a/glance/tests/functional/__init__.py b/glance/tests/functional/__init__.py index cbdcfe4bbc..a17c67c395 100644 --- a/glance/tests/functional/__init__.py +++ b/glance/tests/functional/__init__.py @@ -585,6 +585,9 @@ class ApiServerForMultipleBackend(Server): self.image_location_quota = 2 self.disable_path = None + self.enforce_secure_rbac = True + self.enforce_new_defaults = True + self.needs_database = True default_sql_connection = SQLITE_CONN_TEMPLATE % self.test_dir self.sql_connection = os.environ.get('GLANCE_TEST_SQL_CONNECTION', @@ -626,9 +629,11 @@ image_location_quota=%(image_location_quota)s location_strategy=%(location_strategy)s allow_additional_image_properties = True enabled_backends=file1:file,file2:file,file3:file +enforce_secure_rbac=%(enforce_secure_rbac)s [oslo_policy] policy_file = %(policy_file)s policy_default_rule = %(policy_default_rule)s +enforce_new_defaults=%(enforce_new_defaults)s [paste_deploy] flavor = %(deployment_flavor)s [store_type_location_strategy] diff --git a/glance/tests/unit/test_cache_middleware.py b/glance/tests/unit/test_cache_middleware.py index 034edc4b1a..39288b8213 100644 --- a/glance/tests/unit/test_cache_middleware.py +++ b/glance/tests/unit/test_cache_middleware.py @@ -409,7 +409,8 @@ class TestCacheMiddlewareProcessResponse(base.IsolatedUnitTest): rules = { "restricted": "not ('test_1234':%(x_test_key)s and role:_member_)", - "download_image": "role:admin or rule:restricted" + "download_image": "role:admin or rule:restricted", + "get_image": "" } self.set_policy_rules(rules) cache_filter.policy = glance.api.policy.Enforcer( diff --git a/glance/tests/unit/test_policy.py b/glance/tests/unit/test_policy.py index 70ad783e7a..9f26d0e297 100644 --- a/glance/tests/unit/test_policy.py +++ b/glance/tests/unit/test_policy.py @@ -320,7 +320,8 @@ class TestPolicyEnforcer(base.IsolatedUnitTest): self.assertEqual(False, enforcer.check(context, 'get_image', {})) def test_policy_file_get_image_default_everybody(self): - rules = {"default": ''} + rules = {"default": '', + "get_image": ''} self.set_policy_rules(rules) enforcer = glance.api.policy.Enforcer( diff --git a/glance/tests/unit/utils.py b/glance/tests/unit/utils.py index 3cbb51dbc2..16640a9c1c 100644 --- a/glance/tests/unit/utils.py +++ b/glance/tests/unit/utils.py @@ -20,6 +20,7 @@ from unittest import mock import urllib from oslo_config import cfg +from oslo_policy import policy from glance.async_.flows._internal_plugins import base_download from glance.common import exception @@ -87,6 +88,14 @@ def get_fake_request(path='', method='POST', is_admin=False, user=USER1, return req +def enforcer_from_rules(unparsed_rules): + rules = policy.Rules.from_dict(unparsed_rules) + enforcer = glance.api.policy.Enforcer( + suppress_deprecation_warnings=True) + enforcer.set_rules(rules, overwrite=True) + return enforcer + + def fake_get_size_from_backend(uri, context=None): return 1 diff --git a/glance/tests/unit/v2/test_image_data_resource.py b/glance/tests/unit/v2/test_image_data_resource.py index 3889c2f631..4ed96a1c38 100644 --- a/glance/tests/unit/v2/test_image_data_resource.py +++ b/glance/tests/unit/v2/test_image_data_resource.py @@ -135,7 +135,7 @@ class TestImagesController(base.StoreClearingUnitTest): self.addCleanup(patcher.stop) def test_download(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('abcd', locations=[{'url': 'http://example.com/image', 'metadata': {}, 'status': 'active'}]) @@ -156,7 +156,7 @@ class TestImagesController(base.StoreClearingUnitTest): def test_download_no_location(self): # NOTE(mclaren): NoContent will be raised by the ResponseSerializer # That's tested below. - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) self.image_repo.result = FakeImage('abcd') image = self.controller.download(request, unit_test_utils.UUID2) self.assertEqual('abcd', image.image_id) @@ -179,7 +179,7 @@ class TestImagesController(base.StoreClearingUnitTest): def __len__(self): raise exception.Forbidden() - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('abcd') self.image_repo.result = image image.locations = ImageLocations() @@ -187,7 +187,7 @@ class TestImagesController(base.StoreClearingUnitTest): self.assertEqual('abcd', image.image_id) def test_upload(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('abcd', owner='tenant1') self.image_repo.result = image self.controller.upload(request, unit_test_utils.UUID2, 'YYYY', 4) @@ -217,7 +217,7 @@ class TestImagesController(base.StoreClearingUnitTest): self.assertTrue(mock_enf.called) def test_upload_status(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('abcd') self.image_repo.result = image insurance = {'called': False} @@ -234,7 +234,7 @@ class TestImagesController(base.StoreClearingUnitTest): self.image_repo.saved_image.status) def test_upload_no_size(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('abcd') self.image_repo.result = image self.controller.upload(request, unit_test_utils.UUID2, 'YYYY', None) @@ -256,7 +256,7 @@ class TestImagesController(base.StoreClearingUnitTest): mock_enforce.assert_has_calls(expected_call) def test_upload_invalid(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('abcd') image.status = ValueError() self.image_repo.result = image @@ -270,7 +270,7 @@ class TestImagesController(base.StoreClearingUnitTest): mocked_save = mock.Mock(side_effect=side_effect) mocked_delete = mock.Mock() - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('abcd') image.delete = mocked_delete self.image_repo.result = image @@ -287,7 +287,7 @@ class TestImagesController(base.StoreClearingUnitTest): mocked_save.side_effect = [lambda *a: None, exception.NotAuthenticated(), lambda *a: None] - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) request.environ['keystone.token_info'] = { 'token': { 'roles': [{'name': 'member'}] @@ -309,7 +309,8 @@ class TestImagesController(base.StoreClearingUnitTest): raise exception.Conflict() for fun in [fake_save_not_found, fake_save_conflict]: - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request( + roles=['admin', 'member']) image = FakeImage('abcd', locations=['http://example.com/image']) self.image_repo.result = image self.image_repo.save = fun @@ -325,7 +326,7 @@ class TestImagesController(base.StoreClearingUnitTest): def fake_delete(): raise exception.ImageNotFound() - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('abcd', locations=['http://example.com/image']) self.image_repo.result = image self.image_repo.save = fake_save @@ -340,7 +341,7 @@ class TestImagesController(base.StoreClearingUnitTest): def fake_delete(): raise exception.ImageNotFound() - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('abcd', locations=['http://example.com/image']) self.image_repo.result = image self.image_repo.save = fake_save @@ -355,7 +356,7 @@ class TestImagesController(base.StoreClearingUnitTest): request, str(uuid.uuid4()), 'ABC', 3) def test_upload_data_exists(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage() exc = exception.InvalidImageStatusTransition(cur_status='active', new_status='queued') @@ -365,7 +366,7 @@ class TestImagesController(base.StoreClearingUnitTest): request, unit_test_utils.UUID1, 'YYYY', 4) def test_upload_storage_full(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage() image.set_data = Raise(glance_store.StorageFull) self.image_repo.result = image @@ -374,7 +375,7 @@ class TestImagesController(base.StoreClearingUnitTest): request, unit_test_utils.UUID2, 'YYYYYYY', 7) def test_upload_signature_verification_fails(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage() image.set_data = Raise(cursive_exception.SignatureVerificationError) self.image_repo.result = image @@ -383,7 +384,7 @@ class TestImagesController(base.StoreClearingUnitTest): self.assertEqual('queued', self.image_repo.saved_image.status) def test_image_size_limit_exceeded(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage() image.set_data = Raise(exception.ImageSizeLimitExceeded) self.image_repo.result = image @@ -399,7 +400,8 @@ class TestImagesController(base.StoreClearingUnitTest): request, unit_test_utils.UUID1, 'YYYYYYY', 7) def test_upload_storage_forbidden(self): - request = unit_test_utils.get_fake_request(user=unit_test_utils.USER2) + request = unit_test_utils.get_fake_request( + user=unit_test_utils.USER2, roles=['admin', 'member']) image = FakeImage() image.set_data = Raise(exception.Forbidden) self.image_repo.result = image @@ -414,7 +416,8 @@ class TestImagesController(base.StoreClearingUnitTest): request, unit_test_utils.UUID1, 'ABC', 3) def test_upload_storage_write_denied(self): - request = unit_test_utils.get_fake_request(user=unit_test_utils.USER3) + request = unit_test_utils.get_fake_request( + user=unit_test_utils.USER3, roles=['admin', 'member']) image = FakeImage() image.set_data = Raise(glance_store.StorageWriteDenied) self.image_repo.result = image @@ -424,7 +427,8 @@ class TestImagesController(base.StoreClearingUnitTest): def test_upload_storage_store_disabled(self): """Test that uploading an image file raises StoreDisabled exception""" - request = unit_test_utils.get_fake_request(user=unit_test_utils.USER3) + request = unit_test_utils.get_fake_request( + user=unit_test_utils.USER3, roles=['admin', 'member']) image = FakeImage() image.set_data = Raise(glance_store.StoreAddDisabled) self.image_repo.result = image @@ -484,7 +488,7 @@ class TestImagesController(base.StoreClearingUnitTest): self.assertEqual(activate_log, output_log[2]) def test_restore_image_when_upload_failed(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('fake') image.set_data = Raise(glance_store.StorageWriteDenied) self.image_repo.result = image @@ -496,7 +500,7 @@ class TestImagesController(base.StoreClearingUnitTest): @mock.patch.object(filesystem.Store, 'add') def test_restore_image_when_staging_failed(self, mock_store_add): mock_store_add.side_effect = glance_store.StorageWriteDenied() - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image_id = str(uuid.uuid4()) image = FakeImage('fake') self.image_repo.result = image @@ -507,7 +511,7 @@ class TestImagesController(base.StoreClearingUnitTest): def test_stage(self): image_id = str(uuid.uuid4()) - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage(image_id=image_id) self.image_repo.result = image with mock.patch.object(filesystem.Store, 'add') as mock_add: @@ -518,7 +522,7 @@ class TestImagesController(base.StoreClearingUnitTest): def test_image_already_on_staging(self): image_id = str(uuid.uuid4()) - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage(image_id=image_id) self.image_repo.result = image with mock.patch.object(filesystem.Store, 'add') as mock_store_add: @@ -534,7 +538,7 @@ class TestImagesController(base.StoreClearingUnitTest): def test_image_stage_raises_bad_store_uri(self, mock_store_configure): mock_store_configure.side_effect = AttributeError() image_id = str(uuid.uuid4()) - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) self.assertRaises(exception.BadStoreUri, self.controller.stage, request, image_id, 'YYYY', 4) @@ -542,7 +546,7 @@ class TestImagesController(base.StoreClearingUnitTest): def test_image_stage_raises_storage_full(self, mock_store_add): mock_store_add.side_effect = glance_store.StorageFull() image_id = str(uuid.uuid4()) - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage(image_id=image_id) self.image_repo.result = image with mock.patch.object(self.controller, "_unstage"): @@ -554,7 +558,7 @@ class TestImagesController(base.StoreClearingUnitTest): def test_image_stage_raises_storage_quota_full(self, mock_store_add): mock_store_add.side_effect = exception.StorageQuotaFull("message") image_id = str(uuid.uuid4()) - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage(image_id=image_id) self.image_repo.result = image with mock.patch.object(self.controller, "_unstage"): @@ -566,7 +570,7 @@ class TestImagesController(base.StoreClearingUnitTest): def test_image_stage_raises_storage_write_denied(self, mock_store_add): mock_store_add.side_effect = glance_store.StorageWriteDenied() image_id = str(uuid.uuid4()) - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage(image_id=image_id) self.image_repo.result = image with mock.patch.object(self.controller, "_unstage"): @@ -592,7 +596,7 @@ class TestImagesController(base.StoreClearingUnitTest): def test_image_stage_raises_image_size_exceeded(self, mock_store_add): mock_store_add.side_effect = exception.ImageSizeLimitExceeded() image_id = str(uuid.uuid4()) - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage(image_id=image_id) self.image_repo.result = image with mock.patch.object(self.controller, "_unstage"): @@ -603,7 +607,7 @@ class TestImagesController(base.StoreClearingUnitTest): @mock.patch.object(filesystem.Store, 'add') def test_image_stage_invalid_image_transition(self, mock_store_add): image_id = str(uuid.uuid4()) - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage(image_id=image_id) self.image_repo.result = image with mock.patch.object(filesystem.Store, 'add') as mock_add: @@ -619,7 +623,7 @@ class TestImagesController(base.StoreClearingUnitTest): def _test_image_stage_records_host(self, expected_url): image_id = str(uuid.uuid4()) - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage(image_id=image_id) self.image_repo.result = image with mock.patch.object(filesystem.Store, 'add') as mock_add: @@ -651,7 +655,7 @@ class TestImagesController(base.StoreClearingUnitTest): # image staged. self.config(public_endpoint='http://worker1.example.com') image_id = str(uuid.uuid4()) - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage(image_id=image_id) self.image_repo.result = image exc_cls = glance_store.exceptions.StorageFull @@ -732,7 +736,7 @@ class TestImageDataDeserializer(test_utils.BaseTestCase): self.deserializer.upload, request) def test_stage(self): - req = unit_test_utils.get_fake_request() + req = unit_test_utils.get_fake_request(roles=['admin', 'member']) req.headers['Content-Type'] = 'application/octet-stream' req.headers['Content-Length'] = 4 req.body_file = io.BytesIO(b'YYYY') @@ -1078,7 +1082,7 @@ class TestMultiBackendImagesController(base.MultiStoreClearingUnitTest): self.addCleanup(patcher.stop) def test_upload(self): - request = unit_test_utils.get_fake_request() + request = unit_test_utils.get_fake_request(roles=['admin', 'member']) image = FakeImage('abcd') self.image_repo.result = image self.controller.upload(request, unit_test_utils.UUID2, 'YYYY', 4) diff --git a/glance/tests/unit/v2/test_image_members_resource.py b/glance/tests/unit/v2/test_image_members_resource.py index 457b8505d5..7287a3b5d2 100644 --- a/glance/tests/unit/v2/test_image_members_resource.py +++ b/glance/tests/unit/v2/test_image_members_resource.py @@ -316,6 +316,11 @@ class TestImageMembersController(test_utils.BaseTestCase): self.assertEqual('accepted', output.status) def test_update_done_by_owner(self): + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "modify_image": "'{0}':%(owner)s".format(TENANT1) + }) + self.controller.policy = enforcer request = unit_test_utils.get_fake_request(tenant=TENANT1) self.assertRaises(webob.exc.HTTPForbidden, self.controller.update, request, UUID2, TENANT4, status='accepted') @@ -331,6 +336,10 @@ class TestImageMembersController(test_utils.BaseTestCase): request, UUID2, TENANT4, status='accept') def test_create_private_image(self): + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + }) + self.controller.policy = enforcer request = unit_test_utils.get_fake_request() self.assertRaises(webob.exc.HTTPForbidden, self.controller.create, request, UUID4, TENANT2) @@ -359,7 +368,14 @@ class TestImageMembersController(test_utils.BaseTestCase): self.assertEqual([], found_member) def test_delete_by_member(self): + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "delete_member": "'{0}':%(owner)s".format(TENANT4), + "get_members": "", + "get_member": "" + }) request = unit_test_utils.get_fake_request(tenant=TENANT4) + self.controller.policy = enforcer self.assertRaises(webob.exc.HTTPForbidden, self.controller.delete, request, UUID2, TENANT4) request = unit_test_utils.get_fake_request() @@ -395,6 +411,12 @@ class TestImageMembersController(test_utils.BaseTestCase): request, UUID2, TENANT4) def test_delete_private_image(self): + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "delete_member": "'{0}':%(owner)s".format(TENANT1), + "get_member": "" + }) + self.controller.policy = enforcer request = unit_test_utils.get_fake_request() self.assertRaises(webob.exc.HTTPForbidden, self.controller.delete, request, UUID4, TENANT1) diff --git a/glance/tests/unit/v2/test_images_resource.py b/glance/tests/unit/v2/test_images_resource.py index 203092554a..733e644e81 100644 --- a/glance/tests/unit/v2/test_images_resource.py +++ b/glance/tests/unit/v2/test_images_resource.py @@ -1253,17 +1253,25 @@ class TestImagesController(base.IsolatedUnitTest): tags=tags) def test_create_with_owner_non_admin(self): + enforcer = unit_test_utils.enforcer_from_rules({ + "add_image": "role:member,reader", + }) request = unit_test_utils.get_fake_request() request.context.is_admin = False image = {'owner': '12345'} + self.controller.policy = enforcer self.assertRaises(webob.exc.HTTPForbidden, self.controller.create, request, image=image, extra_properties={}, tags=[]) + enforcer = unit_test_utils.enforcer_from_rules({ + "add_image": "'{0}':%(owner)s".format(TENANT1), + }) request = unit_test_utils.get_fake_request() request.context.is_admin = False image = {'owner': TENANT1} + self.controller.policy = enforcer output = self.controller.create(request, image=image, extra_properties={}, tags=[]) self.assertEqual(TENANT1, output.owner) @@ -1616,7 +1624,7 @@ class TestImagesController(base.IsolatedUnitTest): enforcer, self.notifier, self.store) - request = unit_test_utils.get_fake_request(roles=['spl_role']) + request = unit_test_utils.get_fake_request(roles=['spl_role', 'admin']) image = {'name': 'image-1'} extra_props = {'spl_creator_policy': 'bar'} created_image = self.controller.create(request, image=image, @@ -1629,8 +1637,19 @@ class TestImagesController(base.IsolatedUnitTest): changes = [ {'op': 'replace', 'path': ['spl_creator_policy'], 'value': 'par'}, ] + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "modify_image": "role:spl_role" + }) + + self.controller.policy = enforcer self.assertRaises(webob.exc.HTTPForbidden, self.controller.update, another_request, created_image.image_id, changes) + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "modify_image": "role:admin" + }) + self.controller.policy = enforcer another_request = unit_test_utils.get_fake_request(roles=['admin']) output = self.controller.update(another_request, created_image.image_id, changes) @@ -1655,9 +1674,19 @@ class TestImagesController(base.IsolatedUnitTest): changes = [ {'op': 'add', 'path': ['spl_creator_policy'], 'value': 'bar'}, ] + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "modify_image": "role:fake_role" + }) + + self.controller.policy = enforcer self.assertRaises(webob.exc.HTTPForbidden, self.controller.update, another_request, created_image.image_id, changes) - + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "modify_image": "role:member" + }) + self.controller.policy = enforcer another_request = unit_test_utils.get_fake_request(roles=['member', 'spl_role']) output = self.controller.update(another_request, @@ -1679,6 +1708,11 @@ class TestImagesController(base.IsolatedUnitTest): extra_properties={}, tags=[]) roles = ['fake_member'] + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "modify_image": "role:fake_member" + }) + self.controller.policy = enforcer another_request = unit_test_utils.get_fake_request(roles=roles) changes = [ {'op': 'add', 'path': ['x_owner_foo'], 'value': 'bar'}, @@ -1762,6 +1796,11 @@ class TestImagesController(base.IsolatedUnitTest): created_image = self.controller.create(request, image=image, extra_properties=extra_props, tags=[]) + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "modify_image": "role:fake_role" + }) + self.controller.policy = enforcer another_request = unit_test_utils.get_fake_request(roles=['fake_role']) changes = [ {'op': 'replace', 'path': ['x_owner_foo'], 'value': 'baz'}, @@ -1806,6 +1845,11 @@ class TestImagesController(base.IsolatedUnitTest): created_image = self.controller.create(request, image=image, extra_properties=extra_props, tags=[]) + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "modify_image": "role:fake_role" + }) + self.controller.policy = enforcer another_request = unit_test_utils.get_fake_request(roles=['fake_role']) changes = [ {'op': 'remove', 'path': ['x_owner_foo']} @@ -3420,7 +3464,12 @@ class TestImagesController(base.IsolatedUnitTest): def test_image_import_not_allowed(self, mock_get, mock_new_task): # NOTE(danms): FakeImage is owned by utils.TENANT1. Try to do the # import as TENANT2 and we should get an HTTPForbidden - request = unit_test_utils.get_fake_request(tenant=TENANT2) + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "modify_image": "'{0}':%(owner)s".format(TENANT2) + }) + request = unit_test_utils.get_fake_request() + self.controller.policy = enforcer mock_get.return_value = FakeImage(status='uploading') self.assertRaises(webob.exc.HTTPForbidden, self.controller.import_image, @@ -6008,7 +6057,13 @@ class TestMultiImagesController(base.MultiIsolatedUnitTest): {'method': {'name': 'glance-direct'}}) def test_delete_from_store_as_non_owner(self): + enforcer = unit_test_utils.enforcer_from_rules({ + "get_image": "", + "delete_image_location": "'TENANT4':%(owner)s", + "get_image_location": "" + }) request = unit_test_utils.get_fake_request() + self.controller.policy = enforcer self.assertRaises(webob.exc.HTTPForbidden, self.controller.delete_from_store, request, diff --git a/releasenotes/notes/enable-enforce-scope-and-new-defaults-ef543183e6c2eabb.yaml b/releasenotes/notes/enable-enforce-scope-and-new-defaults-ef543183e6c2eabb.yaml new file mode 100644 index 0000000000..dcfdf7a7e0 --- /dev/null +++ b/releasenotes/notes/enable-enforce-scope-and-new-defaults-ef543183e6c2eabb.yaml @@ -0,0 +1,14 @@ +--- +upgrade: + - | + The Glance service enables the API policies (RBAC) new defaults and scope by + default. The Default value of config options ``[oslo_policy] enforce_scope`` + and ``[oslo_policy] oslo_policy.enforce_new_defaults`` have been changed + to ``True``. + + If you want to disable them then modify the below config options value in + ``glance-api.conf`` file:: + + [oslo_policy] + enforce_new_defaults=False + enforce_scope=False diff --git a/requirements.txt b/requirements.txt index 0554a36474..3e823077a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -41,7 +41,7 @@ oslo.log>=4.5.0 # Apache-2.0 oslo.messaging>=5.29.0,!=9.0.0 # Apache-2.0 oslo.middleware>=3.31.0 # Apache-2.0 oslo.reports>=1.18.0 # Apache-2.0 -oslo.policy>=3.8.1 # Apache-2.0 +oslo.policy>=3.11.0 # Apache-2.0 retrying!=1.3.0,>=1.2.3 # Apache-2.0 osprofiler>=1.4.0 # Apache-2.0