Add tests for application credentials
Application credentials were implemented in keystone in Queens. This patch adds test for create, retrieval, and deleting application credentials and ensures that application credentials that are created can be used for authentication. Updating application credentials is not supported. bp application-credentials Change-Id: I3272fee2881fb918fe83961774f4bd27e30cee02
This commit is contained in:
parent
9c48584e01
commit
0e52d4e706
@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
[`blueprint application-credentials <https://blueprints.launchpad.net/keystone/+spec/application-credentials>`_]
|
||||
Tempest can test keystone's application credentials interface. A new client
|
||||
library is added for application credentials, and a new config option,
|
||||
``[identity-feature-enabled]/application_credentials``, can control whether
|
||||
the application credentials feature is tested (defaults to False,
|
||||
indicating the feature is not enabled in the cloud under test).
|
@ -0,0 +1,48 @@
|
||||
# Copyright 2018 SUSE Linux GmbH
|
||||
#
|
||||
# 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 import decorators
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class ApplicationCredentialsV3AdminTest(base.BaseApplicationCredentialsV3Test,
|
||||
base.BaseIdentityV3AdminTest):
|
||||
|
||||
@decorators.idempotent_id('3b3dd48f-3388-406a-a9e6-4d078a552d0e')
|
||||
def test_create_application_credential_with_roles(self):
|
||||
role = self.setup_test_role()
|
||||
self.os_admin.roles_v3_client.create_user_role_on_project(
|
||||
self.project_id,
|
||||
self.user_id,
|
||||
role['id']
|
||||
)
|
||||
|
||||
app_cred = self.create_application_credential(
|
||||
roles=[{'id': role['id']}])
|
||||
secret = app_cred['secret']
|
||||
|
||||
# Check that the application credential is functional
|
||||
token_id, resp = self.non_admin_token.get_token(
|
||||
app_cred_id=app_cred['id'],
|
||||
app_cred_secret=secret,
|
||||
auth_data=True
|
||||
)
|
||||
self.assertEqual(resp['project']['id'], self.project_id)
|
||||
self.assertEqual(resp['roles'][0]['id'], role['id'])
|
@ -190,6 +190,8 @@ class BaseIdentityV3Test(BaseIdentityTest):
|
||||
cls.non_admin_catalog_client = cls.os_primary.catalog_client
|
||||
cls.non_admin_versions_client =\
|
||||
cls.os_primary.identity_versions_v3_client
|
||||
cls.non_admin_app_creds_client = \
|
||||
cls.os_primary.application_credentials_client
|
||||
|
||||
|
||||
class BaseIdentityV3AdminTest(BaseIdentityV3Test):
|
||||
@ -289,3 +291,30 @@ class BaseIdentityV3AdminTest(BaseIdentityV3Test):
|
||||
test_utils.call_and_ignore_notfound_exc,
|
||||
self.delete_domain, domain['id'])
|
||||
return domain
|
||||
|
||||
|
||||
class BaseApplicationCredentialsV3Test(BaseIdentityV3Test):
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(BaseApplicationCredentialsV3Test, cls).skip_checks()
|
||||
if not CONF.identity_feature_enabled.application_credentials:
|
||||
raise cls.skipException("Application credentials are not available"
|
||||
" in this environment")
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(BaseApplicationCredentialsV3Test, cls).resource_setup()
|
||||
cls.user_id = cls.os_primary.credentials.user_id
|
||||
cls.project_id = cls.os_primary.credentials.project_id
|
||||
|
||||
def create_application_credential(self, name=None, **kwargs):
|
||||
name = name or data_utils.rand_name('application_credential')
|
||||
application_credential = (
|
||||
self.non_admin_app_creds_client.create_application_credential(
|
||||
self.user_id, name=name, **kwargs))['application_credential']
|
||||
self.addCleanup(
|
||||
self.non_admin_app_creds_client.delete_application_credential,
|
||||
self.user_id,
|
||||
application_credential['id'])
|
||||
return application_credential
|
||||
|
85
tempest/api/identity/v3/test_application_credentials.py
Normal file
85
tempest/api/identity/v3/test_application_credentials.py
Normal file
@ -0,0 +1,85 @@
|
||||
# Copyright 2018 SUSE Linux GmbH
|
||||
#
|
||||
# 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.
|
||||
|
||||
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):
|
||||
|
||||
def _list_app_creds(self, name=None):
|
||||
kwargs = dict(user_id=self.user_id)
|
||||
if name:
|
||||
kwargs.update(name=name)
|
||||
return self.non_admin_app_creds_client.list_application_credentials(
|
||||
**kwargs)['application_credentials']
|
||||
|
||||
@decorators.idempotent_id('8080c75c-eddc-4786-941a-c2da7039ae61')
|
||||
def test_create_application_credential(self):
|
||||
app_cred = self.create_application_credential()
|
||||
|
||||
# Check that the secret appears in the create response
|
||||
secret = app_cred['secret']
|
||||
|
||||
# Check that the secret is not retrievable after initial create
|
||||
app_cred = self.non_admin_app_creds_client.show_application_credential(
|
||||
user_id=self.user_id,
|
||||
application_credential_id=app_cred['id']
|
||||
)['application_credential']
|
||||
self.assertNotIn('secret', app_cred)
|
||||
|
||||
# Check that the application credential is functional
|
||||
token_id, resp = self.non_admin_token.get_token(
|
||||
app_cred_id=app_cred['id'],
|
||||
app_cred_secret=secret,
|
||||
auth_data=True
|
||||
)
|
||||
self.assertEqual(resp['project']['id'], self.project_id)
|
||||
|
||||
@decorators.idempotent_id('852daf0c-42b5-4239-8466-d193d0543ed3')
|
||||
def test_create_application_credential_expires(self):
|
||||
expires_at = timeutils.utcnow() + datetime.timedelta(hours=1)
|
||||
|
||||
app_cred = self.create_application_credential(expires_at=expires_at)
|
||||
|
||||
expires_str = expires_at.isoformat()
|
||||
self.assertEqual(expires_str, app_cred['expires_at'])
|
||||
|
||||
@decorators.idempotent_id('ff0cd457-6224-46e7-b79e-0ada4964a8a6')
|
||||
def test_list_application_credentials(self):
|
||||
self.create_application_credential()
|
||||
self.create_application_credential()
|
||||
|
||||
app_creds = self._list_app_creds()
|
||||
self.assertEqual(2, len(app_creds))
|
||||
|
||||
@decorators.idempotent_id('9bb5e5cc-5250-493a-8869-8b665f6aa5f6')
|
||||
def test_query_application_credentials(self):
|
||||
self.create_application_credential()
|
||||
app_cred_two = self.create_application_credential()
|
||||
app_cred_two_name = app_cred_two['name']
|
||||
|
||||
app_creds = self._list_app_creds(name=app_cred_two_name)
|
||||
self.assertEqual(1, len(app_creds))
|
||||
self.assertEqual(app_cred_two_name, app_creds[0]['name'])
|
@ -199,6 +199,8 @@ class Manager(clients.ServiceClients):
|
||||
self.catalog_client = self.identity_v3.CatalogClient(**params_v3)
|
||||
self.project_tags_client = self.identity_v3.ProjectTagsClient(
|
||||
**params_v3)
|
||||
self.application_credentials_client = \
|
||||
self.identity_v3.ApplicationCredentialsClient(**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
|
||||
|
@ -240,7 +240,13 @@ IdentityFeatureGroup = [
|
||||
'settings enabled?'),
|
||||
cfg.BoolOpt('project_tags',
|
||||
default=False,
|
||||
help='Is the project tags identity v3 API available?')
|
||||
help='Is the project tags identity v3 API available?'),
|
||||
# Application credentials is a default feature in Queens. This config
|
||||
# option can removed once Pike is EOL.
|
||||
cfg.BoolOpt('application_credentials',
|
||||
default=False,
|
||||
help='Does the environment have application credentials '
|
||||
'enabled?')
|
||||
]
|
||||
|
||||
compute_group = cfg.OptGroup(name='compute',
|
||||
|
@ -12,6 +12,8 @@
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
|
||||
from tempest.lib.services.identity.v3.application_credentials_client import \
|
||||
ApplicationCredentialsClient
|
||||
from tempest.lib.services.identity.v3.catalog_client import \
|
||||
CatalogClient
|
||||
from tempest.lib.services.identity.v3.credentials_client import \
|
||||
@ -46,11 +48,11 @@ 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__ = ['CatalogClient', 'CredentialsClient', 'DomainsClient',
|
||||
'DomainConfigurationClient', 'EndPointGroupsClient',
|
||||
'EndPointsClient', 'EndPointsFilterClient', 'GroupsClient',
|
||||
'IdentityClient', 'InheritedRolesClient', 'OAUTHConsumerClient',
|
||||
'OAUTHTokenClient', 'PoliciesClient', 'ProjectsClient',
|
||||
'ProjectTagsClient', 'RegionsClient', 'RoleAssignmentsClient',
|
||||
'RolesClient', 'ServicesClient', 'V3TokenClient', 'TrustsClient',
|
||||
'UsersClient', 'VersionsClient']
|
||||
__all__ = ['ApplicationCredentialsClient', 'CatalogClient',
|
||||
'CredentialsClient', 'DomainsClient', 'DomainConfigurationClient',
|
||||
'EndPointGroupsClient', 'EndPointsClient', 'EndPointsFilterClient',
|
||||
'GroupsClient', 'IdentityClient', 'InheritedRolesClient',
|
||||
'OAUTHConsumerClient', 'OAUTHTokenClient', 'PoliciesClient',
|
||||
'ProjectsClient', 'ProjectTagsClient', 'RegionsClient',
|
||||
'RoleAssignmentsClient', 'RolesClient', 'ServicesClient',
|
||||
'V3TokenClient', 'TrustsClient', 'UsersClient', 'VersionsClient']
|
||||
|
@ -0,0 +1,83 @@
|
||||
# Copyright 2018 SUSE Linux GmbH
|
||||
#
|
||||
# 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://developer.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 ApplicationCredentialsClient(rest_client.RestClient):
|
||||
api_version = "v3"
|
||||
|
||||
def create_application_credential(self, user_id, **kwargs):
|
||||
"""Creates an application credential.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://developer.openstack.org/api-ref/identity/v3/index.html#create-application-credential
|
||||
"""
|
||||
post_body = json.dumps({'application_credential': kwargs})
|
||||
resp, body = self.post('users/%s/application_credentials' % user_id,
|
||||
post_body)
|
||||
self.expected_success(201, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def show_application_credential(self, user_id, application_credential_id):
|
||||
"""Gets details of an application credential.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://developer.openstack.org/api-ref/identity/v3/index.html#show-application-credential-details
|
||||
"""
|
||||
resp, body = self.get('users/%s/application_credentials/%s' %
|
||||
(user_id, application_credential_id))
|
||||
self.expected_success(200, resp.status)
|
||||
body = json.loads(body)
|
||||
return rest_client.ResponseBody(resp, body)
|
||||
|
||||
def list_application_credentials(self, user_id, **params):
|
||||
"""Lists out all of a user's application credentials.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://developer.openstack.org/api-ref/identity/v3/index.html#list-application-credentials
|
||||
"""
|
||||
url = 'users/%s/application_credentials' % 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_application_credential(self, user_id,
|
||||
application_credential_id):
|
||||
"""Deletes an application credential.
|
||||
|
||||
For a full list of available parameters, please refer to the official
|
||||
API reference:
|
||||
https://developer.openstack.org/api-ref/identity/v3/index.html#delete-application-credential
|
||||
"""
|
||||
resp, body = self.delete('users/%s/application_credentials/%s' %
|
||||
(user_id, application_credential_id))
|
||||
self.expected_success(204, resp.status)
|
||||
return rest_client.ResponseBody(resp, body)
|
@ -51,7 +51,8 @@ class V3TokenClient(rest_client.RestClient):
|
||||
def auth(self, user_id=None, username=None, password=None, project_id=None,
|
||||
project_name=None, user_domain_id=None, user_domain_name=None,
|
||||
project_domain_id=None, project_domain_name=None, domain_id=None,
|
||||
domain_name=None, token=None):
|
||||
domain_name=None, token=None, app_cred_id=None,
|
||||
app_cred_secret=None):
|
||||
"""Obtains a token from the authentication service
|
||||
|
||||
:param user_id: user id
|
||||
@ -109,6 +110,13 @@ class V3TokenClient(rest_client.RestClient):
|
||||
if _domain:
|
||||
id_obj['password']['user']['domain'] = _domain
|
||||
|
||||
if app_cred_id and app_cred_secret:
|
||||
id_obj['methods'].append('application_credential')
|
||||
id_obj['application_credential'] = {
|
||||
'id': app_cred_id,
|
||||
'secret': app_cred_secret,
|
||||
}
|
||||
|
||||
if (project_id or project_name):
|
||||
_project = dict()
|
||||
|
||||
|
@ -0,0 +1,156 @@
|
||||
# Copyright 2018 SUSE Linux GmbH
|
||||
#
|
||||
# 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 application_credentials_client
|
||||
from tempest.tests.lib import fake_auth_provider
|
||||
from tempest.tests.lib.services import base
|
||||
|
||||
|
||||
class TestApplicationCredentialsClient(base.BaseServiceTest):
|
||||
FAKE_CREATE_APP_CRED = {
|
||||
"application_credential": {
|
||||
"description": "fake application credential",
|
||||
"roles": [
|
||||
{
|
||||
"id": "c60fdd45",
|
||||
"domain_id": None,
|
||||
"name": "Member"
|
||||
}
|
||||
],
|
||||
"expires_at": "2019-02-27T18:30:59.999999Z",
|
||||
"secret": "_BVq0xU5L",
|
||||
"unrestricted": None,
|
||||
"project_id": "ddef321",
|
||||
"id": "5499a186",
|
||||
"name": "one"
|
||||
}
|
||||
}
|
||||
|
||||
FAKE_LIST_APP_CREDS = {
|
||||
"application_credentials": [
|
||||
{
|
||||
"description": "fake application credential",
|
||||
"roles": [
|
||||
{
|
||||
"domain_id": None,
|
||||
"name": "Member",
|
||||
"id": "c60fdd45",
|
||||
}
|
||||
],
|
||||
"expires_at": "2018-02-27T18:30:59.999999Z",
|
||||
"unrestricted": None,
|
||||
"project_id": "ddef321",
|
||||
"id": "5499a186",
|
||||
"name": "one"
|
||||
},
|
||||
{
|
||||
"description": None,
|
||||
"roles": [
|
||||
{
|
||||
"id": "0f1837c8",
|
||||
"domain_id": None,
|
||||
"name": "anotherrole"
|
||||
},
|
||||
{
|
||||
"id": "c60fdd45",
|
||||
"domain_id": None,
|
||||
"name": "Member"
|
||||
}
|
||||
],
|
||||
"expires_at": None,
|
||||
"unrestricted": None,
|
||||
"project_id": "c5403d938",
|
||||
"id": "d441c904f",
|
||||
"name": "two"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
FAKE_APP_CRED_INFO = {
|
||||
"application_credential": {
|
||||
"description": None,
|
||||
"roles": [
|
||||
{
|
||||
"domain_id": None,
|
||||
"name": "Member",
|
||||
"id": "c60fdd45",
|
||||
}
|
||||
],
|
||||
"expires_at": None,
|
||||
"unrestricted": None,
|
||||
"project_id": "ddef321",
|
||||
"id": "5499a186",
|
||||
"name": "one"
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestApplicationCredentialsClient, self).setUp()
|
||||
fake_auth = fake_auth_provider.FakeAuthProvider()
|
||||
self.client = \
|
||||
application_credentials_client.ApplicationCredentialsClient(
|
||||
fake_auth, 'identity', 'regionOne')
|
||||
|
||||
def _test_create_app_cred(self, bytes_body=False):
|
||||
self.check_service_client_function(
|
||||
self.client.create_application_credential,
|
||||
'tempest.lib.common.rest_client.RestClient.post',
|
||||
self.FAKE_CREATE_APP_CRED,
|
||||
bytes_body,
|
||||
status=201,
|
||||
user_id="123456")
|
||||
|
||||
def _test_show_app_cred(self, bytes_body=False):
|
||||
self.check_service_client_function(
|
||||
self.client.show_application_credential,
|
||||
'tempest.lib.common.rest_client.RestClient.get',
|
||||
self.FAKE_APP_CRED_INFO,
|
||||
bytes_body,
|
||||
user_id="123456",
|
||||
application_credential_id="5499a186")
|
||||
|
||||
def _test_list_app_creds(self, bytes_body=False):
|
||||
self.check_service_client_function(
|
||||
self.client.list_application_credentials,
|
||||
'tempest.lib.common.rest_client.RestClient.get',
|
||||
self.FAKE_LIST_APP_CREDS,
|
||||
bytes_body,
|
||||
user_id="123456")
|
||||
|
||||
def test_create_application_credential_with_str_body(self):
|
||||
self._test_create_app_cred()
|
||||
|
||||
def test_create_application_credential_with_bytes_body(self):
|
||||
self._test_create_app_cred(bytes_body=True)
|
||||
|
||||
def test_show_application_credential_with_str_body(self):
|
||||
self._test_show_app_cred()
|
||||
|
||||
def test_show_application_credential_with_bytes_body(self):
|
||||
self._test_show_app_cred(bytes_body=True)
|
||||
|
||||
def test_list_application_credential_with_str_body(self):
|
||||
self._test_list_app_creds()
|
||||
|
||||
def test_list_application_credential_with_bytes_body(self):
|
||||
self._test_list_app_creds(bytes_body=True)
|
||||
|
||||
def test_delete_trust(self):
|
||||
self.check_service_client_function(
|
||||
self.client.delete_application_credential,
|
||||
'tempest.lib.common.rest_client.RestClient.delete',
|
||||
{},
|
||||
user_id="123456",
|
||||
application_credential_id="5499a186",
|
||||
status=204)
|
Loading…
Reference in New Issue
Block a user