Merge "Unit tests for dynamic policy file discovery"
diff --git a/README.rst b/README.rst
index f5b4c00..e983b60 100644
--- a/README.rst
+++ b/README.rst
@@ -5,174 +5,84 @@
 .. image:: http://governance.openstack.org/badges/patrole.svg
     :target: http://governance.openstack.org/reference/tags/index.html
 
-..
-
-=========================================
 Patrole - RBAC Integration Tempest Plugin
 =========================================
 
-Patrole is a tool for verifying that Role-Based Access Control is being
-correctly enforced.
+Patrole is a security validation tool for verifying that Role-Based Access
+Control is correctly configured and enforced in a system. It runs Tempest-based
+API tests using specified RBAC roles, thus allowing deployments to verify that
+only intended roles have access to those APIs.
 
-Patrole allows users to run API tests using specified RBAC roles. This allows
-deployments to verify that only intended roles have access to those APIs.
-This is critical to ensure security, especially in large deployments with
-custom roles.
-
-* Free software: Apache license
-* Documentation: https://docs.openstack.org/developer/patrole
-* Source: https://git.openstack.org/cgit/openstack/patrole
-* Bugs: https://bugs.launchpad.net/patrole
+Patrole currently offers testing for the following OpenStack services: Nova,
+Neutron, Glance, Cinder and Keystone.
 
 Features
-========
-Patrole offers RBAC testing for various OpenStack RBAC policies. It includes
-a decorator that wraps around tests which verifies that when the test calls the
-corresponding API endpoint, access is only granted for correct roles.
+--------
+* Validation of default policy definitions located in policy.json files.
+* Validation of in-code policy definitions.
+* Validation of custom policy file definitions that override default policy
+  definitions.
+* Built-in positive and negative testing. Positive and negative testing
+  are performed using the same tests and role-switching.
+* Valdation of custom roles as well as default OpenStack roles.
 
-Currently, Patrole supports policies contained in code and in policy.json files.
-If both exist, the policy actions in the policy.json are prioritized.
+.. note::
 
-Stable Interface
-----------------
-Patrole offers a stable interface that is guaranteed to be backwards compatible and
-can be directly consumed by other projects. Currently, rbac_exceptions.py and
-rbac_policy_parser.py are guaranteed to be stable.
+    Patrole does not yet support policy.yaml files, the new file format for
+    policy files in OpenStack.
+
+How It Works
+------------
+Patrole leverages ``oslo.policy`` (OpenStack's policy enforcement engine) to
+determine whether a given role is allowed to perform a policy action, given a
+specific role and OpenStack service. The output from ``oslo.policy`` (the
+expected result) and the actual result from test execution are compared to
+each other: if both results match, then the test passes; else it fails.
+
+* Documentation: https://docs.openstack.org/developer/patrole
+* Bugs: https://bugs.launchpad.net/patrole
+
+Quickstart
+----------
+Tempest is a prerequisite for running Patrole. If you do not have Tempest
+installed, please reference the official Tempest documentation for guidance.
+
+Assuming Tempest is installed, the simplest way to configure Patrole is:
+
+1. Open up the ``tempest.conf`` configuration file and include the following
+settings:
+
+.. code-block:: ini
+
+    [rbac]
+    enable_rbac = True
+    rbac_test_role = admin
+
+These settings tell Patrole to run RBAC tests using the "admin" role (which
+is the default admin role in OpenStack) to verify the default policy
+definitions used by OpenStack services. Specifying a different role
+for ``rbac_test_role`` will run Patrole tests against that role. For additional
+information about Patrole's configuration settings, please refer to
+:ref:`patrole-configuration` and :ref:`patrole-sampleconf` for a sample
+configuration file.
+
+2. You are now ready to run Patrole. To do so, you can use any testr-based test
+runner::
+
+    $ testr run patrole_tempest_plugin.tests.api
+
+or::
+
+    $ ostestr --regex '(?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)'
+
+It is also possible to run Patrole using tox::
+
+    tox -eall-plugin -- patrole_tempest_plugin.tests.api
 
 Release Versioning
 ------------------
-`Patrole Release Notes <https://docs.openstack.org/releasenotes/patrole/>`_ show
-what changes have been released.
+`Patrole Release Notes <https://docs.openstack.org/releasenotes/patrole/>`_
+shows which changes have been released for each version.
 
-.. _test-flows:
-
-Test Flows
-----------
-There are several possible test flows.
-
-If the ``rbac_test_role`` is allowed to access the endpoint:
-
-* The test passes if no 403 ``Forbidden`` or ``RbacActionFailed`` exception is raised.
-
-If the ``rbac_test_role`` is not allowed to access the endpoint:
-
-* If the endpoint returns a 403 `Forbidden` exception the test will pass.
-* If the endpoint returns successfully, then the test will fail with an
-  ``RbacOverPermission`` exception.
-* If the endpoint returns something other than a 403 ``Forbidden`` to indicate
-  that the role is not allowed, the test will raise an ``RbacActionFailed`` exception.
-
-.. note::
-
-    Certain services like Neutron *intentionally* raise a 404 instead of a 403
-    for security concerns. Patrole accomodates this behavior by anticipating
-    a 404 instead of a 403, using the ``expected_exception`` argument. For more
-    information about Neutron's policy enforcement, see:
-    `<https://docs.openstack.org/developer/neutron/devref/policy.html#request-authorization>`__.
-
-How It Works
-============
-Patrole leverages oslo_policy (OpenStack's policy enforcement engine) to
-determine whether a given role is allowed to perform a policy action given a
-specific rule and OpenStack service. This is done before test execution inside
-the ``rbac_rule_validation.action`` decorator. Then, inside the test, the API
-that does policy enforcement for the same rule is called. The outcome is
-compared against the result from oslo_policy and a pass or fail is determined
-as outlined above: `Test Flows`_.
-
-.. note::
-
-    Currently, Patrole does not support checking multiple rules against a single
-    API call. Even though some APIs enforce multiple rules (some indirectly),
-    it is increasingly difficult to maintain the tests if multiple policy
-    actions are expected to be called.
-
-Test Execution Workflow
------------------------
-The workflow is as follows:
-
-#. Each test uses the ``rbac_rule_validation.action`` decorator, like below: ::
-
-    @rbac_rule_validation.action(
-        service="nova",
-        rule="os_compute_api:servers:stop")
-    @decorators.idempotent_id('ab4a17d2-166f-4a6d-9944-f17baa576cf2')
-    def test_stop_server(self):
-        # Set the primary credential's role to "rbac_test_role".
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        # Call the API that enforces the policy action specified by "rule".
-        self._test_stop_server()
-
-   The ``service`` attribute accepts an OpenStack service and the ``rule`` attribute
-   accepts a valid OpenStack policy action, like "os_compute_api:servers:stop".
-
-#. The ``rbac_rule_validation.action`` decorator passes these attributes,
-   along with user_id and project_id information derived from the primary
-   Tempest credential (``self.os.credentials.user_id`` and ``self.os.credentials.project_id``),
-   to the ``rbac_policy_parser``.
-
-#. The logic in ``rbac_policy_parser`` then passes all this information along
-   and the role in ``CONF.rbac.rbac_test_role`` to oslo_policy to determine whether
-   the ``rbac_test_role`` is authorized to perform the policy action for the given
-   service.
-
-#. After all of the logic above has executed inside the rbac decorator, the
-   test is executed. The test then sets up test-level resources, if necessary,
-   with **admin** credentials implicitly. This is accomplished through
-   ``rbac_utils.switch_role(toggle_rbac_role=False)``, which is done as part of
-   client setup (inside the call to ``rbac_utils.RbacUtils``): ::
-
-    @classmethod
-    def setup_clients(cls):
-        super(BaseV2ComputeRbacTest, cls).setup_clients()
-        cls.auth_provider = cls.os_primary.auth_provider
-        cls.rbac_utils = rbac_utils.RbacUtils(cls)
-        ...
-
-   This code has *already* executed when the test class is instantiated, because
-   it is located in the base rbac test class. Whenever ``cls.rbac_utils.switch_role``
-   is called, one of two behaviors are possible:
-
-    #. The primary credential's role is changed to admin if ``toggle_rbac_role=False``
-    #. The primary credential's role is changed to ``rbac_test_role`` if
-       ``toggle_rbac_role=True``
-
-   Thus, at the *beginning* of every test and during ``resource_setup`` and
-   ``resource_cleanup``, the primary credential has the admin role.
-
-#. After preliminary test-level setup is performed, like creating a server, a
-   second call to ``self.rbac_utils.switch_role`` is done: ::
-
-    self.rbac_utils.switch_role(cls, toggle_rbac_role=True)
-
-   Now the primary credential has the role specified by ``rbac_test_role``.
-
-#. The API endpoint in which policy enforcement of "os_compute_api:servers:stop"
-   is performed can now be called.
-
-   .. note:
-
-        To determine whether a policy action is enforced, refer to the relevant
-        controller code to make sure that the policy action is indeed enforced.
-
-#. Now that a call is made to "stop_server" with the primary credentials having
-   the role specified by ``rbac_test_role``, either the nova contoller will allow
-   or disallow the action to be performed. Since the "stop_server" policy action in
-   nova is defined as "base.RULE_ADMIN_OR_OWNER", the API will most likely
-   return a successful status code. For more information about this policy action,
-   see `<https://github.com/openstack/nova/blob/master/nova/policies/servers.py>`__.
-
-#. As mentioned above, the result from the API call and the result from oslo_policy
-   are compared for consistency.
-
-#. Finally, after the test has executed, but before ``tearDown`` or ``resource_cleanup``
-   is called, ``self.rbac_utils.switch_role(cls, toggle_rbac_role=False)`` is
-   called, so that the primary credential yet again has admin permissions for
-   test clean up. This call is always performed in the "finally" block inside
-   the ``rbac_rule_validation`` decorator.
-
-.. warning::
-
-    Failure to call ``self.rbac_utils.switch_role(cls, toggle_rbac_role=True)``
-    inside a test with the ``rbac_rule_validation`` decorator applied results
-    in a ``RbacResourceSetupFailed`` being raised, causing the test to fail.
+Patrole's release versioning follows Tempest's conventions. Like Tempest,
+Patrole is branchless and uses versioning instead.
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
new file mode 100644
index 0000000..550d2ce
--- /dev/null
+++ b/doc/source/configuration.rst
@@ -0,0 +1,73 @@
+.. _patrole-configuration:
+
+Patrole Configuration Guide
+===========================
+
+Patrole can be customized by updating Tempest's ``tempest.conf`` configuration
+file. All Patrole-specific configuration options should be included under
+the ``rbac`` group.
+
+RBAC Test Role
+--------------
+
+The RBAC test role governs which role is used when running Patrole tests. For
+example, setting ``rbac_test_role`` to "admin" will execute all RBAC tests
+using admin credentials. Changing the ``rbac_test_role`` value will `override`
+Tempest's primary credentials to use that role.
+
+This implies that, if ``rbac_test_role`` is "admin", regardless of the Tempest
+credentials used by a client, the client will be calling APIs using the admin
+role. That is, ``self.os_primary.servers_client`` will run as though it were
+``self.os_admin.servers_client``.
+
+Similarly, setting ``rbac_test_role`` to a non-admin role results in Tempest's
+primary credentials being overriden by the role specified by
+``rbac_test_role``.
+
+.. note::
+
+    Only the role of the primary Tempest credentials ("os_primary") is
+    modified. The ``user_id`` and ``project_id`` remain unchanged.
+
+Enable RBAC
+-----------
+
+Given the value of ``enable_rbac``, enables or disables Patrole tests. If
+``enable_rbac`` is ``False``, then Patrole tests are skipped.
+
+Strict Policy Check
+-------------------
+
+Currently, many services define their "default" rule to be "anyone allowed".
+If a policy action is not explicitly defined in a policy file, then
+``oslo.policy`` will fall back to the "default" rule. This implies that if
+there's a typo in a policy action specified in a Patrole test, ``oslo.policy``
+can report that the ``rbac_test_role`` will be able to perform the
+non-existent policy action. For a testing framework, this is undesirable
+behavior.
+
+Hence, ``strict_policy_check``, if ``True``, will throw an error in the event
+that a non-existent or bogus policy action is passed to a Patrole test. If
+``False``, however, a ``self.skipException`` will be raised.
+
+Custom Policy Files
+-------------------
+
+Patrole supports testing custom policy file definitions, along with default
+policy definitions. Default policy definitions are used if custom file
+definitions are not specified. If both are specified, the custom policy
+definition takes precedence (that is, replaces the default definition,
+as this is the default behavior in OpenStack).
+
+The ``custom_policy_files`` option allows a user to specify a comma-separated
+list of custom policy file locations that are on the same host as Patrole.
+Each policy file must include the name of the service that is being tested:
+for example, if "compute" tests are executed, then Patrole will use the first
+policy file contained in ``custom_policy_files`` that contains the "nova"
+keyword.
+
+.. note::
+
+    Patrole currently does not support policy files located on a host different
+    than the one on which it is running.
+..
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 35e8439..f58ee7f 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1,24 +1,30 @@
-.. patrole documentation master file, created by
-   sphinx-quickstart on Tue Jul  9 22:26:36 2013.
-   You can adapt this file completely to your liking, but it should at least
-   contain the root `toctree` directive.
+========================================
+Patrole: Tempest Plugin for RBAC Testing
+========================================
 
-Patrole - an OpenStack Tempest Plugin for RBAC Testing
-========================================================
+User's Guide
+============
 
-Contents:
+Patrole Configuration Guide
+---------------------------
 
 .. toctree::
    :maxdepth: 2
 
-   readme
    installation
+   configuration
    usage
+   sampleconf
+
+Developer's Guide
+=================
+
+.. toctree::
+   :maxdepth: 2
+
    contributing
 
 Indices and tables
 ==================
 
-* :ref:`genindex`
-* :ref:`modindex`
 * :ref:`search`
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
index 31f94f4..c244152 100644
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -1,9 +1,11 @@
-============
-Installation
-============
+.. _patrole-installation:
 
-Installation Information
-========================
+==========================
+Patrole Installation Guide
+==========================
+
+Manual Installation Information
+===============================
 
 At the command line::
 
@@ -30,45 +32,3 @@
     ...
 
     enable_plugin patrole git://git.openstack.org/openstack/patrole
-
-Configuration Information
-=========================
-
-tempest.conf
-++++++++++++
-
-To run the RBAC tempest api test, you have to make the following changes to
-the tempest.conf file.
-
-#. ``auth`` section updates ::
-
-    # Allows test cases to create/destroy projects and users. This option
-    # requires that OpenStack Identity API admin credentials are known. If
-    # false, isolated test cases and parallel execution, can still be
-    # achieved configuring a list of test accounts (boolean value)
-    use_dynamic_credentials = True
-
-#. ``rbac`` section updates ::
-
-    # The role that you want the RBAC tests to use for RBAC testing
-    # This needs to be edited to run the test as a different role.
-    rbac_test_role = _member_
-
-    # Enables RBAC Tempest tests if set to True. Otherwise, they are
-    # skipped.
-    enable_rbac = True
-
-    # If set to true, tests throw a RbacParsingException for policies
-    # not found in the policy.json. Otherwise, they throw a
-    # skipException.
-    strict_policy_check = False
-
-    # The following config options set the location of the service's
-    # policy file. For services that have their policy in code (e.g.,
-    # Nova), this would be the location of a custom policy.json, if
-    # one exists.
-    cinder_policy_file = /etc/cinder/policy.json
-    glance_policy_file = /etc/glance/policy.json
-    keystone_policy_file = /etc/keystone/policy.json
-    neutron_policy_file = /etc/neutron/policy.json
-    nova_policy_file = /etc/nova/policy.json
diff --git a/doc/source/readme.rst b/doc/source/readme.rst
deleted file mode 100644
index a6210d3..0000000
--- a/doc/source/readme.rst
+++ /dev/null
@@ -1 +0,0 @@
-.. include:: ../../README.rst
diff --git a/doc/source/sampleconf.rst b/doc/source/sampleconf.rst
new file mode 100644
index 0000000..c262f2d
--- /dev/null
+++ b/doc/source/sampleconf.rst
@@ -0,0 +1,51 @@
+.. _patrole-sampleconf:
+
+Sample Configuration File
+==========================
+
+The following is a sample Patrole configuration for adaptation and use.
+
+.. code-block:: ini
+
+    [rbac]
+
+    # The role that you want the RBAC tests to use for RBAC testing
+    # This needs to be edited to run the test as a different role.
+    rbac_test_role = Member
+
+    # Enables RBAC Tempest tests if set to True. Otherwise, they are
+    # skipped.
+    enable_rbac = True
+
+    # If set to True, tests throw a RbacParsingException for policies
+    # not found in the policy file. Otherwise, they throw a skipException.
+    strict_policy_check = False
+
+    # List of the paths to search for policy files. Each policy path assumes that
+    # the service name is included in the path once. Also assumes Patrole is on the
+    # same host as the policy files. The paths should be ordered by precedence,
+    # with high-priority paths before low-priority paths. The first path that is
+    # found to contain the service's policy file will be used.
+    custom_policy_files = /etc/nova/policy.json,/etc/neutron/policy.json
+
+    # This option determines whether Patrole should run against a
+    # `custom_requirements_file` which defines RBAC requirements. The
+    # purpose of setting this flag to True is to verify that RBAC policy
+    # is in accordance to requirements. The idea is that the
+    # `custom_requirements_file` perfectly defines what the RBAC requirements
+    # are.
+    test_custom_requirements = False
+
+    File path of the yaml file that defines your RBAC requirements. This
+    # file must be located on the same host that Patrole runs on. The yaml
+    # file should be written as follows:
+    custom_requirements_file = patrole/requirements.txt
+
+    # DEPRECATED: The following config options set the location of the service's
+    # policy file. For services that have their policy in code (e.g., Nova),
+    # this would be the location of a custom policy.json, if one exists.
+    cinder_policy_file = /etc/cinder/policy.json
+    glance_policy_file = /etc/glance/policy.json
+    keystone_policy_file = /etc/keystone/policy.json
+    neutron_policy_file = /etc/neutron/policy.json
+    nova_policy_file = /etc/nova/policy.json
diff --git a/doc/source/usage.rst b/doc/source/usage.rst
index d2570bc..7470e9e 100644
--- a/doc/source/usage.rst
+++ b/doc/source/usage.rst
@@ -1,4 +1,4 @@
-..
+.. _patrole-usage:
 
 ========
 Usage
diff --git a/patrole_tempest_plugin/rbac_exceptions.py b/patrole_tempest_plugin/rbac_exceptions.py
index aa3135e..5ccb216 100644
--- a/patrole_tempest_plugin/rbac_exceptions.py
+++ b/patrole_tempest_plugin/rbac_exceptions.py
@@ -16,25 +16,25 @@
 from tempest.lib import exceptions
 
 
-class RbacActionFailed (exceptions.ClientRestClientException):
+class RbacActionFailed(exceptions.ClientRestClientException):
     message = "Rbac action failed"
 
 
-class RbacResourceSetupFailed (exceptions.TempestException):
+class RbacResourceSetupFailed(exceptions.TempestException):
     message = "Rbac resource setup failed"
 
 
-class RbacOverPermission (exceptions.TempestException):
+class RbacOverPermission(exceptions.TempestException):
     message = "Action performed that should not be permitted"
 
 
-class RbacInvalidService (exceptions.TempestException):
+class RbacInvalidService(exceptions.TempestException):
     message = "Attempted to test an invalid service"
 
 
-class RbacParsingException (exceptions.TempestException):
+class RbacParsingException(exceptions.TempestException):
     message = "Attempted to test an invalid policy file or action"
 
 
-class RbacInvalidErrorCode (exceptions.TempestException):
+class RbacInvalidErrorCode(exceptions.TempestException):
     message = "Unsupported error code passed in test"
diff --git a/patrole_tempest_plugin/tests/api/compute/rbac_base.py b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
index 0a3a1f5..4a5dcbf 100644
--- a/patrole_tempest_plugin/tests/api/compute/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
@@ -30,7 +30,7 @@
         super(BaseV2ComputeRbacTest, cls).skip_checks()
         if not CONF.rbac.enable_rbac:
             raise cls.skipException(
-                '%s skipped as RBAC flag not enabled' % cls.__name__)
+                '%s skipped as RBAC testing not enabled' % cls.__name__)
 
     @classmethod
     def setup_clients(cls):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_config_drive_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_config_drive_rbac.py
deleted file mode 100644
index 794e0d2..0000000
--- a/patrole_tempest_plugin/tests/api/compute/test_config_drive_rbac.py
+++ /dev/null
@@ -1,48 +0,0 @@
-#    Copyright 2017 AT&T Corporation.
-#    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 decorators
-from tempest import test
-
-from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.compute import rbac_base
-
-
-class ConfigDriveRbacTest(rbac_base.BaseV2ComputeRbacTest):
-
-    @classmethod
-    def setup_clients(cls):
-        super(ConfigDriveRbacTest, cls).setup_clients()
-        cls.client = cls.servers_client
-
-    @classmethod
-    def skip_checks(cls):
-        super(ConfigDriveRbacTest, cls).skip_checks()
-        if not test.is_extension_enabled('os-config-drive', 'compute'):
-            msg = "%s skipped as os-config-drive extension not enabled." \
-                  % cls.__name__
-            raise cls.skipException(msg)
-
-    @decorators.idempotent_id('55c62ef7-b72b-4970-acc6-05b0a4316e5d')
-    @rbac_rule_validation.action(
-        service="nova",
-        rule="os_compute_api:os-config-drive")
-    def test_create_test_server_with_config_drive(self):
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        # NOTE(felipemonteiro): This policy action is always enforced,
-        # regardless whether the config_drive flag is set to true or false.
-        # However, it has been explicitly set to true below, in case that this
-        # behavior ever changes in the future.
-        self.create_test_server(config_drive=True)
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py
index ffbc530..b115ab5 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_actions_rbac.py
@@ -155,7 +155,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self._test_stop_server()
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:servers:start")
@@ -165,7 +165,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self._test_start_server()
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:servers:resize")
@@ -176,7 +176,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self._test_resize_server(self.flavor_ref_alt)
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:servers:revert_resize")
@@ -188,7 +188,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self._test_revert_resize_server()
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:servers:confirm_resize")
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py
index 842d8b1..a7f16a1 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_migrations_rbac.py
@@ -18,7 +18,6 @@
 from tempest.common import waiters
 from tempest import config
 from tempest.lib import decorators
-from tempest import test
 
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.compute import rbac_base as base
@@ -63,7 +62,7 @@
             if host_record['service'] == 'compute'
         ]
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
                           'Cold migration not available.')
     @rbac_rule_validation.action(
@@ -77,7 +76,7 @@
         waiters.wait_for_server_status(self.admin_servers_client,
                                        server['id'], 'VERIFY_RESIZE')
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @testtools.skipUnless(CONF.compute_feature_enabled.live_migration,
                           'Live migration feature is not enabled.')
     @rbac_rule_validation.action(
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
index afa8c02..c1bf9f7 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_misc_policy_actions_rbac.py
@@ -23,6 +23,7 @@
 from tempest.lib import exceptions as lib_exc
 from tempest import test
 
+from patrole_tempest_plugin import rbac_exceptions
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.compute import rbac_base
 
@@ -84,7 +85,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.servers_client.inject_network_info(self.server_id)
 
-    @test.attr(type=['slow'])
+    @decorators.attr(type=['slow'])
     @test.requires_ext(extension='os-admin-actions', service='compute')
     @rbac_rule_validation.action(
         service="nova",
@@ -113,6 +114,33 @@
         waiters.wait_for_server_status(
             self.os_admin.servers_client, self.server_id, 'ACTIVE')
 
+    @test.requires_ext(extension='os-config-drive', service='compute')
+    @decorators.idempotent_id('2c82e819-382d-4d6f-87f0-a45954cbbc64')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-config-drive")
+    def test_list_servers_with_details_config_drive(self):
+        """Test list servers with config_drive property in response body."""
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        body = self.servers_client.list_servers(detail=True)['servers']
+        # If the first server contains "config_drive", then all the others do.
+        if 'config_drive' not in body[0]:
+            raise rbac_exceptions.RbacActionFailed(
+                '"config_drive" attribute not found in response body.')
+
+    @test.requires_ext(extension='os-config-drive', service='compute')
+    @decorators.idempotent_id('55c62ef7-b72b-4970-acc6-05b0a4316e5d')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-config-drive")
+    def test_show_server_config_drive(self):
+        """Test show server with config_drive property in response body."""
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        body = self.servers_client.show_server(self.server_id)['server']
+        if 'config_drive' not in body:
+            raise rbac_exceptions.RbacActionFailed(
+                '"config_drive" attribute not found in response body.')
+
     @test.requires_ext(extension='os-deferred-delete', service='compute')
     @decorators.idempotent_id('189bfed4-1e6d-475c-bb8c-d57e60895391')
     @rbac_rule_validation.action(
@@ -394,3 +422,24 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.servers_client.add_fixed_ip(self.server['id'],
                                          networkId=network_id)
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-virtual-interfaces")
+    @decorators.idempotent_id('fc719ae3-0f73-4689-8378-1b841f0f2818')
+    def test_list_virtual_interfaces(self):
+        """Test list virtual interfaces, part of os-virtual-interfaces.
+
+        If Neutron is available, then call the API and expect it to fail
+        with a 400 BadRequest (policy enforcement is done before that happens).
+
+        For more information, see:
+        https://developer.openstack.org/api-ref/compute/#servers-virtual-interfaces-servers-os-virtual-interfaces-deprecated
+        """
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        if CONF.service_available.neutron:
+            msg = "Listing virtual interfaces is not supported by this cloud."
+            with self.assertRaisesRegex(lib_exc.BadRequest, msg):
+                self.servers_client.list_virtual_interfaces(self.server['id'])
+        else:
+            self.servers_client.list_virtual_interfaces(self.server['id'])
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
index c6ac1c5..f5d9dc0 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
@@ -219,7 +219,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.servers_client.list_security_groups_by_server(self.server['id'])
 
-    @test.attr(type=["slow"])
+    @decorators.attr(type=["slow"])
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:os-security-groups")
@@ -233,7 +233,7 @@
                         self.servers_client.remove_security_group,
                         self.server['id'], name=sg_name)
 
-    @test.attr(type=["slow"])
+    @decorators.attr(type=["slow"])
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:os-security-groups")
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_virtual_interfaces_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_virtual_interfaces_rbac.py
deleted file mode 100644
index 1480444..0000000
--- a/patrole_tempest_plugin/tests/api/compute/test_server_virtual_interfaces_rbac.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2017 AT&T Corporation.
-# 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 import config
-from tempest.lib import decorators
-from tempest.lib import exceptions
-
-from patrole_tempest_plugin import rbac_rule_validation
-from patrole_tempest_plugin.tests.api.compute import rbac_base as base
-
-CONF = config.CONF
-
-
-class ServerVirtualInterfacesRbacTest(base.BaseV2ComputeRbacTest):
-
-    @rbac_rule_validation.action(
-        service="nova",
-        rule="os_compute_api:os-virtual-interfaces")
-    @decorators.idempotent_id('fc719ae3-0f73-4689-8378-1b841f0f2818')
-    def test_list_virtual_interfaces(self):
-        server = self.create_test_server(wait_until='ACTIVE')
-        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-
-        if CONF.service_available.neutron:
-            msg = "Listing virtual interfaces is not supported by this cloud."
-            with self.assertRaisesRegex(exceptions.BadRequest, msg):
-                self.servers_client.list_virtual_interfaces(server['id'])
-        else:
-            self.servers_client.list_virtual_interfaces(server['id'])
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py
index 98dbee5..943cb69 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_volume_attachments_rbac.py
@@ -17,7 +17,6 @@
 from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest import test
 
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.compute import rbac_base
@@ -30,7 +29,7 @@
     @classmethod
     def setup_clients(cls):
         super(ServerVolumeAttachmentRbacTest, cls).setup_clients()
-        cls.volumes_client = cls.os_primary.volumes_client
+        cls.volumes_client = cls.os_primary.volumes_client_latest
 
     @classmethod
     def skip_checks(cls):
@@ -68,11 +67,12 @@
     @decorators.idempotent_id('997df9c2-6e54-47b6-ab74-e4fdb500f385')
     def test_show_volume_attachment(self):
         attachment = self.attach_volume(self.server, self.volume)
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.servers_client.show_volume_attachment(
             self.server['id'], attachment['id'])
 
-    @test.attr(type='slow')
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:os-volumes-attachments:update")
@@ -80,6 +80,7 @@
     def test_update_volume_attachment(self):
         attachment = self.attach_volume(self.server, self.volume)
         alt_volume = self.create_volume()
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.servers_client.update_attached_volume(
             self.server['id'], attachment['id'], volumeId=alt_volume['id'])
@@ -102,6 +103,7 @@
     @decorators.idempotent_id('12b03e90-d087-46af-9c4d-507d021c4984')
     def test_delete_volume_attachment(self):
         self.attach_volume(self.server, self.volume)
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.servers_client.detach_volume(self.server['id'], self.volume['id'])
         waiters.wait_for_volume_resource_status(self.volumes_client,
diff --git a/patrole_tempest_plugin/tests/api/compute/test_volume_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_volume_rbac.py
index b775872..0935c95 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_volume_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_volume_rbac.py
@@ -34,6 +34,11 @@
     max_microversion = '2.35'
 
     @classmethod
+    def setup_clients(cls):
+        super(VolumeRbacTest, cls).setup_clients()
+        cls.admin_volumes_client = cls.os_admin.volumes_client_latest
+
+    @classmethod
     def resource_setup(cls):
         super(VolumeRbacTest, cls).resource_setup()
         cls.volume = cls.create_volume()
@@ -56,7 +61,7 @@
             size=CONF.volume.volume_size)['volume']
         # Use the admin volumes client to wait, because waiting involves
         # calling show API action which enforces a different policy.
-        waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+        waiters.wait_for_volume_resource_status(self.admin_volumes_client,
                                                 volume['id'], 'available')
         # Use non-deprecated volumes_client for deletion.
         self.addCleanup(self.volumes_client.delete_volume, volume['id'])
diff --git a/patrole_tempest_plugin/tests/api/identity/rbac_base.py b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
index 1ed081e..e8c402e 100644
--- a/patrole_tempest_plugin/tests/api/identity/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
@@ -243,6 +243,7 @@
         cls.trusts_client = cls.os_primary.trusts_client
         cls.users_client = cls.os_primary.users_v3_client
         cls.oauth_token_client = cls.os_primary.oauth_token_client
+        cls.token_client = cls.os_primary.token_v3_client
 
     @classmethod
     def resource_setup(cls):
@@ -254,6 +255,7 @@
         cls.projects = []
         cls.regions = []
         cls.trusts = []
+        cls.tokens = []
 
     @classmethod
     def resource_cleanup(cls):
@@ -289,6 +291,10 @@
             test_utils.call_and_ignore_notfound_exc(
                 cls.trusts_client.delete_trust, trust['id'])
 
+        for token in cls.tokens:
+            test_utils.call_and_ignore_notfound_exc(
+                cls.identity_client.delete_token, token)
+
         super(BaseIdentityV3RbacTest, cls).resource_cleanup()
 
     @classmethod
@@ -375,3 +381,12 @@
         cls.trusts.append(trust)
 
         return trust
+
+    @classmethod
+    def setup_test_token(cls, user_id, password):
+        """Set up a test token."""
+        token = cls.token_client.auth(user_id=user_id,
+                                      password=password).response
+        token_id = token['x-subject-token']
+        cls.tokens.append(token_id)
+        return token_id
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_groups_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_groups_rbac.py
index e7b73b6..00c9f55 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_groups_rbac.py
@@ -73,7 +73,7 @@
 
     @decorators.idempotent_id('5c16368d-1485-4c28-9803-db3fa3510623')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:check_endpoint_group")
+                                 rule="identity:get_endpoint_group")
     def test_check_endpoint_group(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.endpoint_groups_client.check_endpoint_group(
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_negative_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_negative_rbac.py
new file mode 100644
index 0000000..1ab296a
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_negative_rbac.py
@@ -0,0 +1,100 @@
+# Copyright 2017 AT&T Corporation.
+# 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 import config
+from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
+from tempest import test
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.identity import rbac_base
+
+CONF = config.CONF
+
+
+class IdentityTokenV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
+
+    credentials = ['primary', 'admin', 'alt']
+
+    @classmethod
+    def skip_checks(cls):
+        super(IdentityTokenV3RbacTest, cls).skip_checks()
+        # In case of admin, the positive testcase would be used, hence
+        # skipping negative testcase
+        if CONF.rbac.rbac_test_role == CONF.identity.admin_role:
+            raise cls.skipException(
+                "Skipped as admin role doesn't require negative testing")
+
+    def _setup_alt_token(self):
+        return self.setup_test_token(
+            self.os_alt.auth_provider.credentials.user_id,
+            self.os_alt.auth_provider.credentials.password)
+
+    @decorators.idempotent_id('c83c8f1a-79cb-4dc4-b55f-c7d2bfd98b1e')
+    @test.attr(type=['negative'])
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:validate_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_alt.auth_provider.credentials.user_id"
+        })
+    def test_show_token_negative(self):
+        # Explicit negative test for identity:validate_token policy action.
+        # Assert expected exception is Forbidden and then reraise it.
+        alt_token_id = self._setup_alt_token()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        e = self.assertRaises(lib_exc.Forbidden,
+                              self.identity_client.show_token,
+                              alt_token_id)
+        raise e
+
+    @decorators.idempotent_id('2786a55d-a818-433a-af7a-41ebf72ab4da')
+    @test.attr(type=['negative'])
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:revoke_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_alt.auth_provider.credentials.user_id"
+        })
+    def test_delete_token_negative(self):
+        # Explicit negative test for identity:revoke_token policy action.
+        # Assert expected exception is Forbidden and then reraise it.
+        alt_token_id = self._setup_alt_token()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        e = self.assertRaises(lib_exc.Forbidden,
+                              self.identity_client.delete_token,
+                              alt_token_id)
+        raise e
+
+    @decorators.idempotent_id('1ea02ac0-9a96-44bd-bdc3-4dae3c10cc2e')
+    @test.attr(type=['negative'])
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:check_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_alt.auth_provider.credentials.user_id"
+        })
+    def test_check_token_existence_negative(self):
+        # Explicit negative test for identity:check_token policy action.
+        # Assert expected exception is Forbidden and then reraise it.
+        alt_token_id = self._setup_alt_token()
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        e = self.assertRaises(lib_exc.Forbidden,
+                              self.identity_client.check_token_existence,
+                              alt_token_id)
+        raise e
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_rbac.py
new file mode 100644
index 0000000..e6d0dd1
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_rbac.py
@@ -0,0 +1,67 @@
+# Copyright 2017 AT&T Corporation.
+# 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 decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.identity import rbac_base
+
+
+class IdentityTokenV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(IdentityTokenV3RbacTest, cls).resource_setup()
+        cls.user_id = cls.os_primary.auth_provider.credentials.user_id
+        cls.password = cls.os_primary.auth_provider.credentials.password
+
+    @decorators.idempotent_id('201e2fe5-2023-4bce-9189-78b51520a91e')
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:validate_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_primary.auth_provider.credentials.user_id"
+        })
+    def test_show_token(self):
+        token_id = self.setup_test_token(self.user_id, self.password)
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.identity_client.show_token(token_id)
+
+    @decorators.idempotent_id('42a299db-fe0a-4ea0-9824-0bfd13155886')
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:revoke_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_primary.auth_provider.credentials.user_id"
+        })
+    def test_delete_token(self):
+        token_id = self.setup_test_token(self.user_id, self.password)
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.identity_client.delete_token(token_id)
+
+    @decorators.idempotent_id('3554d218-8cd6-4730-a1b2-0e22f9b78f45')
+    @rbac_rule_validation.action(
+        service="keystone",
+        rule="identity:check_token",
+        extra_target_data={
+            "target.token.user_id":
+            "os_primary.auth_provider.credentials.user_id"
+        })
+    def test_check_token_exsitence(self):
+        token_id = self.setup_test_token(self.user_id, self.password)
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.identity_client.check_token_existence(token_id)
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_trusts_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_trusts_rbac.py
index c25af25..0532b2d 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_trusts_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_trusts_rbac.py
@@ -16,7 +16,6 @@
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
-from tempest import test
 
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.identity import rbac_base
@@ -77,7 +76,7 @@
                               trustee_user_id=self.trustee_user_id)
 
     @decorators.idempotent_id('bd72d22a-6e11-4840-bd93-17b382e7f0e0')
-    @test.attr(type=['negative'])
+    @decorators.attr(type=['negative'])
     @rbac_rule_validation.action(
         service="keystone",
         rule="identity:create_trust",
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py
index b666a2d..e8e3ef8 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_actions_rbac.py
@@ -33,6 +33,7 @@
     def setup_clients(cls):
         super(VolumesActionsRbacTest, cls).setup_clients()
         cls.admin_image_client = cls.os_admin.image_client_v2
+        cls.admin_volumes_client = cls.os_admin.volumes_client_latest
 
     @classmethod
     def resource_setup(cls):
@@ -54,7 +55,7 @@
             server['id'], volumeId=volume_id,
             device='/dev/%s' % CONF.compute.volume_device_name)
         waiters.wait_for_volume_resource_status(
-            self.os_admin.volumes_client, volume_id, 'in-use')
+            self.admin_volumes_client, volume_id, 'in-use')
         self.addCleanup(self._detach_volume, volume_id)
 
     def _detach_volume(self, volume_id=None):
@@ -63,7 +64,7 @@
 
         self.volumes_client.detach_volume(volume_id)
         waiters.wait_for_volume_resource_status(
-            self.os_admin.volumes_client, volume_id, 'available')
+            self.admin_volumes_client, volume_id, 'available')
 
     @test.services('compute')
     @rbac_rule_validation.action(service="cinder", rule="volume:attach")
@@ -73,8 +74,8 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self._attach_volume(server)
 
-    @test.attr(type=["slow"])
     @test.services('compute')
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder", rule="volume:detach")
     @decorators.idempotent_id('5a042f6a-688b-42e6-a02e-fe5c47b89b07')
     def test_detach_volume_from_instance(self):
@@ -84,7 +85,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self._detach_volume()
 
-    @test.attr(type=["slow"])
+    @decorators.attr(type=["slow"])
     @test.services('image')
     @rbac_rule_validation.action(
         service="cinder",
@@ -106,7 +107,7 @@
                         image_id)
         waiters.wait_for_image_status(self.admin_image_client, image_id,
                                       'active')
-        waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+        waiters.wait_for_volume_resource_status(self.admin_volumes_client,
                                                 self.volume['id'], 'available')
 
     @rbac_rule_validation.action(service="cinder",
@@ -161,7 +162,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volumes_client.retype_volume(volume['id'], new_type=vol_type)
         waiters.wait_for_volume_retype(
-            self.os_admin.volumes_client, volume['id'], vol_type)
+            self.admin_volumes_client, volume['id'], vol_type)
 
     @rbac_rule_validation.action(
         service="cinder",
@@ -204,7 +205,7 @@
         self.volumes_client.force_detach_volume(
             volume['id'], connector=None,
             attachment_id=attachment['attachment_id'])
-        waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+        waiters.wait_for_volume_resource_status(self.admin_volumes_client,
                                                 volume['id'], 'available')
 
 
@@ -221,8 +222,9 @@
     def setup_clients(cls):
         super(VolumesActionsV310RbacTest, cls).setup_clients()
         cls.admin_image_client = cls.os_admin.image_client_v2
+        cls.admin_volumes_client = cls.os_admin.volumes_client_latest
 
-    @test.attr(type=["slow"])
+    @decorators.attr(type=["slow"])
     @test.services('image')
     @rbac_rule_validation.action(
         service="cinder",
@@ -243,7 +245,7 @@
                         image_id)
         waiters.wait_for_image_status(self.admin_image_client, image_id,
                                       'active')
-        waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+        waiters.wait_for_volume_resource_status(self.admin_volumes_client,
                                                 volume['id'], 'available')
 
 
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_quotas_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_quotas_rbac.py
index 6e9812b..851d468 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_quotas_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_quotas_rbac.py
@@ -29,7 +29,7 @@
     @classmethod
     def setup_clients(cls):
         super(VolumeQuotasRbacTest, cls).setup_clients()
-        cls.quotas_client = cls.os_primary.volume_quotas_client
+        cls.quotas_client = cls.os_primary.volume_quotas_v2_client
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume_extension:quotas:show")
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_transfers_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_transfers_rbac.py
index 212482c..405d02b 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_transfers_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_transfers_rbac.py
@@ -23,13 +23,11 @@
 
 class VolumesTransfersRbacTest(rbac_base.BaseVolumeRbacTest):
 
-    credentials = ['primary', 'admin']
-
     @classmethod
     def setup_clients(cls):
         super(VolumesTransfersRbacTest, cls).setup_clients()
         cls.transfers_client = cls.os_primary.volume_transfers_v2_client
-        cls.adm_volumes_client = cls.os_admin.volumes_v2_client
+        cls.admin_volumes_client = cls.os_admin.volumes_client_latest
 
     @classmethod
     def resource_setup(cls):
@@ -43,7 +41,7 @@
         test_utils.call_and_ignore_notfound_exc(
             self.transfers_client.delete_volume_transfer, transfer['id'])
         waiters.wait_for_volume_resource_status(
-            self.adm_volumes_client, self.volume['id'], 'available')
+            self.admin_volumes_client, self.volume['id'], 'available')
 
     def _create_transfer(self):
         transfer = self.transfers_client.create_volume_transfer(
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_types_extra_specs_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_types_extra_specs_rbac.py
index 97eaab7..210901c 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_types_extra_specs_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_types_extra_specs_rbac.py
@@ -54,23 +54,26 @@
                 self.vol_type['id'], self.spec_key)
 
     @decorators.idempotent_id('76c36be2-2b6c-4acf-9aac-c9dc5c17cdbe')
-    @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:types_extra_specs")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:types_extra_specs:index")
     def test_list_volume_types_extra_specs(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.volume_types_client.list_volume_types_extra_specs(
             self.vol_type['id'])['extra_specs']
 
-    @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:types_extra_specs")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:types_extra_specs:create")
     @decorators.idempotent_id('eea40251-990b-49b0-99ae-10e4585b479b')
     def test_create_volume_type_extra_specs(self):
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self._create_volume_type_extra_specs(ignore_not_found=True)
 
     @decorators.idempotent_id('e2dcc9c6-2fef-431d-afaf-92b45bc76d1a')
-    @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:types_extra_specs")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:types_extra_specs:show")
     def test_show_volume_type_extra_specs(self):
         self._create_volume_type_extra_specs()
 
@@ -79,8 +82,9 @@
             self.vol_type['id'], self.spec_key)
 
     @decorators.idempotent_id('93001912-f938-41c7-8787-62dc7010fd52')
-    @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:types_extra_specs")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:types_extra_specs:delete")
     def test_delete_volume_type_extra_specs(self):
         self._create_volume_type_extra_specs(ignore_not_found=True)
 
@@ -89,8 +93,9 @@
             self.vol_type['id'], self.spec_key)
 
     @decorators.idempotent_id('0a444437-7402-4fbe-a18a-93af2ee00618')
-    @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:types_extra_specs")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="volume_extension:types_extra_specs:update")
     def test_update_volume_type_extra_specs(self):
         self._create_volume_type_extra_specs()
         update_extra_specs = {self.spec_key: "val2"}
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volumes_backup_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volumes_backup_rbac.py
index e6944cc..01c1b08 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volumes_backup_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volumes_backup_rbac.py
@@ -21,7 +21,6 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest import test
 
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.volume import rbac_base
@@ -41,6 +40,11 @@
         if not CONF.volume_feature_enabled.backup:
             raise cls.skipException("Cinder backup feature disabled")
 
+    @classmethod
+    def setup_clients(cls):
+        super(VolumesBackupsRbacTest, cls).setup_clients()
+        cls.admin_backups_client = cls.os_admin.backups_v2_client
+
     def _decode_url(self, backup_url):
         return json.loads(base64.decode_as_text(backup_url))
 
@@ -53,7 +57,7 @@
         backup.update(changes)
         return self._encode_backup(backup)
 
-    @test.attr(type=["slow"])
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:create")
     @decorators.idempotent_id('6887ec94-0bcf-4ab7-b30f-3808a4b5a2a5')
@@ -61,7 +65,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.create_backup(volume_id=self.volume['id'])
 
-    @test.attr(type=["slow"])
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:get")
     @decorators.idempotent_id('abd92bdd-b0fb-4dc4-9cfc-de9e968f8c8a')
@@ -95,21 +99,22 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.backups_client.reset_backup_status(backup_id=backup['id'],
                                                 status='error')
-        waiters.wait_for_volume_resource_status(self.os_admin.backups_client,
+        waiters.wait_for_volume_resource_status(self.admin_backups_client,
                                                 backup['id'], 'error')
 
-    @test.attr(type=["slow"])
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:restore")
     @decorators.idempotent_id('9c794bf9-2446-4f41-8fe0-80b71e757f9d')
     def test_restore_backup(self):
         backup = self.create_backup(volume_id=self.volume['id'])
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         restore = self.backups_client.restore_backup(backup['id'])['restore']
         waiters.wait_for_volume_resource_status(
-            self.os_admin.backups_client, restore['backup_id'], 'available')
+            self.admin_backups_client, restore['backup_id'], 'available')
 
-    @test.attr(type=["slow"])
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:delete")
     @decorators.idempotent_id('d5d0c6a2-413d-437e-a73f-4bf2b41a20ed')
@@ -121,7 +126,7 @@
             volume_id=self.volume['id'])['backup']
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.backups_client.delete_backup, backup['id'])
-        waiters.wait_for_volume_resource_status(self.os_admin.backups_client,
+        waiters.wait_for_volume_resource_status(self.admin_backups_client,
                                                 backup['id'], 'available')
 
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
@@ -129,7 +134,7 @@
         # Wait for deletion so error isn't thrown during clean up.
         self.backups_client.wait_for_resource_deletion(backup['id'])
 
-    @test.attr(type=["slow"])
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:backup-export")
     @decorators.idempotent_id('e984ec8d-e8eb-485c-98bc-f1856020303c')
@@ -138,7 +143,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.backups_client.export_backup(backup['id'])['backup-record']
 
-    @test.attr(type=["slow"])
+    @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
                                  rule="backup:backup-import")
     @decorators.idempotent_id('1e70f039-4556-44cc-9cc1-edf2b7ed648b')
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volumes_extend_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volumes_extend_rbac.py
index 5709669..971e079 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volumes_extend_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volumes_extend_rbac.py
@@ -26,6 +26,11 @@
 class VolumesExtendRbacTest(rbac_base.BaseVolumeRbacTest):
 
     @classmethod
+    def setup_clients(cls):
+        super(VolumesExtendRbacTest, cls).setup_clients()
+        cls.admin_volumes_client = cls.os_admin.volumes_client_latest
+
+    @classmethod
     def resource_setup(cls):
         super(VolumesExtendRbacTest, cls).resource_setup()
         # Create a test shared volume for tests
@@ -40,7 +45,7 @@
         self.volumes_client.extend_volume(self.volume['id'],
                                           new_size=extend_size)
         waiters.wait_for_volume_resource_status(
-            self.os_admin.volumes_client, self.volume['id'], 'available')
+            self.admin_volumes_client, self.volume['id'], 'available')
 
 
 class VolumesExtendV3RbacTest(VolumesExtendRbacTest):
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volumes_manage_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volumes_manage_rbac.py
index 8e71ee8..bea3a46 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volumes_manage_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volumes_manage_rbac.py
@@ -42,6 +42,7 @@
     def setup_clients(cls):
         super(VolumesManageRbacTest, cls).setup_clients()
         cls.volume_manage_client = cls.os_primary.volume_manage_v2_client
+        cls.admin_volumes_client = cls.os_admin.volumes_client_latest
 
     def _manage_volume(self, org_volume):
         # Manage volume
@@ -59,14 +60,14 @@
         new_volume_id = self.volume_manage_client.manage_volume(
             **new_volume_ref)['volume']['id']
 
-        waiters.wait_for_volume_resource_status(self.os_admin.volumes_client,
+        waiters.wait_for_volume_resource_status(self.admin_volumes_client,
                                                 new_volume_id, 'available')
         self.addCleanup(self.delete_volume,
                         self.volumes_client, new_volume_id)
 
     def _unmanage_volume(self, volume):
         self.volumes_client.unmanage_volume(volume['id'])
-        self.volumes_client.wait_for_resource_deletion(volume['id'])
+        self.admin_volumes_client.wait_for_resource_deletion(volume['id'])
 
     @rbac_rule_validation.action(
         service="cinder",
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volumes_snapshots_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volumes_snapshots_rbac.py
index 422a3db..13ced96 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volumes_snapshots_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volumes_snapshots_rbac.py
@@ -32,6 +32,11 @@
             raise cls.skipException("Cinder volume snapshots are disabled")
 
     @classmethod
+    def setup_clients(cls):
+        super(VolumesSnapshotRbacTest, cls).setup_clients()
+        cls.admin_snapshots_client = cls.os_admin.snapshots_v2_client
+
+    @classmethod
     def resource_setup(cls):
         super(VolumesSnapshotRbacTest, cls).resource_setup()
         # Create a test shared volume for tests
@@ -77,8 +82,7 @@
         self.snapshots_client.update_snapshot(
             self.snapshot['id'], **params)['snapshot']
         waiters.wait_for_volume_resource_status(
-            self.os_admin.snapshots_client,
-            self.snapshot['id'], 'available')
+            self.admin_snapshots_client, self.snapshot['id'], 'available')
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:get_all_snapshots")
@@ -99,7 +103,7 @@
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         # Delete the snapshot
         self.snapshots_client.delete_snapshot(temp_snapshot['id'])
-        self.os_admin.snapshots_client.wait_for_resource_deletion(
+        self.admin_snapshots_client.wait_for_resource_deletion(
             temp_snapshot['id'])
 
 
diff --git a/releasenotes/notes/test_tokens_rbac-63a93e507d079a03.yaml b/releasenotes/notes/test_tokens_rbac-63a93e507d079a03.yaml
new file mode 100644
index 0000000..da285eb
--- /dev/null
+++ b/releasenotes/notes/test_tokens_rbac-63a93e507d079a03.yaml
@@ -0,0 +1,3 @@
+---
+features:
+  - Added RBAC test scenarios for the token-related v3 identity API
diff --git a/requirements.txt b/requirements.txt
index 126a3dc..cd6a577 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,5 +7,5 @@
 oslo.log>=3.22.0 # Apache-2.0
 oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
 oslo.policy>=1.23.0 # Apache-2.0
-tempest>=14.0.0 # Apache-2.0
+tempest>=16.1.0 # Apache-2.0
 stevedore>=1.20.0 # Apache-2.0
diff --git a/setup.cfg b/setup.cfg
index 6fd4b8a..212db9e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -27,6 +27,7 @@
 source-dir = doc/source
 build-dir = doc/build
 all_files = 1
+warning-is-error = 1
 
 [upload_sphinx]
 upload-dir = doc/build/html
diff --git a/test-requirements.txt b/test-requirements.txt
index 3e03437..177d0fd 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -13,4 +13,4 @@
 oslotest>=1.10.0 # Apache-2.0
 oslo.policy>=1.23.0 # Apache-2.0
 oslo.log>=3.22.0 # Apache-2.0
-tempest>=14.0.0 # Apache-2.0
+tempest>=16.1.0 # Apache-2.0