From d2cbd3ad4cfa808856fe448541eb752a248dbce9 Mon Sep 17 00:00:00 2001 From: Colleen Murphy Date: Tue, 17 Dec 2019 16:31:14 -0800 Subject: [PATCH] Add Access Rules tests Access rules for application credentials were added in the Train release of keystone. This change adds related CRUD tests for access rules. See the API reference for additional information[1]. [1] https://docs.openstack.org/api-ref/identity/v3/index.html#application-credentials Change-Id: Ifd4ff2245277c2c57f5ccf450923434c0277a724 --- tempest/api/identity/base.py | 1 + tempest/api/identity/v3/test_access_rules.py | 84 ++++++++++++++++ .../v3/test_application_credentials.py | 21 ++++ tempest/clients.py | 2 + tempest/config.py | 5 + tempest/lib/services/identity/v3/__init__.py | 9 +- .../identity/v3/access_rules_client.py | 68 +++++++++++++ .../identity/v3/test_access_rules_client.py | 97 +++++++++++++++++++ 8 files changed, 284 insertions(+), 3 deletions(-) create mode 100644 tempest/api/identity/v3/test_access_rules.py create mode 100644 tempest/lib/services/identity/v3/access_rules_client.py create mode 100644 tempest/tests/lib/services/identity/v3/test_access_rules_client.py diff --git a/tempest/api/identity/base.py b/tempest/api/identity/base.py index 282343c380..5722f0e73e 100644 --- a/tempest/api/identity/base.py +++ b/tempest/api/identity/base.py @@ -192,6 +192,7 @@ class BaseIdentityV3Test(BaseIdentityTest): cls.os_primary.identity_versions_v3_client cls.non_admin_app_creds_client = \ cls.os_primary.application_credentials_client + cls.non_admin_access_rules_client = cls.os_primary.access_rules_client class BaseIdentityV3AdminTest(BaseIdentityV3Test): diff --git a/tempest/api/identity/v3/test_access_rules.py b/tempest/api/identity/v3/test_access_rules.py new file mode 100644 index 0000000000..608eb5966e --- /dev/null +++ b/tempest/api/identity/v3/test_access_rules.py @@ -0,0 +1,84 @@ +# Copyright 2019 SUSE LLC +# +# 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.api.identity import base +from tempest import config +from tempest.lib.common.utils import data_utils +from tempest.lib import decorators +from tempest.lib import exceptions as lib_exc + +CONF = config.CONF + + +class AccessRulesV3Test(base.BaseIdentityV3Test): + + @classmethod + def skip_checks(cls): + super(AccessRulesV3Test, cls).skip_checks() + if not CONF.identity_feature_enabled.access_rules: + raise cls.skipException("Application credential access rules are " + "not available in this environment") + + @classmethod + def resource_setup(cls): + super(AccessRulesV3Test, cls).resource_setup() + cls.user_id = cls.os_primary.credentials.user_id + cls.project_id = cls.os_primary.credentials.project_id + + def setUp(self): + super(AccessRulesV3Test, self).setUp() + ac = self.non_admin_app_creds_client + access_rules = [ + { + "path": "/v2.1/servers/*/ips", + "method": "GET", + "service": "compute" + } + ] + self.app_cred = ac.create_application_credential( + self.user_id, + name=data_utils.rand_name('application_credential'), + access_rules=access_rules + )['application_credential'] + + @decorators.idempotent_id('2354c498-5119-4ba5-9f0d-44f16f78fb0e') + def test_list_access_rules(self): + ar = self.non_admin_access_rules_client.list_access_rules(self.user_id) + self.assertEqual(1, len(ar['access_rules'])) + + @decorators.idempotent_id('795dd507-ca1e-40e9-ba90-ff0a08689ba4') + def test_show_access_rule(self): + access_rule_id = self.app_cred['access_rules'][0]['id'] + self.non_admin_access_rules_client.show_access_rule( + self.user_id, access_rule_id) + + @decorators.idempotent_id('278757e9-e193-4bf8-adf2-0b0a229a17d0') + def test_delete_access_rule(self): + access_rule_id = self.app_cred['access_rules'][0]['id'] + app_cred_id = self.app_cred['id'] + self.assertRaises( + lib_exc.Forbidden, + self.non_admin_access_rules_client.delete_access_rule, + self.user_id, + access_rule_id) + self.non_admin_app_creds_client.delete_application_credential( + self.user_id, app_cred_id) + ar = self.non_admin_access_rules_client.list_access_rules(self.user_id) + self.assertEqual(1, len(ar['access_rules'])) + self.non_admin_access_rules_client.delete_access_rule( + self.user_id, access_rule_id) + ar = self.non_admin_access_rules_client.list_access_rules(self.user_id) + self.assertEqual(0, len(ar['access_rules'])) diff --git a/tempest/api/identity/v3/test_application_credentials.py b/tempest/api/identity/v3/test_application_credentials.py index 1cee902bd0..152e3a6dcb 100644 --- a/tempest/api/identity/v3/test_application_credentials.py +++ b/tempest/api/identity/v3/test_application_credentials.py @@ -19,8 +19,11 @@ import datetime from oslo_utils import timeutils from tempest.api.identity import base +from tempest import config from tempest.lib import decorators +CONF = config.CONF + class ApplicationCredentialsV3Test(base.BaseApplicationCredentialsV3Test): @@ -62,6 +65,24 @@ class ApplicationCredentialsV3Test(base.BaseApplicationCredentialsV3Test): expires_str = expires_at.isoformat() self.assertEqual(expires_str, app_cred['expires_at']) + @decorators.idempotent_id('529936eb-aa5d-463d-9f79-01c113d3b88f') + def test_create_application_credential_access_rules(self): + if not CONF.identity_feature_enabled.access_rules: + raise self.skipException("Application credential access rules are " + "not available in this environment") + access_rules = [ + { + "path": "/v2.1/servers/*/ips", + "method": "GET", + "service": "compute" + } + ] + app_cred = self.create_application_credential( + access_rules=access_rules) + access_rule_resp = app_cred['access_rules'][0] + access_rule_resp.pop('id') + self.assertDictEqual(access_rules[0], access_rule_resp) + @decorators.idempotent_id('ff0cd457-6224-46e7-b79e-0ada4964a8a6') def test_list_application_credentials(self): self.create_application_credential() diff --git a/tempest/clients.py b/tempest/clients.py index 6aed92e8e0..f0ba67e250 100644 --- a/tempest/clients.py +++ b/tempest/clients.py @@ -203,6 +203,8 @@ class Manager(clients.ServiceClients): **params_v3) self.application_credentials_client = \ self.identity_v3.ApplicationCredentialsClient(**params_v3) + self.access_rules_client = \ + self.identity_v3.AccessRulesClient(**params_v3) # Token clients do not use the catalog. They only need default_params. # They read auth_url, so they should only be set if the corresponding diff --git a/tempest/config.py b/tempest/config.py index 32cebc58f3..5087c85352 100644 --- a/tempest/config.py +++ b/tempest/config.py @@ -252,6 +252,11 @@ IdentityFeatureGroup = [ default=False, help='Does the environment have application credentials ' 'enabled?'), + # Access rules for application credentials is a default feature in Train. + # This config option can removed once Stein is EOL. + cfg.BoolOpt('access_rules', + default=False, + help='Does the environment have access rules enabled?'), cfg.BoolOpt('immutable_user_source', default=False, help='Set to True if the environment has a read-only ' diff --git a/tempest/lib/services/identity/v3/__init__.py b/tempest/lib/services/identity/v3/__init__.py index da1c51c4ee..86fa991625 100644 --- a/tempest/lib/services/identity/v3/__init__.py +++ b/tempest/lib/services/identity/v3/__init__.py @@ -12,6 +12,8 @@ # License for the specific language governing permissions and limitations under # the License. +from tempest.lib.services.identity.v3.access_rules_client import \ + AccessRulesClient from tempest.lib.services.identity.v3.application_credentials_client import \ ApplicationCredentialsClient from tempest.lib.services.identity.v3.catalog_client import \ @@ -48,9 +50,10 @@ from tempest.lib.services.identity.v3.trusts_client import TrustsClient from tempest.lib.services.identity.v3.users_client import UsersClient from tempest.lib.services.identity.v3.versions_client import VersionsClient -__all__ = ['ApplicationCredentialsClient', 'CatalogClient', - 'CredentialsClient', 'DomainsClient', 'DomainConfigurationClient', - 'EndPointGroupsClient', 'EndPointsClient', 'EndPointsFilterClient', +__all__ = ['AccessRulesClient', 'ApplicationCredentialsClient', + 'CatalogClient', 'CredentialsClient', 'DomainsClient', + 'DomainConfigurationClient', 'EndPointGroupsClient', + 'EndPointsClient', 'EndPointsFilterClient', 'GroupsClient', 'IdentityClient', 'InheritedRolesClient', 'OAUTHConsumerClient', 'OAUTHTokenClient', 'PoliciesClient', 'ProjectsClient', 'ProjectTagsClient', 'RegionsClient', diff --git a/tempest/lib/services/identity/v3/access_rules_client.py b/tempest/lib/services/identity/v3/access_rules_client.py new file mode 100644 index 0000000000..4f13e479cc --- /dev/null +++ b/tempest/lib/services/identity/v3/access_rules_client.py @@ -0,0 +1,68 @@ +# Copyright 2019 SUSE LLC +# +# 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. + +""" +https://docs.openstack.org/api-ref/identity/v3/index.html#application-credentials +""" + +from oslo_serialization import jsonutils as json +from six.moves.urllib import parse as urllib + +from tempest.lib.common import rest_client + + +class AccessRulesClient(rest_client.RestClient): + api_version = "v3" + + def show_access_rule(self, user_id, access_rule_id): + """Gets details of an access rule. + + For a full list of available parameters, please refer to the official + API reference: + https://docs.openstack.org/api-ref/identity/v3/index.html#show-access-rule-details + """ + resp, body = self.get('users/%s/access_rules/%s' % + (user_id, access_rule_id)) + self.expected_success(200, resp.status) + body = json.loads(body) + return rest_client.ResponseBody(resp, body) + + def list_access_rules(self, user_id, **params): + """Lists out all of a user's access rules. + + For a full list of available parameters, please refer to the official + API reference: + https://docs.openstack.org/api-ref/identity/v3/index.html#list-access-rules + """ + url = 'users/%s/access_rules' % user_id + if params: + url += '?%s' % urllib.urlencode(params) + resp, body = self.get(url) + self.expected_success(200, resp.status) + body = json.loads(body) + return rest_client.ResponseBody(resp, body) + + def delete_access_rule(self, user_id, access_rule_id): + """Deletes an access rule. + + For a full list of available parameters, please refer to the official + API reference: + https://docs.openstack.org/api-ref/identity/v3/index.html#delete-access-rule + """ + resp, body = self.delete('users/%s/access_rules/%s' % + (user_id, access_rule_id)) + self.expected_success(204, resp.status) + return rest_client.ResponseBody(resp, body) diff --git a/tempest/tests/lib/services/identity/v3/test_access_rules_client.py b/tempest/tests/lib/services/identity/v3/test_access_rules_client.py new file mode 100644 index 0000000000..71c9cde2b5 --- /dev/null +++ b/tempest/tests/lib/services/identity/v3/test_access_rules_client.py @@ -0,0 +1,97 @@ +# Copyright 2019 SUSE LLC +# +# 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.services.identity.v3 import access_rules_client +from tempest.tests.lib import fake_auth_provider +from tempest.tests.lib.services import base + + +class TestAccessRulesClient(base.BaseServiceTest): + FAKE_LIST_ACCESS_RULES = { + "links": { + "self": "https://example.com/identity/v3/users/" + + "3e0716ae/access_rules", + "previous": None, + "next": None + }, + "access_rules": [ + { + "path": "/v2.0/metrics", + "links": { + "self": "https://example.com/identity/v3/access_rules/" + + "07d719df00f349ef8de77d542edf010c" + }, + "id": "07d719df00f349ef8de77d542edf010c", + "service": "monitoring", + "method": "GET" + } + ] + } + + FAKE_ACCESS_RULE_INFO = { + "access_rule": { + "path": "/v2.0/metrics", + "links": { + "self": "https://example.com/identity/v3/access_rules/" + + "07d719df00f349ef8de77d542edf010c" + }, + "id": "07d719df00f349ef8de77d542edf010c", + "service": "monitoring", + "method": "GET" + } + } + + def setUp(self): + super(TestAccessRulesClient, self).setUp() + fake_auth = fake_auth_provider.FakeAuthProvider() + self.client = access_rules_client.AccessRulesClient( + fake_auth, 'identity', 'regionOne') + + def _test_show_access_rule(self, bytes_body=False): + self.check_service_client_function( + self.client.show_access_rule, + 'tempest.lib.common.rest_client.RestClient.get', + self.FAKE_ACCESS_RULE_INFO, + bytes_body, + user_id="123456", + access_rule_id="5499a186") + + def _test_list_access_rules(self, bytes_body=False): + self.check_service_client_function( + self.client.list_access_rules, + 'tempest.lib.common.rest_client.RestClient.get', + self.FAKE_LIST_ACCESS_RULES, + bytes_body, + user_id="123456") + + def test_show_access_rule_with_str_body(self): + self._test_show_access_rule() + + def test_show_access_rule_with_bytes_body(self): + self._test_show_access_rule(bytes_body=True) + + def test_list_access_rule_with_str_body(self): + self._test_list_access_rules() + + def test_list_access_rule_with_bytes_body(self): + self._test_list_access_rules(bytes_body=True) + + def test_delete_access_rule(self): + self.check_service_client_function( + self.client.delete_access_rule, + 'tempest.lib.common.rest_client.RestClient.delete', + {}, + user_id="123456", + access_rule_id="5499a186", + status=204)