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
diff --git a/barbican_tempest_plugin/config.py b/barbican_tempest_plugin/config.py
index 67a7986..7b79cb5 100644
--- a/barbican_tempest_plugin/config.py
+++ b/barbican_tempest_plugin/config.py
@@ -20,6 +20,17 @@
                              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")
diff --git a/barbican_tempest_plugin/plugin.py b/barbican_tempest_plugin/plugin.py
index 2acd7cc..7586050 100644
--- a/barbican_tempest_plugin/plugin.py
+++ b/barbican_tempest_plugin/plugin.py
@@ -33,6 +33,10 @@
         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 @@
                 'OrderClient',
                 'QuotaClient',
                 'SecretClient',
-                'SecretMetadataClient'
+                'SecretMetadataClient',
+                'SecretStoresClient'
             ],
         }
         return [v1_params]
diff --git a/barbican_tempest_plugin/services/key_manager/json/__init__.py b/barbican_tempest_plugin/services/key_manager/json/__init__.py
index 7bce46a..8f600e7 100644
--- a/barbican_tempest_plugin/services/key_manager/json/__init__.py
+++ b/barbican_tempest_plugin/services/key_manager/json/__init__.py
@@ -24,6 +24,8 @@
     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 @@
     'OrderClient',
     'QuotaClient',
     'SecretClient',
-    'SecretMetadataClient'
+    'SecretMetadataClient',
+    'SecretStoresClient'
 ]
diff --git a/barbican_tempest_plugin/services/key_manager/json/secret_stores_client.py b/barbican_tempest_plugin/services/key_manager/json/secret_stores_client.py
new file mode 100644
index 0000000..cb5fd5e
--- /dev/null
+++ b/barbican_tempest_plugin/services/key_manager/json/secret_stores_client.py
@@ -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'))
diff --git a/barbican_tempest_plugin/tests/rbac/v1/test_secret_stores.py b/barbican_tempest_plugin/tests/rbac/v1/test_secret_stores.py
new file mode 100644
index 0000000..6f0a00d
--- /dev/null
+++ b/barbican_tempest_plugin/tests/rbac/v1/test_secret_stores.py
@@ -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()