diff --git a/glance_tempest_plugin/tests/rbac/v2/base.py b/glance_tempest_plugin/tests/rbac/v2/base.py index c1e49a4..822e9e9 100644 --- a/glance_tempest_plugin/tests/rbac/v2/base.py +++ b/glance_tempest_plugin/tests/rbac/v2/base.py @@ -9,6 +9,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + import abc from tempest.api.image import base @@ -286,6 +287,7 @@ class MetadefV2RbacNamespaceTest(RbacMetadefBase): cls.project_id = cls.persona.namespaces_client.project_id cls.alt_project_id = cls.alt_persona.namespaces_client.project_id cls.admin_namespace_client = cls.os_project_admin.namespaces_client + cls.namespaces_client = cls.persona.namespaces_client cls.namespace_client = cls.persona.namespaces_client cls.alt_namespace_client = cls.alt_persona.namespaces_client @@ -302,6 +304,40 @@ class MetadefV2RbacNamespaceTest(RbacMetadefBase): for ns in actual: self.assertIn(ns['namespace'], expected_ns) + def assertGetNamespace(self, actual_ns, owner, client): + expected_status = 200 + if (actual_ns['visibility'] == 'private' and + actual_ns['owner'] != owner): + expected_status = exceptions.NotFound + + self.do_request('show_namespace', + expected_status=expected_status, + client=client, + namespace=actual_ns['namespace']) + + def assertUpdateNamespace(self, actual_ns, owner, client): + expected_status = exceptions.Forbidden + if not (actual_ns['visibility'] == 'public' or + actual_ns['owner'] == owner): + expected_status = exceptions.NotFound + + self.do_request('update_namespace', + expected_status=expected_status, + client=client, + description=data_utils.arbitrary_string(), + namespace=actual_ns['namespace']) + + def assertDeleteNamespace(self, actual_ns, owner, client): + expected_status = exceptions.Forbidden + if not (actual_ns['visibility'] == 'public' or + actual_ns['owner'] == owner): + expected_status = exceptions.NotFound + + self.do_request('delete_namespace', + expected_status=expected_status, + client=client, + namespace=actual_ns['namespace']) + class MetadefV2RbacNamespaceTemplate(metaclass=abc.ABCMeta): @@ -350,24 +386,40 @@ class MetadefV2RbacResourceTypeTest(RbacMetadefBase): cls.alt_project_id = cls.alt_persona.namespaces_client.project_id cls.resource_types_client = cls.persona.resource_types_client - def create_resource_types(self): - # Create namespace for two different projects - namespaces = self.create_namespaces() - + def create_resource_types(self, namespace=None, owner=None, client=None, + is_admin=True): namespace_resource_types = [] - for ns in namespaces: - alt_client = None - if ns['namespace'].startswith(self.alt_project_id): - alt_client = self.os_project_alt_admin.resource_types_client - client = alt_client - if alt_client is None: - client = self.os_project_admin.resource_types_client - resource_name = "rs_type_of_%s" % (ns['namespace']) - resource_type = client.create_resource_type_association( - ns['namespace'], name=resource_name) - resource_types = {'namespace': ns, - 'resource_type': resource_type} - namespace_resource_types.append(resource_types) + if is_admin: + # Create namespace for two different projects + namespaces = self.create_namespaces() + + for ns in namespaces: + alt_client = None + if ns['namespace'].startswith(self.alt_project_id): + alt_client = \ + self.os_project_alt_admin.resource_types_client + client = alt_client + if alt_client is None: + client = self.os_project_admin.resource_types_client + + resource_name = "rs_type_of_%s" % (ns['namespace']) + resource_type = client.create_resource_type_association( + ns['namespace'], name=resource_name) + resource_types = {'namespace': ns, + 'resource_type': resource_type} + namespace_resource_types.append(resource_types) + else: + rs_type_name = "rs_type_of_%s" % (namespace['namespace']) + expected_status = exceptions.Forbidden + if not (namespace['visibility'] == 'public' or + namespace['owner'] == owner): + expected_status = exceptions.NotFound + + self.do_request('create_resource_type_association', + expected_status=expected_status, + namespace_id=namespace['namespace'], + name=rs_type_name, + client=client) return namespace_resource_types @@ -378,6 +430,30 @@ class MetadefV2RbacResourceTypeTest(RbacMetadefBase): self.assertIn(rs_type['resource_type']['name'], actual_rs_types) + def assertRSTypeGet(self, actual_rs_type, client, owner): + ns = actual_rs_type['namespace'] + expected_status = 200 + if (ns['visibility'] != 'public' and ns['owner'] != owner): + expected_status = exceptions.NotFound + + self.do_request('list_resource_type_association', + expected_status=expected_status, + namespace_id=ns['namespace'], + client=client) + + def assertRSTypeDelete(self, actual_rs_type, client, owner): + ns = actual_rs_type['namespace'] + expected_status = exceptions.Forbidden + if (ns['visibility'] != 'public' and ns['owner'] != owner): + expected_status = exceptions.NotFound + + self.do_request( + 'delete_resource_type_association', + expected_status=expected_status, + namespace_id=ns['namespace'], + resource_name=actual_rs_type['resource_type']['name'], + client=client) + class MetadefV2RbacResourceTypeTemplate(metaclass=abc.ABCMeta): @@ -422,23 +498,37 @@ class MetadefV2RbacObjectsTest(RbacMetadefBase): cls.alt_persona.namespace_objects_client.project_id cls.objects_client = cls.persona.namespace_objects_client - def create_objects(self): - # Create namespace for two different projects - namespaces = self.create_namespaces() - - client = self.os_project_admin.namespace_objects_client + def create_objects(self, namespace=None, owner=None, client=None, + is_admin=True): namespace_objects = [] - for ns in namespaces: - if ns['namespace'].startswith(self.project_id): - client = self.os_project_alt_admin.namespace_objects_client - object_name = "object_of_%s" % (ns['namespace']) - namespace_object = client.create_namespace_object( - ns['namespace'], name=object_name, - description=data_utils.arbitrary_string()) - - obj = {'namespace': ns, 'object': namespace_object} - namespace_objects.append(obj) - + if is_admin: + # Create namespace for two different projects + namespaces = self.create_namespaces() + for ns in namespaces: + alt_client = None + if ns['namespace'].startswith(self.alt_project_id): + alt_client = \ + self.os_project_alt_admin.namespace_objects_client + client = alt_client + if alt_client is None: + client = self.os_project_admin.namespace_objects_client + object_name = "object_of_%s" % (ns['namespace']) + namespace_object = client.create_namespace_object( + ns['namespace'], name=object_name, + description=data_utils.arbitrary_string()) + obj = {'namespace': ns, 'object': namespace_object} + namespace_objects.append(obj) + else: + object_name = "object_of_%s" % (namespace['namespace']) + expected_status = exceptions.Forbidden + if (namespace['visibility'] == 'private' and + namespace['owner'] != owner): + expected_status = exceptions.NotFound + self.do_request('create_namespace_object', + expected_status=expected_status, + namespace=namespace['namespace'], + name=object_name, + client=client) return namespace_objects def assertObjectsList(self, actual_obj, client, owner=None): @@ -465,6 +555,47 @@ class MetadefV2RbacObjectsTest(RbacMetadefBase): self.assertIn(actual_obj['object']['name'], resp['objects'][0]['name']) + def assertObjectGet(self, actual_obj, owner, client): + ns = actual_obj['namespace'] + expected_status = 200 + if (ns['visibility'] == 'private' and + ns['owner'] != owner): + expected_status = exceptions.NotFound + + self.do_request('show_namespace_object', + expected_status=expected_status, + namespace=actual_obj['namespace']['namespace'], + object_name=actual_obj['object']['name'], + client=client) + + def assertObjectUpdate(self, actual_object, owner, client): + ns = actual_object['namespace'] + expected_status = exceptions.Forbidden + if (ns['visibility'] == 'private' and + ns['owner'] != owner): + expected_status = exceptions.NotFound + + self.do_request('update_namespace_object', + expected_status=expected_status, + name=actual_object['object']['name'], + description=data_utils.arbitrary_string(), + namespace=actual_object['namespace']['namespace'], + object_name=actual_object['object']['name'], + client=client) + + def assertObjectDelete(self, actual_obj, owner, client): + ns = actual_obj['namespace'] + expected_status = exceptions.Forbidden + if (ns['visibility'] == 'private' and + ns['owner'] != owner): + expected_status = exceptions.NotFound + + self.do_request('delete_namespace_object', + expected_status=expected_status, + namespace=actual_obj['namespace']['namespace'], + object_name=actual_obj['object']['name'], + client=client) + class MetadefV2RbacObjectsTemplate(metaclass=abc.ABCMeta): @@ -513,28 +644,39 @@ class MetadefV2RbacPropertiesTest(RbacMetadefBase): cls.alt_project_id = cls.alt_persona.namespaces_client.project_id cls.properties_client = cls.persona.namespace_properties_client - def create_properties(self): - # Create namespace for two different projects - namespaces = self.create_namespaces() - + def create_properties(self, namespace=None, owner=None, client=None, + is_admin=True): namespace_properties = [] - for ns in namespaces: - alt_client = None - if ns['namespace'].startswith(self.alt_project_id): - alt_client = \ - self.os_project_alt_admin.namespace_properties_client - client = alt_client - if alt_client is None: - client = self.os_project_admin.namespace_properties_client - - property_name = "prop_of_%s" % (ns['namespace']) - namespace_property = client.create_namespace_property( - ns['namespace'], name=property_name, title='property', - type='integer') - - prop = {'namespace': ns, 'property': namespace_property} - namespace_properties.append(prop) - + if is_admin: + # Create namespace for two different projects + namespaces = self.create_namespaces() + for ns in namespaces: + alt_client = None + if ns['namespace'].startswith(self.alt_project_id): + alt_client = \ + self.os_project_alt_admin.namespace_properties_client + client = alt_client + if alt_client is None: + client = self.os_project_admin.namespace_properties_client + property_name = "prop_of_%s" % (ns['namespace']) + namespace_property = client.create_namespace_property( + ns['namespace'], name=property_name, title='property', + type='integer') + prop = {'namespace': ns, 'property': namespace_property} + namespace_properties.append(prop) + else: + property_name = "prop_of_%s" % (namespace['namespace']) + expected_status = exceptions.Forbidden + if not (namespace['visibility'] == 'public' or + namespace['owner'] == owner): + expected_status = exceptions.NotFound + self.do_request('create_namespace_property', + expected_status=expected_status, + namespace=namespace['namespace'], + title="property", + type='integer', + name=property_name, + client=client) return namespace_properties def assertPropertyList(self, actual_prop, client, owner=None): @@ -560,6 +702,43 @@ class MetadefV2RbacPropertiesTest(RbacMetadefBase): self.assertEqual(actual_prop['property']['name'], [*resp['properties']][0]) + def assertPropertyGet(self, actual_prop, client, owner=None): + ns = actual_prop['namespace'] + expected_status = 200 + if (ns['visibility'] != 'public' and ns['owner'] != owner): + expected_status = exceptions.NotFound + self.do_request('show_namespace_properties', + expected_status=expected_status, + namespace=ns['namespace'], + property_name=actual_prop['property']['name'], + client=client) + + def assertPropertyUpdate(self, actual_prop, client, owner=None): + ns = actual_prop['namespace'] + expected_status = exceptions.Forbidden + if (ns['visibility'] != 'public' and ns['owner'] != owner): + expected_status = exceptions.NotFound + self.do_request('update_namespace_properties', + expected_status=expected_status, + name=actual_prop['property']['name'], + description=data_utils.arbitrary_string(), + namespace=ns['namespace'], + property_name=actual_prop['property']['name'], + title="UPDATE_Property", + type="string", + client=client) + + def assertPropertyDelete(self, actual_prop, client, owner=None): + ns = actual_prop['namespace'] + expected_status = exceptions.Forbidden + if (ns['visibility'] != 'public' and ns['owner'] != owner): + expected_status = exceptions.NotFound + self.do_request('delete_namespace_property', + expected_status=expected_status, + namespace=ns['namespace'], + property_name=actual_prop['property']['name'], + client=client) + class MetadefV2RbacPropertiesTemplate(metaclass=abc.ABCMeta): @@ -608,47 +787,71 @@ class MetadefV2RbacTagsTest(RbacMetadefBase): cls.alt_project_id = cls.alt_persona.namespaces_client.project_id cls.tags_client = cls.persona.namespace_tags_client - def create_tags(self, namespaces, multiple_tags=False): + def create_tags(self, namespace=None, client=None, owner=None, + multiple_tags=False, is_admin=True): namespace_tags = [] + if is_admin: + # Create namespace for two different projects + if not multiple_tags: + for ns in namespace: + alt_client = None + if ns['namespace'].startswith(self.alt_project_id): + alt_client = \ + self.os_project_alt_admin.namespace_tags_client + client = alt_client + if alt_client is None: + client = self.os_project_admin.namespace_tags_client + tag_name = "tag_of_%s" % (ns['namespace']) + namespace_tag = client.create_namespace_tag( + ns['namespace'], tag_name=tag_name) - if multiple_tags: - tags = [{"name": "tag1"}, {"name": "tag2"}, {"name": "tag3"}] - for ns in namespaces: - alt_client = None - if ns['namespace'].startswith(self.alt_project_id): - alt_client = \ - self.os_project_alt_admin.namespace_tags_client - client = alt_client - if alt_client is None: - client = self.os_project_admin.namespace_tags_client - multiple_tags = client.create_namespace_tags( + tag = {'namespace': ns, 'tag': namespace_tag} + namespace_tags.append(tag) + else: + tags = [{"name": "tag1"}, {"name": "tag2"}, {"name": "tag3"}] + for ns in namespace: + alt_client = None + if ns['namespace'].startswith(self.alt_project_id): + alt_client = \ + self.os_project_alt_admin.namespace_tags_client + client = alt_client + if alt_client is None: + client = self.os_project_admin.namespace_tags_client + namespace_multiple_tags = client.create_namespace_tags( ns['namespace'], tags=tags) - namespace_multiple_tags = {'namespace': ns, - 'tags': multiple_tags} - namespace_tags.append(namespace_multiple_tags) - else: - for ns in namespaces: - alt_client = None - if ns['namespace'].startswith(self.alt_project_id): - alt_client = \ - self.os_project_alt_admin.namespace_tags_client - client = alt_client - if alt_client is None: - client = self.os_project_admin.namespace_tags_client - tag_name = "tag_of_%s" % (ns['namespace']) - namespace_tag = client.create_namespace_tag( - ns['namespace'], tag_name=tag_name) + multiple_tags = {'namespace': ns, + 'tags': namespace_multiple_tags} + namespace_tags.append(multiple_tags) - tag = {'namespace': ns, 'tag': namespace_tag} - namespace_tags.append(tag) + else: + expected_status = exceptions.Forbidden + if (namespace['visibility'] != 'public' and + namespace['owner'] != owner): + expected_status = exceptions.NotFound + + if multiple_tags: + multiple_tags = [{"name": "tag1"}, {"name": "tag2"}, + {"name": "tag3"}] + self.do_request('create_namespace_tags', + expected_status=expected_status, + namespace=namespace['namespace'], + tags=multiple_tags, + client=client) + else: + tag_name = "tag_of_%s" % (namespace['namespace']) + self.do_request('create_namespace_tag', + expected_status=expected_status, + namespace=namespace['namespace'], + tag_name=tag_name, + client=client) return namespace_tags def assertTagsList(self, actual_tag, client, owner=None): ns = actual_tag['namespace'] if owner: - if not (ns['visibility'] == 'public' or ns['owner'] == owner): + if (ns['visibility'] != 'public' and ns['owner'] != owner): self.do_request('list_namespace_tags', expected_status=exceptions.NotFound, client=client, @@ -668,6 +871,77 @@ class MetadefV2RbacTagsTest(RbacMetadefBase): self.assertEqual(actual_tag['tag']['name'], resp['tags'][0]['name']) + def assertTagGet(self, actual_tag, client, owner=None): + ns = actual_tag['namespace'] + expected_status = 200 + if (ns['visibility'] != 'public' and ns['owner'] != owner): + expected_status = exceptions.NotFound + + self.do_request('show_namespace_tag', + expected_status=expected_status, + namespace=ns['namespace'], + tag_name=actual_tag['tag']['name'], + client=client) + + def assertTagUpdate(self, tag, client, owner): + ns = tag['namespace'] + expected_status = exceptions.Forbidden + if (ns['visibility'] != 'public' and ns['owner'] != owner): + expected_status = exceptions.NotFound + + self.do_request('update_namespace_tag', + expected_status=expected_status, + name=data_utils.arbitrary_string(), + namespace=tag['namespace']['namespace'], + tag_name=tag['tag']['name'], + client=client) + + def assertDeleteTags(self, tag, client, owner=None, multiple_tags=False, + is_admin=True): + if is_admin: + namespace = tag['namespace']['namespace'] + if multiple_tags: + self.do_request('delete_namespace_tags', + expected_status=204, + namespace=namespace, + client=client) + # Verify the tags are deleted successfully + resp = self.do_request('list_namespace_tags', + client=client, + namespace=namespace) + self.assertEqual(0, len(resp['tags'])) + else: + tag_name = tag['tag']['name'] + self.do_request('delete_namespace_tag', + expected_status=204, + namespace=namespace, + tag_name=tag_name, + client=client) + + # Verify the tag is deleted successfully + self.do_request('show_namespace_tag', + expected_status=exceptions.NotFound, + client=client, + namespace=namespace, + tag_name=tag_name) + else: + ns = tag['namespace'] + expected_status = exceptions.Forbidden + if (ns['visibility'] != 'public' and ns['owner'] != owner): + expected_status = exceptions.NotFound + + if multiple_tags: + self.do_request('delete_namespace_tags', + expected_status=expected_status, + namespace=ns['namespace'], + client=client) + else: + self.do_request('delete_namespace_tag', + expected_status=expected_status, + namespace=ns['namespace'], + tag_name=tag['tag']['name'], + client=client) + class MetadefV2RbacTagsTemplate(metaclass=abc.ABCMeta): diff --git a/glance_tempest_plugin/tests/rbac/v2/metadefs/test_namespaces.py b/glance_tempest_plugin/tests/rbac/v2/metadefs/test_namespaces.py new file mode 100644 index 0000000..ddf1993 --- /dev/null +++ b/glance_tempest_plugin/tests/rbac/v2/metadefs/test_namespaces.py @@ -0,0 +1,138 @@ +# 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 tempest.lib.common.utils import data_utils +from tempest.lib import exceptions + +from glance_tempest_plugin.tests.rbac.v2 import base as rbac_base + + +class ProjectAdminTests(rbac_base.MetadefV2RbacNamespaceTest, + rbac_base.MetadefV2RbacNamespaceTemplate): + + credentials = ['project_admin', 'project_alt_admin', 'primary'] + + def test_list_namespaces(self): + actual_namespaces = self.create_namespaces() + + # Get above created namespace by admin role + resp = self.do_request('list_namespaces', expected_status=200, + client=self.namespaces_client) + + self.assertListNamespaces(actual_namespaces, resp) + + def test_get_namespace(self): + actual_namespaces = self.create_namespaces() + + # Get above created namespace by admin role + for ns in actual_namespaces: + resp = self.do_request('show_namespace', expected_status=200, + namespace=ns['namespace'], + client=self.namespaces_client) + self.assertEqual(ns['namespace'], resp['namespace']) + + def test_create_namespace(self): + # As this is been covered in other tests for admin role, + # skipping to test only create namespaces seperately. + pass + + def test_update_namespace(self): + actual_namespaces = self.create_namespaces() + + # Updating the above created namespace by admin role + for ns in actual_namespaces: + resp = self.do_request( + 'update_namespace', expected_status=200, + namespace=ns['namespace'], + client=self.namespaces_client, + description=data_utils.arbitrary_string(base_text="updated")) + self.assertNotEqual(ns['description'], resp['description']) + + def test_delete_namespace(self): + actual_namespaces = self.create_namespaces() + + # Deleting the above created namespace by admin role + for ns in actual_namespaces: + self.do_request('delete_namespace', expected_status=204, + namespace=ns['namespace'], + client=self.namespaces_client,) + + # Verify the namespaces are deleted successfully + self.do_request('show_namespace', + expected_status=exceptions.NotFound, + namespace=ns['namespace'], + client=self.admin_namespace_client,) + + +class ProjectMemberTests(rbac_base.MetadefV2RbacNamespaceTest, + rbac_base.MetadefV2RbacNamespaceTemplate): + + credentials = ['project_member', 'project_alt_member', + 'project_admin', 'project_alt_admin', 'primary'] + + def test_get_namespace(self): + + actual_namespaces = self.create_namespaces() + + # Get namespace - member role from 'project' can access all + # namespaces of it's own & only public namespace of 'alt_project' + for actual_ns in actual_namespaces: + self.assertGetNamespace(actual_ns, self.project_id, + self.namespaces_client) + + def test_list_namespaces(self): + actual_namespaces = self.create_namespaces() + + # List namespace - member role from 'project' can access all + # namespaces of it's own & only public namespace of 'alt_project' + resp = self.do_request('list_namespaces', + client=self.namespaces_client, + expected_status=200) + self.assertListNamespaces(actual_namespaces, resp, self.project_id) + + def test_update_namespace(self): + actual_namespaces = self.create_namespaces() + + # Check member role of 'project' is forbidden to update namespace + for actual_ns in actual_namespaces: + self.assertUpdateNamespace(actual_ns, self.project_id, + self.namespaces_client) + + def test_create_namespace(self): + # Check non-admin role of 'project' not allowed to create namespace + self.do_request('create_namespace', + expected_status=exceptions.Forbidden, + client=self.namespaces_client, + namespace=data_utils.arbitrary_string()) + + def test_delete_namespace(self): + actual_namespaces = self.create_namespaces() + + # Check member role of 'project' is forbidden to delete namespace + for actual_ns in actual_namespaces: + self.assertDeleteNamespace(actual_ns, self.project_id, + self.namespaces_client) + + # Verify the namespaces are not deleted + for actual_ns in actual_namespaces: + self.do_request('show_namespace', + expected_status=200, + client=self.admin_namespace_client, + namespace=actual_ns['namespace']) + + +class ProjectReaderTests(ProjectMemberTests): + credentials = ['project_reader', 'project_alt_reader', + 'project_admin', 'project_alt_admin', 'primary'] diff --git a/glance_tempest_plugin/tests/rbac/v2/metadefs/test_objects.py b/glance_tempest_plugin/tests/rbac/v2/metadefs/test_objects.py new file mode 100644 index 0000000..5467a6d --- /dev/null +++ b/glance_tempest_plugin/tests/rbac/v2/metadefs/test_objects.py @@ -0,0 +1,140 @@ +# 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 tempest.lib.common.utils import data_utils +from tempest.lib import exceptions + +from glance_tempest_plugin.tests.rbac.v2 import base as rbac_base + + +class ProjectAdminTests(rbac_base.MetadefV2RbacObjectsTest, + rbac_base.MetadefV2RbacObjectsTemplate): + + credentials = ['project_admin', 'project_alt_admin', 'primary'] + + def test_get_object(self): + ns_objects = self.create_objects() + + # Get all metadef objects with admin role + for obj in ns_objects: + resp = self.do_request( + 'show_namespace_object', + expected_status=200, + client=self.objects_client, + namespace=obj['namespace']['namespace'], + object_name=obj['object']['name']) + self.assertEqual(obj['object']['name'], resp['name']) + + def test_list_objects(self): + ns_objects = self.create_objects() + + # list all metadef objects with admin role + for obj in ns_objects: + self.assertObjectsList(obj, self.objects_client) + + def test_update_object(self): + ns_objects = self.create_objects() + + # update all metadef objects with admin role of 'project' + for obj in ns_objects: + resp = self.do_request( + 'update_namespace_object', + expected_status=200, + namespace=obj['namespace']['namespace'], + client=self.objects_client, + object_name=obj['object']['name'], + name=obj['object']['name'], + description=data_utils.arbitrary_string(base_text="updated")) + self.assertNotEqual(obj['object']['description'], + resp['description']) + + def test_delete_object(self): + ns_objects = self.create_objects() + # delete all metadef objects with admin role of 'project' + for obj in ns_objects: + self.do_request('delete_namespace_object', + expected_status=204, + namespace=obj['namespace']['namespace'], + object_name=obj['object']['name'], + client=self.objects_client) + + # Verify the object is deleted successfully + self.do_request('show_namespace_object', + expected_status=exceptions.NotFound, + client=self.objects_client, + namespace=obj['namespace']['namespace'], + object_name=obj['object']['name']) + + def test_create_object(self): + # As this is been covered in other tests for admin role, + # skipping to test only create objects seperately. + pass + + +class ProjectMemberTests(rbac_base.MetadefV2RbacObjectsTest, + rbac_base.MetadefV2RbacObjectsTemplate): + + credentials = ['project_member', 'project_alt_member', 'project_admin', + 'project_alt_admin', 'primary'] + + def test_create_object(self): + namespaces = self.create_namespaces() + + # Make sure non admin role of 'project' forbidden to + # create objects + for namespace in namespaces: + self.create_objects(namespace, self.project_id, + self.objects_client, is_admin=False) + + def test_get_object(self): + ns_objects = self.create_objects() + + # Get object - member role from 'project' can access all + # objects of it's own & only objects having public namespace of + # 'alt_project' + for obj in ns_objects: + self.assertObjectGet(obj, self.project_id, self.objects_client) + + def test_list_objects(self): + ns_objects = self.create_objects() + + # list objects - member role from 'project' can access all + # objects of it's own & only objects having public namespace of + # 'alt_project' + for obj in ns_objects: + self.assertObjectsList(obj, self.objects_client, self.project_id) + + def test_update_object(self): + ns_objects = self.create_objects() + + # Make sure non admin role of 'project' not allowed to + # update objects + for obj in ns_objects: + self.assertObjectUpdate(obj, self.project_id, self.objects_client) + + def test_delete_object(self): + ns_objects = self.create_objects() + + # Make sure non admin role of 'project' not allowed to + # delete objects + for obj in ns_objects: + self.assertObjectDelete(obj, self.project_id, self.objects_client) + + +class ProjectReaderTests(ProjectMemberTests): + + credentials = ['project_reader', 'project_alt_reader', 'project_admin', + 'project_alt_admin', 'primary'] diff --git a/glance_tempest_plugin/tests/rbac/v2/metadefs/test_properties.py b/glance_tempest_plugin/tests/rbac/v2/metadefs/test_properties.py new file mode 100644 index 0000000..8b74b40 --- /dev/null +++ b/glance_tempest_plugin/tests/rbac/v2/metadefs/test_properties.py @@ -0,0 +1,143 @@ +# 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 tempest.lib import exceptions + +from glance_tempest_plugin.tests.rbac.v2 import base as rbac_base + + +class ProjectAdminTests(rbac_base.MetadefV2RbacPropertiesTest, + rbac_base.MetadefV2RbacPropertiesTemplate): + + credentials = ['project_admin', 'project_alt_admin', 'primary'] + + def test_create_property(self): + # As this is been covered in other tests for admin role, + # skipping to test only create properties separately. + pass + + def test_get_properties(self): + ns_properties = self.create_properties() + + # Get all metadef properties with admin role of 'project' + for prop in ns_properties: + resp = self.do_request( + 'show_namespace_properties', + expected_status=200, + client=self.properties_client, + namespace=prop['namespace']['namespace'], + property_name=prop['property']['name']) + self.assertEqual(prop['property'], resp) + + def test_list_properties(self): + ns_properties = self.create_properties() + # list all metadef properties with admin role of 'project' + for prop in ns_properties: + self.assertPropertyList(prop, self.properties_client) + + def test_update_properties(self): + ns_properties = self.create_properties() + + # update all metadef properties with admin role of 'project' + for prop in ns_properties: + resp = self.do_request( + 'update_namespace_properties', + expected_status=200, + namespace=prop['namespace']['namespace'], + client=self.properties_client, + title="UPDATE_Property", + property_name=prop['property']['name'], + name=prop['property']['name'], + type="string") + self.assertNotEqual(prop['property']['title'], + resp['title']) + + def test_delete_properties(self): + ns_properties = self.create_properties() + + # delete all metadef properties with admin role of 'project' + for prop in ns_properties: + self.do_request('delete_namespace_property', + expected_status=204, + namespace=prop['namespace']['namespace'], + property_name=prop['property']['name'], + client=self.properties_client) + + # Verify the property is deleted successfully + self.do_request('show_namespace_properties', + expected_status=exceptions.NotFound, + client=self.properties_client, + namespace=prop['namespace']['namespace'], + property_name=prop['property']['name']) + + +class ProjectMemberTests(rbac_base.MetadefV2RbacPropertiesTest, + rbac_base.MetadefV2RbacPropertiesTemplate): + + credentials = ['project_member', 'project_alt_member', + 'project_admin', 'project_alt_admin', 'primary'] + + def test_create_property(self): + namespaces = self.create_namespaces() + + # Make sure non admin role of 'project' forbidden to + # create properties + for namespace in namespaces: + self.create_properties(namespace, self.project_id, + self.properties_client, is_admin=False) + + def test_get_properties(self): + ns_properties = self.create_properties() + + # Get property - member role from 'project' can access all + # properties of it's own & only propertys having public namespace of + # 'alt_project' + for prop in ns_properties: + self.assertPropertyGet(prop, self.properties_client, + self.project_id) + + def test_list_properties(self): + ns_properties = self.create_properties() + + # list properties - member role from 'project' can access all + # properties of it's own & only propertys having public namespace of + # 'alt_project' + for prop in ns_properties: + self.assertPropertyList(prop, self.properties_client, + self.project_id) + + def test_update_properties(self): + ns_properties = self.create_properties() + + # Make sure non admin role of 'project' not allowed to + # update properties + for prop in ns_properties: + self.assertPropertyUpdate(prop, self.properties_client, + self.project_id) + + def test_delete_properties(self): + ns_properties = self.create_properties() + + # Make sure non admin role of 'project' not allowed to + # delete properties + for prop in ns_properties: + self.assertPropertyDelete(prop, self.properties_client, + self.project_id) + + +class ProjectReaderTests(ProjectMemberTests): + + credentials = ['project_reader', 'project_alt_reader', + 'project_admin', 'project_alt_admin', 'primary'] diff --git a/glance_tempest_plugin/tests/rbac/v2/metadefs/test_resource_types.py b/glance_tempest_plugin/tests/rbac/v2/metadefs/test_resource_types.py new file mode 100644 index 0000000..5db6d4b --- /dev/null +++ b/glance_tempest_plugin/tests/rbac/v2/metadefs/test_resource_types.py @@ -0,0 +1,129 @@ +# 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 glance_tempest_plugin.tests.rbac.v2 import base as rbac_base + + +class ProjectAdminTests(rbac_base.MetadefV2RbacResourceTypeTest, + rbac_base.MetadefV2RbacResourceTypeTemplate): + + credentials = ['project_admin', 'project_alt_admin', 'primary'] + + def test_create_resource_type(self): + # As this is been covered in other tests for admin role, + # skipping to test only create resource types separately. + pass + + def test_get_resource_type(self): + ns_rs_types = self.create_resource_types() + + # Get all metadef resource types with admin role of 'project' + for rs_type in ns_rs_types: + resp = self.do_request( + 'list_resource_type_association', + expected_status=200, + client=self.resource_types_client, + namespace_id=rs_type['namespace']['namespace']) + self.assertEqual(rs_type['resource_type']['name'], + resp['resource_type_associations'][0]['name']) + + def test_list_resource_types(self): + ns_rs_types = self.create_resource_types() + + # list resource types - with admin role of 'project' + resp = self.do_request('list_resource_types', + expected_status=200, + client=self.resource_types_client) + + # Verify that admin role of 'project' will be able to view available + # resource types + self.assertRSTypeList(ns_rs_types, resp) + + def test_delete_resource_type(self): + ns_rs_types = self.create_resource_types() + + # delete all metadef resource types with admin role of 'project' + for rs_type in ns_rs_types: + self.do_request('delete_resource_type_association', + expected_status=204, + namespace_id=rs_type['namespace']['namespace'], + resource_name=rs_type['resource_type']['name'], + client=self.resource_types_client) + + # Verify the resource types is deleted successfully + resp = self.do_request( + 'list_resource_type_association', expected_status=200, + client=self.resource_types_client, + namespace_id=rs_type['namespace']['namespace']) + self.assertEqual([], resp['resource_type_associations']) + + +class ProjectMemberTests(rbac_base.MetadefV2RbacResourceTypeTest, + rbac_base.MetadefV2RbacResourceTypeTemplate): + + credentials = ['project_member', 'project_alt_member', 'project_admin', + 'project_alt_admin', 'primary'] + + def test_create_resource_type(self): + namespaces = self.create_namespaces() + + # Make sure non admin role of 'project' forbidden to + # create resource types + for namespace in namespaces: + self.create_resource_types(namespace, self.project_id, + self.resource_types_client, + is_admin=False) + + def test_get_resource_type(self): + ns_rs_types = self.create_resource_types() + + # Get resource type - member role from 'project' can access all + # resource types of it's own & only resource types having public + # namespace of 'alt_project' + for rs_type in ns_rs_types: + self.assertRSTypeGet(rs_type, self.resource_types_client, + self.project_id) + + def test_list_resource_types(self): + ns_rs_types = self.create_resource_types() + + # list resource types - with member role of 'project' + resp = self.do_request('list_resource_types', + expected_status=200, + client=self.resource_types_client) + + # Verify that member role of 'project' will be able to view available + # resource types + self.assertRSTypeList(ns_rs_types, resp) + + # list resource types - with member role of 'alt_project' + resp = self.do_request('list_resource_types', + expected_status=200, + client=self.resource_types_client) + + def test_delete_resource_type(self): + ns_rs_types = self.create_resource_types() + + # Make sure non admin role of 'project' not allowed to + # delete resource types + for rs_type in ns_rs_types: + self.assertRSTypeDelete(rs_type, self.resource_types_client, + self.project_id) + + +class ProjectReaderTests(ProjectMemberTests): + + credentials = ['project_reader', 'project_alt_reader', 'project_admin', + 'project_alt_admin', 'primary'] diff --git a/glance_tempest_plugin/tests/rbac/v2/metadefs/test_tags.py b/glance_tempest_plugin/tests/rbac/v2/metadefs/test_tags.py new file mode 100644 index 0000000..4aeb661 --- /dev/null +++ b/glance_tempest_plugin/tests/rbac/v2/metadefs/test_tags.py @@ -0,0 +1,153 @@ +# 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 tempest.lib.common.utils import data_utils + +from glance_tempest_plugin.tests.rbac.v2 import base as rbac_base + + +class ProjectAdminTests(rbac_base.MetadefV2RbacTagsTest, + rbac_base.MetadefV2RbacTagsTemplate): + + credentials = ['project_admin', 'project_alt_admin', 'primary'] + + def test_create_tag(self): + # As this is been covered in other tests for admin role, + # skipping to test only create properties separately. + pass + + def test_get_tags(self): + namespaces = self.create_namespaces() + ns_tags = self.create_tags(namespaces) + + # Get all metadef tags with admin role of 'project' + for tag in ns_tags: + resp = self.do_request( + 'show_namespace_tag', + expected_status=200, + client=self.tags_client, + namespace=tag['namespace']['namespace'], + tag_name=tag['tag']['name']) + self.assertEqual(tag['tag']['name'], resp['name']) + + def test_list_tags(self): + namespaces = self.create_namespaces() + ns_tags = self.create_tags(namespaces) + # list all metadef tags with admin role of 'project' + for tag in ns_tags: + self.assertTagsList(tag, self.tags_client) + + def test_update_tags(self): + namespaces = self.create_namespaces() + ns_tags = self.create_tags(namespaces) + + # update all metadef tags with admin role of 'project' + for tag in ns_tags: + resp = self.do_request( + 'update_namespace_tag', + expected_status=200, + namespace=tag['namespace']['namespace'], + client=self.tags_client, + tag_name=tag['tag']['name'], + name=data_utils.arbitrary_string(base_text="updated-name")) + self.assertNotEqual(tag['tag']['name'], resp['name']) + + def test_delete_tags(self): + namespaces = self.create_namespaces() + ns_tags = self.create_tags(namespaces) + + # delete all metadef tags with admin role of 'project' + for tag in ns_tags: + self.assertDeleteTags(tag, self.tags_client) + + # Create multiple tags + ns_multiple_tags = self.create_tags(namespaces, multiple_tags=True) + # delete all metadef multiple tags with admin role of 'project' + for tags in ns_multiple_tags: + self.assertDeleteTags(tags, self.tags_client, multiple_tags=True) + + +class ProjectMemberTests(rbac_base.MetadefV2RbacTagsTest, + rbac_base.MetadefV2RbacTagsTemplate): + + credentials = ['project_member', 'project_alt_member', + 'project_admin', 'project_alt_admin', 'primary'] + + def test_create_tag(self): + namespaces = self.create_namespaces() + + # Make sure non admin role of 'project' forbidden to + # create tags + for namespace in namespaces: + self.create_tags(namespace, self.tags_client, self.project_id, + is_admin=False) + + # Create Multiple Tags + self.create_tags(namespace, self.tags_client, self.project_id, + multiple_tags=True, is_admin=False) + + def test_get_tags(self): + namespaces = self.create_namespaces() + ns_tags = self.create_tags(namespaces) + + # Get tag - member role from 'project' can access all + # tags of it's own & only tags having public namespace of + # 'alt_project' + for tag in ns_tags: + self.assertTagGet(tag, self.tags_client, self.project_id) + + def test_list_tags(self): + namespaces = self.create_namespaces() + ns_tags = self.create_tags(namespaces) + + # list tags - member role from 'project' can access all + # tags of it's own & only tags having public namespace of + # 'alt_project' + for tag in ns_tags: + self.assertTagsList(tag, self.tags_client, self.project_id) + + def test_update_tags(self): + namespaces = self.create_namespaces() + ns_tags = self.create_tags(namespaces) + + # Make sure non admin role of 'project' not allowed to + # update tags + for tag in ns_tags: + self.assertTagUpdate(tag, self.tags_client, self.project_id) + + def test_delete_tags(self): + namespaces = self.create_namespaces() + ns_tags = self.create_tags(namespaces) + + # Make sure non admin role of 'project' not allowed to + # delete tags + for tag in ns_tags: + self.assertDeleteTags(tag, self.tags_client, self.project_id, + is_admin=False) + + # Create Multiple Tags + ns_multiple_tags = self.create_tags(namespaces, multiple_tags=True) + # Make sure non admin role of 'project' not allowed to + # delete multiple tags + for tags in ns_multiple_tags: + self.assertDeleteTags(tags, self.tags_client, self.project_id, + multiple_tags=True, is_admin=False) + + +class ProjectReaderTests(ProjectMemberTests): + + credentials = ['project_reader', 'project_alt_reader', + 'project_admin', 'project_alt_admin', 'primary']