Add secure-rbac tests for SecretStores API

This patch adds basic RBAC test for the Secret Stores API for
the reader, member and admin personas with project scope.

The tests are skipped by a config option, as they require
the multiple-backends feature to be enabled in barbican.

The devstack instace we're using for gate tests does not have
this enabled, so we default to False for now.

Change-Id: Ibca9d44fb3d0f4fd9945a7e6c636e0fbf6beb42e
This commit is contained in:
Douglas Mendizábal 2021-04-01 16:50:38 -05:00
parent 98b7523de3
commit e654464aa5
5 changed files with 268 additions and 2 deletions

View File

@ -20,6 +20,17 @@ service_option = cfg.BoolOpt("barbican",
help="Whether or not barbican is expected to be "
"available")
barbican_tempest_group = cfg.OptGroup(
name='barbican_tempest',
title='Key Manager (Barbican) service options'
)
BarbicanGroupOpts = [
cfg.BoolOpt('enable_multiple_secret_stores',
default=False,
help="Flag to enable mulitple secret store tests")
]
ephemeral_storage_encryption_group = cfg.OptGroup(
name="ephemeral_storage_encryption",
title="Ephemeral storage encryption options")

View File

@ -33,6 +33,10 @@ class BarbicanTempestPlugin(plugins.TempestPlugin):
conf.register_opt(project_config.service_option,
group='service_available')
conf.register_group(project_config.barbican_tempest_group)
conf.register_opts(project_config.BarbicanGroupOpts,
project_config.barbican_tempest_group)
# Register ephemeral storage encryption options
conf.register_group(project_config.ephemeral_storage_encryption_group)
conf.register_opts(project_config.EphemeralStorageEncryptionGroup,
@ -60,7 +64,8 @@ class BarbicanTempestPlugin(plugins.TempestPlugin):
'OrderClient',
'QuotaClient',
'SecretClient',
'SecretMetadataClient'
'SecretMetadataClient',
'SecretStoresClient'
],
}
return [v1_params]

View File

@ -24,6 +24,8 @@ from barbican_tempest_plugin.services.key_manager.json.secret_client \
import SecretClient
from barbican_tempest_plugin.services.key_manager.json.secret_metadata_client \
import SecretMetadataClient
from barbican_tempest_plugin.services.key_manager.json.secret_stores_client \
import SecretStoresClient
__all__ = [
'ConsumerClient',
@ -31,5 +33,6 @@ __all__ = [
'OrderClient',
'QuotaClient',
'SecretClient',
'SecretMetadataClient'
'SecretMetadataClient',
'SecretStoresClient'
]

View File

@ -0,0 +1,60 @@
# 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 json
from urllib import parse
from barbican_tempest_plugin.services.key_manager.json import base
class SecretStoresClient(base.BarbicanTempestClient):
def list_secret_stores(self, **kwargs):
uri = '/v1/secret-stores'
if kwargs:
uri += '?{}'.format(parse.urlencode(kwargs))
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return json.loads(body.decode('UTF-8'))
def get_secret_store(self, secret_store_id):
uri = '/v1/secret-stores/{}'.format(secret_store_id)
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return json.loads(body.decode('UTF-8'))
def get_global_secret_store(self, **kwargs):
uri = '/v1/secret-stores/global-default'
if kwargs:
uri += '?{}'.format(parse.urlencode(kwargs))
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return json.loads(body.decode('UTF-8'))
def get_preferred_secret_store(self, **kwargs):
uri = '/v1/secret-stores/preferred'
if kwargs:
uri += '?{}'.format(parse.urlencode(kwargs))
resp, body = self.get(uri)
self.expected_success(200, resp.status)
return json.loads(body.decode('UTF-8'))
def set_preferred_secret_store(self, secret_store_id):
uri = '/v1/secret-stores/{}/preferred'.format(secret_store_id)
resp, body = self.post(uri)
self.expected_success(200, resp.status)
return json.loads(body.decode('UTF-8'))
def unset_preferred_secret_store(self, secret_store_id):
uri = '/v1/secret-stores/{}/preferred'.format(secret_store_id)
resp, body = self.delete(uri)
self.expected_success(200, resp.status)
return json.loads(body.decode('UTF-8'))

View File

@ -0,0 +1,187 @@
# 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 abc
from tempest import config
from tempest.lib import exceptions
from barbican_tempest_plugin.tests.rbac.v1 import base
CONF = config.CONF
class BarbicanV1RbacSecretStores:
@abc.abstractmethod
def test_list_secret_stores(self):
"""Test getting a list of all backends
Testing: GET /v1/secret-stores
This test must check:
* whether the persona can list all secret stores
"""
raise NotImplementedError
@abc.abstractmethod
def test_get_secret_store(self):
"""Test get secret store information
Testing: GET /v1/secret-stores/{secret-store-id}
This test must check:
* whether the persona can get information about a specific
secret store
"""
raise NotImplementedError
@abc.abstractmethod
def test_get_global_secret_store(self):
"""Test getting the global secret store
Testing: GET /v1/secret-stores/global-default
This test must check:
* whether the persona can get information about the global
default secret store
"""
raise NotImplementedError
@abc.abstractmethod
def test_get_preferred_secret_store(self):
"""Test getting the preferred secret store
Testing: GET /v1/secret-stores/preferred
This test must check:
* whether the persona can get information about their project's
preferred secret store
"""
raise NotImplementedError
@abc.abstractmethod
def test_set_preferred_secret_store(self):
"""Test setting the preferred secret store
Testing: POST /v1/secret-stores/{secret-store-id}/preferred
This test must check:
* whether the persona can set their project's preferred
secret store
"""
raise NotImplementedError
@abc.abstractmethod
def test_unset_preferred_secret_store(self):
"""Test removing the preferred secret store
Testing: DELETE /v1/secret-stores/{secret-store-id}/preferred
This test must check:
* whether the persona can set their project's preferred
secret store
"""
raise NotImplementedError
class ProjectMemberTests(base.BarbicanV1RbacBase, BarbicanV1RbacSecretStores):
@classmethod
def skip_checks(cls):
"""TODO(redrobot): Run this with multiple backends
We need to set up the devstack plugin to use multiple backends
so we can run these tests.
"""
if not CONF.barbican_tempest.enable_multiple_secret_stores:
raise cls.skipException("enable_multiple_secret_stores is not "
"configured. Skipping RBAC tests.")
@classmethod
def setup_clients(cls):
super().setup_clients()
cls.client = cls.os_project_member.secret_v1.SecretStoresClient()
def test_list_secret_stores(self):
resp = self.do_request('list_secret_stores')
self.assertIn('secret_stores', resp)
def test_get_secret_store(self):
resp = self.do_request('list_secret_stores')
secret_store_id = self.ref_to_uuid(
resp['secret_stores'][0]['secret_store_ref']
)
resp = self.do_request('get_secret_store',
secret_store_id=secret_store_id)
self.assertEqual(secret_store_id,
self.ref_to_uuid(resp['secret_store_ref']))
def test_get_global_secret_store(self):
resp = self.do_request('get_global_secret_store')
self.assertTrue(resp['global_default'])
def test_get_preferred_secret_store(self):
resp = self.do_request('get_preferred_secret_store')
self.assertEqual('ACTIVE', resp['status'])
def test_set_preferred_secret_store(self):
resp = self.do_request('list_secret_stores')
secret_store_id = self.ref_to_uuid(
resp['secret_stores'][0]['secret_store_ref']
)
self.do_request('set_preferred_secret_store',
expected_status=exceptions.Forbidden,
secret_store_id=secret_store_id)
def test_unset_preferred_secret_store(self):
resp = self.do_request('list_secret_stores')
secret_store_id = self.ref_to_uuid(
resp['secret_stores'][0]['secret_store_ref']
)
self.do_request('unset_peferred_secret_store',
expected_status=exceptions.Forbidden,
secret_store_id=secret_store_id)
class ProjectAdminTests(ProjectMemberTests):
@classmethod
def setup_clients(cls):
super().setup_clients()
cls.client = cls.os_project_admin.secret_v1.SecretStoresClient()
def test_set_preferred_secret_store(self):
resp = self.do_request('list_secret_stores')
secret_store_id = self.ref_to_uuid(
resp['secret_stores'][0]['secret_store_ref']
)
self.do_request('set_preferred_secret_store',
secret_store_id=secret_store_id)
resp = self.do_request('get_preferred_secret_store')
self.assertEqual(secret_store_id,
self.ref_to_uuid(resp['secret_store_ref']))
def test_unset_preferred_secret_store(self):
resp = self.do_request('list_secret_stores')
secret_store_id = self.ref_to_uuid(
resp['secret_stores'][0]['secret_store_ref']
)
self.do_request('set_preferred_secret_store',
secret_store_id=secret_store_id)
self.do_request('unset_peferred_secret_store',
secret_store_id=secret_store_id)
resp = self.do_request('get_preferred_secret_store')
self.assertEqual(secret_store_id,
self.ref_to_uuid(resp['secret_store_ref']))
class ProjectReaderTests(ProjectMemberTests):
@classmethod
def setup_clients(cls):
super().setup_clients()
cls.client = cls.os_project_reader.secret_v1.SecretStoresClient()