Merge "Add waiters after creating volume transfer for related tests"
diff --git a/.zuul.yaml b/.zuul.yaml
index 21b5679..fb110f0 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -1,7 +1,9 @@
 - job:
     name: patrole-base
     parent: devstack-tempest
-    description: Patrole base job for admin and member roles.
+    description: |
+      Patrole base job for admin and member roles. This job executes RBAC tests
+      for all the "core" services that Tempest covers, excluding Swift.
     required-projects:
       - name: openstack/tempest
       - name: openstack/patrole
@@ -17,12 +19,13 @@
       - ^setup.cfg$
     vars:
       devstack_localrc:
-        TEMPEST_PLUGINS: "'{{ ansible_user_dir }}/src/git.openstack.org/openstack/patrole'"
+        TEMPEST_PLUGINS: "'/opt/stack/patrole'"
       devstack_plugins:
         patrole: git://git.openstack.org/openstack/patrole.git
       devstack_services:
         tempest: true
         neutron: true
+        neutron-trunk: true
       tempest_concurrency: 2
       tempest_test_regex: (?!.*\[.*\bslow\b.*\])(^patrole_tempest_plugin\.tests\.api)
       tox_envlist: all-plugin
@@ -84,6 +87,11 @@
         RBAC_TEST_ROLE: member
 
 - job:
+    name: patrole-member-rocky
+    parent: patrole-member
+    override-checkout: stable/rocky
+
+- job:
     name: patrole-member-queens
     parent: patrole-member
     override-checkout: stable/queens
@@ -127,18 +135,75 @@
         # Without Swift, c-bak cannot run (in the gate at least).
         c-bak: false
 
+- job:
+    name: patrole-plugin-base
+    parent: patrole-base
+    description: |
+      Patrole plugin job for admin and member roles which
+      runs RBAC tests for neutron-tempest-plugin APIs (if the plugin is installed).
+    required-projects:
+      - name: openstack/tempest
+      - name: openstack/patrole
+      - name: openstack/neutron-tempest-plugin
+    vars:
+      devstack_localrc:
+        TEMPEST_PLUGINS: "'/opt/stack/patrole /opt/stack/neutron-tempest-plugin'"
+      devstack_plugins:
+        neutron: git://git.openstack.org/openstack/neutron.git
+        patrole: git://git.openstack.org/openstack/patrole.git
+        neutron-tempest-plugin: git://git.openstack.org/openstack/neutron-tempest-plugin.git
+      devstack_services:
+        tempest: true
+        neutron: true
+        neutron-segments: true
+        neutron-qos: true
+
+- job:
+    name: patrole-plugin-member
+    parent: patrole-plugin-base
+    voting: false
+    vars:
+      devstack_localrc:
+        RBAC_TEST_ROLE: member
+      tempest_test_regex: (?=.*PluginRbacTest)(^patrole_tempest_plugin\.tests\.api)
+
+- job:
+    name: patrole-plugin-admin
+    parent: patrole-plugin-base
+    voting: false
+    vars:
+      devstack_localrc:
+        RBAC_TEST_ROLE: admin
+      tempest_test_regex: (?=.*PluginRbacTest)(^patrole_tempest_plugin\.tests\.api)
+
 - project:
+    templates:
+      - openstack-cover-jobs
+      - openstack-lower-constraints-jobs
+      - openstack-python36-jobs
+      - openstack-python-jobs
+      - openstack-python35-jobs
+      - check-requirements
+      - publish-openstack-docs-pti
+      - release-notes-jobs-python3
     check:
       jobs:
         - patrole-admin
         - patrole-member
+        - patrole-member-rocky
         - patrole-member-queens
         - patrole-member-pike
         - patrole-py35-member
         - patrole-multinode-admin
         - patrole-multinode-member
-        - openstack-tox-lower-constraints
+        - patrole-plugin-admin
+        - patrole-plugin-member
     gate:
       jobs:
         - patrole-admin
         - patrole-member
+    periodic-stable:
+      jobs:
+        - patrole-member-rocky
+        - patrole-member-queens
+        - patrole-member-pike
diff --git a/HACKING.rst b/HACKING.rst
index 28a977d..87e3b1f 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -33,12 +33,15 @@
 The following are Patrole's specific Commandments:
 
 - [P100] The ``rbac_rule_validation.action`` decorator must be applied to
-  an RBAC test
+  all RBAC tests
 - [P101] RBAC test filenames must end with "_rbac.py"; for example,
   test_servers_rbac.py, not test_servers.py
 - [P102] RBAC test class names must end in 'RbacTest'
 - [P103] ``self.client`` must not be used as a client alias; this allows for
   code that is more maintainable and easier to read
+- [P104] RBAC `plugin test class`_ names must end in 'PluginRbacTest'
+
+.. _plugin test class: https://github.com/openstack/patrole/tree/master/patrole_tempest_plugin/tests/api/network#neutron-plugin-tests
 
 Role Overriding
 ---------------
diff --git a/README.rst b/README.rst
index e9c03c8..fdcbc6b 100644
--- a/README.rst
+++ b/README.rst
@@ -31,10 +31,12 @@
 * Bugs: https://bugs.launchpad.net/patrole
 * Release notes: https://docs.openstack.org/releasenotes/patrole/
 
+.. _design-principles:
+
 Design Principles
 -----------------
 
-As a `Tempest plugin`_, Patrole borrows some `design principles`_ from Tempest,
+As a `Tempest plugin`_, Patrole borrows some design principles from `Tempest design principles`_,
 but not all, as its testing scope is confined to policies.
 
 * *Stability*. Patrole uses OpenStack public interfaces. Tests in Patrole
@@ -59,6 +61,9 @@
     Realistically this is not always possible because some services have
     not yet moved to policy in code.
 
+* *Customizable*. Patrole should be able to validate custom policy overrides to
+  ensure that those overrides enhance rather than undermine the cloud's RBAC
+  configuration. In addition, Patrole should be able to validate any role.
 * *Self-cleaning*. Patrole should attempt to clean up after itself; whenever
   possible we should tear down resources when done.
 
@@ -71,7 +76,7 @@
 * *Self-testing*. Patrole should be self-testing.
 
 .. _Tempest plugin: https://docs.openstack.org/tempest/latest/plugin.html
-.. _design principles: https://docs.openstack.org/tempest/latest/overview.html#design-principles
+.. _Tempest design principles: https://docs.openstack.org/tempest/latest/overview.html#design-principles
 .. _policy in code: https://specs.openstack.org/openstack/oslo-specs/specs/newton/policy-in-code.html
 .. _Nova repository: https://github.com/openstack/nova/tree/master/nova/policies
 .. _Keystone repository: https://github.com/openstack/keystone/tree/master/keystone/common/policies
@@ -115,7 +120,7 @@
 Quickstart
 ----------
 To run Patrole, you must first have `Tempest`_ installed and configured
-properly. Please reference Tempest's `Quickstart`_ guide to do so. Follow all
+properly. Please reference `Tempest_quickstart`_ guide to do so. Follow all
 the steps outlined therein. Afterward, proceed with the steps below.
 
 #. You first need to install Patrole. This is done with pip after you check out
@@ -134,7 +139,7 @@
 
 #. Next you must properly configure Patrole, which is relatively
    straightforward. For details on configuring Patrole refer to the
-   :ref:`patrole-configuration`.
+   `Patrole Configuration <https://docs.openstack.org/patrole/latest/configuration.html#patrole-configuration>`_.
 
 #. Once the configuration is done you're now ready to run Patrole. This can
    be done using the `tempest_run`_ command. This can be done by running::
@@ -165,14 +170,14 @@
 
 #. Log information from tests is captured in ``tempest.log`` under the Tempest
    repository. Some Patrole debugging information is captured in that log
-   related to expected test results and :ref:`role-overriding`.
+   related to expected test results and `Role Overriding <https://docs.openstack.org/patrole/latest/framework/rbac_utils.html#role-overriding>`_.
 
    More detailed RBAC testing log output is emitted to ``patrole.log`` under
    the Patrole repository. To configure Patrole's logging, see the
-   :ref:`patrole-configuration` guide.
+   `Patrole Configuration Guide <https://docs.openstack.org/patrole/latest/configuration.html#patrole-configuration>`_.
 
 .. _Tempest: https://github.com/openstack/tempest
-.. _Quickstart: https://docs.openstack.org/tempest/latest/overview.html#quickstart
+.. _Tempest_quickstart: https://docs.openstack.org/tempest/latest/overview.html#quickstart
 .. _tempest_run: https://docs.openstack.org/tempest/latest/run.html
 .. _testr: https://testrepository.readthedocs.org/en/latest/MANUAL.html
 .. _ostestr: https://docs.openstack.org/os-testr/latest/
diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index bd0068b..502b68c 100644
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -26,15 +26,32 @@
         iniset $TEMPEST_CONFIG policy-feature-enabled volume_extension_volume_actions_attach_policy False
         iniset $TEMPEST_CONFIG policy-feature-enabled volume_extension_volume_actions_reserve_policy False
         iniset $TEMPEST_CONFIG policy-feature-enabled volume_extension_volume_actions_unreserve_policy False
+
+        # TODO(cl566n): Remove these once stable/pike becomes EOL.
+        # These policies were removed in Stein but are available in Pike.
+        iniset $TEMPEST_CONFIG policy-feature-enabled removed_nova_policies_stein False
+        iniset $TEMPEST_CONFIG policy-feature-enabled removed_keystone_policies_stein False
+        iniset $TEMPEST_CONFIG policy-feature-enabled added_cinder_policies_stein False
     fi
 
     if [[ ${DEVSTACK_SERIES} == 'queens' ]]; then
         if [[ "$RBAC_TEST_ROLE" == "member" ]]; then
             RBAC_TEST_ROLE="Member"
         fi
+
+        # TODO(cl566n): Remove these once stable/queens becomes EOL.
+        # These policies were removed in Stein but are available in Queens.
+        iniset $TEMPEST_CONFIG policy-feature-enabled removed_nova_policies_stein False
+        iniset $TEMPEST_CONFIG policy-feature-enabled removed_keystone_policies_stein False
+        iniset $TEMPEST_CONFIG policy-feature-enabled added_cinder_policies_stein False
     fi
 
-    iniset $TEMPEST_CONFIG patrole enable_rbac True
+    if [[ ${DEVSTACK_SERIES} == 'rocky' ]]; then
+        # TODO(cl566n): Policies used by Patrole testing. Remove these once stable/rocky becomes EOL.
+        iniset $TEMPEST_CONFIG policy-feature-enabled added_cinder_policies_stein False
+        iniset $TEMPEST_CONFIG policy-feature-enabled removed_keystone_policies_stein False
+    fi
+
     iniset $TEMPEST_CONFIG patrole rbac_test_role $RBAC_TEST_ROLE
 }
 
diff --git a/doc/source/framework/overview.rst b/doc/source/framework/overview.rst
index 4902f7b..8e04082 100644
--- a/doc/source/framework/overview.rst
+++ b/doc/source/framework/overview.rst
@@ -1,9 +1,11 @@
 RBAC Testing Validation
 =======================
 
---------
-Overview
---------
+.. _validation-workflow-overview:
+
+----------------------------
+Validation Workflow Overview
+----------------------------
 
 RBAC testing validation is broken up into 3 stages:
 
@@ -36,6 +38,16 @@
    ``oslo.policy`` or a 403 from the API call and a ``True`` result from
    ``oslo.policy`` are failing results.
 
+.. warning::
+
+  Note that Patrole cannot currently derive the expected policy result for
+  service-specific ``oslo.policy`` `checks`_, like Neutron's `FieldCheck`_,
+  because such checks are contained within the service's code base itself,
+  which Patrole cannot import.
+
+.. _checks: https://docs.openstack.org/oslo.policy/latest/reference/api/oslo_policy.policy.html#generic-checks
+.. _FieldCheck: https://docs.openstack.org/neutron/pike/contributor/internals/policy.html#fieldcheck-verify-resource-attributes
+
 -------------------------------
 The RBAC Rule Validation Module
 -------------------------------
diff --git a/doc/source/framework/policy_authority.rst b/doc/source/framework/policy_authority.rst
index 822c7b6..37b698c 100644
--- a/doc/source/framework/policy_authority.rst
+++ b/doc/source/framework/policy_authority.rst
@@ -60,3 +60,4 @@
 .. automodule:: patrole_tempest_plugin.policy_authority
    :members:
    :undoc-members:
+   :special-members:
diff --git a/doc/source/framework/rbac_authority.rst b/doc/source/framework/rbac_authority.rst
index 84c372b..40f2a8d 100644
--- a/doc/source/framework/rbac_authority.rst
+++ b/doc/source/framework/rbac_authority.rst
@@ -35,3 +35,4 @@
 .. automodule:: patrole_tempest_plugin.rbac_authority
    :members:
    :undoc-members:
+   :special-members:
diff --git a/doc/source/framework/rbac_utils.rst b/doc/source/framework/rbac_utils.rst
index 7143928..b13a4a3 100644
--- a/doc/source/framework/rbac_utils.rst
+++ b/doc/source/framework/rbac_utils.rst
@@ -23,156 +23,14 @@
 and test execution, respectively. This is especially true when considering
 custom policy rule definitions, which can be arbitrarily complex.
 
-.. _role-overriding:
-
-Role Overriding
-^^^^^^^^^^^^^^^
-
-Role overriding is the way Patrole is able to create resources and delete
-resources -- including those that require admin credentials -- while still
-being able to exercise the same set of Tempest credentials to perform the API
-action that authorizes the policy under test, by manipulating the role of
-the Tempest credentials.
-
-Patrole implicitly splits up each test into 3 stages: set up, test execution,
-and teardown.
-
-The role workflow is as follows:
-
-#. Setup: Admin role is used automatically. The primary credentials are
-   overridden with the admin role.
-#. Test execution: ``[patrole] rbac_test_role`` is used manually via the
-   call to ``with rbac_utils.override_role(self)``. Everything that
-   is executed within this contextmanager uses the primary
-   credentials overridden with the ``[patrole] rbac_test_role``.
-#. Teardown: Admin role is used automatically. The primary credentials have
-   been overridden with the admin role.
-
-.. _Tempest credentials: https://docs.openstack.org/tempest/latest/library/credential_providers.html
-.. _dynamic credentials: https://docs.openstack.org/tempest/latest/configuration.html#dynamic-credentials
-
-Test Setup
-----------
-
-Automatic role override in background.
-
-Resources can be set up inside the ``resource_setup`` class method that Tempest
-provides. These resources are typically reserved for "expensive" resources
-in terms of memory or storage requirements, like volumes and VMs. These
-resources are **always** created via the admin role; Patrole automatically
-handles this.
-
-Like Tempest, however, Patrole must also create resources inside tests
-themselves. At the beginning of each test, the primary credentials have already
-been overridden with the admin role. One can create whatever test-level
-resources one needs, without having to worry about permissions.
-
-Test Execution
---------------
-
-Manual role override required.
-
-"Test execution" here means calling the API endpoint that enforces the policy
-action expected by the ``rbac_rule_validation`` decorator. Test execution
-should be performed *only after* calling
-``with rbac_utils.override_role(self)``.
-
-Immediately after that call, the API endpoint that enforces the policy should
-be called.
-
-Examples
-^^^^^^^^
-
-Always use the contextmanager before calling the API that enforces the
-expected policy action.
-
-Example::
-
-    @rbac_rule_validation.action(
-        service="nova",
-        rule="os_compute_api:os-aggregates:show")
-    def test_show_aggregate_rbac(self):
-        # Do test setup before the ``override_role`` call.
-        aggregate_id = self._create_aggregate()
-        # Call the ``override_role`` method so that the primary credentials
-        # have the test role needed for test execution.
-        with self.rbac_utils.override_role(self):
-            self.aggregates_client.show_aggregate(aggregate_id)
-
-When using a waiter, do the wait outside the contextmanager. "Waiting" always
-entails executing a ``GET`` request to the server, until the state of the
-returned resource matches a desired state. These ``GET`` requests enforce
-a different policy than the one expected. This is undesirable because
-Patrole should only test policies in isolation from one another.
-
-Otherwise, the test result will be tainted, because instead of only the
-expected policy getting enforced with the ``os_primary`` role, at least
-two policies get enforced.
-
-Example using waiter::
-
-    @rbac_rule_validation.action(
-        service="nova",
-        rule="os_compute_api:os-admin-password")
-    def test_change_server_password(self):
-        original_password = self.servers_client.show_password(
-            self.server['id'])
-        self.addCleanup(self.servers_client.change_password, self.server['id'],
-                        adminPass=original_password)
-
-        with self.rbac_utils.override_role(self):
-            self.servers_client.change_password(
-                self.server['id'], adminPass=data_utils.rand_password())
-        # Call the waiter outside the ``override_role`` contextmanager, so that
-        # it is executed with admin role.
-        waiters.wait_for_server_status(
-            self.servers_client, self.server['id'], 'ACTIVE')
-
-Below is an example of a method that enforces multiple policies getting
-called inside the contextmanager. The ``_complex_setup_method`` below
-performs the correct API that enforces the expected policy -- in this
-case ``self.resources_client.create_resource`` -- but then proceeds to
-use a waiter.
-
-Incorrect::
-
-    def _complex_setup_method(self):
-        resource = self.resources_client.create_resource(
-            **kwargs)['resource']
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self._delete_resource, resource)
-        waiters.wait_for_resource_status(
-            self.resources_client, resource['id'], 'available')
-        return resource
-
-    @rbac_rule_validation.action(
-        service="example-service",
-        rule="example-rule")
-    def test_change_server_password(self):
-        # Never call a helper function inside the contextmanager that calls a
-        # bunch of APIs. Only call the API that enforces the policy action
-        # contained in the decorator above.
-        with self.rbac_utils.override_role(self):
-            self._complex_setup_method()
-
-To fix this test, see the "Example using waiter" section above. It is
-recommended to re-implement the logic in a helper method inside a test such
-that only the relevant API is called inside the contextmanager, with
-everything extraneous outside.
-
-Test Cleanup
-------------
-
-Automatic role override in background.
-
-After the test -- no matter whether it ended successfully or in failure --
-the credentials are overridden with the admin role by the Patrole framework,
-*before* ``tearDown`` or ``tearDownClass`` are called. This means that
-resources are always cleaned up using the admin role.
-
 Implementation
 --------------
 
 .. automodule:: patrole_tempest_plugin.rbac_utils
    :members:
    :private-members:
+   :special-members:
+
+.. _Tempest credentials: https://docs.openstack.org/tempest/latest/library/credential_providers.html
+.. _dynamic credentials: https://docs.openstack.org/tempest/latest/configuration.html#dynamic-credentials
+
diff --git a/doc/source/framework/rbac_validation.rst b/doc/source/framework/rbac_validation.rst
index 186dfe2..6cd1534 100644
--- a/doc/source/framework/rbac_validation.rst
+++ b/doc/source/framework/rbac_validation.rst
@@ -17,3 +17,4 @@
 .. automodule:: patrole_tempest_plugin.rbac_rule_validation
    :members:
    :private-members:
+   :special-members:
diff --git a/doc/source/framework/requirements_authority.rst b/doc/source/framework/requirements_authority.rst
index 6c4fcc0..628f0c0 100644
--- a/doc/source/framework/requirements_authority.rst
+++ b/doc/source/framework/requirements_authority.rst
@@ -103,3 +103,4 @@
 .. automodule:: patrole_tempest_plugin.requirements_authority
    :members:
    :undoc-members:
+   :special-members:
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 560857e..c03aac6 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -10,6 +10,14 @@
 
    overview
 
+RBAC Overview
+-------------
+
+.. toctree::
+   :maxdepth: 2
+
+   rbac-overview
+
 User's Guide
 ============
 
@@ -46,6 +54,7 @@
 
    HACKING
    REVIEWING
+   test_writing_guide
 
 Framework
 ---------
diff --git a/doc/source/rbac-overview.rst b/doc/source/rbac-overview.rst
new file mode 100644
index 0000000..09ab17d
--- /dev/null
+++ b/doc/source/rbac-overview.rst
@@ -0,0 +1,286 @@
+==================================
+Role-Based Access Control Overview
+==================================
+
+Introduction
+------------
+
+Role-Based Access Control (RBAC) is used by most OpenStack services to control
+user access to resources. Authorization is granted if a user has the necessary
+role to perform an action. Patrole is concerned with validating that each of
+these resources *can* be accessed by authorized users and *cannot* be accessed
+by unauthorized users.
+
+OpenStack services use `oslo.policy`_ as the library for RBAC authorization.
+Patrole relies on the same library for deriving expected test results.
+
+.. _policy-in-code:
+
+Policy in Code
+--------------
+
+Services publish their policy-to-API mapping via policy in code documentation.
+This mapping includes the list of APIs that authorize a policy, for each
+policy declared within a service.
+
+For example, Nova's policy in code documentation is located in the
+`Nova repository`_ under ``nova/policies``. Likewise, Keystone's policy in
+code documentation is located in the `Keystone repository`_ under
+``keystone/common/policies``. The other OpenStack services follow the same
+directory layout pattern with respect to policy in code.
+
+The policy in code `governance goal`_ enumerates many advantages with following
+this RBAC design approach. A so-called library of in-code policies offers the
+following advantages, with respect to facilitating validation:
+
+* includes every policy enforced by an OpenStack service, enabling the
+  possibility of complete Patrole test coverage for that service (otherwise
+  one has to read the source code to discover all the policies)
+* provides the policy-to-API mapping for each policy which can be used
+  to write correct Patrole tests (otherwise reading source code and
+  experimentation are required to derive this mapping)
+* by extension, the policy-to-API mapping facilitates writing multi-policy
+  Patrole tests (otherwise even more experimentation and code reading is
+  required to arrive at all the policies enforced by an API)
+* policy in code documentation includes additional information, like
+  descriptions and (in the case of some services, like Keystone)
+  `scope types`_, which help with understanding how to correctly write
+  Patrole tests
+* by extension, such information helps to determine whether a Patrole test
+  should assume :term:`hard authorization` or :term:`soft authorization`
+
+Policy in Code (Default) Validation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+By default, Patrole validates default OpenStack policies. This is so that
+the out-of-the-box defaults are sanity-checked, to ensure that OpenStack
+services are secure, from an RBAC perspective, for each release.
+
+Patrole strives to validate RBAC by using the policy in code documentation,
+wherever possible. See :ref:`validation-workflow-overview` for more details.
+
+.. _custom-policies:
+
+Custom Policies
+---------------
+
+Operators can override policy in code defaults using `policy.yaml`_. While
+this allows operators to offer more fine-grained RBAC control to their tenants,
+it opens the door to misconfiguration and bugs. Patrole can be used to validate
+that custom policy overrides don't break anything and work as expected.
+
+Custom Policy Validation
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+While testing default policy behavior is a valid use case, oftentimes default
+policies are modified with custom overrides in production. OpenStack's
+`policy.yaml`_ documentation claims that "modifying policy can have unexpected
+side effects", which is why Patrole was created: to ensure that custom
+overrides allow the principle of least privilege to be tailor-made to exact
+specifications via policy overrides, without:
+
+* causing unintended side effects (breaking API endpoints, breaking
+  cross-service workflows, breaking the policy file itself); or
+* resulting in poor RBAC configuration, promoting security vulnerabilities
+
+This has implications on Patrole's :ref:`design-principles`: validating custom
+overrides requires the ability to handle arbitrary roles, which requires logic
+capable of dynamically determining expected test behavior.
+
+Note that support for custom policies is limited. This is because custom
+policies can be arbitrarily complex, requiring that tests be very robust
+in order to handle all edge cases.
+
+.. _multiple-policies:
+
+Multiple Policies
+-----------------
+
+Behind the scenes, many APIs enforce multiple policies, for many reasons,
+including:
+
+* to control complex cross-service workflows;
+* to control whether a server is booted from an image or booted from a volume
+  (for example);
+* to control whether a response body should contain additional information
+  conditioned upon successful policy authorization.
+
+This makes `policy in code`_ especially important for policy validation: it
+is difficult to keep track of all the policies being enforced across all the
+individual APIs, without policy in code documentation.
+
+Multi-Policy Validation
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Patrole offers support for validating APIs that enforce multiple policies.
+Perhaps in an ideal world each API endpoint would enforce only one policy,
+but in reality some API endpoints enforce multiple policies. Thus, to offer
+accurate validation, Patrole handles multiple policies:
+
+* for services *with* policy in code documentation: this documentation
+  indicates that a single API endpoint enforces multiple policy actions.
+* for services *without* policy in code documentation: the API code clearly
+  shows multiple policy actions being validated. Note that in this case some
+  degree of log tracing is required by developers to confirm that the expected
+  policies are getting enforced, prior to the tests getting merged.
+
+.. todo::
+
+  Link to multi-policy validation documentation section once it has been
+  written.
+
+.. _error-codes:
+
+Error Codes
+-----------
+
+Most OpenStack services raise a ``403 Forbidden`` following failed
+:term:`hard authorization`. Neutron, however, can raise a ``404 NotFound``
+as well. See Neutron's `authorization policy enforcement`_ documentation
+for more details.
+
+Admin Context Policy
+--------------------
+
+The so-called "admin context" policy refers to the following policy definition
+(using the legacy policy file syntax):
+
+.. code-block:: javascript
+
+  {
+    "context_is_admin": "role:admin"
+    ...
+  }
+
+Which is unfortunately used to bypass ``oslo.policy`` authorization checks,
+for example:
+
+.. code-block:: python
+
+  # This function is responsible for calling oslo.policy to check whether
+  # requests are authorized to perform an API action.
+  def enforce(context, action, target, [...]):
+    # Here this condition, if True, skips over the enforce call below which
+    # is what calls oslo.policy.
+    if context.is_admin:
+        return True
+    _ENFORCER.enforce([...])  # This is what can be skipped over.
+    [...]
+
+This type of behavior is currently present in many services. Unless such
+logic is removed in the future for services that implement it, Patrole
+won't really be able to validate that admin role works from an ``oslo.policy``
+perspective.
+
+Glossary
+--------
+
+The following nomenclature is used throughout Patrole documentation so it is
+important to understand what each term means in order to understand concepts
+related to RBAC in Patrole.
+
+.. glossary::
+
+  authorize
+
+    The act of ``oslo.policy`` determining whether a user can perform a
+    :term:`policy` given his or her :term:`role`.
+
+  enforce
+
+    See :term:`authorize`.
+
+  hard authorization
+
+    The `do_raise`_ flag controls whether policy authorization should result
+    in an exception getting raised or a boolean value getting returned.
+    Hard authorization results in an exception getting raised. Usually, this
+    results in a ``403 Forbidden`` getting returned for unauthorized requests.
+    (See :ref:`error-codes` for further details.)
+
+    Related term: :term:`soft authorization`.
+
+  oslo.policy
+
+    The OpenStack library providing support for RBAC policy enforcement across
+    all OpenStack services. See the `official documentation`_ for more
+    information.
+
+  policy
+
+    Defines an RBAC rule. Each policy is defined by a one-line statement in
+    the form "<target>" : "<rule>". For more information, reference OpenStack's
+    `policy documentation`_.
+
+  policy action
+
+    See :term:`policy target`.
+
+  policy file
+
+    Prior to `governance goal`_ used by all OpenStack services to define
+    policy defaults. Still used by some services, which is why Patrole
+    needs to read the policy files to derive policy information for testing.
+
+  policy in code
+
+    Registers default OpenStack policies for a service in the service's code
+    base.
+
+    Beginning with the Queens release, policy in code became a
+    `governance goal`_.
+
+  policy rule
+
+    The policy rule determines under which circumstances the API call is
+    permitted.
+
+  policy target
+
+    The name of a policy.
+
+  requirements file
+
+    Requirements-driven approach to declaring the expected RBAC test results
+    referenced by Patrole. Uses a high-level YAML syntax to crystallize policy
+    requirements concisely and unambiguously. See :ref:`requirements-authority`
+    for more information.
+
+  role
+
+    A designation for the set of actions that describe what a user can do in
+    the system. Roles are managed through the `Keystone Roles API`_.
+
+  Role-Based Access Control (RBAC)
+
+    May be formally defined as "an approach to restricting system access to
+    authorized users."
+
+  rule
+
+    See :term:`policy rule`. Note that currently the Patrole code base
+    conflates "rule" with :term:`policy target` in some places.
+
+  soft authorization
+
+    The `do_raise`_ flag controls whether policy authorization should result
+    in an exception getting raised or a boolean value getting returned.
+    Soft authorization results in a boolean value getting returned. When policy
+    authorization evaluates to true, additional operations are performed as a
+    part of the API request or additional information is included in the
+    response body (see `response filtering`_ for an example).
+
+    Related term: :term:`hard authorization`.
+
+.. _Nova repository: https://github.com/openstack/nova/tree/master/nova/policies
+.. _Keystone repository: https://github.com/openstack/keystone/tree/master/keystone/common/policies
+.. _governance goal: https://governance.openstack.org/tc/goals/queens/policy-in-code.html
+.. _scope types: https://docs.openstack.org/keystone/latest/admin/identity-tokens.html#authorization-scopes
+.. _policy.yaml: https://docs.openstack.org/ocata/config-reference/policy-yaml-file.html
+.. _oslo.policy: https://docs.openstack.org/oslo.policy/latest/
+.. _policy documentation: https://docs.openstack.org/kilo/config-reference/content/policy-json-file.html
+.. _do_raise: https://docs.openstack.org/oslo.policy/latest/reference/api/oslo_policy.policy.html#oslo_policy.policy.Enforcer.enforce
+.. _authorization policy enforcement: https://docs.openstack.org/neutron/latest/contributor/internals/policy.html
+.. _official documentation: https://docs.openstack.org/oslo.policy/latest/
+.. _Keystone Roles API: https://developer.openstack.org/api-ref/identity/v3/#roles
+.. _response filtering: https://docs.openstack.org/neutron/latest/contributor/internals/policy.html#response-filtering
diff --git a/doc/source/test_writing_guide.rst b/doc/source/test_writing_guide.rst
new file mode 100644
index 0000000..1291201
--- /dev/null
+++ b/doc/source/test_writing_guide.rst
@@ -0,0 +1,166 @@
+Patrole Test Writing Overview
+=============================
+
+Introduction
+------------
+
+Patrole tests are broken up into 3 stages:
+
+#. :ref:`rbac-test-setup`
+#. :ref:`rbac-test-execution`
+#. :ref:`rbac-test-cleanup`
+
+See the :ref:`framework overview documentation <validation-workflow-overview>`
+for a high-level explanation of the entire testing work flow and framework
+implementation. The guide that follows is concerned with helping developers
+know how to write Patrole tests.
+
+.. _role-overriding:
+
+Role Overriding
+---------------
+
+Role overriding is the way Patrole is able to create resources and delete
+resources -- including those that require admin credentials -- while still
+being able to exercise the same set of Tempest credentials to perform the API
+action that authorizes the policy under test, by manipulating the role of
+the Tempest credentials.
+
+Patrole implicitly splits up each test into 3 stages: set up, test execution,
+and teardown.
+
+The role workflow is as follows:
+
+#. Setup: Admin role is used automatically. The primary credentials are
+   overridden with the admin role.
+#. Test execution: ``[patrole] rbac_test_role`` is used manually via the
+   call to ``with rbac_utils.override_role(self)``. Everything that
+   is executed within this contextmanager uses the primary
+   credentials overridden with the ``[patrole] rbac_test_role``.
+#. Teardown: Admin role is used automatically. The primary credentials have
+   been overridden with the admin role.
+
+.. _rbac-test-setup:
+
+Test Setup
+----------
+
+Automatic role override in background.
+
+Resources can be set up inside the ``resource_setup`` class method that Tempest
+provides. These resources are typically reserved for "expensive" resources
+in terms of memory or storage requirements, like volumes and VMs. These
+resources are **always** created via the admin role; Patrole automatically
+handles this.
+
+Like Tempest, however, Patrole must also create resources inside tests
+themselves. At the beginning of each test, the primary credentials have already
+been overridden with the admin role. One can create whatever test-level
+resources one needs, without having to worry about permissions.
+
+.. _rbac-test-execution:
+
+Test Execution
+--------------
+
+Manual role override required.
+
+"Test execution" here means calling the API endpoint that enforces the policy
+action expected by the ``rbac_rule_validation`` decorator. Test execution
+should be performed *only after* calling
+``with rbac_utils.override_role(self)``.
+
+Immediately after that call, the API endpoint that enforces the policy should
+be called.
+
+Examples
+^^^^^^^^
+
+Always use the contextmanager before calling the API that enforces the
+expected policy action.
+
+Example::
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rules=["os_compute_api:os-aggregates:show"])
+    def test_show_aggregate_rbac(self):
+        # Do test setup before the ``override_role`` call.
+        aggregate_id = self._create_aggregate()
+        # Call the ``override_role`` method so that the primary credentials
+        # have the test role needed for test execution.
+        with self.rbac_utils.override_role(self):
+            self.aggregates_client.show_aggregate(aggregate_id)
+
+When using a waiter, do the wait outside the contextmanager. "Waiting" always
+entails executing a ``GET`` request to the server, until the state of the
+returned resource matches a desired state. These ``GET`` requests enforce
+a different policy than the one expected. This is undesirable because
+Patrole should only test policies in isolation from one another.
+
+Otherwise, the test result will be tainted, because instead of only the
+expected policy getting enforced with the ``os_primary`` role, at least
+two policies get enforced.
+
+Example using waiter::
+
+    @rbac_rule_validation.action(
+        service="nova",
+        rules=["os_compute_api:os-admin-password"])
+    def test_change_server_password(self):
+        original_password = self.servers_client.show_password(
+            self.server['id'])
+        self.addCleanup(self.servers_client.change_password, self.server['id'],
+                        adminPass=original_password)
+
+        with self.rbac_utils.override_role(self):
+            self.servers_client.change_password(
+                self.server['id'], adminPass=data_utils.rand_password())
+        # Call the waiter outside the ``override_role`` contextmanager, so that
+        # it is executed with admin role.
+        waiters.wait_for_server_status(
+            self.servers_client, self.server['id'], 'ACTIVE')
+
+Below is an example of a method that enforces multiple policies getting
+called inside the contextmanager. The ``_complex_setup_method`` below
+performs the correct API that enforces the expected policy -- in this
+case ``self.resources_client.create_resource`` -- but then proceeds to
+use a waiter.
+
+Incorrect::
+
+    def _complex_setup_method(self):
+        resource = self.resources_client.create_resource(
+            **kwargs)['resource']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self._delete_resource, resource)
+        waiters.wait_for_resource_status(
+            self.resources_client, resource['id'], 'available')
+        return resource
+
+    @rbac_rule_validation.action(
+        service="example-service",
+        rules=["example-rule"])
+    def test_change_server_password(self):
+        # Never call a helper function inside the contextmanager that calls a
+        # bunch of APIs. Only call the API that enforces the policy action
+        # contained in the decorator above.
+        with self.rbac_utils.override_role(self):
+            self._complex_setup_method()
+
+To fix this test, see the "Example using waiter" section above. It is
+recommended to re-implement the logic in a helper method inside a test such
+that only the relevant API is called inside the contextmanager, with
+everything extraneous outside.
+
+.. _rbac-test-cleanup:
+
+Test Cleanup
+------------
+
+Automatic role override in background.
+
+After the test -- no matter whether it ended successfully or in failure --
+the credentials are overridden with the admin role by the Patrole framework,
+*before* ``tearDown`` or ``tearDownClass`` are called. This means that
+resources are always cleaned up using the admin role.
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index 4dc27b9..dc0ed25 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -22,20 +22,16 @@
 PatroleGroup = [
     cfg.StrOpt('rbac_test_role',
                default='admin',
-               help="""The current RBAC role against which to run Patrole
-tests."""),
-    cfg.BoolOpt('enable_rbac',
-                default=True,
-                help="Enables RBAC tests."),
-    # TODO(rb560u): There needs to be support for reading these JSON files from
-    # other hosts. It may be possible to leverage the v3 identity policy API.
+               help="""The current RBAC role against which to run
+Patrole tests."""),
     cfg.ListOpt('custom_policy_files',
                 default=['/etc/%s/policy.json'],
                 help="""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.
+ordered by precedence, with high-priority paths before low-priority paths. All
+the paths that are found to contain the service's policy file will be used and
+all policy files will be merged. Allowed ``json`` or ``yaml`` formats.
 """),
     cfg.BoolOpt('test_custom_requirements',
                 default=False,
@@ -155,7 +151,27 @@
                 default=True,
                 help="""Is the Cinder policy
 "volume_extension:volume_actions:unreserve" available in the cloud? This policy
-was changed in a backwards-incompatible way.""")
+was changed in a backwards-incompatible way."""),
+    # *** Include feature flags for groups of policies below. ***
+    # Best practice is to capture new policies, removed policies, renamed
+    # policies in a group, per release.
+    #
+    # TODO(felipemonteiro): Remove these feature flags once Stein is EOL.
+    cfg.BoolOpt('removed_nova_policies_stein',
+                default=True,
+                help="""Are the Nova API extension policies available in the
+cloud (e.g. os_compute_api:os-extended-availability-zone)? These policies were
+removed in Stein because Nova API extension concept was removed in Pike."""),
+    cfg.BoolOpt('removed_keystone_policies_stein',
+                default=True,
+                help="""Are the obsolete Keystone policies available in the
+cloud (e.g. identity:[create|update|get|delete]_credential)? These policies
+were removed in Stein."""),
+    cfg.BoolOpt('added_cinder_policies_stein',
+                default=True,
+                help="""Are the Cinder Stein policies available in the cloud
+(e.g. [create|update|get|delete]_encryption_policy)? These policies are added
+in Stein.""")
 ]
 
 
diff --git a/patrole_tempest_plugin/hacking/checks.py b/patrole_tempest_plugin/hacking/checks.py
index d106da8..1f06258 100644
--- a/patrole_tempest_plugin/hacking/checks.py
+++ b/patrole_tempest_plugin/hacking/checks.py
@@ -36,6 +36,8 @@
 RULE_VALIDATION_DECORATOR = re.compile(
     r'\s*@rbac_rule_validation.action\(.*')
 IDEMPOTENT_ID_DECORATOR = re.compile(r'\s*@decorators\.idempotent_id\((.*)\)')
+PLUGIN_RBAC_TEST = re.compile(
+    r"class .+\(.+PluginRbacTest\)|class .+PluginRbacTest\(.+\)")
 
 have_rbac_decorator = False
 
@@ -211,6 +213,44 @@
             return 0, "Do not use 'self.client' as a service client alias"
 
 
+def no_plugin_rbac_test_suffix_in_plugin_test_class_name(physical_line,
+                                                         filename):
+    """Check that Plugin RBAC class names end with "PluginRbacTest"
+
+    P104
+    """
+    suffix = "PluginRbacTest"
+    if "patrole_tempest_plugin/tests/api" in filename:
+        if PLUGIN_RBAC_TEST.match(physical_line):
+            subclass, superclass = physical_line.split('(')
+            subclass = subclass.split('class')[1].strip()
+            superclass = superclass.split(')')[0].strip()
+            if "." in superclass:
+                superclass = superclass.split(".")[1]
+
+            both_have = all(
+                clazz.endswith(suffix) for clazz in [subclass, superclass])
+            none_have = not any(
+                clazz.endswith(suffix) for clazz in [subclass, superclass])
+
+            if not (both_have or none_have):
+                if (subclass.startswith("Base") and
+                        superclass.startswith("Base")):
+                    return
+
+                # Case 1: Subclass of "BasePluginRbacTest" must end in `suffix`
+                # Case 2: Subclass that ends in `suffix` must inherit from base
+                # class ending in `suffix`.
+                if not subclass.endswith(suffix):
+                    error = ("Plugin RBAC test subclasses must end in "
+                             "'PluginRbacTest'")
+                    return len(subclass) - 1, error
+                elif not superclass.endswith(suffix):
+                    error = ("Plugin RBAC test subclasses must inherit from a "
+                             "'PluginRbacTest' base class")
+                    return len(superclass) - 1, error
+
+
 def factory(register):
     register(import_no_clients_in_api_tests)
     register(no_setup_teardown_class_for_tests)
@@ -223,3 +263,4 @@
     register(no_rbac_rule_validation_decorator)
     register(no_rbac_suffix_in_test_filename)
     register(no_rbac_test_suffix_in_test_class_name)
+    register(no_plugin_rbac_test_suffix_in_plugin_test_class_name)
diff --git a/patrole_tempest_plugin/policy_authority.py b/patrole_tempest_plugin/policy_authority.py
index 3339a5d..2a49b6c 100644
--- a/patrole_tempest_plugin/policy_authority.py
+++ b/patrole_tempest_plugin/policy_authority.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import collections
 import copy
-import json
+import glob
 import os
 
 from oslo_log import log as logging
@@ -103,17 +104,14 @@
         if extra_target_data is None:
             extra_target_data = {}
 
-        self.validate_service(service)
+        self.service = self.validate_service(service)
 
         # Prioritize dynamically searching for policy files over relying on
         # deprecated service-specific policy file locations.
-        self.path = None
         if CONF.patrole.custom_policy_files:
             self.discover_policy_files()
-            self.path = self.policy_files.get(service)
 
-        self.rules = policy.Rules.load(self._get_policy_data(service),
-                                       'default')
+        self.rules = self.get_rules()
         self.project_id = project_id
         self.user_id = user_id
         self.extra_target_data = extra_target_data
@@ -139,19 +137,22 @@
             raise rbac_exceptions.RbacInvalidServiceException(
                 "%s is NOT a valid service." % service)
 
+        return service
+
     @classmethod
     def discover_policy_files(cls):
         """Dynamically discover the policy file for each service in
-        ``cls.available_services``. Pick the first candidate path found
+        ``cls.available_services``. Pick all candidate paths found
         out of the potential paths in ``[patrole] custom_policy_files``.
         """
         if not hasattr(cls, 'policy_files'):
-            cls.policy_files = {}
+            cls.policy_files = collections.defaultdict(list)
             for service in cls.available_services:
                 for candidate_path in CONF.patrole.custom_policy_files:
-                    if os.path.isfile(candidate_path % service):
-                        cls.policy_files.setdefault(service,
-                                                    candidate_path % service)
+                    path = candidate_path % service
+                    for filename in glob.iglob(path):
+                        if os.path.isfile(filename):
+                            cls.policy_files[service].append(filename)
 
     def allowed(self, rule_name, role):
         """Checks if a given rule in a policy is allowed with given role.
@@ -168,71 +169,60 @@
             is_admin=is_admin_context)
         return is_allowed
 
-    def _get_policy_data(self, service):
-        file_policy_data = {}
-        mgr_policy_data = {}
-        policy_data = {}
-
+    def get_rules(self):
+        rules = policy.Rules()
         # Check whether policy file exists and attempt to read it.
-        if self.path and os.path.isfile(self.path):
+        for path in self.policy_files[self.service]:
             try:
-                with open(self.path, 'r') as policy_file:
-                    file_policy_data = policy_file.read()
-                file_policy_data = json.loads(file_policy_data)
-            except (IOError, ValueError) as e:
-                msg = "Failed to read policy file for service. "
-                if isinstance(e, IOError):
-                    msg += "Please check that policy path exists."
-                else:
-                    msg += "JSON may be improperly formatted."
-                LOG.debug(msg)
-                file_policy_data = {}
+                with open(path, 'r') as fp:
+                    for k, v in policy.Rules.load(fp.read()).items():
+                        if k not in rules:
+                            rules[k] = v
+                        # If the policy name and rule are the same, no
+                        # ambiguity, so no reason to warn.
+                        elif str(v) != str(rules[k]):
+                            msg = ("The same policy name: %s was found in "
+                                   "multiple policies files for service %s. "
+                                   "This can lead to policy rule ambiguity. "
+                                   "Using rule: %s; Rule from file: %s")
+                            LOG.warning(msg, k, self.service, rules[k], v)
+            except (ValueError, IOError):
+                LOG.warning("Failed to read policy file '%s' for service %s.",
+                            path, self.service)
 
         # Check whether policy actions are defined in code. Nova and Keystone,
         # for example, define their default policy actions in code.
         mgr = stevedore.named.NamedExtensionManager(
             'oslo.policy.policies',
-            names=[service],
-            on_load_failure_callback=None,
+            names=[self.service],
             invoke_on_load=True,
             warn_on_missing_entrypoint=False)
 
         if mgr:
-            policy_generator = {policy.name: policy.obj for policy in mgr}
-            if policy_generator and service in policy_generator:
-                for rule in policy_generator[service]:
-                    mgr_policy_data[rule.name] = str(rule.check)
+            policy_generator = {plc.name: plc.obj for plc in mgr}
+            if self.service in policy_generator:
+                for rule in policy_generator[self.service]:
+                    if rule.name not in rules:
+                        rules[rule.name] = rule.check
+                    elif str(rule.check) != str(rules[rule.name]):
+                        msg = ("The same policy name: %s was found in the "
+                               "policies files and in the code for service "
+                               "%s. This can lead to policy rule ambiguity. "
+                               "Using rule: %s; Rule from code: %s")
+                        LOG.warning(msg, rule.name, self.service,
+                                    rules[rule.name], rule.check)
 
-        # If data from both file and code exist, combine both together.
-        if file_policy_data and mgr_policy_data:
-            # Add the policy actions from code first.
-            for action, rule in mgr_policy_data.items():
-                policy_data[action] = rule
-            # Overwrite with any custom policy actions defined in policy.json.
-            for action, rule in file_policy_data.items():
-                policy_data[action] = rule
-        elif file_policy_data:
-            policy_data = file_policy_data
-        elif mgr_policy_data:
-            policy_data = mgr_policy_data
-        else:
-            error_message = (
-                'Policy file for {0} service was not found among the '
+        if not rules:
+            msg = (
+                'Policy files for {0} service were not found among the '
                 'registered in-code policies or in any of the possible policy '
-                'files: {1}.'.format(service,
-                                     [loc % service for loc in
-                                      CONF.patrole.custom_policy_files])
-            )
-            raise rbac_exceptions.RbacParsingException(error_message)
+                'files: {1}.'.format(
+                    self.service,
+                    [loc % self.service
+                     for loc in CONF.patrole.custom_policy_files]))
+            raise rbac_exceptions.RbacParsingException(msg)
 
-        try:
-            policy_data = json.dumps(policy_data)
-        except (TypeError, ValueError):
-            error_message = 'Policy file for {0} service is invalid.'.format(
-                service)
-            raise rbac_exceptions.RbacParsingException(error_message)
-
-        return policy_data
+        return rules
 
     def _is_admin_context(self, role):
         """Checks whether a role has admin context.
@@ -296,9 +286,11 @@
 
     def _try_rule(self, apply_rule, target, access_data, o):
         if apply_rule not in self.rules:
-            message = ("Policy action \"{0}\" not found in policy file: {1} or"
-                       " among registered policy in code defaults for service."
-                       ).format(apply_rule, self.path)
+            message = ('Policy action "{0}" not found in policy files: '
+                       '{1} or among registered policy in code defaults for '
+                       '{2} service.').format(apply_rule,
+                                              self.policy_files[self.service],
+                                              self.service)
             LOG.debug(message)
             raise rbac_exceptions.RbacParsingException(message)
         else:
diff --git a/patrole_tempest_plugin/rbac_exceptions.py b/patrole_tempest_plugin/rbac_exceptions.py
index 809a7ed..6bdd7df 100644
--- a/patrole_tempest_plugin/rbac_exceptions.py
+++ b/patrole_tempest_plugin/rbac_exceptions.py
@@ -16,46 +16,41 @@
 from tempest.lib import exceptions
 
 
-class RbacConflictingPolicies(exceptions.TempestException):
-    message = ("Conflicting policies preventing this action from being "
-               "performed.")
+class BasePatroleException(exceptions.TempestException):
+    message = "An unknown RBAC exception occurred"
 
 
-class RbacMalformedResponse(exceptions.TempestException):
+class RbacMalformedResponse(BasePatroleException):
     message = ("The response body is missing the expected %(attribute)s due "
                "to policy enforcement failure.")
 
-    def __init__(self, empty=False, extra_attr=False, **kwargs):
+    def __init__(self, empty=False, **kwargs):
         if empty:
             self.message = ("The response body is empty due to policy "
                             "enforcement failure.")
             kwargs = {}
-        if extra_attr:
-            self.message = ("The response body contained an unexpected "
-                            "attribute due to policy enforcement failure.")
-            kwargs = {}
         super(RbacMalformedResponse, self).__init__(**kwargs)
 
 
-class RbacResourceSetupFailed(exceptions.TempestException):
+class RbacResourceSetupFailed(BasePatroleException):
     message = "RBAC resource setup failed"
 
 
-class RbacOverPermissionException(exceptions.TempestException):
+class RbacOverPermissionException(BasePatroleException):
     """Raised when the expected result is failure but the actual result is
     pass.
     """
     message = "Unauthorized action was allowed to be performed"
 
 
-class RbacUnderPermissionException(exceptions.TempestException):
+class RbacUnderPermissionException(BasePatroleException):
     """Raised when the expected result is pass but the actual result is
     failure.
     """
     message = "Authorized action was not allowed to be performed"
 
 
-class RbacExpectedWrongException(exceptions.TempestException):
+class RbacExpectedWrongException(BasePatroleException):
     """Raised when the expected exception does not match the actual exception
     raised, when both are instances of Forbidden or NotFound, indicating
     the test provides a wrong argument to `expected_error_codes`.
@@ -64,16 +59,30 @@
                "instead. Actual exception: %(exception)s")
 
 
-class RbacInvalidServiceException(exceptions.TempestException):
+class RbacInvalidServiceException(BasePatroleException):
     """Raised when an invalid service is passed to ``rbac_rule_validation``
     decorator.
     """
     message = "Attempted to test an invalid service"
 
 
-class RbacParsingException(exceptions.TempestException):
+class RbacParsingException(BasePatroleException):
     message = "Attempted to test an invalid policy file or action"
 
 
-class RbacInvalidErrorCode(exceptions.TempestException):
+class RbacInvalidErrorCode(BasePatroleException):
     message = "Unsupported error code passed in test"
+
+
+class RbacOverrideRoleException(BasePatroleException):
+    """Raised when override_role is used incorrectly or fails somehow.
+
+    Used for safeguarding against false positives that might occur when the
+    expected exception isn't raised inside the ``override_role`` context.
+    Specifically, when:
+
+    * ``override_role`` isn't called
+    * an exception is raised before ``override_role`` context
+    * an exception is raised after ``override_role`` context
+    """
+    message = "Override role failure or incorrect usage"
diff --git a/patrole_tempest_plugin/rbac_rule_validation.py b/patrole_tempest_plugin/rbac_rule_validation.py
index e6d1e80..c85376f 100644
--- a/patrole_tempest_plugin/rbac_rule_validation.py
+++ b/patrole_tempest_plugin/rbac_rule_validation.py
@@ -17,7 +17,6 @@
 import logging
 import sys
 
-from oslo_log import versionutils
 from oslo_utils import excutils
 import six
 
@@ -38,8 +37,9 @@
 RBACLOG = logging.getLogger('rbac_reporting')
 
 
-def action(service, rule='', rules=None,
-           expected_error_code=_DEFAULT_ERROR_CODE, expected_error_codes=None,
+def action(service,
+           rules=None,
+           expected_error_codes=None,
            extra_target_data=None):
     """A decorator for verifying OpenStack policy enforcement.
 
@@ -72,26 +72,15 @@
     As such, negative and positive testing can be applied using this decorator.
 
     :param str service: An OpenStack service. Examples: "nova" or "neutron".
-    :param str rule: (DEPRECATED) A policy action defined in a policy.json file
-        or in code.
-    :param list rules: A list of policy actions defined in a policy.json file
-        or in code. The rules are logical-ANDed together to derive the expected
-        result.
+    :param list rules: A list of policy actions defined in a policy file or in
+        code. The rules are logical-ANDed together to derive the expected
+        result. Also accepts list of callables that return a policy action.
 
         .. note::
 
             Patrole currently only supports custom JSON policy files.
 
-    :param int expected_error_code: (DEPRECATED) Overrides default value of 403
-        (Forbidden) with endpoint-specific error code. Currently only supports
-        403 and 404. Support for 404 is needed because some services, like
-        Neutron, intentionally throw a 404 for security reasons.
-
-        .. warning::
-
-            A 404 should not be provided *unless* the endpoint masks a
-            ``Forbidden`` exception as a ``NotFound`` exception.
-
+    :type rules: list[str] or list[callable]
     :param list expected_error_codes: When the ``rules`` list parameter is
         used, then this list indicates the expected error code to use if one
         of the rules does not allow the role being tested. This list must
@@ -110,8 +99,14 @@
         c) if both api_action1 and api_action2 fail, then the expected error
            code is the first error seen (404).
 
-        If an error code is missing from the list, it is defaulted to 403.
+        If it is not passed, then it is defaulted to 403.
 
+        .. warning::
+
+            A 404 should not be provided *unless* the endpoint masks a
+            ``Forbidden`` exception as a ``NotFound`` exception.
+
+    :type expected_error_codes: list[int]
     :param dict extra_target_data: Dictionary, keyed with ``oslo.policy``
         generic check names, whose values are string literals that reference
         nested ``tempest.test.BaseTestCase`` attributes. Used by
@@ -132,7 +127,8 @@
     Examples::
 
         @rbac_rule_validation.action(
-            service="nova", rule="os_compute_api:os-agents")
+            service="nova",
+            rules=["os_compute_api:os-agents"])
         def test_list_agents_rbac(self):
             # The call to `override_role` is mandatory.
             with self.rbac_utils.override_role(self):
@@ -142,8 +138,7 @@
     if extra_target_data is None:
         extra_target_data = {}
 
-    rules, expected_error_codes = _prepare_multi_policy(rule, rules,
-                                                        expected_error_code,
+    rules, expected_error_codes = _prepare_multi_policy(rules,
                                                         expected_error_codes)
 
     def decorator(test_func):
@@ -167,7 +162,6 @@
                     disallowed_rules.append(rule)
                 allowed = allowed and _allowed
 
-            exp_error_code = expected_error_code
             if disallowed_rules:
                 # Choose the first disallowed rule and expect the error
                 # code corresponding to it.
@@ -176,10 +170,13 @@
                 LOG.debug("%s: Expecting %d to be raised for policy name: %s",
                           test_func.__name__, exp_error_code,
                           disallowed_rules[0])
+            else:
+                exp_error_code = expected_error_codes[0]
 
             expected_exception, irregular_msg = _get_exception_type(
                 exp_error_code)
 
+            caught_exception = None
             test_status = 'Allowed'
 
             try:
@@ -192,14 +189,16 @@
                     test_status = ('Error, %s' % (msg))
                     LOG.error(msg)
             except (expected_exception,
-                    rbac_exceptions.RbacConflictingPolicies,
-                    rbac_exceptions.RbacMalformedResponse) as e:
+                    rbac_exceptions.RbacMalformedResponse) as actual_exception:
+                caught_exception = actual_exception
                 test_status = 'Denied'
+
                 if irregular_msg:
                     LOG.warning(irregular_msg,
                                 test_func.__name__,
                                 ', '.join(rules),
                                 service)
+
                 if allowed:
                     msg = ("Role %s was not allowed to perform the following "
                            "actions: %s. Expected allowed actions: %s. "
@@ -209,8 +208,10 @@
                                sorted(disallowed_rules)))
                     LOG.error(msg)
                     raise rbac_exceptions.RbacUnderPermissionException(
-                        "%s Exception was: %s" % (msg, e))
+                        "%s Exception was: %s" % (msg, actual_exception))
             except Exception as actual_exception:
+                caught_exception = actual_exception
+
                 if _check_for_expected_mismatch_exception(expected_exception,
                                                           actual_exception):
                     LOG.error('Expected and actual exceptions do not match. '
@@ -249,11 +250,19 @@
                         "Allowed" if allowed else "Denied",
                         test_status)
 
+                # Sanity-check that ``override_role`` was called to eliminate
+                # false-positives and bad test flows resulting from exceptions
+                # getting raised too early, too late or not at all, within
+                # the scope of an RBAC test.
+                _validate_override_role_called(
+                    test_obj,
+                    actual_exception=caught_exception)
+
         return wrapper
     return decorator
 
 
-def _prepare_multi_policy(rule, rules, exp_error_code, exp_error_codes):
+def _prepare_multi_policy(rules, exp_error_codes):
     if exp_error_codes:
         if not rules:
             msg = ("The `rules` list must be provided if using the "
@@ -263,34 +272,15 @@
             msg = ("The `expected_error_codes` list is not the same length "
                    "as the `rules` list.")
             raise ValueError(msg)
-        if exp_error_code:
-            deprecation_msg = (
-                "The `exp_error_code` argument has been deprecated in favor "
-                "of `exp_error_codes` and will be removed in a future "
-                "version.")
-            versionutils.report_deprecated_feature(LOG, deprecation_msg)
-            LOG.debug("The `exp_error_codes` argument will be used instead of "
-                      "`exp_error_code`.")
         if not isinstance(exp_error_codes, (tuple, list)):
             exp_error_codes = [exp_error_codes]
     else:
         exp_error_codes = []
-        if exp_error_code:
-            exp_error_codes.append(exp_error_code)
 
     if rules is None:
         rules = []
     elif not isinstance(rules, (tuple, list)):
         rules = [rules]
-    if rule:
-        deprecation_msg = (
-            "The `rule` argument has been deprecated in favor of `rules` "
-            "and will be removed in a future version.")
-        versionutils.report_deprecated_feature(LOG, deprecation_msg)
-        if rules:
-            LOG.debug("The `rules` argument will be used instead of `rule`.")
-        else:
-            rules.append(rule)
 
     # Fill in the exp_error_codes if needed. This is needed for the scenarios
     # where no exp_error_codes array is provided, so the error codes must be
@@ -302,7 +292,11 @@
         for i in range(num_rules - num_ecs):
             exp_error_codes.append(_DEFAULT_ERROR_CODE)
 
-    return rules, exp_error_codes
+    evaluated_rules = [
+        r() if callable(r) else r for r in rules
+    ]
+
+    return evaluated_rules, exp_error_codes
 
 
 def _is_authorized(test_obj, service, rule, extra_target_data):
@@ -389,7 +383,7 @@
         irregular_msg = ("NotFound exception was caught for test %s. Expected "
                          "policies which may have caused the error: %s. The "
                          "service %s throws a 404 instead of a 403, which is "
-                         "irregular.")
+                         "irregular")
     return expected_exception, irregular_msg
 
 
@@ -431,8 +425,63 @@
 
 def _check_for_expected_mismatch_exception(expected_exception,
                                            actual_exception):
+    """Checks that ``expected_exception`` matches ``actual_exception``.
+
+    Since Patrole must handle 403/404 it is important that the expected and
+    actual error codes match.
+
+    :param excepted_exception: Expected exception for test.
+    :param actual_exception: Actual exception raised by test.
+    :returns: True if match, else False.
+    :rtype: boolean
+    """
     permission_exceptions = (lib_exc.Forbidden, lib_exc.NotFound)
     if isinstance(actual_exception, permission_exceptions):
         if not isinstance(actual_exception, expected_exception.__class__):
             return True
     return False
+
+
+def _validate_override_role_called(test_obj, actual_exception):
+    """Validates that :func:`rbac_utils.RbacUtils.override_role` is called
+    during each Patrole test.
+
+    Useful for validating that the expected exception isn't raised too early
+    (before ``override_role`` call) or too late (after ``override_call``) or
+    at all (which is a bad test).
+
+    :param test_obj: An instance or subclass of ``tempest.test.BaseTestCase``.
+    :param actual_exception: Actual exception raised by test.
+    :raises RbacOverrideRoleException: If ``override_role`` isn't called, is
+        called too early, or is called too late.
+    """
+    called = test_obj._validate_override_role_called()
+    base_msg = ('This error is unrelated to RBAC and is due to either '
+                'an API or override role failure. Exception: %s' %
+                actual_exception)
+
+    if not called:
+        if actual_exception is not None:
+            msg = ('Caught exception (%s) but it was raised before the '
+                   '`override_role` context. ' % actual_exception.__class__)
+        else:
+            msg = 'Test missing required `override_role` call. '
+        msg += base_msg
+        LOG.error(msg)
+        raise rbac_exceptions.RbacOverrideRoleException(msg)
+    else:
+        exc_caught_in_ctx = test_obj._validate_override_role_caught_exc()
+        # This block is only executed if ``override_role`` is called. If
+        # an exception is raised and the exception wasn't raised in the
+        # ``override_role`` context and if the exception isn't a valid
+        # exception type (instance of ``BasePatroleException``), then this is
+        # a legitimate error.
+        if (not exc_caught_in_ctx and
+            actual_exception is not None and
+            not isinstance(actual_exception,
+                           rbac_exceptions.BasePatroleException)):
+            msg = ('Caught exception (%s) but it was raised after the '
+                   '`override_role` context. ' % actual_exception.__class__)
+            msg += base_msg
+            LOG.error(msg)
+            raise rbac_exceptions.RbacOverrideRoleException(msg)
diff --git a/patrole_tempest_plugin/rbac_utils.py b/patrole_tempest_plugin/rbac_utils.py
index 6c40aa1..b7ac8d9 100644
--- a/patrole_tempest_plugin/rbac_utils.py
+++ b/patrole_tempest_plugin/rbac_utils.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from contextlib import contextmanager
+import sys
 import time
 
 from oslo_log import log as logging
@@ -22,6 +23,7 @@
 from tempest import clients
 from tempest.common import credentials_factory as credentials
 from tempest import config
+from tempest.lib import exceptions as lib_exc
 
 from patrole_tempest_plugin import rbac_exceptions
 
@@ -49,10 +51,11 @@
         # Intialize the admin roles_client to perform role switching.
         admin_mgr = clients.Manager(
             credentials.get_configured_admin_credentials())
-        if test_obj.get_identity_version() == 'v3':
+        if CONF.identity_feature_enabled.api_v3:
             admin_roles_client = admin_mgr.roles_v3_client
         else:
-            admin_roles_client = admin_mgr.roles_client
+            raise lib_exc.InvalidConfiguration(
+                "Patrole role overriding only supports v3 identity API.")
 
         self.admin_roles_client = admin_roles_client
         self._override_role(test_obj, False)
@@ -82,7 +85,7 @@
         Example::
 
             @rbac_rule_validation.action(service='test',
-                                         rule='a:test:rule')
+                                         rules=['a:test:rule'])
             def test_foo(self):
                 # Allocate test-level resources here.
                 with self.rbac_utils.override_role(self):
@@ -94,11 +97,17 @@
                 # if the API call above threw an exception, any code below this
                 # point in the test is not executed.
         """
+        test_obj._set_override_role_called()
         self._override_role(test_obj, True)
         try:
             # Execute the test.
             yield
         finally:
+            # Check whether an exception was raised. If so, remember that
+            # for future validation.
+            exc = sys.exc_info()[0]
+            if exc is not None:
+                test_obj._set_override_role_caught_exc()
             # This code block is always executed, no matter the result of the
             # test. Automatically switch back to the admin role for test clean
             # up.
@@ -136,7 +145,9 @@
             with excutils.save_and_reraise_exception():
                 LOG.exception(exp)
         finally:
-            test_obj.os_primary.auth_provider.clear_auth()
+            auth_providers = test_obj.get_auth_providers()
+            for provider in auth_providers:
+                provider.clear_auth()
             # Fernet tokens are not subsecond aware so sleep to ensure we are
             # passing the second boundary before attempting to authenticate.
             # Only sleep if a token revocation occurred as a result of role
@@ -144,7 +155,9 @@
             # ``[identity] admin_role`` == ``[patrole] rbac_test_role``.
             if not role_already_present:
                 time.sleep(1)
-            test_obj.os_primary.auth_provider.set_auth()
+
+            for provider in auth_providers:
+                provider.set_auth()
 
     def _get_roles_by_name(self):
         available_roles = self.admin_roles_client.list_roles()['roles']
@@ -217,16 +230,51 @@
                 cls.setup_rbac_utils()
     """
 
+    # Shows if override_role was called.
+    __override_role_called = False
+    # Shows if exception raised during override_role.
+    __override_role_caught_exc = False
+
     @classmethod
-    def skip_rbac_checks(cls):
-        if not CONF.patrole.enable_rbac:
-            raise cls.skipException(
-                'Patrole testing not enabled so skipping %s.' % cls.__name__)
+    def get_auth_providers(cls):
+        """Returns list of auth_providers used within test.
+
+        Tests may redefine this method to include their own or third party
+        client auth_providers.
+        """
+        return [cls.os_primary.auth_provider]
 
     @classmethod
     def setup_rbac_utils(cls):
         cls.rbac_utils = RbacUtils(cls)
 
+    def _set_override_role_called(self):
+        """Helper for tracking whether ``override_role`` was called."""
+        self.__override_role_called = True
+
+    def _set_override_role_caught_exc(self):
+        """Helper for tracking whether exception was thrown inside
+        ``override_role``.
+        """
+        self.__override_role_caught_exc = True
+
+    def _validate_override_role_called(self):
+        """Idempotently validate that ``override_role`` is called and reset
+        its value to False for sequential tests.
+        """
+        was_called = self.__override_role_called
+        self.__override_role_called = False
+        return was_called
+
+    def _validate_override_role_caught_exc(self):
+        """Idempotently validate that exception was caught inside
+        ``override_role``, so that, by process of elimination, it can be
+        determined whether one was thrown outside (which is invalid).
+        """
+        caught_exception = self.__override_role_caught_exc
+        self.__override_role_caught_exc = False
+        return caught_exception
+
 
 def is_admin():
     """Verifies whether the current test role equals the admin role.
diff --git a/patrole_tempest_plugin/tests/api/README.rst b/patrole_tempest_plugin/tests/api/README.rst
new file mode 120000
index 0000000..e2853ec
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/README.rst
@@ -0,0 +1 @@
+../../../doc/source/field_guide/rbac.rst
\ No newline at end of file
diff --git a/patrole_tempest_plugin/tests/api/compute/rbac_base.py b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
index 18d2f48..ab4551e 100644
--- a/patrole_tempest_plugin/tests/api/compute/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/compute/rbac_base.py
@@ -12,24 +12,16 @@
 #    under the License.
 
 from tempest.api.compute import base as compute_base
-from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 
 from patrole_tempest_plugin import rbac_utils
 
-CONF = config.CONF
-
 
 class BaseV2ComputeRbacTest(rbac_utils.RbacUtilsMixin,
                             compute_base.BaseV2ComputeTest):
 
     @classmethod
-    def skip_checks(cls):
-        super(BaseV2ComputeRbacTest, cls).skip_checks()
-        cls.skip_rbac_checks()
-
-    @classmethod
     def setup_clients(cls):
         super(BaseV2ComputeRbacTest, cls).setup_clients()
         cls.setup_rbac_utils()
diff --git a/patrole_tempest_plugin/tests/api/compute/test_agents_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_agents_rbac.py
index a046f96..617aa5b 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_agents_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_agents_rbac.py
@@ -42,7 +42,7 @@
         return kwargs
 
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-agents")
+        service="nova", rules=["os_compute_api:os-agents"])
     @decorators.idempotent_id('d1bc6d97-07f5-4f45-ac29-1c619a6a7e27')
     def test_list_agents_rbac(self):
         with self.rbac_utils.override_role(self):
@@ -50,7 +50,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-agents")
+        rules=["os_compute_api:os-agents"])
     @decorators.idempotent_id('77d6cae4-1ced-47f7-af2e-3d6a45958fd6')
     def test_create_agent(self):
         params = {'hypervisor': 'kvm', 'os': 'win', 'architecture': 'x86',
@@ -63,7 +63,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-agents")
+        rules=["os_compute_api:os-agents"])
     @decorators.idempotent_id('b22f2681-9ffb-439b-b240-dae503e41020')
     def test_update_agent(self):
         params = self._param_helper(
@@ -84,7 +84,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-agents")
+        rules=["os_compute_api:os-agents"])
     @decorators.idempotent_id('c5042af8-0682-43b0-abc4-bf33349e23dd')
     def test_delete_agent(self):
         params = self._param_helper(
diff --git a/patrole_tempest_plugin/tests/api/compute/test_aggregates_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_aggregates_rbac.py
index b7cd392..dea8bb9 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_aggregates_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_aggregates_rbac.py
@@ -81,14 +81,14 @@
                         host=self.host)
 
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-aggregates:create")
+        service="nova", rules=["os_compute_api:os-aggregates:create"])
     @decorators.idempotent_id('ba754393-896e-434a-9704-452ff4a84f3f')
     def test_create_aggregate_rbac(self):
         with self.rbac_utils.override_role(self):
             self._create_aggregate()
 
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-aggregates:show")
+        service="nova", rules=["os_compute_api:os-aggregates:show"])
     @decorators.idempotent_id('8fb0b749-b120-4727-b3fb-bcfa3fa6f55b')
     def test_show_aggregate_rbac(self):
         aggregate_id = self._create_aggregate()
@@ -96,14 +96,14 @@
             self.aggregates_client.show_aggregate(aggregate_id)
 
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-aggregates:index")
+        service="nova", rules=["os_compute_api:os-aggregates:index"])
     @decorators.idempotent_id('146284da-5dd6-4c97-b598-42b480f014c6')
     def test_list_aggregate_rbac(self):
         with self.rbac_utils.override_role(self):
             self.aggregates_client.list_aggregates()
 
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-aggregates:update")
+        service="nova", rules=["os_compute_api:os-aggregates:update"])
     @decorators.idempotent_id('c94e0d69-99b6-477e-b301-2cd0e9d0ad81')
     def test_update_aggregate_rbac(self):
         aggregate_id = self._create_aggregate()
@@ -113,7 +113,7 @@
                                                     name=new_name)
 
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-aggregates:delete")
+        service="nova", rules=["os_compute_api:os-aggregates:delete"])
     @decorators.idempotent_id('5a50c5a6-0f12-4405-a1ce-2288ae895ea6')
     def test_delete_aggregate_rbac(self):
         aggregate_id = self._create_aggregate()
@@ -121,7 +121,7 @@
             self.aggregates_client.delete_aggregate(aggregate_id)
 
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-aggregates:add_host")
+        service="nova", rules=["os_compute_api:os-aggregates:add_host"])
     @decorators.idempotent_id('97e6e9df-5291-4faa-8147-755b2d1f1ce2')
     def test_add_host_to_aggregate_rbac(self):
         aggregate_id = self._create_aggregate()
@@ -129,7 +129,7 @@
             self._add_host_to_aggregate(aggregate_id)
 
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-aggregates:remove_host")
+        service="nova", rules=["os_compute_api:os-aggregates:remove_host"])
     @decorators.idempotent_id('5b035a25-75d2-4d72-b4d6-0f0337335628')
     def test_remove_host_from_aggregate_rbac(self):
         aggregate_id = self._create_aggregate()
@@ -138,7 +138,7 @@
             self.aggregates_client.remove_host(aggregate_id, host=self.host)
 
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-aggregates:set_metadata")
+        service="nova", rules=["os_compute_api:os-aggregates:set_metadata"])
     @decorators.idempotent_id('ed6f3849-065c-4ae9-a81e-6ad7ed0d3d9d')
     def test_set_metadata_on_aggregate_rbac(self):
         aggregate_id = self._create_aggregate()
diff --git a/patrole_tempest_plugin/tests/api/compute/test_availability_zone_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_availability_zone_rbac.py
index 66dce5c..d8b165c 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_availability_zone_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_availability_zone_rbac.py
@@ -28,15 +28,17 @@
                    "enabled." % cls.__name__)
             raise cls.skipException(msg)
 
-    @rbac_rule_validation.action(service="nova", rule="os_compute_api:"
-                                 "os-availability-zone:list")
+    @rbac_rule_validation.action(
+        service="nova",
+        rules=["os_compute_api:os-availability-zone:list"])
     @decorators.idempotent_id('cd34e7ea-d26e-4fa3-a8d0-f8883726ce3d')
     def test_get_availability_zone_list_rbac(self):
         with self.rbac_utils.override_role(self):
             self.availability_zone_client.list_availability_zones()
 
-    @rbac_rule_validation.action(service="nova", rule="os_compute_api:"
-                                 "os-availability-zone:detail")
+    @rbac_rule_validation.action(
+        service="nova",
+        rules=["os_compute_api:os-availability-zone:detail"])
     @decorators.idempotent_id('2f61c191-6ece-4f21-b487-39d749e3d38e')
     def test_get_availability_zone_list_detail_rbac(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_fixed_ips_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_fixed_ips_rbac.py
index f426cf3..25f7d5f 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_fixed_ips_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_fixed_ips_rbac.py
@@ -56,7 +56,7 @@
     @decorators.idempotent_id('c89391f7-4844-4a70-a116-37c1336efb99')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-fixed-ips")
+        rules=["os_compute_api:os-fixed-ips"])
     def test_show_fixed_ip_details(self):
         with self.rbac_utils.override_role(self):
             self.fixed_ips_client.show_fixed_ip(self.ip)
@@ -64,7 +64,7 @@
     @decorators.idempotent_id('f0314501-735d-4315-9856-959e01e82f0d')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-fixed-ips")
+        rules=["os_compute_api:os-fixed-ips"])
     def test_set_reserve(self):
         with self.rbac_utils.override_role(self):
             self.fixed_ips_client.reserve_fixed_ip(self.ip, reserve="None")
@@ -72,7 +72,7 @@
     @decorators.idempotent_id('866a6fdc-a237-4502-9bf2-52fe82aba356')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-fixed-ips")
+        rules=["os_compute_api:os-fixed-ips"])
     def test_set_unreserve(self):
         with self.rbac_utils.override_role(self):
             self.fixed_ips_client.reserve_fixed_ip(self.ip, unreserve="None")
diff --git a/patrole_tempest_plugin/tests/api/compute/test_flavor_access_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_flavor_access_rbac.py
index d6364c9..317c1ad 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_flavor_access_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_flavor_access_rbac.py
@@ -13,8 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-from tempest import config
+import testtools
 
+from tempest import config
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
@@ -34,10 +35,12 @@
         cls.public_flavor_id = CONF.compute.flavor_ref
         cls.tenant_id = cls.os_primary.credentials.tenant_id
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('a2bd3740-765d-4c95-ac98-9e027378c75e')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-access")
+        rules=["os_compute_api:os-flavor-access"])
     def test_show_flavor_contains_is_public_key(self):
         public_flavor_id = CONF.compute.flavor_ref
 
@@ -50,10 +53,12 @@
             raise rbac_exceptions.RbacMalformedResponse(
                 attribute=expected_attr)
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('dd388146-9750-4124-82ba-62deff1052bb')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-access")
+        rules=["os_compute_api:os-flavor-access"])
     def test_list_flavors_details_contains_is_public_key(self):
         expected_attr = 'os-flavor-access:is_public'
 
@@ -72,7 +77,7 @@
     @decorators.idempotent_id('39cb5c8f-9990-436f-9282-fc76a41d9bac')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-access:add_tenant_access")
+        rules=["os_compute_api:os-flavor-access:add_tenant_access"])
     def test_add_flavor_access(self):
         with self.rbac_utils.override_role(self):
             self.flavors_client.add_flavor_access(
@@ -83,7 +88,7 @@
     @decorators.idempotent_id('61b8621f-52e4-473a-8d07-e228af8853d1')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-access:remove_tenant_access")
+        rules=["os_compute_api:os-flavor-access:remove_tenant_access"])
     def test_remove_flavor_access(self):
         self.flavors_client.add_flavor_access(
             flavor_id=self.flavor_id, tenant_id=self.tenant_id)
@@ -98,7 +103,7 @@
     @decorators.idempotent_id('e1cf59fb-7f32-40a1-96b9-248ab23dd581')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-access")
+        rules=["os_compute_api:os-flavor-access"])
     def test_list_flavor_access(self):
         # Add flavor access for os_primary so that it can access the flavor or
         # else a NotFound is raised.
diff --git a/patrole_tempest_plugin/tests/api/compute/test_flavor_extra_specs_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_flavor_extra_specs_rbac.py
index 816492c..b781540 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_flavor_extra_specs_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_flavor_extra_specs_rbac.py
@@ -50,7 +50,7 @@
     @decorators.idempotent_id('daee891d-dfe9-4501-a39c-29f2371bec3c')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-extra-specs:show")
+        rules=["os_compute_api:os-flavor-extra-specs:show"])
     def test_show_flavor_extra_spec(self):
         key = self._set_flavor_extra_spec()
         with self.rbac_utils.override_role(self):
@@ -59,7 +59,7 @@
     @decorators.idempotent_id('fcffeca2-ed04-4e85-bf93-02fb5643f22b')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-extra-specs:create")
+        rules=["os_compute_api:os-flavor-extra-specs:create"])
     def test_set_flavor_extra_spec(self):
         with self.rbac_utils.override_role(self):
             self._set_flavor_extra_spec()
@@ -67,7 +67,7 @@
     @decorators.idempotent_id('42b85279-6bfa-4f58-b7a2-258c284f03c5')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-extra-specs:update")
+        rules=["os_compute_api:os-flavor-extra-specs:update"])
     def test_update_flavor_extra_spec(self):
         key = self._set_flavor_extra_spec()
         update_val = data_utils.rand_name(self.__class__.__name__ + '-val')
@@ -78,7 +78,7 @@
     @decorators.idempotent_id('4b0e5471-e010-4c09-8965-80898e6760a3')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-extra-specs:delete")
+        rules=["os_compute_api:os-flavor-extra-specs:delete"])
     def test_unset_flavor_extra_spec(self):
         key = self._set_flavor_extra_spec()
         with self.rbac_utils.override_role(self):
@@ -87,7 +87,7 @@
     @decorators.idempotent_id('02c3831a-3ce9-476e-a722-d805ac2da621')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-extra-specs:index")
+        rules=["os_compute_api:os-flavor-extra-specs:index"])
     def test_list_flavor_extra_specs(self):
         self._set_flavor_extra_spec()
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_flavor_manage_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_flavor_manage_rbac.py
index f0f267c..f968d4e 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_flavor_manage_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_flavor_manage_rbac.py
@@ -32,7 +32,7 @@
     @decorators.idempotent_id('a4e7faec-7a4b-4809-9856-90d5b747ca35')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-manage:create")
+        rules=["os_compute_api:os-flavor-manage:create"])
     def test_create_flavor_manage(self):
         with self.rbac_utils.override_role(self):
             self.create_flavor()
@@ -40,7 +40,7 @@
     @decorators.idempotent_id('782e988e-061b-4c40-896f-a77c70c2b057')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-manage:delete")
+        rules=["os_compute_api:os-flavor-manage:delete"])
     def test_delete_flavor_manage(self):
         flavor_id = self.create_flavor()['id']
 
diff --git a/patrole_tempest_plugin/tests/api/compute/test_flavor_rxtx_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_flavor_rxtx_rbac.py
index fbc03cf..0748e67 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_flavor_rxtx_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_flavor_rxtx_rbac.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.common import utils
 from tempest import config
 from tempest.lib import decorators
@@ -33,10 +35,12 @@
             msg = "os-flavor-rxtx extension not enabled."
             raise cls.skipException(msg)
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('5e1fd9f0-9a08-485a-ad9c-0fc66e4d64b7')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-rxtx")
+        rules=["os_compute_api:os-flavor-rxtx"])
     def test_list_flavors_details_rxtx(self):
         with self.rbac_utils.override_role(self):
             result = self.flavors_client.list_flavors(detail=True)['flavors']
@@ -44,10 +48,12 @@
             raise rbac_exceptions.RbacMalformedResponse(
                 attribute='rxtx_factor')
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('70c55a07-c843-4627-a29d-ba78673c1e63')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-flavor-rxtx")
+        rules=["os_compute_api:os-flavor-rxtx"])
     def test_get_flavor_rxtx(self):
         with self.rbac_utils.override_role(self):
             result = self.flavors_client.show_flavor(
diff --git a/patrole_tempest_plugin/tests/api/compute/test_floating_ip_pools_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_floating_ip_pools_rbac.py
index 7467130..eef7943 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_floating_ip_pools_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_floating_ip_pools_rbac.py
@@ -48,7 +48,7 @@
     @decorators.idempotent_id('c1a17153-b25d-4444-a721-5897d7737482')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-floating-ip-pools")
+        rules=["os_compute_api:os-floating-ip-pools"])
     def test_list_floating_ip_pools(self):
         with self.rbac_utils.override_role(self):
             self.fip_pools_client.list_floating_ip_pools()
diff --git a/patrole_tempest_plugin/tests/api/compute/test_floating_ips_bulk_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_floating_ips_bulk_rbac.py
index 3ccef73..4a8426c 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_floating_ips_bulk_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_floating_ips_bulk_rbac.py
@@ -89,7 +89,7 @@
     @decorators.idempotent_id('9a49e73f-96a0-4e93-830a-22c4e443b486')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-floating-ips-bulk")
+        rules=["os_compute_api:os-floating-ips-bulk"])
     def test_create_floating_ips_bulk(self):
         with self.rbac_utils.override_role(self):
             self._create_floating_ips_bulk()
@@ -97,7 +97,7 @@
     @decorators.idempotent_id('3b5c8a02-005d-4256-8a95-6fa2f389c6cf')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-floating-ips-bulk")
+        rules=["os_compute_api:os-floating-ips-bulk"])
     def test_list_floating_ips_bulk(self):
         with self.rbac_utils.override_role(self):
             self.fip_bulk_client.list_floating_ips_bulk()
@@ -105,7 +105,7 @@
     @decorators.idempotent_id('37c2b759-c494-4e20-9dba-6a67b2df9573')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-floating-ips-bulk")
+        rules=["os_compute_api:os-floating-ips-bulk"])
     def test_delete_floating_ips_bulk(self):
         self._create_floating_ips_bulk()
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py
index 1045512..0f37a80 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_floating_ips_rbac.py
@@ -44,7 +44,7 @@
     @decorators.idempotent_id('ac1b3053-f755-4cda-85a0-30e88b88d7ba')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-floating-ips")
+        rules=["os_compute_api:os-floating-ips"])
     def test_list_floating_ips(self):
         with self.rbac_utils.override_role(self):
             self.floating_ips_client.list_floating_ips()
@@ -52,7 +52,7 @@
     @decorators.idempotent_id('bebe52b3-5269-4e72-80c8-5a4a39c3bfa6')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-floating-ips")
+        rules=["os_compute_api:os-floating-ips"])
     def test_show_floating_ip(self):
         body = self.floating_ips_client.create_floating_ip(
             pool=CONF.network.floating_network_name)['floating_ip']
@@ -64,7 +64,7 @@
     @decorators.idempotent_id('2bfb8745-c329-4ee9-95f6-c165a1989dbf')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-floating-ips")
+        rules=["os_compute_api:os-floating-ips"])
     def test_create_floating_ips(self):
         with self.rbac_utils.override_role(self):
             body = self.floating_ips_client.create_floating_ip(
@@ -75,7 +75,7 @@
     @decorators.idempotent_id('d3028373-5027-4e7a-b761-01c515403ecb')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-floating-ips")
+        rules=["os_compute_api:os-floating-ips"])
     def test_delete_floating_ip(self):
         body = self.floating_ips_client.create_floating_ip(
             pool=CONF.network.floating_network_name)['floating_ip']
diff --git a/patrole_tempest_plugin/tests/api/compute/test_hosts_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_hosts_rbac.py
index 41d2656..f2d8113 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_hosts_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_hosts_rbac.py
@@ -36,7 +36,7 @@
     @decorators.idempotent_id('035b7935-2fae-4218-8d37-27fa83097494')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-hosts")
+        rules=["os_compute_api:os-hosts"])
     def test_list_hosts(self):
         with self.rbac_utils.override_role(self):
             self.hosts_client.list_hosts()
@@ -44,7 +44,7 @@
     @decorators.idempotent_id('bc10d8b4-d2c3-4d4e-9d2b-31d1bd3e1b51')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-hosts")
+        rules=["os_compute_api:os-hosts"])
     def test_show_host_details(self):
         hosts = self.hosts_client.list_hosts()['hosts']
         hosts = [host for host in hosts if host['service'] == 'compute']
diff --git a/patrole_tempest_plugin/tests/api/compute/test_hypervisor_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_hypervisor_rbac.py
index 33f40e7..5488556 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_hypervisor_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_hypervisor_rbac.py
@@ -39,7 +39,7 @@
     @decorators.idempotent_id('17bbeb9a-e73e-445f-a771-c794448ef562')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-hypervisors")
+        rules=["os_compute_api:os-hypervisors"])
     def test_list_hypervisors(self):
         with self.rbac_utils.override_role(self):
             self.hypervisor_client.list_hypervisors()
@@ -47,7 +47,7 @@
     @decorators.idempotent_id('36b95c7d-1085-487a-a674-b7c1ca35f520')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-hypervisors")
+        rules=["os_compute_api:os-hypervisors"])
     def test_list_hypervisors_with_details(self):
         with self.rbac_utils.override_role(self):
             self.hypervisor_client.list_hypervisors(detail=True)
@@ -55,7 +55,7 @@
     @decorators.idempotent_id('8a7f6f9e-34a6-4480-8875-bba566c3a581')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-hypervisors")
+        rules=["os_compute_api:os-hypervisors"])
     def test_show_hypervisor(self):
         with self.rbac_utils.override_role(self):
             self.hypervisor_client.show_hypervisor(self.hypervisor['id'])
@@ -63,7 +63,7 @@
     @decorators.idempotent_id('ca0e465c-6365-4a7f-ae58-6f8ddbca06c2')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-hypervisors")
+        rules=["os_compute_api:os-hypervisors"])
     def test_show_hypervisor_statistics(self):
         with self.rbac_utils.override_role(self):
             self.hypervisor_client.show_hypervisor_statistics()
@@ -71,7 +71,7 @@
     @decorators.idempotent_id('109b37c5-91ba-4da5-b2a2-d7618d84406d')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-hypervisors")
+        rules=["os_compute_api:os-hypervisors"])
     def test_show_hypervisor_uptime(self):
         with self.rbac_utils.override_role(self):
             self.hypervisor_client.show_hypervisor_uptime(
@@ -102,7 +102,7 @@
     @decorators.idempotent_id('b86f03cf-2e79-4d88-9eea-62f761591413')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-hypervisors")
+        rules=["os_compute_api:os-hypervisors"])
     def test_list_servers_on_hypervisor(self):
         with self.rbac_utils.override_role(self):
             self.hypervisor_client.list_servers_on_hypervisor(
@@ -111,7 +111,7 @@
     @decorators.idempotent_id('3dbc71c1-8f04-4674-a67c-dcb2fd99b1b4')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-hypervisors")
+        rules=["os_compute_api:os-hypervisors"])
     def test_search_hypervisor(self):
         with self.rbac_utils.override_role(self):
             self.hypervisor_client.search_hypervisor(
diff --git a/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
index f36b8ec..f6c1b67 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_images_rbac.py
@@ -13,6 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
 from tempest.common import image as common_image
 from tempest import config
 from tempest.lib.common.utils import data_utils
@@ -20,6 +22,7 @@
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
+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
 
@@ -76,7 +79,7 @@
     @decorators.idempotent_id('b861f302-b72b-4055-81db-c62ff30b136d')
     @rbac_rule_validation.action(
         service="glance",
-        rule="get_images")
+        rules=["get_images"])
     def test_list_images(self):
         with self.rbac_utils.override_role(self):
             self.compute_images_client.list_images()
@@ -84,7 +87,7 @@
     @decorators.idempotent_id('4365ae0f-15ee-4b54-a527-1679faaed140')
     @rbac_rule_validation.action(
         service="glance",
-        rule="get_images")
+        rules=["get_images"])
     def test_list_images_with_details(self):
         with self.rbac_utils.override_role(self):
             self.compute_images_client.list_images(detail=True)
@@ -92,7 +95,7 @@
     @decorators.idempotent_id('886dfcae-51bf-4610-9e52-82d7189524c2')
     @rbac_rule_validation.action(
         service="glance",
-        rule="get_image")
+        rules=["get_image"])
     def test_show_image_details(self):
         with self.rbac_utils.override_role(self):
             self.compute_images_client.show_image(self.image['id'])
@@ -100,7 +103,7 @@
     @decorators.idempotent_id('5888c7aa-0803-46d4-a3fb-5d4729465cd5')
     @rbac_rule_validation.action(
         service="glance",
-        rule="delete_image")
+        rules=["delete_image"])
     def test_delete_image(self):
         image = self.glance_image_client.create_image(
             name=data_utils.rand_name(self.__class__.__name__ + '-image'))
@@ -161,7 +164,7 @@
     @decorators.idempotent_id('dbe09d4c-e615-48cb-b908-a06a0f410a8e')
     @rbac_rule_validation.action(
         service="glance",
-        rule="get_image")
+        rules=["get_image"])
     def test_show_image_metadata_item(self):
         self.compute_images_client.set_image_metadata(self.image['id'],
                                                       meta={'foo': 'bar'})
@@ -175,7 +178,7 @@
     @decorators.idempotent_id('59f66079-d564-47e8-81b0-03c2e84d339e')
     @rbac_rule_validation.action(
         service="glance",
-        rule="get_image")
+        rules=["get_image"])
     def test_list_image_metadata(self):
         with self.rbac_utils.override_role(self):
             self.compute_images_client.list_image_metadata(self.image['id'])
@@ -183,7 +186,7 @@
     @decorators.idempotent_id('575604aa-909f-4b1b-a5a5-cfae1f63044b')
     @rbac_rule_validation.action(
         service="glance",
-        rule="modify_image")
+        rules=["modify_image"])
     def test_create_image_metadata(self):
         with self.rbac_utils.override_role(self):
             # NOTE(felipemonteiro): Although the name of the client function
@@ -197,7 +200,7 @@
     @decorators.idempotent_id('fb8c4eb6-00e5-454c-b8bc-0e801ec369f1')
     @rbac_rule_validation.action(
         service="glance",
-        rule="modify_image")
+        rules=["modify_image"])
     def test_update_image_metadata(self):
         with self.rbac_utils.override_role(self):
             self.compute_images_client.set_image_metadata(self.image['id'],
@@ -208,7 +211,7 @@
     @decorators.idempotent_id('9c7c2036-af9b-49a8-8ba1-09b027ee5def')
     @rbac_rule_validation.action(
         service="glance",
-        rule="modify_image")
+        rules=["modify_image"])
     def test_update_image_metadata_item(self):
         with self.rbac_utils.override_role(self):
             self.compute_images_client.set_image_metadata_item(
@@ -219,7 +222,7 @@
     @decorators.idempotent_id('5f0dc4e6-0761-4613-9bde-0a6acdc78f46')
     @rbac_rule_validation.action(
         service="glance",
-        rule="modify_image")
+        rules=["modify_image"])
     def test_delete_image_metadata_item(self):
         self.compute_images_client.set_image_metadata(self.image['id'],
                                                       meta={'foo': 'bar'})
@@ -245,18 +248,67 @@
     # https://developer.openstack.org/api-ref/compute/#images-deprecated
     max_microversion = '2.35'
 
+    @classmethod
+    def skip_checks(cls):
+        super(ImageSizeRbacTest, cls).skip_checks()
+        if not CONF.service_available.glance:
+            skip_msg = ("%s skipped as glance is not available" % cls.__name__)
+            raise cls.skipException(skip_msg)
+
+    @classmethod
+    def setup_clients(cls):
+        super(ImageSizeRbacTest, cls).setup_clients()
+        if CONF.image_feature_enabled.api_v2:
+            cls.glance_image_client = cls.os_primary.image_client_v2
+        elif CONF.image_feature_enabled.api_v1:
+            cls.glance_image_client = cls.os_primary.image_client
+        else:
+            raise lib_exc.InvalidConfiguration(
+                'Either api_v1 or api_v2 must be True in '
+                '[image-feature-enabled].')
+
+    @classmethod
+    def resource_setup(cls):
+        super(ImageSizeRbacTest, cls).resource_setup()
+        params = {'name': data_utils.rand_name(cls.__name__ + '-image')}
+        if CONF.image_feature_enabled.api_v1:
+            params = {'headers': common_image.image_meta_to_headers(**params)}
+
+        cls.image = cls.glance_image_client.create_image(**params)
+        cls.addClassResourceCleanup(
+            cls.glance_image_client.wait_for_resource_deletion,
+            cls.image['id'])
+        cls.addClassResourceCleanup(
+            cls.glance_image_client.delete_image, cls.image['id'])
+
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('fe34d2a6-5743-45bf-8f92-a1d703d7c7ab')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:image-size")
-    def test_list_images(self):
+        rules=["os_compute_api:image-size"])
+    def test_show_image_includes_image_size(self):
         with self.rbac_utils.override_role(self):
-            self.compute_images_client.list_images()
+            body = self.compute_images_client.show_image(self.image['id'])[
+                'image']
 
+        expected_attr = 'OS-EXT-IMG-SIZE:size'
+        if expected_attr not in body:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute=expected_attr)
+
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('08342c7d-297d-42ee-b398-90fce2443792')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:image-size")
-    def test_list_images_with_details(self):
+        rules=["os_compute_api:image-size"])
+    def test_list_images_with_details_includes_image_size(self):
         with self.rbac_utils.override_role(self):
-            self.compute_images_client.list_images(detail=True)
+            body = self.compute_images_client.list_images(detail=True)[
+                'images']
+
+        expected_attr = 'OS-EXT-IMG-SIZE:size'
+        if expected_attr not in body[0]:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute=expected_attr)
diff --git a/patrole_tempest_plugin/tests/api/compute/test_instance_usages_audit_log_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_instance_usages_audit_log_rbac.py
index 347b7df..163d29a 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_instance_usages_audit_log_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_instance_usages_audit_log_rbac.py
@@ -36,7 +36,7 @@
 
     @decorators.idempotent_id('c80246c0-5c13-4ab0-97ba-91551cd53dc1')
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-instance-usage-audit-log")
+        service="nova", rules=["os_compute_api:os-instance-usage-audit-log"])
     def test_list_instance_usage_audit_logs(self):
         with self.rbac_utils.override_role(self):
             (self.instance_usages_audit_log_client
@@ -44,7 +44,7 @@
 
     @decorators.idempotent_id('ded8bfbd-5d90-4a58-aee0-d31231bf3c9b')
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-instance-usage-audit-log")
+        service="nova", rules=["os_compute_api:os-instance-usage-audit-log"])
     def test_show_instance_usage_audit_log(self):
         now = datetime.datetime.now()
 
diff --git a/patrole_tempest_plugin/tests/api/compute/test_keypairs_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_keypairs_rbac.py
index b359ad2..c024a38 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_keypairs_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_keypairs_rbac.py
@@ -34,7 +34,7 @@
     @decorators.idempotent_id('16e0ae81-e05f-48cd-b253-cf31ab0732f0')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-keypairs:create")
+        rules=["os_compute_api:os-keypairs:create"])
     def test_create_keypair(self):
         with self.rbac_utils.override_role(self):
             self._create_keypair()
@@ -42,7 +42,7 @@
     @decorators.idempotent_id('85a5eb99-40ec-4e77-9358-bee2cdf9d7df')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-keypairs:show")
+        rules=["os_compute_api:os-keypairs:show"])
     def test_show_keypair(self):
         kp_name = self._create_keypair()['keypair']['name']
         with self.rbac_utils.override_role(self):
@@ -51,7 +51,7 @@
     @decorators.idempotent_id('6bff9f1c-b809-43c1-8d63-61fbd19d49d3')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-keypairs:delete")
+        rules=["os_compute_api:os-keypairs:delete"])
     def test_delete_keypair(self):
         kp_name = self._create_keypair()['keypair']['name']
         with self.rbac_utils.override_role(self):
@@ -60,7 +60,7 @@
     @decorators.idempotent_id('6bb31346-ff7f-4b10-978e-170ac5fcfa3e')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-keypairs:index")
+        rules=["os_compute_api:os-keypairs:index"])
     def test_index_keypair(self):
         with self.rbac_utils.override_role(self):
             self.keypairs_client.list_keypairs()
diff --git a/patrole_tempest_plugin/tests/api/compute/test_limits_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_limits_rbac.py
index 9442a5a..f1e0393 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_limits_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_limits_rbac.py
@@ -28,7 +28,7 @@
             raise cls.skipException(msg)
 
     @rbac_rule_validation.action(service="nova",
-                                 rule="os_compute_api:limits")
+                                 rules=["os_compute_api:limits"])
     @decorators.idempotent_id('3fb60f83-9a5f-4fdd-89d9-26c3710844a1')
     def test_show_limits(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_migrations_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_migrations_rbac.py
index 1597a04..6596ac9 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_migrations_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_migrations_rbac.py
@@ -32,7 +32,7 @@
     @decorators.idempotent_id('5795231c-3729-448c-a072-9a225db1a328')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-migrations:index")
+        rules=["os_compute_api:os-migrations:index"])
     def test_list_services(self):
         with self.rbac_utils.override_role(self):
             self.migrations_client.list_migrations()
diff --git a/patrole_tempest_plugin/tests/api/compute/test_quota_class_sets_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_quota_class_sets_rbac.py
index 2f86763..201922c 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_quota_class_sets_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_quota_class_sets_rbac.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.common import identity
 from tempest.common import tempest_fixtures as fixtures
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
@@ -24,6 +25,8 @@
 
 class QuotaClassesRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
+    credentials = ['primary', 'admin']
+
     def setUp(self):
         # All test cases in this class need to externally lock on doing
         # anything with default quota values.
@@ -48,16 +51,19 @@
     def resource_setup(cls):
         super(QuotaClassesRbacTest, cls).resource_setup()
         # Create a project with its own quota.
-        project_name = data_utils.rand_name(cls.__name__ + '-Project')
-        cls.project_id = cls.identity_projects_client.create_project(
-            project_name)['project']['id']
+        project_name = data_utils.rand_name(cls.__name__ + '-project')
+        project_desc = project_name + '-desc'
+        project = identity.identity_utils(cls.os_admin).create_project(
+            name=project_name, description=project_desc)
+        cls.project_id = project['id']
         cls.addClassResourceCleanup(
-            cls.identity_projects_client.delete_project, cls.project_id)
+            identity.identity_utils(cls.os_admin).delete_project,
+            cls.project_id)
 
     @decorators.idempotent_id('c10198ed-9df2-440e-a49b-367dadc6de94')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-quota-class-sets:show")
+        rules=["os_compute_api:os-quota-class-sets:show"])
     def test_show_quota_class_set(self):
         with self.rbac_utils.override_role(self):
             self.quota_classes_client.show_quota_class_set('default')
@@ -65,7 +71,7 @@
     @decorators.idempotent_id('81889e69-efd2-4e96-bb4c-ee3b646b9755')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-quota-class-sets:update")
+        rules=["os_compute_api:os-quota-class-sets:update"])
     def test_update_quota_class_set(self):
         # Update the pre-existing quotas for the project_id.
         quota_class_set = self.quota_classes_client.show_quota_class_set(
diff --git a/patrole_tempest_plugin/tests/api/compute/test_quota_sets_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_quota_sets_rbac.py
index ec4511a..2b05408 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_quota_sets_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_quota_sets_rbac.py
@@ -13,9 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.common import identity
 from tempest.common import utils
 from tempest.lib.common.utils import data_utils
-from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
@@ -24,6 +24,8 @@
 
 class QuotaSetsRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
+    credentials = ['primary', 'admin']
+
     @classmethod
     def setup_clients(cls):
         super(QuotaSetsRbacTest, cls).setup_clients()
@@ -52,7 +54,7 @@
     @decorators.idempotent_id('8229ceb0-db6a-4a2c-99c2-de226905d8b6')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-quota-sets:update")
+        rules=["os_compute_api:os-quota-sets:update"])
     def test_update_quota_set(self):
         default_quota_set = self.quotas_client.show_default_quota_set(
             self.tenant_id)['quota_set']
@@ -69,7 +71,7 @@
     @decorators.idempotent_id('58df5613-8f3c-400a-8b4b-2bae624d05e9')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-quota-sets:defaults")
+        rules=["os_compute_api:os-quota-sets:defaults"])
     def test_show_default_quota_set(self):
         with self.rbac_utils.override_role(self):
             self.quotas_client.show_default_quota_set(self.tenant_id)
@@ -77,7 +79,7 @@
     @decorators.idempotent_id('e8169ac4-c402-4864-894e-aba74e3a459c')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-quota-sets:show")
+        rules=["os_compute_api:os-quota-sets:show"])
     def test_show_quota_set(self):
         with self.rbac_utils.override_role(self):
             self.quotas_client.show_quota_set(self.tenant_id)
@@ -85,14 +87,17 @@
     @decorators.idempotent_id('4e240644-bf61-4872-9c32-8289ee2fdbbd')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-quota-sets:delete")
+        rules=["os_compute_api:os-quota-sets:delete"])
     def test_delete_quota_set(self):
         project_name = data_utils.rand_name(
             self.__class__.__name__ + '-project')
-        project = self.projects_client.create_project(name=project_name)
-        project_id = project['project']['id']
-        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
-                        self.projects_client.delete_project, project_id)
+        project_desc = project_name + '-desc'
+        project = identity.identity_utils(self.os_admin).create_project(
+            name=project_name, description=project_desc)
+        project_id = project['id']
+        self.addCleanup(
+            identity.identity_utils(self.os_admin).delete_project,
+            project_id)
 
         with self.rbac_utils.override_role(self):
             self.quotas_client.delete_quota_set(project_id)
@@ -100,7 +105,7 @@
     @decorators.idempotent_id('ac9184b6-f3b3-4e17-a632-4b92c6500f86')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-quota-sets:detail")
+        rules=["os_compute_api:os-quota-sets:detail"])
     def test_show_quota_set_details(self):
         with self.rbac_utils.override_role(self):
             self.quotas_client.show_quota_set(self.tenant_id,
diff --git a/patrole_tempest_plugin/tests/api/compute/test_security_groups_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_security_groups_rbac.py
index fa89a79..33d2ed1 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_security_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_security_groups_rbac.py
@@ -55,7 +55,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-security-groups")
+        rules=["os_compute_api:os-security-groups"])
     @decorators.idempotent_id('3db159c6-a467-469f-9a25-574197885520')
     def test_list_security_groups_by_server(self):
         with self.rbac_utils.override_role(self):
@@ -64,7 +64,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-security-groups")
+        rules=["os_compute_api:os-security-groups"])
     @decorators.idempotent_id('ea1ca73f-2d1d-43cb-9a46-900d7927b357')
     def test_create_security_group_for_server(self):
         sg_name = self.create_security_group()['name']
@@ -78,7 +78,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-security-groups")
+        rules=["os_compute_api:os-security-groups"])
     @decorators.idempotent_id('0ad2e856-e2d3-4ac5-a620-f93d0d3d2626')
     def test_remove_security_group_from_server(self):
         sg_name = self.create_security_group()['name']
@@ -116,7 +116,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-security-groups")
+        rules=["os_compute_api:os-security-groups"])
     @decorators.idempotent_id('4ac58e49-48c1-4fca-a6c3-3f95fb99eb77')
     def test_list_security_groups(self):
         with self.rbac_utils.override_role(self):
@@ -124,7 +124,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-security-groups")
+        rules=["os_compute_api:os-security-groups"])
     @decorators.idempotent_id('e8fe7f5a-69ee-412d-81d3-a8c7a488b54d')
     def test_create_security_groups(self):
         with self.rbac_utils.override_role(self):
@@ -132,7 +132,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-security-groups")
+        rules=["os_compute_api:os-security-groups"])
     @decorators.idempotent_id('59127e8e-302d-11e7-93ae-92361f002671')
     def test_delete_security_groups(self):
         sec_group_id = self.create_security_group()['id']
@@ -141,7 +141,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-security-groups")
+        rules=["os_compute_api:os-security-groups"])
     @decorators.idempotent_id('3de5c6bc-b822-469e-a627-82427d38b067')
     def test_update_security_groups(self):
         sec_group_id = self.create_security_group()['id']
@@ -154,7 +154,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-security-groups")
+        rules=["os_compute_api:os-security-groups"])
     @decorators.idempotent_id('6edc0320-302d-11e7-93ae-92361f002671')
     def test_show_security_groups(self):
         sec_group_id = self.create_security_group()['id']
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 1fe52e9..a64bd20 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
@@ -119,7 +119,7 @@
                           'Pause is not available.')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-pause-server:pause")
+        rules=["os_compute_api:os-pause-server:pause"])
     def test_pause_server(self):
         with self.rbac_utils.override_role(self):
             self._pause_server()
@@ -129,7 +129,7 @@
                           'Pause is not available.')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-pause-server:unpause")
+        rules=["os_compute_api:os-pause-server:unpause"])
     def test_unpause_server(self):
         self._pause_server()
         with self.rbac_utils.override_role(self):
@@ -139,7 +139,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:stop")
+        rules=["os_compute_api:servers:stop"])
     @decorators.idempotent_id('ab4a17d2-166f-4a6d-9944-f17baa576cf2')
     def test_stop_server(self):
         with self.rbac_utils.override_role(self):
@@ -148,7 +148,7 @@
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:start")
+        rules=["os_compute_api:servers:start"])
     @decorators.idempotent_id('8876bfa9-4d10-406e-a335-a57e451abb12')
     def test_start_server(self):
         self._stop_server()
@@ -161,7 +161,7 @@
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:resize")
+        rules=["os_compute_api:servers:resize"])
     @decorators.idempotent_id('0546fbdd-2d8f-4ce8-ac00-f1e2129d0765')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize is not available.')
@@ -172,7 +172,7 @@
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:revert_resize")
+        rules=["os_compute_api:servers:revert_resize"])
     @decorators.idempotent_id('d41b64b8-a72d-414a-a4c5-94e1eb5e5a96')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize is not available.')
@@ -187,7 +187,7 @@
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:confirm_resize")
+        rules=["os_compute_api:servers:confirm_resize"])
     @decorators.idempotent_id('f51620cb-dfcb-4e5d-b421-2e0edaa1316e')
     @testtools.skipUnless(CONF.compute_feature_enabled.resize,
                           'Resize is not available.')
@@ -202,7 +202,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:rebuild")
+        rules=["os_compute_api:servers:rebuild"])
     @decorators.idempotent_id('54b1a30b-c96c-472c-9c83-ccaf6ec7e20b')
     def test_rebuild_server(self):
         with self.rbac_utils.override_role(self):
@@ -212,7 +212,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:reboot")
+        rules=["os_compute_api:servers:reboot"])
     @decorators.idempotent_id('19f27856-56e1-44f8-8615-7257f6b85cbb')
     def test_reboot_server(self):
         with self.rbac_utils.override_role(self):
@@ -222,7 +222,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:index")
+        rules=["os_compute_api:servers:index"])
     @decorators.idempotent_id('631f0d86-7607-4198-8312-9da2f05464a4')
     def test_server_index(self):
         with self.rbac_utils.override_role(self):
@@ -230,7 +230,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:detail")
+        rules=["os_compute_api:servers:detail"])
     @decorators.idempotent_id('96093480-3ce5-4a8b-b569-aed870379c24')
     def test_server_detail(self):
         with self.rbac_utils.override_role(self):
@@ -238,7 +238,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:detail:get_all_tenants")
+        rules=["os_compute_api:servers:detail:get_all_tenants"])
     @decorators.idempotent_id('a9e5a1c0-acfe-49a2-b2b1-fd8b19d61f71')
     def test_server_detail_all_tenants(self):
         with self.rbac_utils.override_role(self):
@@ -246,7 +246,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:index:get_all_tenants")
+        rules=["os_compute_api:servers:index:get_all_tenants"])
     @decorators.idempotent_id('4b93ba56-69e6-41f5-82c4-84a5c4c42091')
     def test_server_index_all_tenants(self):
         with self.rbac_utils.override_role(self):
@@ -254,7 +254,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:show")
+        rules=["os_compute_api:servers:show"])
     @decorators.idempotent_id('eaaf4f51-31b5-497f-8f0f-f527e5f70b83')
     def test_show_server(self):
         with self.rbac_utils.override_role(self):
@@ -263,7 +263,7 @@
     @utils.services('image')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:create_image")
+        rules=["os_compute_api:servers:create_image"])
     @decorators.idempotent_id('ba0ac859-99f4-4055-b5e0-e0905a44d331')
     def test_create_image(self):
         with self.rbac_utils.override_role(self):
@@ -273,7 +273,7 @@
     @utils.services('image', 'volume')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:create_image:allow_volume_backed")
+        rules=["os_compute_api:servers:create_image:allow_volume_backed"])
     @decorators.idempotent_id('8b869f73-49b3-4cc4-a0ce-ef64f8e1d6f9')
     def test_create_image_from_volume_backed_server(self):
         # volume_backed=True creates a volume and create server will be
@@ -299,7 +299,7 @@
     @utils.services('image')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-create-backup")
+        rules=["os_compute_api:os-create-backup"])
     def test_create_backup(self):
         # Prioritize glance v2 over v1 for deleting/waiting for image status.
         if CONF.image_feature_enabled.api_v2:
@@ -334,7 +334,7 @@
     @decorators.idempotent_id('0b70c527-af75-4bed-9ccf-4f1310a8b60f')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-shelve:shelve")
+        rules=["os_compute_api:os-shelve:shelve"])
     def test_shelve_server(self):
         with self.rbac_utils.override_role(self):
             self._shelve_server()
@@ -343,7 +343,7 @@
     @decorators.idempotent_id('4b6e849a-9182-49ff-9257-e97e751b475e')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-shelve:unshelve")
+        rules=["os_compute_api:os-shelve:unshelve"])
     def test_unshelve_server(self):
         self._shelve_server()
         with self.rbac_utils.override_role(self):
@@ -364,7 +364,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-evacuate")
+        rules=["os_compute_api:os-evacuate"])
     @decorators.idempotent_id('78ecef3c-faff-412a-83be-47651963eb21')
     def test_evacuate_server(self):
         fake_host_name = data_utils.rand_name(
@@ -396,7 +396,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:show:host_status")
+        rules=["os_compute_api:servers:show:host_status"])
     @decorators.idempotent_id('736da575-86f8-4b2a-9902-dd37dc9a409b')
     def test_show_server_host_status(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_consoles_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_consoles_rbac.py
index fa2f359..4570ea1 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_consoles_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_consoles_rbac.py
@@ -37,7 +37,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-console-output")
+        rules=["os_compute_api:os-console-output"])
     @decorators.idempotent_id('90fd80f6-456c-11e7-a919-92ebcb67fe33')
     def test_get_console_output(self):
         with self.rbac_utils.override_role(self):
@@ -61,7 +61,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-remote-consoles")
+        rules=["os_compute_api:os-remote-consoles"])
     @decorators.idempotent_id('b0a72c02-9b15-4dcb-b186-efe8753370ab')
     def test_get_vnc_console_output(self):
         with self.rbac_utils.override_role(self):
@@ -86,7 +86,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-remote-consoles")
+        rules=["os_compute_api:os-remote-consoles"])
     @decorators.idempotent_id('879597de-87e0-4da9-a60a-28c8088dc508')
     def test_get_remote_console_output(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_groups_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_groups_rbac.py
index 1674b1a..26ef0fe 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_groups_rbac.py
@@ -31,7 +31,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-groups:create")
+        rules=["os_compute_api:os-server-groups:create"])
     @decorators.idempotent_id('7f3eae94-6130-47e9-81ac-34009f55be2f')
     def test_create_server_group(self):
         with self.rbac_utils.override_role(self):
@@ -39,7 +39,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-groups:delete")
+        rules=["os_compute_api:os-server-groups:delete"])
     @decorators.idempotent_id('832d9be3-632e-47b2-93d2-5897db43e3e2')
     def test_delete_server_group(self):
         server_group = self.create_test_server_group()
@@ -48,7 +48,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-groups:index")
+        rules=["os_compute_api:os-server-groups:index"])
     @decorators.idempotent_id('5eccd67f-5945-483b-b1c8-de851ebfc1c1')
     def test_list_server_groups(self):
         with self.rbac_utils.override_role(self):
@@ -56,7 +56,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-groups:show")
+        rules=["os_compute_api:os-server-groups:show"])
     @decorators.idempotent_id('62534e3f-7e99-4a3d-a08e-33e056460cf2')
     def test_show_server_group(self):
         server_group = self.create_test_server_group()
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_metadata_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_metadata_rbac.py
index 05b1758..6193938 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_metadata_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_metadata_rbac.py
@@ -35,7 +35,7 @@
     @decorators.idempotent_id('b07bbc27-58e2-4581-869d-ad228cec5d9a')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:server-metadata:index")
+        rules=["os_compute_api:server-metadata:index"])
     def test_list_server_metadata(self):
         with self.rbac_utils.override_role(self):
             self.servers_client.list_server_metadata(self.server['id'])
@@ -43,7 +43,7 @@
     @decorators.idempotent_id('6e76748b-2417-4fa2-b41a-c0cc4bff356b')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:server-metadata:update_all")
+        rules=["os_compute_api:server-metadata:update_all"])
     def test_set_server_metadata(self):
         with self.rbac_utils.override_role(self):
             self.servers_client.set_server_metadata(self.server['id'], {})
@@ -51,7 +51,7 @@
     @decorators.idempotent_id('1060bac4-fe16-4a77-be64-d8e482a06eab')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:server-metadata:create")
+        rules=["os_compute_api:server-metadata:create"])
     def test_update_server_metadata(self):
         with self.rbac_utils.override_role(self):
             self.servers_client.update_server_metadata(self.server['id'], {})
@@ -59,7 +59,7 @@
     @decorators.idempotent_id('93dd8323-d3fa-48d1-8bd6-91c1b62fc341')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:server-metadata:show")
+        rules=["os_compute_api:server-metadata:show"])
     def test_show_server_metadata_item(self):
         with self.rbac_utils.override_role(self):
             self.servers_client.show_server_metadata_item(
@@ -68,7 +68,7 @@
     @decorators.idempotent_id('79511293-4bd7-447d-ba7e-634d0f4da70c')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:server-metadata:update")
+        rules=["os_compute_api:server-metadata:update"])
     def test_set_server_metadata_item(self):
         with self.rbac_utils.override_role(self):
             self.servers_client.set_server_metadata_item(
@@ -77,7 +77,7 @@
     @decorators.idempotent_id('feec5064-678d-40bc-a88f-c856e18d1e31')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:server-metadata:delete")
+        rules=["os_compute_api:server-metadata:delete"])
     def test_delete_server_metadata_item(self):
         with self.rbac_utils.override_role(self):
             self.servers_client.delete_server_metadata_item(
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 a867b81..8ae8bc0 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
@@ -62,7 +62,7 @@
                           'Cold migration not available.')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-migrate-server:migrate")
+        rules=["os_compute_api:os-migrate-server:migrate"])
     @decorators.idempotent_id('c6f1607c-9fed-4c00-807e-9ba675b98b1b')
     def test_cold_migration(self):
         server = self.create_test_server(wait_until="ACTIVE")
@@ -76,7 +76,7 @@
                           'Live migration feature is not enabled.')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-migrate-server:migrate_live")
+        rules=["os_compute_api:os-migrate-server:migrate_live"])
     @decorators.idempotent_id('33520834-72c8-4edb-a189-a2e0fc9eb0d3')
     def test_migration_live(self):
         server_id = self.create_test_server(wait_until="ACTIVE",
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 d97f382..88bea25 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
@@ -81,7 +81,7 @@
     @utils.requires_ext(extension='os-admin-actions', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-admin-actions:reset_state")
+        rules=["os_compute_api:os-admin-actions:reset_state"])
     @decorators.idempotent_id('ae84dd0b-f364-462e-b565-3457f9c019ef')
     def test_reset_server_state(self):
         """Test reset server state, part of os-admin-actions."""
@@ -93,7 +93,7 @@
     @utils.requires_ext(extension='os-admin-actions', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-admin-actions:inject_network_info")
+        rules=["os_compute_api:os-admin-actions:inject_network_info"])
     @decorators.idempotent_id('ce48c340-51c1-4cff-9b6e-0cc5ef008630')
     def test_inject_network_info(self):
         """Test inject network info, part of os-admin-actions."""
@@ -103,7 +103,7 @@
     @utils.requires_ext(extension='os-admin-actions', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-admin-actions:reset_network")
+        rules=["os_compute_api:os-admin-actions:reset_network"])
     @decorators.idempotent_id('2911a242-15c4-4fcb-80d5-80a8930661b0')
     def test_reset_network(self):
         """Test reset network, part of os-admin-actions."""
@@ -114,7 +114,7 @@
                           'Change password not available.')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-admin-password")
+        rules=["os_compute_api:os-admin-password"])
     @decorators.idempotent_id('908a7d59-3a66-441c-94cf-38e57ed14956')
     def test_change_server_password(self):
         """Test change admin password, part of os-admin-password."""
@@ -129,11 +129,13 @@
         waiters.wait_for_server_status(
             self.servers_client, self.server['id'], 'ACTIVE')
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @utils.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")
+        rules=["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."""
         with self.rbac_utils.override_role(self):
@@ -144,11 +146,13 @@
             raise rbac_exceptions.RbacMalformedResponse(
                 attribute=expected_attr)
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @utils.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")
+        rules=["os_compute_api:os-config-drive"])
     def test_show_server_config_drive(self):
         """Test show server with config_drive property in response body."""
         with self.rbac_utils.override_role(self):
@@ -162,18 +166,20 @@
     @decorators.idempotent_id('189bfed4-1e6d-475c-bb8c-d57e60895391')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-deferred-delete")
+        rules=["os_compute_api:os-deferred-delete"])
     def test_force_delete_server(self):
         """Test force delete server, part of os-deferred-delete."""
         with self.rbac_utils.override_role(self):
             # Force-deleting a server enforces os-deferred-delete.
             self.servers_client.force_delete_server(self.server['id'])
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('d873740a-7b10-40a9-943d-7cc18115370e')
     @utils.requires_ext(extension='OS-EXT-AZ', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-extended-availability-zone")
+        rules=["os_compute_api:os-extended-availability-zone"])
     def test_list_servers_with_details_extended_availability_zone(self):
         """Test list servers OS-EXT-AZ:availability_zone attr in resp body."""
         expected_attr = 'OS-EXT-AZ:availability_zone'
@@ -185,11 +191,13 @@
             raise rbac_exceptions.RbacMalformedResponse(
                 attribute=expected_attr)
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('727e5360-770a-4b9c-8015-513a40216635')
     @utils.requires_ext(extension='OS-EXT-AZ', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-extended-availability-zone")
+        rules=["os_compute_api:os-extended-availability-zone"])
     def test_show_server_extended_availability_zone(self):
         """Test show server OS-EXT-AZ:availability_zone attr in resp body."""
         expected_attr = 'OS-EXT-AZ:availability_zone'
@@ -200,11 +208,13 @@
             raise rbac_exceptions.RbacMalformedResponse(
                 attribute=expected_attr)
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('4aa5d93e-4887-468a-8eb4-b6eca0ca6437')
     @utils.requires_ext(extension='OS-EXT-SRV-ATTR', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-extended-server-attributes")
+        rules=["os_compute_api:os-extended-server-attributes"])
     def test_list_servers_extended_server_attributes(self):
         """Test list servers with details, with extended server attributes in
         response body.
@@ -222,11 +232,13 @@
                 raise rbac_exceptions.RbacMalformedResponse(
                     attribute=whole_attr)
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('2ed7aee2-94b2-4a9f-ae63-a51b7f94fe30')
     @utils.requires_ext(extension='OS-EXT-SRV-ATTR', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-extended-server-attributes")
+        rules=["os_compute_api:os-extended-server-attributes"])
     def test_show_server_extended_server_attributes(self):
         """Test show server with extended server attributes in response
         body.
@@ -244,11 +256,13 @@
                 raise rbac_exceptions.RbacMalformedResponse(
                     attribute=whole_attr)
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('82053c27-3134-4003-9b55-bc9fafdb0e3b')
     @utils.requires_ext(extension='OS-EXT-STS', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-extended-status")
+        rules=["os_compute_api:os-extended-status"])
     def test_list_servers_extended_status(self):
         """Test list servers with extended properties in response body."""
         with self.rbac_utils.override_role(self):
@@ -261,11 +275,13 @@
                 raise rbac_exceptions.RbacMalformedResponse(
                     attribute=attr)
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('7d2620a5-eea1-4a8b-96ea-86ad77a73fc8')
     @utils.requires_ext(extension='OS-EXT-STS', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-extended-status")
+        rules=["os_compute_api:os-extended-status"])
     def test_show_server_extended_status(self):
         """Test show server with extended properties in response body."""
         with self.rbac_utils.override_role(self):
@@ -278,11 +294,13 @@
                 raise rbac_exceptions.RbacMalformedResponse(
                     attribute=attr)
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('21e39cbe-6c32-48fc-80dd-3e1fece6053f')
     @utils.requires_ext(extension='os-extended-volumes', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-extended-volumes")
+        rules=["os_compute_api:os-extended-volumes"])
     def test_list_servers_with_details_extended_volumes(self):
         """Test list servers os-extended-volumes:volumes_attached attr in resp
         body.
@@ -295,11 +313,13 @@
             raise rbac_exceptions.RbacMalformedResponse(
                 attribute=expected_attr)
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @decorators.idempotent_id('7f163708-0d25-4138-8512-dfdd72a92989')
     @utils.requires_ext(extension='os-extended-volumes', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-extended-volumes")
+        rules=["os_compute_api:os-extended-volumes"])
     def test_show_server_extended_volumes(self):
         """Test show server os-extended-volumes:volumes_attached attr in resp
         body.
@@ -316,7 +336,7 @@
     @decorators.idempotent_id('9d1b131d-407e-4fa3-8eef-eb2c4526f1da')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-instance-actions")
+        rules=["os_compute_api:os-instance-actions"])
     def test_list_instance_actions(self):
         """Test list instance actions, part of os-instance-actions."""
         with self.rbac_utils.override_role(self):
@@ -326,7 +346,7 @@
     @decorators.idempotent_id('eb04c439-4215-4029-9ccb-5b3c041bfc25')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-instance-actions:events")
+        rules=["os_compute_api:os-instance-actions:events"])
     def test_show_instance_action(self):
         """Test show instance action, part of os-instance-actions.
 
@@ -348,9 +368,11 @@
             raise rbac_exceptions.RbacMalformedResponse(
                 attribute='events.traceback')
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-keypairs")
+        rules=["os_compute_api:os-keypairs"])
     @decorators.idempotent_id('81e6fa34-c06b-42ca-b195-82bf8699b940')
     def test_show_server_keypair(self):
         with self.rbac_utils.override_role(self):
@@ -360,9 +382,11 @@
             raise rbac_exceptions.RbacMalformedResponse(
                 attribute='key_name')
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-keypairs")
+        rules=["os_compute_api:os-keypairs"])
     @decorators.idempotent_id('41ca4280-ec59-4b80-a9b1-6bc6366faf39')
     def test_list_servers_keypairs(self):
         with self.rbac_utils.override_role(self):
@@ -373,7 +397,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-lock-server:lock")
+        rules=["os_compute_api:os-lock-server:lock"])
     @decorators.idempotent_id('b81e10fb-1864-498f-8c1d-5175c6fec5fb')
     def test_lock_server(self):
         """Test lock server, part of os-lock-server."""
@@ -383,7 +407,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-lock-server:unlock")
+        rules=["os_compute_api:os-lock-server:unlock"])
     @decorators.idempotent_id('d50ef8e8-4bce-11e7-b114-b2f933d5fe66')
     def test_unlock_server(self):
         """Test unlock server, part of os-lock-server."""
@@ -414,7 +438,7 @@
     @utils.requires_ext(extension='os-rescue', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-rescue")
+        rules=["os_compute_api:os-rescue"])
     @decorators.idempotent_id('fbbb2afc-ed0e-4552-887d-ac00fb5d436e')
     def test_rescue_server(self):
         """Test rescue server, part of os-rescue."""
@@ -427,7 +451,7 @@
     @utils.requires_ext(extension='os-rescue', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-rescue")
+        rules=["os_compute_api:os-rescue"])
     def test_unrescue_server(self):
         """Test unrescue server, part of os-rescue."""
         self.servers_client.rescue_server(self.server['id'])
@@ -442,7 +466,7 @@
     @utils.requires_ext(extension='os-server-diagnostics', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-diagnostics")
+        rules=["os_compute_api:os-server-diagnostics"])
     @decorators.idempotent_id('5dabfcc4-bedb-417b-8247-b3ee7c5c0f3e')
     def test_show_server_diagnostics(self):
         """Test show server diagnostics, part of os-server-diagnostics."""
@@ -453,7 +477,7 @@
     @decorators.idempotent_id('aaf43f78-c178-4581-ac18-14afd3f1f6ba')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-password")
+        rules=["os_compute_api:os-server-password"])
     def test_delete_server_password(self):
         """Test delete server password, part of os-server-password."""
         with self.rbac_utils.override_role(self):
@@ -462,17 +486,19 @@
     @utils.requires_ext(extension='os-server-password', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-password")
+        rules=["os_compute_api:os-server-password"])
     @decorators.idempotent_id('f677971a-7d20-493c-977f-6ff0a74b5b2c')
     def test_get_server_password(self):
         """Test show server password, part of os-server-password."""
         with self.rbac_utils.override_role(self):
             self.servers_client.show_password(self.server['id'])
 
+    @testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
+                      "This API extension policy was removed in Stein")
     @utils.requires_ext(extension='OS-SRV-USG', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-usage")
+        rules=["os_compute_api:os-server-usage"])
     @decorators.idempotent_id('f0437ead-b9fb-462a-9f3d-ce53fac9d57a')
     def test_show_server_usage(self):
         """Test show server usage, part of os-server-usage.
@@ -494,7 +520,7 @@
     @utils.requires_ext(extension='os-simple-tenant-usage', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-simple-tenant-usage:list")
+        rules=["os_compute_api:os-simple-tenant-usage:list"])
     @decorators.idempotent_id('2aef094f-0452-4df6-a66a-0ec22a92b16e')
     def test_list_simple_tenant_usages(self):
         """Test list tenant usages, part of os-simple-tenant-usage."""
@@ -504,7 +530,7 @@
     @utils.requires_ext(extension='os-simple-tenant-usage', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-simple-tenant-usage:show")
+        rules=["os_compute_api:os-simple-tenant-usage:show"])
     @decorators.idempotent_id('fe7eacda-15c4-4bf7-93ef-1091c4546a9d')
     def test_show_simple_tenant_usage(self):
         """Test show tenant usage, part of os-simple-tenant-usage."""
@@ -518,7 +544,7 @@
     @decorators.idempotent_id('b775930f-237c-431c-83ae-d33ed1b9700b')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-suspend-server:suspend")
+        rules=["os_compute_api:os-suspend-server:suspend"])
     def test_suspend_server(self):
         """Test suspend server, part of os-suspend-server."""
         with self.rbac_utils.override_role(self):
@@ -532,7 +558,7 @@
     @decorators.idempotent_id('4d90bd02-11f8-45b1-a8a1-534665584675')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-suspend-server:resume")
+        rules=["os_compute_api:os-suspend-server:resume"])
     def test_resume_server(self):
         """Test resume server, part of os-suspend-server."""
         self.servers_client.suspend_server(self.server['id'])
@@ -628,7 +654,7 @@
     @decorators.idempotent_id('ddf53cb6-4a0a-4e5a-91e3-6c32aaa3b9b6')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-attach-interfaces")
+        rules=["os_compute_api:os-attach-interfaces"])
     def test_list_interfaces(self):
         """Test list interfaces, part of os-attach-interfaces."""
         with self.rbac_utils.override_role(self):
@@ -640,7 +666,7 @@
     @utils.requires_ext(extension='os-attach-interfaces', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-attach-interfaces")
+        rules=["os_compute_api:os-attach-interfaces"])
     def test_show_interface(self):
         """Test show interfaces, part of os-attach-interfaces."""
         interface = self._attach_interface_to_server()
@@ -654,7 +680,7 @@
     @decorators.idempotent_id('d2d3a24d-4738-4bce-a287-36d664746cde')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-attach-interfaces:create")
+        rules=["os_compute_api:os-attach-interfaces:create"])
     def test_create_interface(self):
         """Test create interface, part of os-attach-interfaces."""
         network_id = self.network['id']
@@ -678,7 +704,7 @@
     @decorators.idempotent_id('55b05692-ed44-4608-a84c-cd4219c82799')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-attach-interfaces:delete")
+        rules=["os_compute_api:os-attach-interfaces:delete"])
     def test_delete_interface(self):
         """Test delete interface, part of os-attach-interfaces."""
         interface = self._attach_interface_to_server()
@@ -693,7 +719,7 @@
     @utils.requires_ext(extension='os-ips', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:ips:index")
+        rules=["os_compute_api:ips:index"])
     def test_list_addresses(self):
         """Test list server addresses, part of ips policy family."""
         with self.rbac_utils.override_role(self):
@@ -703,7 +729,7 @@
     @utils.requires_ext(extension='os-ips', service='compute')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:ips:show")
+        rules=["os_compute_api:ips:show"])
     def test_list_addresses_by_network(self):
         """Test list server addresses by network, part of ips policy family."""
         addresses = self.servers_client.list_addresses(self.server['id'])[
@@ -718,7 +744,7 @@
                           "Interface attachment is not available.")
     @utils.requires_ext(extension='os-multinic', service='compute')
     @rbac_rule_validation.action(
-        service="nova", rule="os_compute_api:os-multinic")
+        service="nova", rules=["os_compute_api:os-multinic"])
     @decorators.idempotent_id('bd3e2c74-130a-40f0-8085-124d93fe67da')
     def test_add_fixed_ip(self):
         """Test add fixed ip to server network, part of os-multinic."""
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 cae31f6..826e469 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_rbac.py
@@ -21,9 +21,7 @@
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest.lib import exceptions
 
-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 as base
 
@@ -47,7 +45,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:create")
+        rules=["os_compute_api:servers:create"])
     @decorators.idempotent_id('4f34c73a-6ddc-4677-976f-71320fa855bd')
     def test_create_server(self):
         with self.rbac_utils.override_role(self):
@@ -61,7 +59,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:create:forced_host")
+        rules=["os_compute_api:servers:create:forced_host"])
     @decorators.idempotent_id('0ae3c401-52ab-41bc-ab96-c598a65d9ae5')
     def test_create_server_forced_host(self):
         # Retrieve 'nova' zone host information from availiability_zone_list
@@ -88,7 +86,7 @@
     @utils.services('volume')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:create:attach_volume")
+        rules=["os_compute_api:servers:create:attach_volume"])
     @decorators.idempotent_id('eeddac5e-15aa-454f-838d-db608aae4dd8')
     def test_create_server_attach_volume(self):
         # To create a bootable volume, the UUID of the image from which
@@ -126,7 +124,7 @@
     @utils.services('network')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:create:attach_network")
+        rules=["os_compute_api:servers:create:attach_network"])
     @decorators.idempotent_id('b44cd4ff-50a4-42ce-ada3-724e213cd540')
     def test_create_server_attach_network(self):
         def _create_network_resources():
@@ -165,7 +163,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:delete")
+        rules=["os_compute_api:servers:delete"])
     @decorators.idempotent_id('062e3440-e873-4b41-9317-bf6d8be50c12')
     def test_delete_server(self):
         server = self.create_test_server(wait_until='ACTIVE')
@@ -177,18 +175,12 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:servers:update")
+        rules=["os_compute_api:servers:update"])
     @decorators.idempotent_id('077b17cb-5621-43b9-8adf-5725f0d7a863')
     def test_update_server(self):
         new_name = data_utils.rand_name(self.__class__.__name__ + '-server')
         with self.rbac_utils.override_role(self):
-            try:
-                self.servers_client.update_server(self.server['id'],
-                                                  name=new_name)
-                waiters.wait_for_server_status(self.servers_client,
-                                               self.server['id'], 'ACTIVE')
-            except exceptions.ServerFault as e:
-                # Some other policy may have blocked it.
-                LOG.info("ServerFault exception caught. Some other policy "
-                         "blocked updating of server")
-                raise rbac_exceptions.RbacConflictingPolicies(e)
+            self.servers_client.update_server(self.server['id'],
+                                              name=new_name)
+        waiters.wait_for_server_status(self.servers_client,
+                                       self.server['id'], 'ACTIVE')
diff --git a/patrole_tempest_plugin/tests/api/compute/test_server_tags_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_server_tags_rbac.py
index 70e7da9..ac571b9 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_server_tags_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_server_tags_rbac.py
@@ -47,7 +47,7 @@
     @decorators.idempotent_id('99e73dd3-adec-4044-b46c-84bdded35d09')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-tags:index")
+        rules=["os_compute_api:os-server-tags:index"])
     def test_list_tags(self):
         with self.rbac_utils.override_role(self):
             self.servers_client.list_tags(self.server['id'])
@@ -55,7 +55,7 @@
     @decorators.idempotent_id('9297c99e-94eb-429f-93cf-9b1838e33622')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-tags:show")
+        rules=["os_compute_api:os-server-tags:show"])
     def test_check_tag_existence(self):
         tag_name = self._add_tag_to_server()
         with self.rbac_utils.override_role(self):
@@ -65,7 +65,7 @@
     @decorators.idempotent_id('0d84ee94-d3ca-4635-8edf-b7f67ab8e4a3')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-tags:update")
+        rules=["os_compute_api:os-server-tags:update"])
     def test_update_tag(self):
         with self.rbac_utils.override_role(self):
             self._add_tag_to_server()
@@ -73,7 +73,7 @@
     @decorators.idempotent_id('115c2694-00aa-41ee-99f6-9eab4040c182')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-tags:delete")
+        rules=["os_compute_api:os-server-tags:delete"])
     def test_delete_tag(self):
         tag_name = self._add_tag_to_server()
         with self.rbac_utils.override_role(self):
@@ -82,7 +82,7 @@
     @decorators.idempotent_id('a8e19b87-6580-4bc8-9933-e62561ff667d')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-tags:update_all")
+        rules=["os_compute_api:os-server-tags:update_all"])
     def test_update_all_tags(self):
         new_tag_name = data_utils.rand_name(self.__class__.__name__ + '-tag')
         with self.rbac_utils.override_role(self):
@@ -92,7 +92,7 @@
     @decorators.idempotent_id('89d51936-e333-42f9-a045-132a4865ba1a')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-server-tags:delete_all")
+        rules=["os_compute_api:os-server-tags:delete_all"])
     def test_delete_all_tags(self):
         with self.rbac_utils.override_role(self):
             self.servers_client.delete_all_tags(self.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 a510d1e..cb76605 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
@@ -51,7 +51,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes-attachments:index")
+        rules=["os_compute_api:os-volumes-attachments:index"])
     @decorators.idempotent_id('529b668b-6edb-41d5-8886-d7dbd0614678')
     def test_list_volume_attachments(self):
         with self.rbac_utils.override_role(self):
@@ -60,7 +60,7 @@
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes-attachments:create")
+        rules=["os_compute_api:os-volumes-attachments:create"])
     @decorators.idempotent_id('21c2c3fd-fbe8-41b1-8ef8-115ec47d54c1')
     def test_create_volume_attachment(self):
         with self.rbac_utils.override_role(self):
@@ -69,7 +69,7 @@
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes-attachments:show")
+        rules=["os_compute_api:os-volumes-attachments:show"])
     @decorators.idempotent_id('997df9c2-6e54-47b6-ab74-e4fdb500f385')
     def test_show_volume_attachment(self):
         attachment = self.attach_volume(self.server, self.volume)
@@ -83,7 +83,7 @@
                           'In-place swapping of volumes not supported.')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes-attachments:update")
+        rules=["os_compute_api:os-volumes-attachments:update"])
     @decorators.idempotent_id('bd667186-eca6-4b78-ab6a-3e2fabcb971f')
     def test_update_volume_attachment(self):
         attachment = self.attach_volume(self.server, self.volume)
@@ -108,7 +108,7 @@
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes-attachments:delete")
+        rules=["os_compute_api:os-volumes-attachments:delete"])
     @decorators.idempotent_id('12b03e90-d087-46af-9c4d-507d021c4984')
     def test_delete_volume_attachment(self):
         self.attach_volume(self.server, self.volume)
diff --git a/patrole_tempest_plugin/tests/api/compute/test_services_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_services_rbac.py
index 183d990..f32e117 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_services_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_services_rbac.py
@@ -31,7 +31,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-services")
+        rules=["os_compute_api:os-services"])
     @decorators.idempotent_id('7472261b-9c6d-453a-bcb3-aecaa29ad281')
     def test_list_services(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/compute/test_tenant_networks_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_tenant_networks_rbac.py
index 49fb34c..a4ccc10 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_tenant_networks_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_tenant_networks_rbac.py
@@ -54,7 +54,7 @@
     @decorators.idempotent_id('42b39ba1-14aa-4799-9518-34367d0da67a')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-tenant-networks")
+        rules=["os_compute_api:os-tenant-networks"])
     def test_list_show_tenant_networks(self):
         with self.rbac_utils.override_role(self):
             self.tenant_networks_client.list_tenant_networks()
diff --git a/patrole_tempest_plugin/tests/api/compute/test_virtual_interfaces_rbac.py b/patrole_tempest_plugin/tests/api/compute/test_virtual_interfaces_rbac.py
index ae77a34..8376be0 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_virtual_interfaces_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_virtual_interfaces_rbac.py
@@ -45,7 +45,7 @@
 
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-virtual-interfaces")
+        rules=["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.
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 b07fb3f..3c693ab 100644
--- a/patrole_tempest_plugin/tests/api/compute/test_volume_rbac.py
+++ b/patrole_tempest_plugin/tests/api/compute/test_volume_rbac.py
@@ -60,7 +60,7 @@
     @decorators.idempotent_id('2402013e-a624-43e3-9518-44a5d1dbb32d')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes")
+        rules=["os_compute_api:os-volumes"])
     def test_create_volume(self):
         with self.rbac_utils.override_role(self):
             volume = self.volumes_extensions_client.create_volume(
@@ -73,7 +73,7 @@
     @decorators.idempotent_id('69b3888c-dff2-47b0-9fa4-0672619c9054')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes")
+        rules=["os_compute_api:os-volumes"])
     def test_list_volumes(self):
         with self.rbac_utils.override_role(self):
             self.volumes_extensions_client.list_volumes()
@@ -81,7 +81,7 @@
     @decorators.idempotent_id('4ba0a820-040f-488b-86bb-be2e920ea12c')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes")
+        rules=["os_compute_api:os-volumes"])
     def test_show_volume(self):
         with self.rbac_utils.override_role(self):
             self.volumes_extensions_client.show_volume(self.volume['id'])
@@ -89,7 +89,7 @@
     @decorators.idempotent_id('6e7870f2-1bb2-4b58-96f8-6782071ef327')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes")
+        rules=["os_compute_api:os-volumes"])
     def test_delete_volume(self):
         volume = self.create_volume()
         with self.rbac_utils.override_role(self):
@@ -98,7 +98,7 @@
     @decorators.idempotent_id('0c3eaa4f-69d6-4a13-9dda-19585f36b1c1')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes")
+        rules=["os_compute_api:os-volumes"])
     def test_create_snapshot(self):
         s_name = data_utils.rand_name(self.__class__.__name__ + '-Snapshot')
         with self.rbac_utils.override_role(self):
@@ -110,7 +110,7 @@
     @decorators.idempotent_id('e944e816-416c-11e7-a919-92ebcb67fe33')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes")
+        rules=["os_compute_api:os-volumes"])
     def test_list_snapshots(self):
         with self.rbac_utils.override_role(self):
             self.snapshots_extensions_client.list_snapshots()
@@ -118,7 +118,7 @@
     @decorators.idempotent_id('19c2e6bd-585b-472f-a8d7-71ea9299c655')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes")
+        rules=["os_compute_api:os-volumes"])
     def test_show_snapshot(self):
         s_name = data_utils.rand_name(self.__class__.__name__ + '-Snapshot')
         snapshot = self.snapshots_extensions_client.create_snapshot(
@@ -131,7 +131,7 @@
     @decorators.idempotent_id('f4f5635c-416c-11e7-a919-92ebcb67fe33')
     @rbac_rule_validation.action(
         service="nova",
-        rule="os_compute_api:os-volumes")
+        rules=["os_compute_api:os-volumes"])
     def test_delete_snapshot(self):
         s_name = data_utils.rand_name(self.__class__.__name__ + '-Snapshot')
         snapshot = self.snapshots_extensions_client.create_snapshot(
diff --git a/patrole_tempest_plugin/tests/api/identity/rbac_base.py b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
index 91b3d1e..44f5962 100644
--- a/patrole_tempest_plugin/tests/api/identity/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
@@ -16,13 +16,11 @@
 from oslo_log import log as logging
 
 from tempest.api.identity import base
-from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 
 from patrole_tempest_plugin import rbac_utils
 
-CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
@@ -30,11 +28,6 @@
                            base.BaseIdentityTest):
 
     @classmethod
-    def skip_checks(cls):
-        super(BaseIdentityRbacTest, cls).skip_checks()
-        cls.skip_rbac_checks()
-
-    @classmethod
     def setup_clients(cls):
         super(BaseIdentityRbacTest, cls).setup_clients()
         cls.setup_rbac_utils()
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_application_credentials_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_application_credentials_rbac.py
index c7a6033..1d466f3 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_application_credentials_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_application_credentials_rbac.py
@@ -52,23 +52,26 @@
         return application_credential
 
     @decorators.idempotent_id('b53bee14-e9df-4929-b257-6def76c12e4d')
-    @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_application_credential")
+    @rbac_rule_validation.action(
+        service="keystone",
+        rules=["identity:create_application_credential"])
     def test_create_application_credential(self):
         with self.rbac_utils.override_role(self):
             self._create_application_credential()
 
     @decorators.idempotent_id('58b3c3a0-5ad0-44f7-8da7-0736f71f7168')
-    @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_application_credentials")
+    @rbac_rule_validation.action(
+        service="keystone",
+        rules=["identity:list_application_credentials"])
     def test_list_application_credentials(self):
         with self.rbac_utils.override_role(self):
             self.application_credentials_client.list_application_credentials(
                 user_id=self.user_id)
 
     @decorators.idempotent_id('d7b13968-a8a6-47fd-8e1d-7cc7f565c7f8')
-    @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_application_credential")
+    @rbac_rule_validation.action(
+        service="keystone",
+        rules=["identity:get_application_credential"])
     def test_show_application_credential(self):
         app_cred = self._create_application_credential()
         with self.rbac_utils.override_role(self):
@@ -76,8 +79,9 @@
                 user_id=self.user_id, application_credential_id=app_cred['id'])
 
     @decorators.idempotent_id('521b7c0f-1dd5-47a6-ae95-95c0323d7735')
-    @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_application_credential")
+    @rbac_rule_validation.action(
+        service="keystone",
+        rules=["identity:delete_application_credential"])
     def test_delete_application_credential(self):
         app_cred = self._create_application_credential()
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py
index 8393696..a63192f 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_auth_rbac.py
@@ -32,14 +32,14 @@
 
     @decorators.idempotent_id('2a9fbf7f-6feb-4161-ae4b-faf7d6421b1a')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_auth_projects")
+                                 rules=["identity:get_auth_projects"])
     def test_list_auth_projects(self):
         with self.rbac_utils.override_role(self):
             self.identity_client.list_auth_projects()
 
     @decorators.idempotent_id('6a40af0d-7265-4657-b6b2-87a2828e263e')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_auth_domains")
+                                 rules=["identity:get_auth_domains"])
     def test_list_auth_domain(self):
         with self.rbac_utils.override_role(self):
             self.identity_client.list_auth_domains()
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py
index af6feb6..26e34da 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_credentials_rbac.py
@@ -13,13 +13,21 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import testtools
+
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.identity import rbac_base
 
+CONF = config.CONF
 
+
+@testtools.skipIf(
+    CONF.policy_feature_enabled.removed_keystone_policies_stein,
+    "This policy is unavailable in Stein so cannot be tested.")
 class IdentityCredentialsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
     def _create_user_project_and_credential(self):
@@ -29,7 +37,7 @@
         return credential
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_credential")
+                                 rules=["identity:create_credential"])
     @decorators.idempotent_id('c1ab6d34-c59f-4ae1-bae9-bb3c1089b48e')
     def test_create_credential(self):
         project = self.setup_test_project()
@@ -38,7 +46,7 @@
             self.setup_test_credential(user=user)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_credential")
+                                 rules=["identity:update_credential"])
     @decorators.idempotent_id('cfb05ce3-bffb-496e-a3c2-9515d730da63')
     def test_update_credential(self):
         credential = self._create_user_project_and_credential()
@@ -54,7 +62,7 @@
                 project_id=credential['project_id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_credential")
+                                 rules=["identity:delete_credential"])
     @decorators.idempotent_id('87ab42af-8d41-401b-90df-21e72919fcde')
     def test_delete_credential(self):
         credential = self._create_user_project_and_credential()
@@ -63,7 +71,7 @@
             self.creds_client.delete_credential(credential['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_credential")
+                                 rules=["identity:get_credential"])
     @decorators.idempotent_id('1b6eeae6-f1e8-4cdf-8903-1c002b1fc271')
     def test_show_credential(self):
         credential = self._create_user_project_and_credential()
@@ -72,7 +80,7 @@
             self.creds_client.show_credential(credential['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_credentials")
+                                 rules=["identity:list_credentials"])
     @decorators.idempotent_id('3de303e2-12a7-4811-805a-f18906472038')
     def test_list_credentials(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_domain_configuration_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_domain_configuration_rbac.py
index 8db8906..4fa3937 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_domain_configuration_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_domain_configuration_rbac.py
@@ -53,14 +53,14 @@
         return domain_config
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_domain_config")
+                                 rules=["identity:create_domain_config"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd115')
     def test_create_domain_config(self):
         with self.rbac_utils.override_role(self):
             self._create_domain_config(self.domain_id)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_domain_config")
+                                 rules=["identity:get_domain_config"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd118')
     def test_show_domain_config(self):
         with self.rbac_utils.override_role(self):
@@ -68,7 +68,7 @@
 
     @decorators.idempotent_id('1b539f95-4991-4e09-960f-fa771e1007d7')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_domain_config")
+                                 rules=["identity:get_domain_config"])
     def test_show_domain_group_config(self):
         with self.rbac_utils.override_role(self):
             self.domain_config_client.show_domain_group_config(
@@ -76,7 +76,7 @@
 
     @decorators.idempotent_id('590c774d-a294-44f8-866e-aac9f4ab3809')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_domain_config")
+                                 rules=["identity:get_domain_config"])
     def test_show_domain_group_option_config(self):
         with self.rbac_utils.override_role(self):
             self.domain_config_client.show_domain_group_option_config(
@@ -85,7 +85,7 @@
     @decorators.idempotent_id('21053885-1ce3-4167-b5e3-e470253481da')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:get_security_compliance_domain_config")
+        rules=["identity:get_security_compliance_domain_config"])
     def test_show_security_compliance_domain_config(self):
         # The "security_compliance" group can only be shown for the default
         # domain.
@@ -95,28 +95,28 @@
 
     @decorators.idempotent_id('d1addd10-9ae4-4360-9961-47324fd22f23')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_domain_config_default")
+                                 rules=["identity:get_domain_config_default"])
     def test_show_default_config_settings(self):
         with self.rbac_utils.override_role(self):
             self.domain_config_client.show_default_config_settings()
 
     @decorators.idempotent_id('63183377-251f-4622-81f0-6b58a8a285c9')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_domain_config_default")
+                                 rules=["identity:get_domain_config_default"])
     def test_show_default_group_config(self):
         with self.rbac_utils.override_role(self):
             self.domain_config_client.show_default_group_config('identity')
 
     @decorators.idempotent_id('6440e9c1-e8da-474d-9118-89996fffe5f8')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_domain_config_default")
+                                 rules=["identity:get_domain_config_default"])
     def test_show_default_group_option(self):
         with self.rbac_utils.override_role(self):
             self.domain_config_client.show_default_group_option('identity',
                                                                 'driver')
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_domain_config")
+                                 rules=["identity:update_domain_config"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd116')
     def test_update_domain_config(self):
         updated_config = {'ldap': {'url': data_utils.rand_url()}}
@@ -126,7 +126,7 @@
 
     @decorators.idempotent_id('6e32bf96-dbe9-4ac8-b814-0e79fa948285')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_domain_config")
+                                 rules=["identity:update_domain_config"])
     def test_update_domain_group_config(self):
         with self.rbac_utils.override_role(self):
             self.domain_config_client.update_domain_group_config(
@@ -134,14 +134,14 @@
 
     @decorators.idempotent_id('d2c510da-a077-4c67-9522-27745ef2812b')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_domain_config")
+                                 rules=["identity:update_domain_config"])
     def test_update_domain_group_option_config(self):
         with self.rbac_utils.override_role(self):
             self.domain_config_client.update_domain_group_option_config(
                 self.domain_id, 'identity', 'driver', driver='ldap')
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_domain_config")
+                                 rules=["identity:delete_domain_config"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd117')
     def test_delete_domain_config(self):
         with self.rbac_utils.override_role(self):
@@ -149,7 +149,7 @@
 
     @decorators.idempotent_id('f479694b-df02-4d5a-88b6-c8b52f9341eb')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_domain_config")
+                                 rules=["identity:delete_domain_config"])
     def test_delete_domain_group_config(self):
         with self.rbac_utils.override_role(self):
             self.domain_config_client.delete_domain_group_config(
@@ -157,7 +157,7 @@
 
     @decorators.idempotent_id('f594bde3-31c9-414f-922d-0ddafdc0ca40')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_domain_config")
+                                 rules=["identity:delete_domain_config"])
     def test_delete_domain_group_option_config(self):
         with self.rbac_utils.override_role(self):
             self.domain_config_client.delete_domain_group_option_config(
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_domains_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_domains_rbac.py
index 3837051..ab38876 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_domains_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_domains_rbac.py
@@ -23,14 +23,14 @@
 class IdentityDomainsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_domain")
+                                 rules=["identity:create_domain"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd110')
     def test_create_domain(self):
         with self.rbac_utils.override_role(self):
             self.setup_test_domain()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_domain")
+                                 rules=["identity:update_domain"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd111')
     def test_update_domain(self):
         domain = self.setup_test_domain()
@@ -42,7 +42,7 @@
                                               name=new_domain_name)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_domain")
+                                 rules=["identity:delete_domain"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd112')
     def test_delete_domain(self):
         domain = self.setup_test_domain()
@@ -54,7 +54,7 @@
             self.domains_client.delete_domain(domain['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_domain")
+                                 rules=["identity:get_domain"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd113')
     def test_show_domain(self):
         domain = self.setup_test_domain()
@@ -62,7 +62,7 @@
             self.domains_client.show_domain(domain['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_domains")
+                                 rules=["identity:list_domains"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd114')
     def test_list_domains(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py
index ad1fd9b..ec5ffa3 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_endpoints_rbac.py
@@ -23,7 +23,7 @@
 class IdentityEndpointsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_endpoint")
+                                 rules=["identity:create_endpoint"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd127')
     def test_create_endpoint(self):
         service = self.setup_test_service()
@@ -31,7 +31,7 @@
             self.setup_test_endpoint(service=service)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_endpoint")
+                                 rules=["identity:update_endpoint"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd128')
     def test_update_endpoint(self):
         endpoint = self.setup_test_endpoint()
@@ -43,7 +43,7 @@
                 url=new_url)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_endpoint")
+                                 rules=["identity:delete_endpoint"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd129')
     def test_delete_endpoint(self):
         endpoint = self.setup_test_endpoint()
@@ -52,7 +52,7 @@
             self.endpoints_client.delete_endpoint(endpoint['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_endpoint")
+                                 rules=["identity:get_endpoint"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd130')
     def test_show_endpoint(self):
         endpoint = self.setup_test_endpoint()
@@ -61,7 +61,7 @@
             self.endpoints_client.show_endpoint(endpoint['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_endpoints")
+                                 rules=["identity:list_endpoints"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd131')
     def test_list_endpoints(self):
         with self.rbac_utils.override_role(self):
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 6e58289..be49d84 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
@@ -58,14 +58,14 @@
         return endpoint_group['id']
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_endpoint_group")
+                                 rules=["identity:create_endpoint_group"])
     @decorators.idempotent_id('b4765906-52ec-477b-b441-a8508ced68e3')
     def test_create_endpoint_group(self):
         with self.rbac_utils.override_role(self):
             self._create_endpoint_group(ignore_not_found=True)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_endpoint_groups")
+                                 rules=["identity:list_endpoint_groups"])
     @decorators.idempotent_id('089aa3a7-ba1f-4f70-a1cf-f298a845058a')
     def test_list_endpoint_groups(self):
         with self.rbac_utils.override_role(self):
@@ -73,14 +73,14 @@
 
     @decorators.idempotent_id('5c16368d-1485-4c28-9803-db3fa3510623')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_endpoint_group")
+                                 rules=["identity:get_endpoint_group"])
     def test_check_endpoint_group(self):
         with self.rbac_utils.override_role(self):
             self.endpoint_groups_client.check_endpoint_group(
                 self.endpoint_group_id)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_endpoint_group")
+                                 rules=["identity:get_endpoint_group"])
     @decorators.idempotent_id('bd2b6fb8-661f-4255-84b2-50fea4a1dc61')
     def test_show_endpoint_group(self):
         with self.rbac_utils.override_role(self):
@@ -88,7 +88,7 @@
                 self.endpoint_group_id)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_endpoint_group")
+                                 rules=["identity:update_endpoint_group"])
     @decorators.idempotent_id('028b9198-ec35-4bd5-8f72-e23dfb7a0c8e')
     def test_update_endpoint_group(self):
         updated_name = data_utils.rand_name(
@@ -99,7 +99,7 @@
                 self.endpoint_group_id, name=updated_name)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_endpoint_group")
+                                 rules=["identity:delete_endpoint_group"])
     @decorators.idempotent_id('88cc105e-70d9-48ac-927e-200ef41e070c')
     def test_delete_endpoint_group(self):
         endpoint_group_id = self._create_endpoint_group(ignore_not_found=True)
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_projects_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_projects_rbac.py
index 1045b9b..17e918d 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_projects_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_ep_filter_projects_rbac.py
@@ -44,7 +44,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:add_endpoint_to_project")
+        rules=["identity:add_endpoint_to_project"])
     @decorators.idempotent_id('9199ec13-816d-4efe-b8b1-e1cd026b9747')
     def test_add_endpoint_to_project(self):
         # Adding endpoints to projects
@@ -53,7 +53,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:list_projects_for_endpoint")
+        rules=["identity:list_projects_for_endpoint"])
     @decorators.idempotent_id('f53dca42-ec8a-48e9-924b-0bbe6c99727f')
     def test_list_projects_for_endpoint(self):
         with self.rbac_utils.override_role(self):
@@ -62,7 +62,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:check_endpoint_in_project")
+        rules=["identity:check_endpoint_in_project"])
     @decorators.idempotent_id('0c1425eb-833c-4aa1-a21d-52ffa41fdc6a')
     def test_check_endpoint_in_project(self):
         self._add_endpoint_to_project()
@@ -72,7 +72,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:list_endpoints_for_project")
+        rules=["identity:list_endpoints_for_project"])
     @decorators.idempotent_id('5d86c659-c6ad-41e0-854e-3823e95c7cc2')
     def test_list_endpoints_in_project(self):
         with self.rbac_utils.override_role(self):
@@ -81,7 +81,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:remove_endpoint_from_project")
+        rules=["identity:remove_endpoint_from_project"])
     @decorators.idempotent_id('b4e21c10-4f47-427b-9b8a-f5b5601adfda')
     def test_remove_endpoint_from_project(self):
         self._add_endpoint_to_project(ignore_not_found=True)
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py
index 06148d9..9814e3b 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_groups_rbac.py
@@ -30,14 +30,14 @@
         return (group['id'], user['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_group")
+                                 rules=["identity:create_group"])
     @decorators.idempotent_id('88377f51-9074-4d64-a22f-f8931d048c9a')
     def test_create_group(self):
         with self.rbac_utils.override_role(self):
             self.setup_test_group()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_group")
+                                 rules=["identity:update_group"])
     @decorators.idempotent_id('790fb7be-a657-4a64-9b83-c43425cf180b')
     def test_update_group(self):
         group = self.setup_test_group()
@@ -48,7 +48,7 @@
             self.groups_client.update_group(group['id'], name=new_group_name)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_group")
+                                 rules=["identity:delete_group"])
     @decorators.idempotent_id('646b52da-2a5f-486a-afb0-51fdc86a6c12')
     def test_delete_group(self):
         group = self.setup_test_group()
@@ -57,7 +57,7 @@
             self.groups_client.delete_group(group['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_group")
+                                 rules=["identity:get_group"])
     @decorators.idempotent_id('d530f0ad-42b9-429b-ad05-e53ac95a040e')
     def test_show_group(self):
         group = self.setup_test_group()
@@ -66,14 +66,14 @@
             self.groups_client.show_group(group['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_groups")
+                                 rules=["identity:list_groups"])
     @decorators.idempotent_id('c4d0f76b-735f-4fd0-868b-0006bc420ff4')
     def test_list_groups(self):
         with self.rbac_utils.override_role(self):
             self.groups_client.list_groups()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:add_user_to_group")
+                                 rules=["identity:add_user_to_group"])
     @decorators.idempotent_id('fdd49b74-3ed3-4736-9f0e-9027a32017ac')
     def test_add_user_group(self):
         group = self.setup_test_group()
@@ -83,7 +83,7 @@
             self.groups_client.add_group_user(group['id'], user['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:remove_user_from_group")
+                                 rules=["identity:remove_user_from_group"])
     @decorators.idempotent_id('8a60d11c-7d2b-47e5-a0f3-9ea900ca66fe')
     def test_remove_user_group(self):
         group_id, user_id = self._create_user_and_add_to_new_group()
@@ -92,7 +92,7 @@
             self.groups_client.delete_group_user(group_id, user_id)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_users_in_group")
+                                 rules=["identity:list_users_in_group"])
     @decorators.idempotent_id('b3e394a7-079e-4a0d-a4ff-9b266293d1ee')
     def test_list_user_group(self):
         group = self.setup_test_group()
@@ -101,7 +101,7 @@
             self.groups_client.list_group_users(group['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:check_user_in_group")
+                                 rules=["identity:check_user_in_group"])
     @decorators.idempotent_id('d3603241-fd87-4a2d-94f9-f32469d1aaba')
     def test_check_user_group(self):
         group_id, user_id = self._create_user_and_add_to_new_group()
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_consumers_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_consumers_rbac.py
index f591e15..ecd534d 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_consumers_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_consumers_rbac.py
@@ -34,14 +34,14 @@
         return consumer
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_consumer")
+                                 rules=["identity:create_consumer"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d970')
     def test_create_consumer(self):
         with self.rbac_utils.override_role(self):
             self._create_consumer()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_consumer")
+                                 rules=["identity:delete_consumer"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d971')
     def test_delete_consumer(self):
         consumer = self._create_consumer()
@@ -50,7 +50,7 @@
             self.consumers_client.delete_consumer(consumer['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_consumer")
+                                 rules=["identity:update_consumer"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d972')
     def test_update_consumer(self):
         consumer = self._create_consumer()
@@ -62,7 +62,7 @@
                                                   updated_description)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_consumer")
+                                 rules=["identity:get_consumer"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d973')
     def test_show_consumer(self):
         consumer = self._create_consumer()
@@ -71,7 +71,7 @@
             self.consumers_client.show_consumer(consumer['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_consumers")
+                                 rules=["identity:list_consumers"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d975')
     def test_list_consumers(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_tokens_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_tokens_rbac.py
index 13731d5..30b386f 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_tokens_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_oauth_tokens_rbac.py
@@ -80,7 +80,7 @@
         return access_key
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:authorize_request_token")
+                                 rules=["identity:authorize_request_token"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d976')
     def test_authorize_request_token(self):
         _, request_token = self._create_consumer_and_request_token()
@@ -91,7 +91,7 @@
                 self.role_ids)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_access_token")
+                                 rules=["identity:get_access_token"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d977')
     def test_get_access_token(self):
         access_token = self._create_access_token()
@@ -101,7 +101,7 @@
                                                      access_token)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_access_token_role")
+                                 rules=["identity:get_access_token_role"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d980')
     def test_get_access_token_role(self):
         access_token = self._create_access_token()
@@ -111,14 +111,14 @@
                 self.user_id, access_token, self.role_ids[0])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_access_tokens")
+                                 rules=["identity:list_access_tokens"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d979')
     def test_list_access_tokens(self):
         with self.rbac_utils.override_role(self):
             self.oauth_token_client.list_access_tokens(self.user_id)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_access_token_roles")
+                                 rules=["identity:list_access_token_roles"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d978')
     def test_list_access_token_roles(self):
         access_token = self._create_access_token()
@@ -128,7 +128,7 @@
                 self.user_id, access_token)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_access_token")
+                                 rules=["identity:delete_access_token"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d981')
     def test_revoke_access_token(self):
         access_token = self._create_access_token()
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_policies_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_policies_rbac.py
index a8c10ca..b0a3087 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_policies_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_policies_rbac.py
@@ -23,14 +23,14 @@
 class IdentityPoliciesV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_policy")
+                                 rules=["identity:create_policy"])
     @decorators.idempotent_id('de2f7ecb-fbf0-41f3-abf4-b97b5e082fd5')
     def test_create_policy(self):
         with self.rbac_utils.override_role(self):
             self.setup_test_policy()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_policy")
+                                 rules=["identity:update_policy"])
     @decorators.idempotent_id('9cfed3c6-0b27-4d15-be67-e06e0cfb01b9')
     def test_update_policy(self):
         policy = self.setup_test_policy()
@@ -42,7 +42,7 @@
                                                type=updated_policy_type)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_policy")
+                                 rules=["identity:delete_policy"])
     @decorators.idempotent_id('dcd93f75-1e1b-4fbe-bee0-9c4c7b201735')
     def test_delete_policy(self):
         policy = self.setup_test_policy()
@@ -51,7 +51,7 @@
             self.policies_client.delete_policy(policy['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_policy")
+                                 rules=["identity:get_policy"])
     @decorators.idempotent_id('d7e415c2-945a-4504-9571-0e2d0dd8594b')
     def test_show_policy(self):
         policy = self.setup_test_policy()
@@ -60,7 +60,7 @@
             self.policies_client.show_policy(policy['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_policies")
+                                 rules=["identity:list_policies"])
     @decorators.idempotent_id('35a56161-4054-4237-8a78-7ce805dce202')
     def test_list_policies(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_policy_association_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_policy_association_rbac.py
index 2a69224..f56b6e4 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_policy_association_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_policy_association_rbac.py
@@ -58,7 +58,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:create_policy_association_for_endpoint")
+        rules=["identity:create_policy_association_for_endpoint"])
     @decorators.idempotent_id('1b3f4f62-4f4a-4d27-be27-9a113058597f')
     def test_update_policy_association_for_endpoint(self):
         with self.rbac_utils.override_role(self):
@@ -67,7 +67,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:check_policy_association_for_endpoint")
+        rules=["identity:check_policy_association_for_endpoint"])
     @decorators.idempotent_id('25ce8c89-e751-465c-8d35-52bacd774beb')
     def test_show_policy_association_for_endpoint(self):
         self._update_policy_association_for_endpoint(
@@ -78,7 +78,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:delete_policy_association_for_endpoint")
+        rules=["identity:delete_policy_association_for_endpoint"])
     @decorators.idempotent_id('95cad2d8-bcd0-4c4e-a8f7-cc80601e43a1')
     def test_delete_policy_association_for_endpoint(self):
         self._update_policy_association_for_endpoint(
@@ -89,7 +89,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:create_policy_association_for_service")
+        rules=["identity:create_policy_association_for_service"])
     @decorators.idempotent_id('57fb80fe-6ce2-4995-b710-4692b3fc3cdc')
     def test_update_policy_association_for_service(self):
         with self.rbac_utils.override_role(self):
@@ -98,7 +98,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:check_policy_association_for_service")
+        rules=["identity:check_policy_association_for_service"])
     @decorators.idempotent_id('5cbe285f-4888-4f98-978f-30210ff28b74')
     def test_show_policy_association_for_service(self):
         self._update_policy_association_for_service(
@@ -109,7 +109,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:delete_policy_association_for_service")
+        rules=["identity:delete_policy_association_for_service"])
     @decorators.idempotent_id('f754455c-02a4-4fb6-8c73-64ef453f955f')
     def test_delete_policy_association_for_service(self):
         self._update_policy_association_for_service(
@@ -120,7 +120,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:create_policy_association_for_region_and_service")
+        rules=["identity:create_policy_association_for_region_and_service"])
     @decorators.idempotent_id('54d2a93e-c84d-4079-8ea9-2fb227c262a1')
     def test_update_policy_association_for_region_and_service(self):
         with self.rbac_utils.override_role(self):
@@ -129,7 +129,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:check_policy_association_for_region_and_service")
+        rules=["identity:check_policy_association_for_region_and_service"])
     @decorators.idempotent_id('0763b780-52c1-47bc-9316-1fe12a2ab0bc')
     def test_show_policy_association_for_region_and_service(self):
         self._update_policy_association_for_region_and_service(
@@ -141,7 +141,7 @@
 
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:delete_policy_association_for_region_and_service")
+        rules=["identity:delete_policy_association_for_region_and_service"])
     @decorators.idempotent_id('9c956888-81d4-4a24-8203-bff7b8a7834c')
     def test_delete_policy_association_for_region_and_service(self):
         self._update_policy_association_for_region_and_service(
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_project_tags_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_project_tags_rbac.py
index e0be9cb..debc2e9 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_project_tags_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_project_tags_rbac.py
@@ -42,7 +42,7 @@
 
     @decorators.idempotent_id('acbd7b2d-0a4d-4990-9fab-eccad69d4238')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_project_tag")
+                                 rules=["identity:create_project_tag"])
     def test_update_project_tag(self):
         tag = data_utils.rand_name(self.__class__.__name__ + '-Tag')
         with self.rbac_utils.override_role(self):
@@ -50,14 +50,14 @@
 
     @decorators.idempotent_id('e122d7d1-bb6d-43af-b489-afa8c609b9ae')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_project_tags")
+                                 rules=["identity:list_project_tags"])
     def test_list_project_tags(self):
         with self.rbac_utils.override_role(self):
             self.project_tags_client.list_project_tags(self.project_id)
 
     @decorators.idempotent_id('716f9081-4626-4594-a82c-e7dc037464ac')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_project_tags")
+                                 rules=["identity:update_project_tags"])
     def test_update_all_project_tags(self):
         tags = [
             data_utils.rand_name(self.__class__.__name__ + '-Tag')
@@ -69,7 +69,7 @@
 
     @decorators.idempotent_id('974cb1da-d7d4-4863-99da-4a3f0c801729')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_project_tag")
+                                 rules=["identity:get_project_tag"])
     def test_check_project_tag_existence(self):
         tag = data_utils.rand_name(self.__class__.__name__ + '-Tag')
         self.project_tags_client.update_project_tag(self.project_id, tag)
@@ -80,7 +80,7 @@
 
     @decorators.idempotent_id('ffe0c8e1-f9eb-43c5-8097-1e938fc08e07')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_project_tag")
+                                 rules=["identity:delete_project_tag"])
     def test_delete_project_tag(self):
         tag = data_utils.rand_name(self.__class__.__name__ + '-Tag')
         self.project_tags_client.update_project_tag(self.project_id, tag)
@@ -90,7 +90,7 @@
 
     @decorators.idempotent_id('94d0ef63-e9e3-4287-9c5e-bd5464467d77')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_project_tags")
+                                 rules=["identity:delete_project_tags"])
     def test_delete_all_project_tags(self):
         with self.rbac_utils.override_role(self):
             self.project_tags_client.delete_all_project_tags(self.project_id)
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py
index 0b394b4..e1e6f08 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_projects_rbac.py
@@ -23,14 +23,14 @@
 class IdentityProjectV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_project")
+                                 rules=["identity:create_project"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1564-080044d0d904')
     def test_create_project(self):
         with self.rbac_utils.override_role(self):
             self.setup_test_project()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_project")
+                                 rules=["identity:update_project"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1564-080044d0d905')
     def test_update_project(self):
         project = self.setup_test_project()
@@ -42,7 +42,7 @@
                                                 description=new_desc)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_project")
+                                 rules=["identity:delete_project"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1564-080044d0d906')
     def test_delete_project(self):
         project = self.setup_test_project()
@@ -51,7 +51,7 @@
             self.projects_client.delete_project(project['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_project")
+                                 rules=["identity:get_project"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1564-080044d0d907')
     def test_show_project(self):
         project = self.setup_test_project()
@@ -60,7 +60,7 @@
             self.projects_client.show_project(project['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_projects")
+                                 rules=["identity:list_projects"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1564-080044d0d908')
     def test_list_projects(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_regions_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_regions_rbac.py
index 14b9de5..1d81319 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_regions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_regions_rbac.py
@@ -23,14 +23,14 @@
 class IdentityRegionsV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_region")
+                                 rules=["identity:create_region"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd119')
     def test_create_region(self):
         with self.rbac_utils.override_role(self):
             self.setup_test_region()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_region")
+                                 rules=["identity:update_region"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd120')
     def test_update_region(self):
         region = self.setup_test_region()
@@ -42,7 +42,7 @@
                                               description=new_description)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_region")
+                                 rules=["identity:delete_region"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd121')
     def test_delete_region(self):
         region = self.setup_test_region()
@@ -51,7 +51,7 @@
             self.regions_client.delete_region(region['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_region")
+                                 rules=["identity:get_region"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd122')
     def test_show_region(self):
         region = self.setup_test_region()
@@ -60,7 +60,7 @@
             self.regions_client.show_region(region['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_regions")
+                                 rules=["identity:list_regions"])
     @decorators.idempotent_id('6bdaecd4-0843-4ed6-ab64-3a57ab0cd123')
     def test_list_regions(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_role_assignments_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_role_assignments_rbac.py
index 90cf255..3eabac8 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_role_assignments_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_role_assignments_rbac.py
@@ -23,7 +23,7 @@
 
     @decorators.idempotent_id('afe57adb-1b9c-43d9-84a9-f0cf4c94e416')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_role_assignments")
+                                 rules=["identity:list_role_assignments"])
     def test_list_role_assignments(self):
         with self.rbac_utils.override_role(self):
             self.role_assignments_client.list_role_assignments()
@@ -31,7 +31,7 @@
     @decorators.idempotent_id('36c7a990-857e-415c-8717-38d7200a9894')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:list_role_assignments_for_tree")
+        rules=["identity:list_role_assignments_for_tree"])
     def test_list_role_assignments_for_tree(self):
         project = self.setup_test_project()
 
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py
index 099c702..8242194 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_roles_rbac.py
@@ -34,14 +34,14 @@
         cls.user = cls.setup_test_user()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_role")
+                                 rules=["identity:create_role"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d904')
     def test_create_role(self):
         with self.rbac_utils.override_role(self):
             self.setup_test_role()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_role")
+                                 rules=["identity:update_role"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d905')
     def test_update_role(self):
         new_role_name = data_utils.rand_name(
@@ -52,7 +52,7 @@
                                           name=new_role_name)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_role")
+                                 rules=["identity:delete_role"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d906')
     def test_delete_role(self):
         role = self.setup_test_role()
@@ -61,21 +61,21 @@
             self.roles_client.delete_role(role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_role")
+                                 rules=["identity:get_role"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d907')
     def test_show_role(self):
         with self.rbac_utils.override_role(self):
             self.roles_client.show_role(self.role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_roles")
+                                 rules=["identity:list_roles"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d908')
     def test_list_roles(self):
         with self.rbac_utils.override_role(self):
             self.roles_client.list_roles()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_grant")
+                                 rules=["identity:create_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d909')
     def test_create_user_role_on_project(self):
         with self.rbac_utils.override_role(self):
@@ -90,7 +90,7 @@
                         self.role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_grant")
+                                 rules=["identity:create_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90c')
     def test_create_group_role_on_project(self):
         with self.rbac_utils.override_role(self):
@@ -105,7 +105,7 @@
                         self.role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_grant")
+                                 rules=["identity:create_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90f')
     def test_create_user_role_on_domain(self):
         with self.rbac_utils.override_role(self):
@@ -120,7 +120,7 @@
                         self.role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_grant")
+                                 rules=["identity:create_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d912')
     def test_create_group_role_on_domain(self):
         with self.rbac_utils.override_role(self):
@@ -135,7 +135,7 @@
                         self.role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:check_grant")
+                                 rules=["identity:check_grant"])
     @decorators.idempotent_id('22921b1e-1a33-4026-bff9-f236d6dd149c')
     def test_check_user_role_existence_on_project(self):
         self.roles_client.create_user_role_on_project(
@@ -156,7 +156,7 @@
 
     @decorators.idempotent_id('92f8e67d-85bf-407d-9814-edd5664abc47')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:check_grant")
+                                 rules=["identity:check_grant"])
     def test_check_user_role_existence_on_domain(self):
         self.roles_client.create_user_role_on_domain(
             self.domain['id'],
@@ -176,7 +176,7 @@
 
     @decorators.idempotent_id('8738d3d2-8c84-4423-b36c-7c59eaa08b73')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:check_grant")
+                                 rules=["identity:check_grant"])
     def test_check_role_from_group_on_project_existence(self):
         self.roles_client.create_group_role_on_project(
             self.project['id'],
@@ -196,7 +196,7 @@
 
     @decorators.idempotent_id('e7d73bd0-cf5e-4c0c-9c93-cf53e23232d6')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:check_grant")
+                                 rules=["identity:check_grant"])
     def test_check_role_from_group_on_domain_existence(self):
         self.roles_client.create_group_role_on_domain(
             self.domain['id'],
@@ -215,7 +215,7 @@
                 self.role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:revoke_grant")
+                                 rules=["identity:revoke_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90a')
     def test_delete_role_from_user_on_project(self):
         self.roles_client.create_user_role_on_project(
@@ -235,7 +235,7 @@
                 self.role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:revoke_grant")
+                                 rules=["identity:revoke_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90d')
     def test_delete_role_from_group_on_project(self):
         self.roles_client.create_group_role_on_project(
@@ -255,7 +255,7 @@
                 self.role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:revoke_grant")
+                                 rules=["identity:revoke_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d910')
     def test_delete_role_from_user_on_domain(self):
         self.roles_client.create_user_role_on_domain(
@@ -275,7 +275,7 @@
                 self.role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:revoke_grant")
+                                 rules=["identity:revoke_grant"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d913')
     def test_delete_role_from_group_on_domain(self):
         self.roles_client.create_group_role_on_domain(
@@ -295,7 +295,7 @@
                 self.role['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_grants")
+                                 rules=["identity:list_grants"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90b')
     def test_list_user_roles_on_project(self):
         with self.rbac_utils.override_role(self):
@@ -304,7 +304,7 @@
                 self.user['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_grants")
+                                 rules=["identity:list_grants"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d90e')
     def test_list_group_roles_on_project(self):
         with self.rbac_utils.override_role(self):
@@ -313,7 +313,7 @@
                 self.group['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_grants")
+                                 rules=["identity:list_grants"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d911')
     def test_list_user_roles_on_domain(self):
         with self.rbac_utils.override_role(self):
@@ -322,7 +322,7 @@
                 self.user['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_grants")
+                                 rules=["identity:list_grants"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1395-080044d0d914')
     def test_list_group_roles_on_domain(self):
         with self.rbac_utils.override_role(self):
@@ -332,7 +332,7 @@
 
     @decorators.idempotent_id('2aef3eaa-8156-4962-a01d-c9bb0e499e15')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_implied_role")
+                                 rules=["identity:create_implied_role"])
     def test_create_role_inference_rule(self):
         with self.rbac_utils.override_role(self):
             self.roles_client.create_role_inference_rule(
@@ -342,7 +342,7 @@
 
     @decorators.idempotent_id('83f997b2-55c4-4894-b1f2-e175b19d1fa5')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_implied_role")
+                                 rules=["identity:get_implied_role"])
     def test_show_role_inference_rule(self):
         self.roles_client.create_role_inference_rule(
             self.role['id'], self.implies_role['id'])
@@ -355,14 +355,14 @@
 
     @decorators.idempotent_id('f7bb39bf-0b06-468e-a8b0-60a4fb1f258d')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_implied_roles")
+                                 rules=["identity:list_implied_roles"])
     def test_list_role_inferences_rules(self):
         with self.rbac_utils.override_role(self):
             self.roles_client.list_role_inferences_rules(self.role['id'])
 
     @decorators.idempotent_id('eca2d502-09bb-45cd-9773-bce2e7bcddd1')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:check_implied_role")
+                                 rules=["identity:check_implied_role"])
     def test_check_role_inference_rule(self):
         self.roles_client.create_role_inference_rule(
             self.role['id'], self.implies_role['id'])
@@ -375,7 +375,7 @@
 
     @decorators.idempotent_id('13a5db1e-dd4a-4ca1-81ec-d5452aaaf54b')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_implied_role")
+                                 rules=["identity:delete_implied_role"])
     def test_delete_role_inference_rule(self):
         self.roles_client.create_role_inference_rule(
             self.role['id'], self.implies_role['id'])
@@ -389,7 +389,7 @@
 
     @decorators.idempotent_id('05869f2b-4dd4-425a-905e-eec9a6f06374')
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_role_inference_rules")
+                                 rules=["identity:list_role_inference_rules"])
     def test_list_all_role_inference_rules(self):
         with self.rbac_utils.override_role(self):
             self.roles_client.list_all_role_inference_rules()
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py
index 6ab17ff..41ba5ba 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_services_rbac.py
@@ -23,14 +23,14 @@
 class IdentitySericesV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_service")
+                                 rules=["identity:create_service"])
     @decorators.idempotent_id('9a4bb317-f0bb-4005-8df0-4b672885b7c8')
     def test_create_service(self):
         with self.rbac_utils.override_role(self):
             self.setup_test_service()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_service")
+                                 rules=["identity:update_service"])
     @decorators.idempotent_id('b39447d1-2cf6-40e5-a899-46f287f2ecf0')
     def test_update_service(self):
         service = self.setup_test_service()
@@ -43,7 +43,7 @@
                                                 type=service['type'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_service")
+                                 rules=["identity:delete_service"])
     @decorators.idempotent_id('177b991a-438d-4bef-8e9f-9c6cc5a1c9e8')
     def test_delete_service(self):
         service = self.setup_test_service()
@@ -52,7 +52,7 @@
             self.services_client.delete_service(service['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_service")
+                                 rules=["identity:get_service"])
     @decorators.idempotent_id('d89a9ac6-cd53-428d-84c0-5bc71f4a432d')
     def test_show_service(self):
         service = self.setup_test_service()
@@ -61,7 +61,7 @@
             self.services_client.show_service(service['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_services")
+                                 rules=["identity:list_services"])
     @decorators.idempotent_id('706e6bea-3385-4718-919c-0b5121395806')
     def test_list_services(self):
         with self.rbac_utils.override_role(self):
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
index 00d522c..c1c2934 100644
--- 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
@@ -13,7 +13,6 @@
 #    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
 
@@ -21,8 +20,6 @@
 from patrole_tempest_plugin import rbac_utils
 from patrole_tempest_plugin.tests.api.identity import rbac_base
 
-CONF = config.CONF
-
 
 class IdentityTokenV3RbacTest(rbac_base.BaseIdentityV3RbacTest):
 
@@ -46,7 +43,7 @@
     @decorators.attr(type=['negative'])
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:validate_token",
+        rules=["identity:validate_token"],
         extra_target_data={
             "target.token.user_id":
             "os_alt.auth_provider.credentials.user_id"
@@ -65,7 +62,7 @@
     @decorators.attr(type=['negative'])
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:revoke_token",
+        rules=["identity:revoke_token"],
         extra_target_data={
             "target.token.user_id":
             "os_alt.auth_provider.credentials.user_id"
@@ -84,7 +81,7 @@
     @decorators.attr(type=['negative'])
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:check_token",
+        rules=["identity:check_token"],
         extra_target_data={
             "target.token.user_id":
             "os_alt.auth_provider.credentials.user_id"
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
index 23ee768..a648774 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_tokens_rbac.py
@@ -30,7 +30,7 @@
     @decorators.idempotent_id('201e2fe5-2023-4bce-9189-78b51520a91e')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:validate_token",
+        rules=["identity:validate_token"],
         extra_target_data={
             "target.token.user_id":
             "os_primary.auth_provider.credentials.user_id"
@@ -43,7 +43,7 @@
     @decorators.idempotent_id('42a299db-fe0a-4ea0-9824-0bfd13155886')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:revoke_token",
+        rules=["identity:revoke_token"],
         extra_target_data={
             "target.token.user_id":
             "os_primary.auth_provider.credentials.user_id"
@@ -56,7 +56,7 @@
     @decorators.idempotent_id('3554d218-8cd6-4730-a1b2-0e22f9b78f45')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:check_token",
+        rules=["identity:check_token"],
         extra_target_data={
             "target.token.user_id":
             "os_primary.auth_provider.credentials.user_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 91dbb53..985b992 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
@@ -65,7 +65,7 @@
     @decorators.idempotent_id('7ab595a7-9b71-45fe-91d8-2793b0292f72')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:create_trust",
+        rules=["identity:create_trust"],
         extra_target_data={
             "trust.trustor_user_id": "os_primary.credentials.user_id"
         })
@@ -78,7 +78,7 @@
     @decorators.attr(type=['negative'])
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:create_trust",
+        rules=["identity:create_trust"],
         extra_target_data={
             "trust.trustor_user_id": "os_alt.credentials.user_id"
         })
@@ -94,7 +94,7 @@
     @decorators.idempotent_id('d9a6fd06-08f6-462c-a86c-ce009adf1230')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:delete_trust")
+        rules=["identity:delete_trust"])
     def test_delete_trust(self):
         trust = self.setup_test_trust(trustor_user_id=self.trustor_user_id,
                                       trustee_user_id=self.trustee_user_id)
@@ -105,7 +105,7 @@
     @decorators.idempotent_id('f2e32896-bf66-4f4e-89cf-e7fba0ef1f38')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:list_trusts")
+        rules=["identity:list_trusts"])
     def test_list_trusts(self):
         with self.rbac_utils.override_role(self):
             self.trusts_client.list_trusts(
@@ -114,7 +114,7 @@
     @decorators.idempotent_id('3c9ff92f-a73e-4f9b-8865-e017f38c70f5')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:list_roles_for_trust")
+        rules=["identity:list_roles_for_trust"])
     def test_list_roles_for_trust(self):
         with self.rbac_utils.override_role(self):
             self.trusts_client.list_trust_roles(self.trust['id'])
@@ -122,7 +122,7 @@
     @decorators.idempotent_id('3bb4f97b-cecd-4c7d-ad10-b88ee6c5d573')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:get_role_for_trust")
+        rules=["identity:get_role_for_trust"])
     def test_show_trust_role(self):
         with self.rbac_utils.override_role(self):
             self.trusts_client.show_trust_role(
@@ -131,7 +131,7 @@
     @decorators.idempotent_id('0184e0fb-641e-4b52-ab73-81c1ce6ca5c1')
     @rbac_rule_validation.action(
         service="keystone",
-        rule="identity:get_trust")
+        rules=["identity:get_trust"])
     def test_show_trust(self):
         with self.rbac_utils.override_role(self):
             self.trusts_client.show_trust(self.trust['id'])
diff --git a/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py b/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py
index bd97535..c633bb8 100644
--- a/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py
+++ b/patrole_tempest_plugin/tests/api/identity/v3/test_users_rbac.py
@@ -28,14 +28,14 @@
         cls.default_user_id = cls.os_primary.credentials.user_id
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:create_user")
+                                 rules=["identity:create_user"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d904')
     def test_create_user(self):
         with self.rbac_utils.override_role(self):
             self.setup_test_user()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:update_user")
+                                 rules=["identity:update_user"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d905')
     def test_update_user(self):
         user = self.setup_test_user()
@@ -48,7 +48,7 @@
                                           email=new_email)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:delete_user")
+                                 rules=["identity:delete_user"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d906')
     def test_delete_user(self):
         user = self.setup_test_user()
@@ -57,28 +57,28 @@
             self.users_client.delete_user(user['id'])
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_users")
+                                 rules=["identity:list_users"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d907')
     def test_list_users(self):
         with self.rbac_utils.override_role(self):
             self.users_client.list_users()
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:get_user")
+                                 rules=["identity:get_user"])
     @decorators.idempotent_id('0f148510-63bf-11e6-4522-080044d0d908')
     def test_show_own_user(self):
         with self.rbac_utils.override_role(self):
             self.users_client.show_user(self.default_user_id)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_groups_for_user")
+                                 rules=["identity:list_groups_for_user"])
     @decorators.idempotent_id('bd5946d4-46d2-423d-a800-a3e7aabc18b3')
     def test_list_own_user_group(self):
         with self.rbac_utils.override_role(self):
             self.users_client.list_user_groups(self.default_user_id)
 
     @rbac_rule_validation.action(service="keystone",
-                                 rule="identity:list_user_projects")
+                                 rules=["identity:list_user_projects"])
     @decorators.idempotent_id('0f148510-63bf-11e6-1564-080044d0d909')
     def test_list_own_user_projects(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/image/rbac_base.py b/patrole_tempest_plugin/tests/api/image/rbac_base.py
index 954790d..becd564 100644
--- a/patrole_tempest_plugin/tests/api/image/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/image/rbac_base.py
@@ -12,22 +12,14 @@
 #    under the License.
 
 from tempest.api.image import base as image_base
-from tempest import config
 
 from patrole_tempest_plugin import rbac_utils
 
-CONF = config.CONF
-
 
 class BaseV2ImageRbacTest(rbac_utils.RbacUtilsMixin,
                           image_base.BaseV2ImageTest):
 
     @classmethod
-    def skip_checks(cls):
-        super(BaseV2ImageRbacTest, cls).skip_checks()
-        cls.skip_rbac_checks()
-
-    @classmethod
     def setup_clients(cls):
         super(BaseV2ImageRbacTest, cls).setup_clients()
         cls.setup_rbac_utils()
diff --git a/patrole_tempest_plugin/tests/api/image/test_image_namespace_objects_rbac.py b/patrole_tempest_plugin/tests/api/image/test_image_namespace_objects_rbac.py
index 3ad5c74..24e5806 100644
--- a/patrole_tempest_plugin/tests/api/image/test_image_namespace_objects_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/test_image_namespace_objects_rbac.py
@@ -24,7 +24,7 @@
 class ImageNamespacesObjectsRbacTest(rbac_base.BaseV2ImageRbacTest):
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="add_metadef_object")
+                                 rules=["add_metadef_object"])
     @decorators.idempotent_id("772156f2-e33d-432e-8521-12385746c2f0")
     def test_create_metadef_object_in_namespace(self):
         """Create Metadef Object Namespace Test
@@ -45,7 +45,7 @@
                         namespace['namespace'], object_name)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_metadef_objects")
+                                 rules=["get_metadef_objects"])
     @decorators.idempotent_id("48b50ecb-237d-4909-be62-b6a05c47b64d")
     def test_list_metadef_objects_in_namespace(self):
         """List Metadef Object Namespace Test
@@ -59,7 +59,7 @@
                 namespace['namespace'])
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="modify_metadef_object")
+                                 rules=["modify_metadef_object"])
     @decorators.idempotent_id("cd130b1d-89fa-479c-a90e-498d895fb455")
     def test_update_metadef_object_in_namespace(self):
         """Update Metadef Object Namespace Test
@@ -83,7 +83,7 @@
                 namespace['namespace'], object_name, name=new_name)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_metadef_object")
+                                 rules=["get_metadef_object"])
     @decorators.idempotent_id("93c61420-5b80-4a0e-b6f3-4ccc6e90b865")
     def test_show_metadef_object_in_namespace(self):
         """Show Metadef Object Namespace Test
diff --git a/patrole_tempest_plugin/tests/api/image/test_image_namespace_property_rbac.py b/patrole_tempest_plugin/tests/api/image/test_image_namespace_property_rbac.py
index 75cf66d..1059450 100644
--- a/patrole_tempest_plugin/tests/api/image/test_image_namespace_property_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/test_image_namespace_property_rbac.py
@@ -29,7 +29,7 @@
         cls.resource_name = body['resource_types'][0]['name']
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="add_metadef_property")
+                                 rules=["add_metadef_property"])
     @decorators.idempotent_id('383555ca-677b-43e9-b809-acc2b5a0176c')
     def test_add_md_properties(self):
         """Create Image Metadef Namespace Property Test
@@ -45,7 +45,7 @@
                 title=property_name, name=self.resource_name)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_metadef_properties")
+                                 rules=["get_metadef_properties"])
     @decorators.idempotent_id('d5177611-c2b5-4000-bd9c-1987af9222ea')
     def test_get_md_properties(self):
         """List Image Metadef Namespace Properties Test
@@ -58,7 +58,7 @@
                 namespace=namespace['namespace'])
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_metadef_property")
+                                 rules=["get_metadef_property"])
     @decorators.idempotent_id('cfeda2af-bcab-433e-80c7-4b40c774aed5')
     def test_get_md_property(self):
         """Get Image Metadef Namespace Property Test
@@ -77,7 +77,7 @@
                 namespace['namespace'], self.resource_name)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="modify_metadef_property")
+                                 rules=["modify_metadef_property"])
     @decorators.idempotent_id('fdaf9363-4010-4f2f-8192-1b28f6b22e69')
     def test_modify_md_properties(self):
         """Modify Image Metadef Namespace Policy Test
diff --git a/patrole_tempest_plugin/tests/api/image/test_image_namespace_rbac.py b/patrole_tempest_plugin/tests/api/image/test_image_namespace_rbac.py
index 204263a..aade60f 100644
--- a/patrole_tempest_plugin/tests/api/image/test_image_namespace_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/test_image_namespace_rbac.py
@@ -24,7 +24,7 @@
 class ImageNamespacesRbacTest(rbac_base.BaseV2ImageRbacTest):
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="add_metadef_namespace")
+                                 rules=["add_metadef_namespace"])
     @decorators.idempotent_id('e0730ead-b824-4ffc-b774-9469df0e4da6')
     def test_create_metadef_namespace(self):
         """Create Image Metadef Namespace Test
@@ -43,7 +43,7 @@
             namespace_name)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_metadef_namespaces")
+                                 rules=["get_metadef_namespaces"])
     @decorators.idempotent_id('f0b12538-9047-489e-98a5-2d78f48ce789')
     def test_list_metadef_namespaces(self):
         """List Image Metadef Namespace Test
@@ -54,7 +54,7 @@
             self.namespaces_client.list_namespaces()
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="modify_metadef_namespace")
+                                 rules=["modify_metadef_namespace"])
     @decorators.idempotent_id('72c14a7e-927d-4f1a-9e1f-25475552922b')
     def test_modify_metadef_namespace(self):
         """Modify Image Metadef Namespace Test
diff --git a/patrole_tempest_plugin/tests/api/image/test_image_namespace_tags_rbac.py b/patrole_tempest_plugin/tests/api/image/test_image_namespace_tags_rbac.py
index 1a85b74..bc387ee 100644
--- a/patrole_tempest_plugin/tests/api/image/test_image_namespace_tags_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/test_image_namespace_tags_rbac.py
@@ -67,14 +67,14 @@
 
     @decorators.idempotent_id('50bedccb-9d0b-4138-8d95-31a89250edf6')
     @rbac_rule_validation.action(service="glance",
-                                 rule="add_metadef_tag")
+                                 rules=["add_metadef_tag"])
     def test_create_namespace_tag(self):
         with self.rbac_utils.override_role(self):
             self._create_namespace_tag()
 
     @decorators.idempotent_id('4acf70cc-05da-4b1e-87b2-d5e4475164e7')
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_metadef_tag")
+                                 rules=["get_metadef_tag"])
     def test_show_namespace_tag(self):
         tag_name = self._create_namespace_tag()
         with self.rbac_utils.override_role(self):
@@ -83,7 +83,7 @@
 
     @decorators.idempotent_id('01593828-3edb-461e-8abc-8fdeb3927e37')
     @rbac_rule_validation.action(service="glance",
-                                 rule="modify_metadef_tag")
+                                 rules=["modify_metadef_tag"])
     def test_update_namespace_tag(self):
         tag_name = self._create_namespace_tag()
         updated_tag_name = data_utils.rand_name(
@@ -95,14 +95,14 @@
 
     @decorators.idempotent_id('20ffaf76-ebdc-4267-a1ad-194346f5cc91')
     @rbac_rule_validation.action(service="glance",
-                                 rule="add_metadef_tags")
+                                 rules=["add_metadef_tags"])
     def test_create_namespace_tags(self):
         with self.rbac_utils.override_role(self):
             self._create_namespace_tag(multiple=True)
 
     @decorators.idempotent_id('d37c1501-e787-449d-89b3-754a942a459a')
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_metadef_tags")
+                                 rules=["get_metadef_tags"])
     def test_list_namespace_tags(self):
         with self.rbac_utils.override_role(self):
             self.namespace_tags_client.list_namespace_tags(self.namespace)
diff --git a/patrole_tempest_plugin/tests/api/image/test_image_resource_types_rbac.py b/patrole_tempest_plugin/tests/api/image/test_image_resource_types_rbac.py
index 7b03158..94f9cd4 100644
--- a/patrole_tempest_plugin/tests/api/image/test_image_resource_types_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/test_image_resource_types_rbac.py
@@ -36,7 +36,7 @@
             cls.namespaces_client.delete_namespace, cls.namespace_name)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="list_metadef_resource_types")
+                                 rules=["list_metadef_resource_types"])
     @decorators.idempotent_id('0416fc4d-cfdc-447b-88b6-d9f1dd0382f7')
     def test_list_metadef_resource_types(self):
         """List Metadef Resource Type Image Test
@@ -47,7 +47,7 @@
             self.resource_types_client.list_resource_types()
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_metadef_resource_type")
+                                 rules=["get_metadef_resource_type"])
     @decorators.idempotent_id('3698d53c-71ae-4803-a2c3-c272c054f25c')
     def test_get_metadef_resource_type(self):
         """Get Metadef Resource Type Image Test
@@ -58,8 +58,9 @@
             self.resource_types_client.list_resource_type_association(
                 self.namespace_name)
 
-    @rbac_rule_validation.action(service="glance",
-                                 rule="add_metadef_resource_type_association")
+    @rbac_rule_validation.action(
+        service="glance",
+        rules=["add_metadef_resource_type_association"])
     @decorators.idempotent_id('ef9fbc60-3e28-4164-a25c-d30d892f7939')
     def test_add_metadef_resource_type(self):
         type_name = data_utils.rand_name()
diff --git a/patrole_tempest_plugin/tests/api/image/test_images_member_rbac.py b/patrole_tempest_plugin/tests/api/image/test_images_member_rbac.py
index 952c41f..656703a 100644
--- a/patrole_tempest_plugin/tests/api/image/test_images_member_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/test_images_member_rbac.py
@@ -36,7 +36,7 @@
         cls.image_member_client = cls.os_primary.image_member_client_v2
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="add_member")
+                                 rules=["add_member"])
     @decorators.idempotent_id('b1b85ace-6484-11e6-881e-080027d0d606')
     def test_add_image_member(self):
 
@@ -51,7 +51,7 @@
                 image_id, member=self.alt_tenant_id)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="delete_member")
+                                 rules=["delete_member"])
     @decorators.idempotent_id('ba075234-6484-11e6-881e-080027d0d606')
     def test_delete_image_member(self):
 
@@ -68,8 +68,8 @@
                                                          self.alt_tenant_id)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_member",
-                                 expected_error_code=404)
+                                 rules=["get_member"],
+                                 expected_error_codes=[404])
     @decorators.idempotent_id('c01fd308-6484-11e6-881e-080027d0d606')
     def test_show_image_member(self):
 
@@ -88,7 +88,7 @@
                                                        self.alt_tenant_id)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="modify_member")
+                                 rules=["modify_member"])
     @decorators.idempotent_id('ca448bb2-6484-11e6-881e-080027d0d606')
     def test_update_image_member(self):
 
@@ -110,7 +110,7 @@
                 status='pending')
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_members")
+                                 rules=["get_members"])
     @decorators.idempotent_id('d0a2dc20-6484-11e6-881e-080027d0d606')
     def test_list_image_members(self):
 
diff --git a/patrole_tempest_plugin/tests/api/image/test_images_rbac.py b/patrole_tempest_plugin/tests/api/image/test_images_rbac.py
index e97e803..aa96112 100644
--- a/patrole_tempest_plugin/tests/api/image/test_images_rbac.py
+++ b/patrole_tempest_plugin/tests/api/image/test_images_rbac.py
@@ -42,7 +42,7 @@
         return self.image_client.store_image_file(image_id, image_file)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="add_image")
+                                 rules=["add_image"])
     @decorators.idempotent_id('0f148510-63bf-11e6-b348-080027d0d606')
     def test_create_image(self):
 
@@ -54,7 +54,7 @@
             self._create_image()
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="upload_image")
+                                 rules=["upload_image"])
     @decorators.idempotent_id('fdc0c7e2-ad58-4c5a-ba9d-1f6046a5b656')
     def test_upload_image(self):
 
@@ -69,7 +69,7 @@
 
     @decorators.idempotent_id('f0c268f3-cb51-49aa-9bd5-d30cf647322f')
     @rbac_rule_validation.action(service="glance",
-                                 rule="download_image")
+                                 rules=["download_image"])
     def test_download_image(self):
 
         """Download Image Test
@@ -83,7 +83,7 @@
             self.image_client.show_image_file(image['id'])
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="delete_image")
+                                 rules=["delete_image"])
     @decorators.idempotent_id('3b5c341e-645b-11e6-ac4f-080027d0d606')
     def test_delete_image(self):
 
@@ -98,7 +98,7 @@
         self.image_client.wait_for_resource_deletion(image['id'])
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_image")
+                                 rules=["get_image"])
     @decorators.idempotent_id('3085c7c6-645b-11e6-ac4f-080027d0d606')
     def test_show_image(self):
 
@@ -112,7 +112,7 @@
             self.image_client.show_image(image['id'])
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="get_images")
+                                 rules=["get_images"])
     @decorators.idempotent_id('bf1a4e94-645b-11e6-ac4f-080027d0d606')
     def test_list_images(self):
 
@@ -124,7 +124,7 @@
             self.image_client.list_images()['images']
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="modify_image")
+                                 rules=["modify_image"])
     @decorators.idempotent_id('32ecf48c-645e-11e6-ac4f-080027d0d606')
     def test_update_image(self):
 
@@ -142,7 +142,7 @@
 
     @decorators.idempotent_id('244050d9-1b9a-446a-b3c5-f26f3ba8eb75')
     @rbac_rule_validation.action(service="glance",
-                                 rule="modify_image")
+                                 rules=["modify_image"])
     def test_create_image_tag(self):
 
         """Create image tag
@@ -158,7 +158,7 @@
 
     @decorators.idempotent_id('c4a0bf9c-b78b-48c6-a31f-72c95f943c6e')
     @rbac_rule_validation.action(service="glance",
-                                 rule="modify_image")
+                                 rules=["modify_image"])
     def test_delete_image_tag(self):
 
         """Delete image tag
@@ -173,7 +173,7 @@
             self.image_client.delete_image_tag(image['id'], tag_name)
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="publicize_image")
+                                 rules=["publicize_image"])
     @decorators.idempotent_id('0ea4809c-6461-11e6-ac4f-080027d0d606')
     def test_publicize_image(self):
 
@@ -186,7 +186,7 @@
 
     @decorators.idempotent_id('0f2d8427-134a-4d3c-a102-5fcdf5443d09')
     @rbac_rule_validation.action(service="glance",
-                                 rule="communitize_image")
+                                 rules=["communitize_image"])
     def test_communitize_image(self):
 
         """Communitize Image Test
@@ -197,7 +197,7 @@
             self._create_image(visibility='community')
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="deactivate")
+                                 rules=["deactivate"])
     @decorators.idempotent_id('b488458c-65df-11e6-9947-080027824017')
     def test_deactivate_image(self):
 
@@ -212,7 +212,7 @@
             self.image_client.deactivate_image(image['id'])
 
     @rbac_rule_validation.action(service="glance",
-                                 rule="reactivate")
+                                 rules=["reactivate"])
     @decorators.idempotent_id('d3fa28b8-65df-11e6-9947-080027824017')
     def test_reactivate_image(self):
 
diff --git a/patrole_tempest_plugin/tests/api/network/README.rst b/patrole_tempest_plugin/tests/api/network/README.rst
new file mode 100644
index 0000000..20d6196
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/README.rst
@@ -0,0 +1,49 @@
+.. _network-rbac-tests:
+
+Network RBAC Tests
+==================
+
+What are these tests?
+---------------------
+
+These tests are RBAC tests for Neutron and its associated plugins. They are
+broken up into the following categories:
+
+* :ref:`neutron-rbac-tests`
+* :ref:`neutron-plugin-rbac-tests`
+
+.. _neutron-rbac-tests:
+
+Neutron tests
+^^^^^^^^^^^^^
+
+Neutron RBAC tests inherit from the base class ``BaseNetworkRbacTest``. They
+test many of the Neutron policies found in the service's `policy.json file`_.
+These tests are gated in many `Zuul jobs`_ (master, n-1, n-2) against many
+roles (member, admin).
+
+.. _neutron-plugin-rbac-tests:
+
+Neutron plugin tests
+^^^^^^^^^^^^^^^^^^^^
+
+The Neutron RBAC plugin tests focus on testing RBAC for various Neutron
+extensions and plugins, or, stated differently:
+
+* tests that rely on `neutron-tempest-plugin`_
+* external Neutron plugins
+
+These tests inherit from the base class ``BaseNetworkPluginRbacTest``. If an
+extension or plugin is not enabled in the cloud, the corresponding tests are
+gracefully skipped.
+
+.. note::
+
+  Patrole should import as few dependencies from ``neutron_tempest_plugin`` as
+  possible (such as ``neutron_tempest_plugin.api.clients`` for the service
+  clients) because the module is not a `stable interface`_.
+
+.. _policy.json file: https://github.com/openstack/neutron/blob/master/etc/policy.json
+.. _Zuul jobs: https://github.com/openstack/patrole/blob/master/.zuul.yaml
+.. _neutron-tempest-plugin: https://github.com/openstack/neutron-tempest-plugin
+.. _stable interface: https://github.com/openstack/neutron-tempest-plugin/tree/master/neutron_tempest_plugin#warning
diff --git a/patrole_tempest_plugin/tests/api/network/rbac_base.py b/patrole_tempest_plugin/tests/api/network/rbac_base.py
index 3065c13..6102347 100644
--- a/patrole_tempest_plugin/tests/api/network/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/network/rbac_base.py
@@ -14,22 +14,61 @@
 #    under the License.
 
 from tempest.api.network import base as network_base
-from tempest import config
 
 from patrole_tempest_plugin import rbac_utils
 
-CONF = config.CONF
-
 
 class BaseNetworkRbacTest(rbac_utils.RbacUtilsMixin,
                           network_base.BaseNetworkTest):
 
     @classmethod
-    def skip_checks(cls):
-        super(BaseNetworkRbacTest, cls).skip_checks()
-        cls.skip_rbac_checks()
-
-    @classmethod
     def setup_clients(cls):
         super(BaseNetworkRbacTest, cls).setup_clients()
         cls.setup_rbac_utils()
+
+
+class BaseNetworkPluginRbacTest(BaseNetworkRbacTest):
+    """Base class to be used with tests that require neutron-tempest-plugin.
+    """
+
+    @classmethod
+    def get_auth_providers(cls):
+        """Register auth_provider from neutron-tempest-plugin.
+        """
+        providers = super(BaseNetworkPluginRbacTest, cls).get_auth_providers()
+        if cls.is_neutron_tempest_plugin_avaliable():
+            providers.append(cls.ntp_client.auth_provider)
+        return providers
+
+    @classmethod
+    def skip_checks(cls):
+        super(BaseNetworkPluginRbacTest, cls).skip_checks()
+
+        if not cls.is_neutron_tempest_plugin_avaliable():
+            msg = ("neutron-tempest-plugin not installed.")
+            raise cls.skipException(msg)
+
+    @classmethod
+    def is_neutron_tempest_plugin_avaliable(cls):
+        try:
+            import neutron_tempest_plugin  # noqa
+            return True
+        except ImportError:
+            return False
+
+    @classmethod
+    def get_client_manager(cls, credential_type=None, roles=None,
+                           force_new=None):
+        manager = super(BaseNetworkPluginRbacTest, cls).get_client_manager(
+            credential_type=credential_type,
+            roles=roles,
+            force_new=force_new
+        )
+
+        # Import neutron-tempest-plugin clients
+        if cls.is_neutron_tempest_plugin_avaliable():
+            from neutron_tempest_plugin.api import clients
+            neutron_tempest_manager = clients.Manager(manager.credentials)
+            cls.ntp_client = neutron_tempest_manager.network_client
+
+        return manager
diff --git a/patrole_tempest_plugin/tests/api/network/test_address_scope_rbac.py b/patrole_tempest_plugin/tests/api/network/test_address_scope_rbac.py
new file mode 100644
index 0000000..893942e
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_address_scope_rbac.py
@@ -0,0 +1,139 @@
+# Copyright 2018 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.common import utils
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+
+class AddressScopePluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(AddressScopePluginRbacTest, cls).skip_checks()
+        if not utils.is_extension_enabled('address-scope', 'network'):
+            msg = "address-scope extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(AddressScopePluginRbacTest, cls).resource_setup()
+        cls.network = cls.create_network()
+
+    def _create_address_scope(self, name=None, **kwargs):
+        name = name or data_utils.rand_name(self.__class__.__name__)
+        address_scope = self.ntp_client.create_address_scope(name=name,
+                                                             ip_version=6,
+                                                             **kwargs)
+        address_scope = address_scope['address_scope']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ntp_client.delete_address_scope,
+                        address_scope['id'])
+        return address_scope
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_address_scope"],
+                                 expected_error_codes=[403])
+    @decorators.idempotent_id('8cb2d6b5-23c2-4648-997b-7a6ae55be3ad')
+    def test_create_address_scope(self):
+
+        """Create Address Scope
+
+        RBAC test for the neutron create_address_scope policy
+        """
+        with self.rbac_utils.override_role(self):
+            self._create_address_scope()
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_address_scope",
+                                        "create_address_scope:shared"],
+                                 expected_error_codes=[403, 403])
+    @decorators.idempotent_id('0c3f55c0-6ebe-4251-afca-62c5cb4632ca')
+    def test_create_address_scope_shared(self):
+
+        """Create Shared Address Scope
+
+        RBAC test for the neutron create_address_scope:shared policy
+        """
+        with self.rbac_utils.override_role(self):
+            self._create_address_scope(shared=True)
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_address_scope"],
+                                 expected_error_codes=[404])
+    @decorators.idempotent_id('a53f741b-46f6-412f-936f-ac920d449da8')
+    def test_get_address_scope(self):
+
+        """Get Address Scope
+
+        RBAC test for the neutron get_address_scope policy
+        """
+        address_scope = self._create_address_scope()
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.show_address_scope(address_scope['id'])
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_address_scope",
+                                        "update_address_scope"],
+                                 expected_error_codes=[404, 403])
+    @decorators.idempotent_id('3ce4d606-e067-4ef5-840f-96c680226e73')
+    def test_update_address_scope(self):
+
+        """Update Address Scope
+
+        RBAC test for neutron update_address_scope policy
+        """
+        address_scope = self._create_address_scope()
+        name = data_utils.rand_name(self.__class__.__name__)
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.update_address_scope(address_scope['id'],
+                                                 name=name)
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_address_scope",
+                                        "update_address_scope",
+                                        "update_address_scope:shared"],
+                                 expected_error_codes=[404, 403, 403])
+    @decorators.idempotent_id('77d3a9d2-721a-4d9f-9654-6b52f113df85')
+    def test_update_address_scope_shared(self):
+
+        """Update Shared Address Scope
+
+        RBAC test for neutron update_address_scope:shared policy
+        """
+        address_scope = self._create_address_scope(shared=True)
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.update_address_scope(address_scope['id'],
+                                                 shared=True)
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_address_scope",
+                                        "delete_address_scope"],
+                                 expected_error_codes=[404, 403])
+    @decorators.idempotent_id('277d8e47-e498-4452-b969-a91f747296ba')
+    def test_delete_address_scope(self):
+
+        """Delete Address Scope
+
+        RBAC test for neutron delete_address_scope policy
+        """
+        address_scope = self._create_address_scope()
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.delete_address_scope(address_scope['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_agents_rbac.py b/patrole_tempest_plugin/tests/api/network/test_agents_rbac.py
index 2756a10..c778d9c 100644
--- a/patrole_tempest_plugin/tests/api/network/test_agents_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_agents_rbac.py
@@ -14,6 +14,7 @@
 #    under the License.
 
 from tempest.common import utils
+from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
 
@@ -38,8 +39,8 @@
 
     @decorators.idempotent_id('f88e38e0-ab52-4b97-8ffa-48a27f9d199b')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_agent",
-                                 expected_error_code=404)
+                                 rules=["get_agent"],
+                                 expected_error_codes=[404])
     def test_show_agent(self):
         """Show agent test.
 
@@ -99,7 +100,7 @@
 
     @decorators.idempotent_id('5d2bbdbc-40a5-43d2-828a-84dc93fcc453')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_l3-routers")
+                                 rules=["get_l3-routers"])
     def test_list_routers_on_l3_agent(self):
         """List routers on L3 agent test.
 
@@ -110,7 +111,7 @@
 
     @decorators.idempotent_id('466b2a10-8747-4c09-855a-bd90a1c86ce7')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_l3-router")
+                                 rules=["create_l3-router"])
     def test_create_router_on_l3_agent(self):
         """Create router on L3 agent test.
 
@@ -126,7 +127,7 @@
 
     @decorators.idempotent_id('8138cfc9-3e48-4a34-adf6-894077aa1be4')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="delete_l3-router")
+                                 rules=["delete_l3-router"])
     def test_delete_router_from_l3_agent(self):
         """Delete router from L3 agent test.
 
@@ -192,7 +193,7 @@
 
     @decorators.idempotent_id('dc84087b-4c2a-4878-8ed0-40370e19da17')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_dhcp-networks")
+                                 rules=["get_dhcp-networks"])
     def test_list_networks_hosted_by_one_dhcp_agent(self):
         """List networks hosted by one DHCP agent test.
 
@@ -204,7 +205,7 @@
 
     @decorators.idempotent_id('14e014ac-f355-46d3-b6d8-98f2c9ec1610')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_dhcp-network")
+                                 rules=["create_dhcp-network"])
     def test_add_dhcp_agent_to_network(self):
         """Add DHCP agent to network test.
 
@@ -220,7 +221,7 @@
 
     @decorators.idempotent_id('937a4302-4b49-407d-9980-5843d7badc38')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="delete_dhcp-network")
+                                 rules=["delete_dhcp-network"])
     def test_delete_network_from_dhcp_agent(self):
         """Delete DHCP agent from network test.
 
@@ -235,3 +236,32 @@
         with self.rbac_utils.override_role(self):
             self.agents_client.delete_network_from_dhcp_agent(
                 self.agent['id'], network_id=network_id)
+
+
+class L3AgentsPluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(L3AgentsPluginRbacTest, cls).skip_checks()
+        if not utils.is_extension_enabled('l3_agent_scheduler', 'network'):
+            msg = "l3_agent_scheduler extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(L3AgentsPluginRbacTest, cls).resource_setup()
+        name = data_utils.rand_name(cls.__name__ + '-Router')
+        cls.router = cls.ntp_client.create_router(name)['router']
+
+    @decorators.idempotent_id('5d2bbdbc-40a5-43d2-828a-84dc93bcd321')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_l3-agents"])
+    def test_list_l3_agents_on_router(self):
+        """List L3 agents on router test.
+
+        RBAC test for the neutron get_l3-agents policy
+        """
+        with self.rbac_utils.override_role(self):
+            # NOTE: It is not empty list since it's a special case where
+            # policy.enforce is called from the controller.
+            self.ntp_client.list_l3_agents_hosting_router(self.router['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_auto_allocated_topology_rbac.py b/patrole_tempest_plugin/tests/api/network/test_auto_allocated_topology_rbac.py
new file mode 100644
index 0000000..bcf62d7
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_auto_allocated_topology_rbac.py
@@ -0,0 +1,44 @@
+# Copyright 2018 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.common import utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+
+class AutoAllocationTopologyPluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(AutoAllocationTopologyPluginRbacTest, cls).skip_checks()
+        if not utils.is_extension_enabled('auto-allocated-topology',
+                                          'network'):
+            msg = "auto-allocated-topology extension not enabled."
+            raise cls.skipException(msg)
+
+    @decorators.idempotent_id('299CB831-F6B2-49CA-882B-E9A8E36945A2')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_auto_allocated_topology"],
+                                 expected_error_codes=[404])
+    def test_show_auto_allocated_topology(self):
+        """Show auto_allocated_topology.
+
+        RBAC test for the neutron "get_auto_allocated_topology" policy
+        """
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.get_auto_allocated_topology(
+                tenant_id=self.os_primary.credentials.tenant_id)
diff --git a/patrole_tempest_plugin/tests/api/network/test_dscp_marking_rule_rbac.py b/patrole_tempest_plugin/tests/api/network/test_dscp_marking_rule_rbac.py
new file mode 100644
index 0000000..b9f8365
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_dscp_marking_rule_rbac.py
@@ -0,0 +1,106 @@
+# Copyright 2018 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.common import utils
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+
+class DscpMarkingRulePluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(DscpMarkingRulePluginRbacTest, cls).skip_checks()
+        if not utils.is_extension_enabled('qos', 'network'):
+            msg = "qos extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(DscpMarkingRulePluginRbacTest, cls).resource_setup()
+        name = data_utils.rand_name(cls.__class__.__name__ + '-qos')
+        cls.policy_id = cls.ntp_client.create_qos_policy(
+            name=name)["policy"]["id"]
+        cls.addClassResourceCleanup(
+            cls.ntp_client.delete_qos_policy, cls.policy_id)
+
+    def create_policy_dscp_marking_rule(cls):
+        rule = cls.ntp_client.create_dscp_marking_rule(cls.policy_id, 10)
+        rule_id = rule['dscp_marking_rule']['id']
+        cls.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            cls.ntp_client.delete_dscp_marking_rule, cls.policy_id, rule_id)
+        return rule_id
+
+    @decorators.idempotent_id('2717AB75-E4CF-4CA4-AF04-5BEC0C808AA5')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_policy_dscp_marking_rule"])
+    def test_create_policy_dscp_marking_rule(self):
+        """Create policy_dscp_marking_rule.
+
+        RBAC test for the neutron "create_policy_dscp_marking_rule" policy
+        """
+
+        with self.rbac_utils.override_role(self):
+            self.create_policy_dscp_marking_rule()
+
+    @decorators.idempotent_id('3D68F50E-B948-4B25-8A72-F6F4890BBC6F')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_policy_dscp_marking_rule"],
+                                 expected_error_codes=[404])
+    def test_show_policy_dscp_marking_rule(self):
+        """Show policy_dscp_marking_rule.
+
+        RBAC test for the neutron "get_policy_dscp_marking_rule" policy
+        """
+        rule_id = self.create_policy_dscp_marking_rule()
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.show_dscp_marking_rule(self.policy_id, rule_id)
+
+    @decorators.idempotent_id('33830794-8731-45C3-BC97-17718555DD7C')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_policy_dscp_marking_rule",
+                                        "update_policy_dscp_marking_rule"],
+                                 expected_error_codes=[404, 403])
+    def test_update_policy_dscp_marking_rule(self):
+        """Update policy_dscp_marking_rule.
+
+        RBAC test for the neutron "update_policy_dscp_marking_rule" policy
+        """
+        rule_id = self.create_policy_dscp_marking_rule()
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.update_dscp_marking_rule(
+                self.policy_id, rule_id, dscp_mark=16)
+
+    @decorators.idempotent_id('7BF564DD-3648-4D12-8A8B-6D5E576D1843')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_policy_dscp_marking_rule",
+                                        "delete_policy_dscp_marking_rule"],
+                                 expected_error_codes=[404, 403])
+    def test_delete_policy_dscp_marking_rule(self):
+        """Delete policy_dscp_marking_rule.
+
+        RBAC test for the neutron "delete_policy_dscp_marking_rule" policy
+        """
+        rule_id = self.create_policy_dscp_marking_rule()
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.delete_dscp_marking_rule(self.policy_id, rule_id)
diff --git a/patrole_tempest_plugin/tests/api/network/test_flavors_rbac.py b/patrole_tempest_plugin/tests/api/network/test_flavors_rbac.py
new file mode 100644
index 0000000..f8ef0bb
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_flavors_rbac.py
@@ -0,0 +1,187 @@
+# Copyright 2018 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+
+class FlavorsPluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(FlavorsPluginRbacTest, cls).resource_setup()
+        providers = cls.ntp_client.list_service_providers()
+        if not providers["service_providers"]:
+            raise cls.skipException("No service_providers available.")
+        cls.service_type = providers["service_providers"][0]["service_type"]
+
+    @decorators.idempotent_id('2632a61b-831e-4da5-82c8-a5f7d448589b')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_flavor"])
+    def test_create_flavor(self):
+        """Create flavor.
+
+        RBAC test for the neutron "create_flavor" policy
+        """
+        with self.rbac_utils.override_role(self):
+            flavor = self.ntp_client.create_flavor(
+                service_type=self.service_type)
+
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.ntp_client.delete_flavor, flavor["flavor"]["id"])
+
+    @decorators.idempotent_id('9c53164c-117d-4b44-a5cb-96f08386513f')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_flavor",
+                                        "update_flavor"],
+                                 expected_error_codes=[404, 403])
+    def test_update_flavor(self):
+        """Update flavor.
+
+        RBAC test for the neutron "update_flavor" policy
+        """
+        flavor = self.ntp_client.create_flavor(service_type=self.service_type)
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.ntp_client.delete_flavor, flavor["flavor"]["id"])
+
+        name = data_utils.rand_name(self.__class__.__name__ + '-Flavor')
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.update_flavor(flavor["flavor"]["id"], name=name)
+
+    @decorators.idempotent_id('1de15f9e-5080-4259-ab41-e230bb312de8')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_flavor",
+                                        "delete_flavor"],
+                                 expected_error_codes=[404, 403])
+    def test_delete_flavor(self):
+        """Delete flavor.
+
+        RBAC test for the neutron "delete_flavor" policy
+        """
+        flavor = self.ntp_client.create_flavor(service_type=self.service_type)
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.ntp_client.delete_flavor, flavor["flavor"]["id"])
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.delete_flavor(flavor["flavor"]["id"])
+
+    @decorators.idempotent_id('c2baf35f-e6c1-4833-9114-aadd9b1f6aaa')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_flavor"],
+                                 expected_error_codes=[404])
+    def test_show_flavor(self):
+        """Show flavor.
+
+        RBAC test for the neutron "get_flavor" policy
+        """
+        flavor = self.ntp_client.create_flavor(service_type=self.service_type)
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.ntp_client.delete_flavor, flavor["flavor"]["id"])
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.show_flavor(flavor["flavor"]["id"])
+
+    @decorators.idempotent_id('ab10bd5d-987e-4255-966f-947670ffd0fa')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_flavors"])
+    def test_list_flavors(self):
+        """List flavors.
+
+        RBAC test for the neutron "get_flavors" policy
+        """
+        flavor = self.ntp_client.create_flavor(service_type=self.service_type)
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.ntp_client.delete_flavor, flavor["flavor"]["id"])
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.list_flavors()
+
+
+class FlavorsServiceProfilePluginRbacTest(base.BaseNetworkPluginRbacTest):
+    @classmethod
+    def resource_setup(cls):
+        super(FlavorsServiceProfilePluginRbacTest, cls).resource_setup()
+        providers = cls.ntp_client.list_service_providers()
+        if not providers["service_providers"]:
+            raise cls.skipException("No service_providers available.")
+        cls.service_type = providers["service_providers"][0]["service_type"]
+
+        cls.flavor_id = cls.create_flavor()
+        cls.service_profile_id = cls.create_service_profile()
+
+    @classmethod
+    def create_flavor(cls):
+        flavor = cls.ntp_client.create_flavor(service_type=cls.service_type)
+        flavor_id = flavor["flavor"]["id"]
+        cls.addClassResourceCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            cls.ntp_client.delete_flavor, flavor_id)
+        return flavor_id
+
+    @classmethod
+    def create_service_profile(cls):
+        service_profile = cls.ntp_client.create_service_profile(
+            metainfo=json.dumps({'foo': 'bar'}))
+        service_profile_id = service_profile["service_profile"]["id"]
+        cls.addClassResourceCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            cls.ntp_client.delete_service_profile, service_profile_id)
+        return service_profile_id
+
+    def create_flavor_service_profile(self, flavor_id, service_profile_id):
+        self.ntp_client.create_flavor_service_profile(
+            flavor_id, service_profile_id)
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.ntp_client.delete_flavor_service_profile,
+            flavor_id, service_profile_id)
+
+    @decorators.idempotent_id('aa84b4c5-0dd6-4c34-aa81-3a76507f9b81')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_flavor_service_profile"])
+    def test_create_flavor_service_profile(self):
+        """Create flavor_service_profile.
+
+        RBAC test for the neutron "create_flavor_service_profile" policy
+        """
+        with self.rbac_utils.override_role(self):
+            self.create_flavor_service_profile(self.flavor_id,
+                                               self.service_profile_id)
+
+    @decorators.idempotent_id('3b680d9e-946a-4670-ab7f-0e4576675833')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["delete_flavor_service_profile"])
+    def test_delete_flavor_service_profile(self):
+        """Delete flavor_service_profile.
+
+        RBAC test for the neutron "delete_flavor_service_profile" policy
+        """
+        self.create_flavor_service_profile(self.flavor_id,
+                                           self.service_profile_id)
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.delete_flavor_service_profile(
+                self.flavor_id, self.service_profile_id)
diff --git a/patrole_tempest_plugin/tests/api/network/test_floating_ips_rbac.py b/patrole_tempest_plugin/tests/api/network/test_floating_ips_rbac.py
index ed52c34..336490a 100644
--- a/patrole_tempest_plugin/tests/api/network/test_floating_ips_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_floating_ips_rbac.py
@@ -63,7 +63,7 @@
         return floating_ip
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_floatingip")
+                                 rules=["create_floatingip"])
     @decorators.idempotent_id('f8f7474c-b8a5-4174-af84-73097d6ced38')
     def test_create_floating_ip(self):
         """Create floating IP.
@@ -76,8 +76,7 @@
     @rbac_rule_validation.action(
         service="neutron",
         rules=["create_floatingip",
-               "create_floatingip:floating_ip_address"],
-        expected_error_codes=[403, 403])
+               "create_floatingip:floating_ip_address"])
     @decorators.idempotent_id('a8bb826a-403d-4130-a55d-120a0a660806')
     def test_create_floating_ip_floatingip_address(self):
         """Create floating IP with address.
@@ -105,8 +104,8 @@
                 floating_ip['id'], port_id=None)
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_floatingip",
-                                 expected_error_code=404)
+                                 rules=["get_floatingip"],
+                                 expected_error_codes=[404])
     @decorators.idempotent_id('f8846fd0-c976-48fe-a148-105303931b32')
     def test_show_floating_ip(self):
         """Show floating IP.
diff --git a/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py b/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py
index adab1e6..bf49053 100644
--- a/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_metering_label_rules_rbac.py
@@ -63,7 +63,7 @@
         return label_rule
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_metering_label_rule")
+                                 rules=["create_metering_label_rule"])
     @decorators.idempotent_id('81e81776-9d41-4d5e-b5c4-59d5c54a31ad')
     def test_create_metering_label_rule(self):
         """Create metering label rule.
@@ -74,8 +74,8 @@
             self._create_metering_label_rule(self.label)
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_metering_label_rule",
-                                 expected_error_code=404)
+                                 rules=["get_metering_label_rule"],
+                                 expected_error_codes=[404])
     @decorators.idempotent_id('e21b40c3-d44d-412f-84ea-836ca8603bcb')
     def test_show_metering_label_rule(self):
         """Show metering label rule.
diff --git a/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py b/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py
index 0231868..ed6e316 100644
--- a/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_metering_labels_rbac.py
@@ -47,7 +47,7 @@
         return label
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_metering_label")
+                                 rules=["create_metering_label"])
     @decorators.idempotent_id('e8cfc8b8-c159-48f0-93b3-591625a02f8b')
     def test_create_metering_label(self):
         """Create metering label.
@@ -58,8 +58,8 @@
             self._create_metering_label()
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_metering_label",
-                                 expected_error_code=404)
+                                 rules=["get_metering_label"],
+                                 expected_error_codes=[404])
     @decorators.idempotent_id('c57f6636-c702-4755-8eac-5e73bc1f7d14')
     def test_show_metering_label(self):
         """Show metering label.
diff --git a/patrole_tempest_plugin/tests/api/network/test_network_segments_rbac.py b/patrole_tempest_plugin/tests/api/network/test_network_segments_rbac.py
index 0097c7b..c985111 100644
--- a/patrole_tempest_plugin/tests/api/network/test_network_segments_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_network_segments_rbac.py
@@ -67,8 +67,7 @@
 
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_network",
-                                        "create_network:segments"],
-                                 expected_error_codes=[403, 403])
+                                        "create_network:segments"])
     @decorators.idempotent_id('9e1d0c3d-92e3-40e3-855e-bfbb72ea6e0b')
     def test_create_network_segments(self):
         """Create network with segments.
diff --git a/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py b/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
index 1a0e186..96ba378 100644
--- a/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_networks_rbac.py
@@ -19,6 +19,7 @@
 from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
+from tempest.lib import exceptions as lib_exc
 
 from patrole_tempest_plugin import rbac_exceptions
 from patrole_tempest_plugin import rbac_rule_validation
@@ -66,6 +67,9 @@
                         shared_network=None,
                         router_external=None,
                         router_private=None,
+                        provider_network_type=None,
+                        provider_physical_network=None,
+                        provider_segmentation_id=None,
                         segments=None,
                         **kwargs):
         if not net_id:
@@ -73,13 +77,19 @@
 
         if admin is not None:
             kwargs['admin_state_up'] = admin
-        elif shared_network is not None:
+        if shared_network is not None:
             kwargs['shared'] = shared_network
-        elif router_external is not None:
+        if router_external is not None:
             kwargs['router:external'] = router_external
-        elif router_private is not None:
+        if router_private is not None:
             kwargs['router:private'] = router_private
-        elif segments is not None:
+        if provider_network_type is not None:
+            kwargs['provider:network_type'] = provider_network_type
+        if provider_physical_network is not None:
+            kwargs['provider:physical_network'] = provider_physical_network
+        if provider_segmentation_id is not None:
+            kwargs['provider:segmentation_id'] = provider_segmentation_id
+        if segments is not None:
             kwargs['segments'] = segments
 
         updated_network = self.networks_client.update_network(
@@ -87,7 +97,7 @@
         return updated_network
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_network")
+                                 rules=["create_network"])
     @decorators.idempotent_id('95b9baab-1ece-4e2b-89c8-8d671d974e54')
     def test_create_network(self):
 
@@ -100,8 +110,25 @@
 
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_network",
-                                        "create_network:shared"],
-                                 expected_error_codes=[403, 403])
+                                        "create_network:is_default"])
+    @decorators.idempotent_id('28602661-5ac7-407e-b739-e393f619f5e3')
+    def test_create_network_is_default(self):
+
+        """Create Is Default Network Test
+
+        RBAC test for the neutron create_network:is_default policy
+        """
+        try:
+            with self.rbac_utils.override_role(self):
+                self._create_network(is_default=True)
+        except lib_exc.Conflict as exc:
+            # A default network might already exist
+            self.assertIn('A default external network already exists',
+                          str(exc))
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_network",
+                                        "create_network:shared"])
     @decorators.idempotent_id('ccabf2a9-28c8-44b2-80e6-ffd65d43eef2')
     def test_create_network_shared(self):
 
@@ -115,8 +142,7 @@
     @utils.requires_ext(extension='external-net', service='network')
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_network",
-                                        "create_network:router:external"],
-                                 expected_error_codes=[403, 403])
+                                        "create_network:router:external"])
     @decorators.idempotent_id('51adf2a7-739c-41e0-8857-3b4c460cbd24')
     def test_create_network_router_external(self):
 
@@ -131,8 +157,30 @@
     @rbac_rule_validation.action(
         service="neutron",
         rules=["create_network",
-               "create_network:provider:network_type"],
-        expected_error_codes=[403, 403])
+               "create_network:provider:physical_network"])
+    @decorators.idempotent_id('76783fed-9ff3-4499-a0d1-82d99eec364e')
+    def test_create_network_provider_physical_network(self):
+
+        """Create Network Physical Network Provider Test
+
+        RBAC test for neutron create_network:provider:physical_network policy
+        """
+        try:
+            with self.rbac_utils.override_role(self):
+                self._create_network(provider_physical_network='provider',
+                                     provider_network_type='flat')
+        except lib_exc.BadRequest as exc:
+            # There probably won't be a physical network called 'provider', but
+            # we aren't testing state of the network
+            self.assertIn("Invalid input for operation: physical_network " +
+                          "'provider' unknown for flat provider network.",
+                          str(exc))
+
+    @utils.requires_ext(extension='provider', service='network')
+    @rbac_rule_validation.action(
+        service="neutron",
+        rules=["create_network",
+               "create_network:provider:network_type"])
     @decorators.idempotent_id('3c42f7b8-b80c-44ef-8fa4-69ec4b1836bc')
     def test_create_network_provider_network_type(self):
 
@@ -147,8 +195,7 @@
     @rbac_rule_validation.action(
         service="neutron",
         rules=["create_network",
-               "create_network:provider:segmentation_id"],
-        expected_error_codes=[403, 403])
+               "create_network:provider:segmentation_id"])
     @decorators.idempotent_id('b9decb7b-68ef-4504-b99b-41edbf7d2af5')
     def test_create_network_provider_segmentation_id(self):
 
@@ -209,9 +256,84 @@
         with self.rbac_utils.override_role(self):
             self._update_network(net_id=network['id'], router_external=True)
 
+    @utils.requires_ext(extension='provider', service='network')
+    @rbac_rule_validation.action(
+        service="neutron",
+        rules=["get_network",
+               "update_network",
+               "update_network:provider:network_type"],
+        expected_error_codes=[404, 403, 403])
+    @decorators.idempotent_id('d064ef96-662b-47b6-94b7-9106dcd7ba8c')
+    def test_update_network_provider_network_type(self):
+
+        """Update Network Provider Network Type Test
+
+        RBAC test for neutron update_network:provider:network_type policy
+        """
+        try:
+            with self.rbac_utils.override_role(self):
+                self._update_network(self.network['id'],
+                                     provider_network_type='vxlan')
+        except lib_exc.BadRequest as exc:
+            # Per the api documentation, most plugins don't support updating
+            # provider attributes
+            self.assertIn(
+                "Plugin does not support updating provider attributes",
+                str(exc))
+
+    @utils.requires_ext(extension='provider', service='network')
+    @rbac_rule_validation.action(
+        service="neutron",
+        rules=["get_network",
+               "update_network",
+               "update_network:provider:physical_network"],
+        expected_error_codes=[404, 403, 403])
+    @decorators.idempotent_id('e3a55660-f75c-494e-a1b1-a8b36cc789ef')
+    def test_update_network_provider_physical_network(self):
+
+        """Update Network Provider Physical Network Test
+
+        RBAC test for neutron update_network:provider:physical_network policy
+        """
+        try:
+            with self.rbac_utils.override_role(self):
+                self._update_network(self.network['id'],
+                                     provider_physical_network='provider')
+        except lib_exc.BadRequest as exc:
+            # Per the api documenation, most plugins don't support updating
+            # provider attributes
+            self.assertIn(
+                "Plugin does not support updating provider attributes",
+                str(exc))
+
+    @utils.requires_ext(extension='provider', service='network')
+    @rbac_rule_validation.action(
+        service="neutron",
+        rules=["get_network",
+               "update_network",
+               "update_network:provider:segmentation_id"],
+        expected_error_codes=[404, 403, 403])
+    @decorators.idempotent_id('f6164228-b670-45fd-9ff9-b101930318c7')
+    def test_update_network_provider_segmentation_id(self):
+
+        """Update Network Provider Segmentation Id Test
+
+        RBAC test for neutron update_network:provider:segmentation_id policy
+        """
+        try:
+            with self.rbac_utils.override_role(self):
+                self._update_network(self.network['id'],
+                                     provider_segmentation_id=400)
+        except lib_exc.BadRequest as exc:
+            # Per the api documenation, most plugins don't support updating
+            # provider attributes
+            self.assertIn(
+                "Plugin does not support updating provider attributes",
+                str(exc))
+
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_network",
-                                 expected_error_code=404)
+                                 rules=["get_network"],
+                                 expected_error_codes=[404])
     @decorators.idempotent_id('0eb62d04-338a-4ff4-a8fa-534e52110534')
     def test_show_network(self):
 
@@ -325,7 +447,7 @@
     @utils.requires_ext(extension='dhcp_agent_scheduler', service='network')
     @decorators.idempotent_id('b524f19f-fbb4-4d11-a85d-03bfae17bf0e')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_dhcp-agents")
+                                 rules=["get_dhcp-agents"])
     def test_list_dhcp_agents_on_hosting_network(self):
 
         """List DHCP Agents on Hosting Network Test
diff --git a/patrole_tempest_plugin/tests/api/network/test_policy_bandwidth_limit_rule_rbac.py b/patrole_tempest_plugin/tests/api/network/test_policy_bandwidth_limit_rule_rbac.py
new file mode 100644
index 0000000..8f9635d
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_policy_bandwidth_limit_rule_rbac.py
@@ -0,0 +1,109 @@
+# 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.common import utils
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+
+class PolicyBandwidthLimitRulePluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(PolicyBandwidthLimitRulePluginRbacTest, cls).skip_checks()
+        if not utils.is_extension_enabled('qos', 'network'):
+            msg = "qos extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(PolicyBandwidthLimitRulePluginRbacTest, cls).resource_setup()
+        name = data_utils.rand_name(cls.__class__.__name__ + '-qos-policy')
+        cls.policy_id = cls.ntp_client.create_qos_policy(
+            name=name)["policy"]["id"]
+        cls.addClassResourceCleanup(cls.ntp_client.delete_qos_policy,
+                                    cls.policy_id)
+
+    def _create_bandwidth_limit_rule(self):
+        rule = self.ntp_client.create_bandwidth_limit_rule(
+            self.policy_id, max_kbps=1000, max_burst_kbps=1000,
+            direction="egress")
+        rule_id = rule['bandwidth_limit_rule']['id']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ntp_client.delete_bandwidth_limit_rule,
+                        self.policy_id, rule_id)
+        return rule_id
+
+    @decorators.idempotent_id('E0FDCB39-E16D-4AF5-9165-3FEFD116E69D')
+    @rbac_rule_validation.action(
+        service="neutron", rules=["create_policy_bandwidth_limit_rule"])
+    def test_create_policy_bandwidth_limit_rule(self):
+        """Create bandwidth_limit_rule.
+
+        RBAC test for the neutron "create_policy_bandwidth_limit_rule" policy
+        """
+
+        with self.rbac_utils.override_role(self):
+            self._create_bandwidth_limit_rule()
+
+    @decorators.idempotent_id('A092BD50-364F-4F55-B81A-37DAD6E77B95')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_policy_bandwidth_limit_rule"],
+                                 expected_error_codes=[404])
+    def test_show_policy_bandwidth_limit_rule(self):
+        """Show bandwidth_limit_rule.
+
+        RBAC test for the neutron "get_policy_bandwidth_limit_rule" policy
+        """
+        rule_id = self._create_bandwidth_limit_rule()
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.show_bandwidth_limit_rule(self.policy_id, rule_id)
+
+    @decorators.idempotent_id('CAA27599-082B-44B9-AF09-8C9B8E777ED7')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_policy_bandwidth_limit_rule",
+                                        "update_policy_bandwidth_limit_rule"],
+                                 expected_error_codes=[404, 403])
+    def test_update_policy_bandwidth_limit_rule(self):
+        """Update bandwidth_limit_rule.
+
+        RBAC test for the neutron "update_policy_bandwidth_limit_rule" policy
+        """
+        rule_id = self._create_bandwidth_limit_rule()
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.update_bandwidth_limit_rule(
+                self.policy_id, rule_id, max_kbps=2000)
+
+    @decorators.idempotent_id('BF6D9ED7-4B04-423D-857D-455DB0705852')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_policy_bandwidth_limit_rule",
+                                        "delete_policy_bandwidth_limit_rule"],
+                                 expected_error_codes=[404, 403])
+    def test_delete_policy_bandwidth_limit_rule(self):
+        """Delete bandwidth_limit_rule.
+
+        RBAC test for the neutron "delete_policy_bandwidth_limit_rule" policy
+        """
+        rule_id = self._create_bandwidth_limit_rule()
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.delete_bandwidth_limit_rule(self.policy_id,
+                                                        rule_id)
diff --git a/patrole_tempest_plugin/tests/api/network/test_policy_minimum_bandwidth_rule_rbac.py b/patrole_tempest_plugin/tests/api/network/test_policy_minimum_bandwidth_rule_rbac.py
new file mode 100644
index 0000000..4f85cb6
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_policy_minimum_bandwidth_rule_rbac.py
@@ -0,0 +1,112 @@
+# 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.common import utils
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+
+class PolicyMinimumBandwidthRulePluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(PolicyMinimumBandwidthRulePluginRbacTest, cls).skip_checks()
+        if not utils.is_extension_enabled('qos', 'network'):
+            msg = "qos extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(PolicyMinimumBandwidthRulePluginRbacTest, cls).resource_setup()
+        name = data_utils.rand_name(cls.__class__.__name__ + '-qos')
+        cls.policy_id = cls.ntp_client.create_qos_policy(
+            name=name)["policy"]["id"]
+        cls.addClassResourceCleanup(test_utils.call_and_ignore_notfound_exc,
+                                    cls.ntp_client.delete_qos_policy,
+                                    cls.policy_id)
+
+    def create_minimum_bandwidth_rule(self):
+        rule = self.ntp_client.create_minimum_bandwidth_rule(
+            self.policy_id, direction="egress", min_kbps=1000)
+        rule_id = rule['minimum_bandwidth_rule']['id']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ntp_client.delete_minimum_bandwidth_rule,
+                        self.policy_id, rule_id)
+        return rule_id
+
+    @decorators.idempotent_id('25B5EF3A-DF2A-4C80-A498-3BE14A321D97')
+    @rbac_rule_validation.action(
+        service="neutron", rules=["create_policy_minimum_bandwidth_rule"])
+    def test_create_policy_minimum_bandwidth_rule(self):
+        """Create policy_minimum_bandwidth_rule.
+
+        RBAC test for the neutron "create_policy_minimum_bandwidth_rule" policy
+        """
+
+        with self.rbac_utils.override_role(self):
+            self.create_minimum_bandwidth_rule()
+
+    @decorators.idempotent_id('01DD902C-47C5-45D2-9A0E-7AF05981DF21')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_policy_minimum_bandwidth_rule"],
+                                 expected_error_codes=[404])
+    def test_show_policy_minimum_bandwidth_rule(self):
+        """Show policy_minimum_bandwidth_rule.
+
+        RBAC test for the neutron "get_policy_minimum_bandwidth_rule" policy
+        """
+        rule_id = self.create_minimum_bandwidth_rule()
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.show_minimum_bandwidth_rule(
+                self.policy_id, rule_id)
+
+    @decorators.idempotent_id('50AFE69B-455C-413A-BDC6-26B42DC8D55D')
+    @rbac_rule_validation.action(
+        service="neutron",
+        rules=["get_policy_minimum_bandwidth_rule",
+               "update_policy_minimum_bandwidth_rule"],
+        expected_error_codes=[404, 403])
+    def test_update_policy_minimum_bandwidth_rule(self):
+        """Update policy_minimum_bandwidth_rule.
+
+        RBAC test for the neutron "update_policy_minimum_bandwidth_rule" policy
+        """
+        rule_id = self.create_minimum_bandwidth_rule()
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.update_minimum_bandwidth_rule(
+                self.policy_id, rule_id, min_kbps=2000)
+
+    @decorators.idempotent_id('2112E325-C3B2-4071-8A93-B218F275A83B')
+    @rbac_rule_validation.action(
+        service="neutron",
+        rules=["get_policy_minimum_bandwidth_rule",
+               "delete_policy_minimum_bandwidth_rule"],
+        expected_error_codes=[404, 403])
+    def test_delete_policy_minimum_bandwidth_rule(self):
+        """Delete policy_minimum_bandwidth_rule.
+
+        RBAC test for the neutron "delete_policy_minimum_bandwidth_rule" policy
+        """
+        rule_id = self.create_minimum_bandwidth_rule()
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.delete_minimum_bandwidth_rule(
+                self.policy_id, rule_id)
diff --git a/patrole_tempest_plugin/tests/api/network/test_ports_rbac.py b/patrole_tempest_plugin/tests/api/network/test_ports_rbac.py
index 2cf3cd6..b65bd73 100644
--- a/patrole_tempest_plugin/tests/api/network/test_ports_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_ports_rbac.py
@@ -59,7 +59,7 @@
         return ip_list
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_port")
+                                 rules=["create_port"])
     @decorators.idempotent_id('0ec8c551-625c-4864-8a52-85baa7c40f22')
     def test_create_port(self):
 
@@ -69,8 +69,7 @@
     @decorators.idempotent_id('045ee797-4962-4913-b96a-5d7ea04099e7')
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_port",
-                                        "create_port:device_owner"],
-                                 expected_error_codes=[403, 403])
+                                        "create_port:device_owner"])
     def test_create_port_device_owner(self):
         with self.rbac_utils.override_role(self):
             self.create_port(self.network,
@@ -79,8 +78,7 @@
     @decorators.idempotent_id('c4fa8844-f5ef-4daa-bfa2-b89897dfaedf')
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_port",
-                                        "create_port:port_security_enabled"],
-                                 expected_error_codes=[403, 403])
+                                        "create_port:port_security_enabled"])
     def test_create_port_security_enabled(self):
         with self.rbac_utils.override_role(self):
             self.create_port(self.network, port_security_enabled=True)
@@ -88,8 +86,7 @@
     @utils.requires_ext(extension='binding', service='network')
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_port",
-                                        "create_port:binding:host_id"],
-                                 expected_error_codes=[403, 403])
+                                        "create_port:binding:host_id"])
     @decorators.idempotent_id('a54bd6b8-a7eb-4101-bfe8-093930b0d660')
     def test_create_port_binding_host_id(self):
 
@@ -102,8 +99,7 @@
     @utils.requires_ext(extension='binding', service='network')
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_port",
-                                        "create_port:binding:profile"],
-                                 expected_error_codes=[403, 403])
+                                        "create_port:binding:profile"])
     @decorators.idempotent_id('98fa38ab-c2ed-46a0-99f0-59f18cbd257a')
     def test_create_port_binding_profile(self):
 
@@ -120,8 +116,7 @@
         '"create_port:fixed_ips:ip_address" must be available in the cloud.')
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_port",
-                                        "create_port:fixed_ips:ip_address"],
-                                 expected_error_codes=[403, 403])
+                                        "create_port:fixed_ips:ip_address"])
     @decorators.idempotent_id('2551e10d-006a-413c-925a-8c6f834c09ac')
     def test_create_port_fixed_ips_ip_address(self):
 
@@ -137,8 +132,7 @@
 
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_port",
-                                        "create_port:mac_address"],
-                                 expected_error_codes=[403, 403])
+                                        "create_port:mac_address"])
     @decorators.idempotent_id('aee6d0be-a7f3-452f-aefc-796b4eb9c9a8')
     def test_create_port_mac_address(self):
 
@@ -150,8 +144,7 @@
 
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_port",
-                                        "create_port:allowed_address_pairs"],
-                                 expected_error_codes=[403, 403])
+                                        "create_port:allowed_address_pairs"])
     @decorators.idempotent_id('b638d1f4-d903-4ca8-aa2a-6fd603c5ec3a')
     def test_create_port_allowed_address_pairs(self):
 
@@ -166,8 +159,8 @@
             self.create_port(**post_body)
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_port",
-                                 expected_error_code=404)
+                                 rules=["get_port"],
+                                 expected_error_codes=[404])
     @decorators.idempotent_id('a9d41cb8-78a2-4b97-985c-44e4064416f4')
     def test_show_port(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/network/test_qos_rbac.py b/patrole_tempest_plugin/tests/api/network/test_qos_rbac.py
new file mode 100644
index 0000000..aae326c
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_qos_rbac.py
@@ -0,0 +1,100 @@
+# Copyright 2018 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.common import utils
+from tempest.lib.common.utils import data_utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+
+class QosPluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(QosPluginRbacTest, cls).skip_checks()
+        if not utils.is_extension_enabled('qos', 'network'):
+            msg = "qos extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(QosPluginRbacTest, cls).resource_setup()
+        cls.network = cls.create_network()
+
+    def create_policy(self, name=None):
+        name = name or data_utils.rand_name(self.__class__.__name__)
+        policy = self.ntp_client.create_qos_policy(name)['policy']
+        self.addCleanup(test_utils.call_and_ignore_notfound_exc,
+                        self.ntp_client.delete_qos_policy, policy['id'])
+        return policy
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_policy"],
+                                 expected_error_codes=[403])
+    @decorators.idempotent_id('2ade2e48-7f82-4650-a69c-933d8d594636')
+    def test_create_policy(self):
+
+        """Create Policy Test
+
+        RBAC test for the neutron create_policy policy
+        """
+        with self.rbac_utils.override_role(self):
+            self.create_policy()
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_policy"],
+                                 expected_error_codes=[404])
+    @decorators.idempotent_id('d004a8de-b226-4eb4-9fdc-8202a7f64c56')
+    def test_get_policy(self):
+
+        """Show Policy Test
+
+        RBAC test for the neutron get_policy policy
+        """
+        policy = self.create_policy()
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.show_qos_policy(policy['id'])
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_policy", "update_policy"],
+                                 expected_error_codes=[404, 403])
+    @decorators.idempotent_id('fb74d56f-1dfc-490b-a9e1-454af583eefb')
+    def test_update_policy(self):
+
+        """Update Policy Test
+
+        RBAC test for the neutron update_policy policy
+        """
+        policy = self.create_policy()
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.update_qos_policy(policy['id'],
+                                              description='updated')
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_policy", "delete_policy"],
+                                 expected_error_codes=[404, 403])
+    @decorators.idempotent_id('ef4c23a6-4095-47a6-958e-1df585f7d8db')
+    def test_delete_policy(self):
+
+        """Delete Policy Test
+
+        RBAC test for the neutron delete_policy policy
+        """
+        policy = self.create_policy()
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.delete_qos_policy(policy['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_rbac_policies_rbac.py b/patrole_tempest_plugin/tests/api/network/test_rbac_policies_rbac.py
new file mode 100644
index 0000000..9c88bc0
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_rbac_policies_rbac.py
@@ -0,0 +1,111 @@
+# Copyright 2018 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.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+
+class RbacPoliciesPluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def resource_setup(cls):
+        super(RbacPoliciesPluginRbacTest, cls).resource_setup()
+        cls.tenant_id = cls.os_primary.credentials.tenant_id
+        cls.network_id = cls.create_network()['id']
+
+    def create_rbac_policy(self, tenant_id, network_id):
+        policy = self.ntp_client.create_rbac_policy(
+            target_tenant=tenant_id,
+            object_type="network",
+            object_id=network_id,
+            action="access_as_shared"
+        )
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.ntp_client.delete_rbac_policy, policy["rbac_policy"]["id"])
+
+        return policy["rbac_policy"]["id"]
+
+    @decorators.idempotent_id('effd9545-99ad-4c3c-92dd-ea422602c868')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_rbac_policy",
+                                        "create_rbac_policy:target_tenant"])
+    def test_create_rbac_policy(self):
+        """Create RBAC policy.
+
+        RBAC test for the neutron "create_rbac_policy" policy
+
+        We can't validate "create_rbac_policy:target_tenant" for all cases
+        since if "restrict_wildcard" rule is modified then Patrole won't be
+        able to determine the correct result since that requires relying on
+        Neutron's custom FieldCheck oslo.policy rule.
+        """
+
+        with self.rbac_utils.override_role(self):
+            self.create_rbac_policy(self.tenant_id, self.network_id)
+
+    @decorators.idempotent_id('f5d836d8-3b64-412d-a283-ee29761017f3')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_rbac_policy",
+                                        "update_rbac_policy",
+                                        "update_rbac_policy:target_tenant"],
+                                 expected_error_codes=[404, 403, 403])
+    def test_update_rbac_policy(self):
+        """Update RBAC policy.
+
+        RBAC test for the neutron "update_rbac_policy" policy
+
+        We can't validate "create_rbac_policy:target_tenant" for all cases
+        since if "restrict_wildcard" rule is modified then Patrole won't be
+        able to determine the correct result since that requires relying on
+        Neutron's custom FieldCheck oslo.policy rule.
+        """
+        policy_id = self.create_rbac_policy(self.tenant_id, self.network_id)
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.update_rbac_policy(
+                policy_id, target_tenant=self.tenant_id)
+
+    @decorators.idempotent_id('9308ab18-426c-41b7-bce5-11081f7dd259')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_rbac_policy"],
+                                 expected_error_codes=[404])
+    def test_show_rbac_policy(self):
+        """Show RBAC policy.
+
+        RBAC test for the neutron "get_rbac_policy" policy
+        """
+        policy_id = self.create_rbac_policy(self.tenant_id, self.network_id)
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.show_rbac_policy(policy_id)
+
+    @decorators.idempotent_id('54aa9bce-efea-47fb-b0e4-12012f82f285')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_rbac_policy",
+                                        "delete_rbac_policy"],
+                                 expected_error_codes=[404, 403])
+    def test_delete_rbac_policy(self):
+        """Delete RBAC policy.
+
+        RBAC test for the neutron "delete_rbac_policy" policy
+        """
+        policy_id = self.create_rbac_policy(self.tenant_id, self.network_id)
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.delete_rbac_policy(policy_id)
diff --git a/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py b/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py
index a3d973d..f850a3e 100644
--- a/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_routers_rbac.py
@@ -56,7 +56,7 @@
         return unused_ip[0]
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_router")
+                                 rules=["create_router"])
     @decorators.idempotent_id('acc5005c-bdb6-4192-bc9f-ece9035bb488')
     def test_create_router(self):
         """Create Router
@@ -72,8 +72,7 @@
     @utils.requires_ext(extension='l3-ha', service='network')
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_router",
-                                        "create_router:ha"],
-                                 expected_error_codes=[403, 403])
+                                        "create_router:ha"])
     def test_create_high_availability_router(self):
         """Create high-availability router
 
@@ -88,8 +87,7 @@
     @utils.requires_ext(extension='dvr', service='network')
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_router",
-                                        "create_router:distributed"],
-                                 expected_error_codes=[403, 403])
+                                        "create_router:distributed"])
     def test_create_distributed_router(self):
         """Create distributed router
 
@@ -104,8 +102,7 @@
     @rbac_rule_validation.action(
         service="neutron",
         rules=["create_router",
-               "create_router:external_gateway_info:enable_snat"],
-        expected_error_codes=[403, 403])
+               "create_router:external_gateway_info:enable_snat"])
     @decorators.idempotent_id('3c5acd49-0ec7-4109-ab51-640557b48ebc')
     def test_create_router_enable_snat(self):
         """Create Router Snat
@@ -126,8 +123,7 @@
     @rbac_rule_validation.action(
         service="neutron",
         rules=["create_router",
-               "create_router:external_gateway_info:external_fixed_ips"],
-        expected_error_codes=[403, 403])
+               "create_router:external_gateway_info:external_fixed_ips"])
     @decorators.idempotent_id('d0354369-a040-4349-b869-645c8aed13cd')
     def test_create_router_external_fixed_ips(self):
         """Create Router Fixed IPs
@@ -151,8 +147,8 @@
                         router['router']['id'])
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_router",
-                                 expected_error_code=404)
+                                 rules=["get_router"],
+                                 expected_error_codes=[404])
     @decorators.idempotent_id('bfbdbcff-f115-4d3e-8cd5-6ada33fd0e21')
     def test_show_router(self):
         """Get Router
diff --git a/patrole_tempest_plugin/tests/api/network/test_security_groups_rbac.py b/patrole_tempest_plugin/tests/api/network/test_security_groups_rbac.py
index 1cf841d..9112bf6 100644
--- a/patrole_tempest_plugin/tests/api/network/test_security_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_security_groups_rbac.py
@@ -70,7 +70,7 @@
         return sec_group_rule
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_security_group")
+                                 rules=["create_security_group"])
     @decorators.idempotent_id('db7003ce-5717-4e5b-afc7-befa35e8c67f')
     def test_create_security_group(self):
 
@@ -78,8 +78,8 @@
             self._create_security_group()
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_security_group",
-                                 expected_error_code=404)
+                                 rules=["get_security_group"],
+                                 expected_error_codes=[404])
     @decorators.idempotent_id('56335e77-aef2-4b54-86c7-7f772034b585')
     def test_show_security_group(self):
 
@@ -116,7 +116,7 @@
                 description="test description")
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_security_group")
+                                 rules=["get_security_group"])
     @decorators.idempotent_id('fbaf8d96-ed3e-49af-b24c-5fb44f05bbb7')
     def test_list_security_groups(self):
 
@@ -129,7 +129,7 @@
             raise rbac_exceptions.RbacMalformedResponse(empty=True)
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_security_group_rule")
+                                 rules=["create_security_group_rule"])
     @decorators.idempotent_id('953d78df-00cd-416f-9cbd-b7cb4ea65772')
     def test_create_security_group_rule(self):
 
@@ -149,8 +149,8 @@
                 sec_group_rule['id'])
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_security_group_rule",
-                                 expected_error_code=404)
+                                 rules=["get_security_group_rule"],
+                                 expected_error_codes=[404])
     @decorators.idempotent_id('84b4038c-261e-4a94-90d5-c885739ab0d5')
     def test_show_security_group_rule(self):
 
@@ -160,7 +160,7 @@
                 sec_group_rule['id'])
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_security_group_rule")
+                                 rules=["get_security_group_rule"])
     @decorators.idempotent_id('05739ab6-fa35-11e6-bc64-92361f002671')
     def test_list_security_group_rules(self):
 
diff --git a/patrole_tempest_plugin/tests/api/network/test_segments_rbac.py b/patrole_tempest_plugin/tests/api/network/test_segments_rbac.py
new file mode 100644
index 0000000..9725e2b
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_segments_rbac.py
@@ -0,0 +1,122 @@
+# Copyright 2018 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.
+
+import random
+
+from tempest.common import utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+
+class SegmentsPluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(SegmentsPluginRbacTest, cls).skip_checks()
+        if not utils.is_extension_enabled('segment', 'network'):
+            msg = "segment extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(SegmentsPluginRbacTest, cls).resource_setup()
+        cls.network = cls.create_network()
+
+    @classmethod
+    def get_free_segmentation_id(cls):
+        # Select unused segmentation_id to prevent usage conflict
+        segments = cls.ntp_client.list_segments()["segments"]
+        segmentation_ids = [s["segmentation_id"] for s in segments]
+
+        # With 2+ concurrency, tests that ran in the same moment may fail due
+        # to usage conflict. To prevent it we select segmentation to start
+        # randomly.
+        segmentation_id = random.randint(1000, 5000)
+        while segmentation_id in segmentation_ids:
+            segmentation_id += 1
+
+        return segmentation_id
+
+    @classmethod
+    def create_segment(cls, network):
+        segmentation_id = cls.get_free_segmentation_id()
+
+        seg = cls.ntp_client.create_segment(
+            network_id=network['id'], network_type="gre",
+            segmentation_id=segmentation_id)
+        cls.addClassResourceCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            cls.ntp_client.delete_segment, seg['segment']['id'])
+
+        return seg
+
+    @decorators.idempotent_id('c02618e7-bb20-1a3a-83c8-6eec2af08126')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_segment"])
+    def test_create_segment(self):
+        """Create segment.
+
+        RBAC test for the neutron "create_segment" policy
+        """
+        with self.rbac_utils.override_role(self):
+            self.create_segment(self.network)
+
+    @decorators.idempotent_id('c02618e7-bb20-1a3a-83c8-6eec2af08127')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_segment"],
+                                 expected_error_codes=[404])
+    def test_show_segment(self):
+        """Show segment.
+
+        RBAC test for the neutron "get_segment" policy
+        """
+        segment = self.create_segment(self.network)
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.show_segment(segment['segment']['id'])
+
+    @decorators.idempotent_id('c02618e7-bb20-1a3a-83c8-6eec2af08128')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_segment",
+                                        "update_segment"],
+                                 expected_error_codes=[404, 403])
+    def test_update_segment(self):
+        """Update segment.
+
+        RBAC test for the neutron "update_segment" policy
+        """
+        segment = self.create_segment(self.network)
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.update_segment(segment['segment']['id'],
+                                           name="NewName")
+
+    @decorators.idempotent_id('c02618e7-bb20-1a3a-83c8-6eec2af08129')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_segment",
+                                        "delete_segment"],
+                                 expected_error_codes=[404, 403])
+    def test_delete_segment(self):
+        """Delete segment.
+
+        RBAC test for the neutron "delete_segment" policy
+        """
+        segment = self.create_segment(self.network)
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.delete_segment(segment['segment']['id'])
diff --git a/patrole_tempest_plugin/tests/api/network/test_service_providers_rbac.py b/patrole_tempest_plugin/tests/api/network/test_service_providers_rbac.py
index fd85444..561a72c 100644
--- a/patrole_tempest_plugin/tests/api/network/test_service_providers_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_service_providers_rbac.py
@@ -22,7 +22,7 @@
 class ServiceProvidersRbacTest(base.BaseNetworkRbacTest):
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_service_provider")
+                                 rules=["get_service_provider"])
     @decorators.idempotent_id('15f573b7-474a-4b37-8629-7fac86553ce5')
     def test_list_service_providers(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py b/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py
index 124b59a..bc6b923 100644
--- a/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_subnetpools_rbac.py
@@ -53,7 +53,7 @@
         return subnetpool
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_subnetpool")
+                                 rules=["create_subnetpool"])
     @decorators.idempotent_id('1b5509fd-2c32-44a8-a786-1b6ca162dbd1')
     def test_create_subnetpool(self):
         """Create subnetpool.
@@ -65,6 +65,31 @@
 
     @rbac_rule_validation.action(service="neutron",
                                  rules=["create_subnetpool",
+                                        "create_subnetpool:is_default"],
+                                 expected_error_codes=[403, 403])
+    @decorators.idempotent_id('1b5509fd-2c32-44a8-a786-1b6ca162dbd2')
+    def test_create_subnetpool_default(self):
+        """Create default subnetpool.
+
+        RBAC test for the neutron create_subnetpool:is_default policy
+        """
+        # Most likely we already have default subnetpools for ipv4 and ipv6,
+        # so we temporary mark them as is_default=False, to let this test pass.
+        def_pools = self.subnetpools_client.list_subnetpools(is_default=True)
+        for default_pool in def_pools["subnetpools"]:
+            self.subnetpools_client.update_subnetpool(default_pool["id"],
+                                                      is_default=False)
+
+            self.addCleanup(self.subnetpools_client.update_subnetpool,
+                            default_pool["id"], is_default=True)
+
+        with self.rbac_utils.override_role(self):
+            # It apparently only enforces the policy for is_default=True.
+            # It does nothing for is_default=False
+            self._create_subnetpool(is_default=True)
+
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_subnetpool",
                                         "create_subnetpool:shared"],
                                  expected_error_codes=[403, 403])
     @decorators.idempotent_id('cf730989-0d47-40bc-b39a-99e7de484723')
@@ -77,8 +102,8 @@
             self._create_subnetpool(shared=True)
 
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_subnetpool",
-                                 expected_error_code=404)
+                                 rules=["get_subnetpool"],
+                                 expected_error_codes=[404])
     @decorators.idempotent_id('4f5aee26-0507-4b6d-b44c-3128a25094d2')
     def test_show_subnetpool(self):
         """Show subnetpool.
@@ -107,8 +132,7 @@
     @decorators.idempotent_id('a16f4e5c-0675-415f-b636-00af00638693')
     @rbac_rule_validation.action(service="neutron",
                                  rules=["update_subnetpool",
-                                        "update_subnetpool:is_default"],
-                                 expected_error_codes=[403, 403])
+                                        "update_subnetpool:is_default"])
     def test_update_subnetpool_is_default(self):
         """Update default subnetpool.
 
diff --git a/patrole_tempest_plugin/tests/api/network/test_subnets_rbac.py b/patrole_tempest_plugin/tests/api/network/test_subnets_rbac.py
index 77d4b42..9a5ebe4 100644
--- a/patrole_tempest_plugin/tests/api/network/test_subnets_rbac.py
+++ b/patrole_tempest_plugin/tests/api/network/test_subnets_rbac.py
@@ -39,7 +39,7 @@
 
     @decorators.idempotent_id('0481adeb-4301-44d5-851c-35910cc18a6b')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="create_subnet")
+                                 rules=["create_subnet"])
     def test_create_subnet(self):
         """Create subnet.
 
@@ -50,8 +50,8 @@
 
     @decorators.idempotent_id('c02618e7-bb20-4abd-83c8-6eec2af08752')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_subnet",
-                                 expected_error_code=404)
+                                 rules=["get_subnet"],
+                                 expected_error_codes=[404])
     def test_show_subnet(self):
         """Show subnet.
 
@@ -62,7 +62,7 @@
 
     @decorators.idempotent_id('e2ddc415-5cab-43f4-9b61-166aed65d637')
     @rbac_rule_validation.action(service="neutron",
-                                 rule="get_subnet")
+                                 rules=["get_subnet"])
     def test_list_subnets(self):
         """List subnets.
 
diff --git a/patrole_tempest_plugin/tests/api/network/test_trunks_rbac.py b/patrole_tempest_plugin/tests/api/network/test_trunks_rbac.py
new file mode 100644
index 0000000..063fd55
--- /dev/null
+++ b/patrole_tempest_plugin/tests/api/network/test_trunks_rbac.py
@@ -0,0 +1,85 @@
+# Copyright 2018 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.common import utils
+from tempest.lib.common.utils import test_utils
+from tempest.lib import decorators
+
+from patrole_tempest_plugin import rbac_rule_validation
+from patrole_tempest_plugin.tests.api.network import rbac_base as base
+
+
+class TrunksPluginRbacTest(base.BaseNetworkPluginRbacTest):
+
+    @classmethod
+    def skip_checks(cls):
+        super(TrunksPluginRbacTest, cls).skip_checks()
+        if not utils.is_extension_enabled('trunk', 'network'):
+            msg = "trunk extension not enabled."
+            raise cls.skipException(msg)
+
+    @classmethod
+    def resource_setup(cls):
+        super(TrunksPluginRbacTest, cls).resource_setup()
+        cls.network = cls.create_network()
+        cls.port_id = cls.create_port(cls.network)["id"]
+
+    def create_trunk(self, port_id):
+        trunk = self.ntp_client.create_trunk(port_id, [])
+        self.addCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            self.ntp_client.delete_trunk, trunk["trunk"]['id'])
+
+        return trunk
+
+    @decorators.idempotent_id('c02618e7-bb20-1a3a-83c8-6eec2af08130')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["create_trunk"])
+    def test_create_trunk(self):
+        """Create trunk.
+
+        RBAC test for the neutron "create_trunk" policy
+        """
+        with self.rbac_utils.override_role(self):
+            self.create_trunk(self.port_id)
+
+    @decorators.idempotent_id('c02618e7-bb20-1a3a-83c8-6eec2af08131')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_trunk"],
+                                 expected_error_codes=[404])
+    def test_show_trunk(self):
+        """Show trunk.
+
+        RBAC test for the neutron "get_trunk" policy
+        """
+        trunk = self.create_trunk(self.port_id)
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.show_trunk(trunk['trunk']['id'])
+
+    @decorators.idempotent_id('c02618e7-bb20-1a3a-83c8-6eec2af08132')
+    @rbac_rule_validation.action(service="neutron",
+                                 rules=["get_trunk",
+                                        "delete_trunk"],
+                                 expected_error_codes=[404, 403])
+    def test_delete_trunk(self):
+        """Delete trunk.
+
+        RBAC test for the neutron "delete_trunk" policy
+        """
+        trunk = self.create_trunk(self.port_id)
+
+        with self.rbac_utils.override_role(self):
+            self.ntp_client.delete_trunk(trunk['trunk']['id'])
diff --git a/patrole_tempest_plugin/tests/api/volume/rbac_base.py b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
index 798f311..14b3151 100644
--- a/patrole_tempest_plugin/tests/api/volume/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/volume/rbac_base.py
@@ -12,14 +12,12 @@
 #    under the License.
 
 from tempest.api.volume import base as vol_base
-from tempest import config
+from tempest.common import waiters
 from tempest.lib.common.utils import data_utils
 from tempest.lib.common.utils import test_utils
 
 from patrole_tempest_plugin import rbac_utils
 
-CONF = config.CONF
-
 
 class BaseVolumeRbacTest(rbac_utils.RbacUtilsMixin,
                          vol_base.BaseVolumeTest):
@@ -31,11 +29,6 @@
     _api_version = 3
 
     @classmethod
-    def skip_checks(cls):
-        super(BaseVolumeRbacTest, cls).skip_checks()
-        cls.skip_rbac_checks()
-
-    @classmethod
     def setup_clients(cls):
         super(BaseVolumeRbacTest, cls).setup_clients()
         cls.setup_rbac_utils()
@@ -58,6 +51,29 @@
             cls.volume_types_client.delete_volume_type, volume_type['id'])
         return volume_type
 
+    @classmethod
+    def _create_backup(cls, volume_id, backup_client=None, **kwargs):
+        """Wrapper utility that returns a test backup.
+
+        Tempest has an instance-level version. This is a class-level version.
+        """
+        if backup_client is None:
+            backup_client = cls.backups_client
+        if 'name' not in kwargs:
+            name = data_utils.rand_name(cls.__name__ + '-Backup')
+            kwargs['name'] = name
+
+        backup = backup_client.create_backup(
+            volume_id=volume_id, **kwargs)['backup']
+        cls.addClassResourceCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            backup_client.delete_backup, backup['id'])
+        waiters.wait_for_volume_resource_status(backup_client, backup['id'],
+                                                'available')
+        waiters.wait_for_volume_resource_status(cls.volumes_client, volume_id,
+                                                'available')
+        return backup
+
     def create_group_type(self, name=None, ignore_notfound=False, **kwargs):
         """Create a test group-type"""
         name = name or data_utils.rand_name(
diff --git a/patrole_tempest_plugin/tests/api/volume/test_capabilities_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_capabilities_rbac.py
index 82b89ab..fa1157a 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_capabilities_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_capabilities_rbac.py
@@ -36,7 +36,7 @@
         cls.hosts_client = cls.os_primary.volume_hosts_v2_client
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:capabilities")
+                                 rules=["volume_extension:capabilities"])
     @decorators.idempotent_id('40928b74-2141-11e7-93ae-92361f002671')
     def test_show_back_end_capabilities(self):
         host = self.hosts_client.list_hosts()['hosts'][0]['host_name']
diff --git a/patrole_tempest_plugin/tests/api/volume/test_encryption_types_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_encryption_types_rbac.py
index f10e41b..0eb0244 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_encryption_types_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_encryption_types_rbac.py
@@ -13,12 +13,36 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import functools
+
 from tempest.common import utils
+from tempest import config
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
+CONF = config.CONF
+
+
+def _get_volume_type_encryption_policy(action):
+    feature_flag = CONF.policy_feature_enabled.added_cinder_policies_stein
+
+    if feature_flag:
+        return "volume_extension:volume_type_encryption:%s" % action
+
+    return "volume_extension:volume_type_encryption"
+
+
+_CREATE_VOLUME_TYPE_ENCRYPTION = functools.partial(
+    _get_volume_type_encryption_policy, "create")
+_SHOW_VOLUME_TYPE_ENCRYPTION = functools.partial(
+    _get_volume_type_encryption_policy, "get")
+_UPDATE_VOLUME_TYPE_ENCRYPTION = functools.partial(
+    _get_volume_type_encryption_policy, "update")
+_DELETE_VOLUME_TYPE_ENCRYPTION = functools.partial(
+    _get_volume_type_encryption_policy, "delete")
+
 
 class EncryptionTypesV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
@@ -45,7 +69,7 @@
     @decorators.idempotent_id('ffd94ce5-c24b-4b6c-84c9-c5aad8c3010c')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_type_encryption")
+        rules=[_CREATE_VOLUME_TYPE_ENCRYPTION])
     def test_create_volume_type_encryption(self):
         vol_type_id = self.create_volume_type()['id']
         with self.rbac_utils.override_role(self):
@@ -57,7 +81,7 @@
     @decorators.idempotent_id('6599e72e-acef-4c0d-a9b2-463fca30d1da')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_type_encryption")
+        rules=[_DELETE_VOLUME_TYPE_ENCRYPTION])
     def test_delete_volume_type_encryption(self):
         vol_type_id = self._create_volume_type_encryption()
         with self.rbac_utils.override_role(self):
@@ -66,7 +90,7 @@
     @decorators.idempotent_id('42da9fec-32fd-4dca-9242-8a53b2fed25a')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_type_encryption")
+        rules=[_UPDATE_VOLUME_TYPE_ENCRYPTION])
     def test_update_volume_type_encryption(self):
         vol_type_id = self._create_volume_type_encryption()
         with self.rbac_utils.override_role(self):
@@ -77,7 +101,7 @@
     @decorators.idempotent_id('1381a3dc-248f-4282-b231-c9399018c804')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_type_encryption")
+        rules=[_SHOW_VOLUME_TYPE_ENCRYPTION])
     def test_show_volume_type_encryption(self):
         vol_type_id = self._create_volume_type_encryption()
         with self.rbac_utils.override_role(self):
@@ -86,7 +110,7 @@
     @decorators.idempotent_id('d4ed3cf8-52b2-4fa2-910d-e405361f0881')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_type_encryption")
+        rules=[_SHOW_VOLUME_TYPE_ENCRYPTION])
     def test_show_encryption_specs_item(self):
         vol_type_id = self._create_volume_type_encryption()
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/volume/test_group_snapshots_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_group_snapshots_rbac.py
index 73d7bf2..1d59f9b 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_group_snapshots_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_group_snapshots_rbac.py
@@ -89,7 +89,7 @@
     @decorators.idempotent_id('653df0e8-d90a-474a-a5ce-3c2339aff7ba')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:create_group_snapshot"
+        rules=["group:create_group_snapshot"]
     )
     def test_create_group_snapshot(self):
         with self.rbac_utils.override_role(self):
@@ -112,7 +112,7 @@
     @decorators.idempotent_id('8b966844-4421-4f73-940b-9157cb878331')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:get_group_snapshot"
+        rules=["group:get_group_snapshot"]
     )
     def test_show_group_snapshot_rbac(self):
         group_snapshot_name = data_utils.rand_name('group_snapshot')
@@ -125,7 +125,7 @@
     @decorators.idempotent_id('e9de6dae-1efb-47cd-a3a8-d1f4b8f9f3ff')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:get_all_group_snapshots"
+        rules=["group:get_all_group_snapshots"]
     )
     def test_list_group_snapshot_rbac(self):
         with self.rbac_utils.override_role(self):
@@ -134,7 +134,7 @@
     @decorators.idempotent_id('cf2e25ee-ca58-4ad6-b98d-33235c77db7b')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:delete_group_snapshot"
+        rules=["group:delete_group_snapshot"]
         )
     def test_delete_group_snapshot_rbac(self):
         group_snapshot_name = data_utils.rand_name('group_snapshot')
@@ -186,7 +186,7 @@
     @decorators.idempotent_id('3f0c842e-0c72-4f5e-a9c2-281070be3e2c')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:reset_group_snapshot_status"
+        rules=["group:reset_group_snapshot_status"]
         )
     def test_reset_group_snapshot_rbac(self):
         group_snapshot_name = data_utils.rand_name('group_snapshot')
diff --git a/patrole_tempest_plugin/tests/api/volume/test_group_type_specs.py b/patrole_tempest_plugin/tests/api/volume/test_group_type_specs.py
index a34a55d..9c41dfc 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_group_type_specs.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_group_type_specs.py
@@ -27,7 +27,7 @@
     @decorators.idempotent_id('b2859734-00ad-4a22-88ee-541698e90d12')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:group_types_specs"
+        rules=["group:group_types_specs"]
     )
     def test_group_type_specs_create(self):
         # Create new group type
@@ -47,7 +47,7 @@
     @decorators.idempotent_id('469d0253-aa13-423f-8264-231ac17effbf')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:group_types_specs"
+        rules=["group:group_types_specs"]
     )
     def test_group_type_specs_show(self):
         group_type = self.create_group_type()
@@ -65,7 +65,7 @@
     @decorators.idempotent_id('2e706a4e-dec9-46bf-9426-1c5b6f3ce102')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:group_types_specs"
+        rules=["group:group_types_specs"]
     )
     def test_group_type_specs_update(self):
         group_type = self.create_group_type()
@@ -81,7 +81,7 @@
     @decorators.idempotent_id('fd5e332b-fb2c-4957-ace9-11d60ddd5472')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:group_types_specs"
+        rules=["group:group_types_specs"]
     )
     def test_group_type_specs_list(self):
         group_type = self.create_group_type()
@@ -92,7 +92,7 @@
     @decorators.idempotent_id('d9639a07-e441-4576-baf6-7ec732b16572')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:group_types_specs"
+        rules=["group:group_types_specs"]
     )
     def test_group_type_specs_delete(self):
         group_type = self.create_group_type()
diff --git a/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py
index ecd193b..c117d23 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_groups_rbac.py
@@ -63,7 +63,7 @@
     @decorators.idempotent_id('43235328-66ae-424f-bc7f-f709c0ca268c')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:create")
+        rules=["group:create"])
     def test_create_group(self, name=None):
 
         group_name = name or data_utils.rand_name(
@@ -80,7 +80,7 @@
     @decorators.idempotent_id('9dc34a62-ae3e-439e-92b6-9389ea4c2863')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:get")
+        rules=["group:get"])
     def test_show_group(self):
         group = self._create_group(group_type=self.group_type_id,
                                    volume_types=[self.volume_type_id])
@@ -91,7 +91,7 @@
     @decorators.idempotent_id('db43841b-a173-4317-acfc-f83e4e48e4ee')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:get_all")
+        rules=["group:get_all"])
     def test_list_groups(self):
         with self.rbac_utils.override_role(self):
             self.groups_client.list_groups()['groups']
@@ -99,7 +99,7 @@
     @decorators.idempotent_id('5378da93-9c26-4ad4-b039-0555e2b8f668')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:get_all")
+        rules=["group:get_all"])
     def test_list_groups_with_details(self):
         with self.rbac_utils.override_role(self):
             self.groups_client.list_groups(detail=True)['groups']
@@ -107,7 +107,7 @@
     @decorators.idempotent_id('f499fc48-df83-4917-bf8d-783ebf6f080b')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:update")
+        rules=["group:update"])
     def test_update_group(self):
         group = self._create_group(group_type=self.group_type_id,
                                    volume_types=[self.volume_type_id])
@@ -119,7 +119,7 @@
     @decorators.idempotent_id('66fda391-5774-42a9-a018-80b34e57ab76')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:delete")
+        rules=["group:delete"])
     def test_delete_group(self):
 
         group = self._create_group(ignore_notfound=True,
@@ -146,7 +146,7 @@
     @decorators.idempotent_id('b849c1d4-3215-4f9d-b1e6-0aeb4b2b65ac')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:reset_status")
+        rules=["group:reset_status"])
     def test_reset_group_status(self):
         group = self._create_group(ignore_notfound=False,
                                    group_type=self.group_type_id,
@@ -166,7 +166,7 @@
     @decorators.idempotent_id('2820f12c-4681-4c7f-b28d-e6925637dff6')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:group_types_manage")
+        rules=["group:group_types_manage"])
     def test_create_group_type(self):
         with self.rbac_utils.override_role(self):
             self.create_group_type(ignore_notfound=True)
@@ -174,7 +174,7 @@
     @decorators.idempotent_id('f77f8156-4fc9-4f02-be15-8930f748e10c')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:group_types_manage")
+        rules=["group:group_types_manage"])
     def test_delete_group_type(self):
         group_type = self.create_group_type(ignore_notfound=True)
 
@@ -184,7 +184,7 @@
     @decorators.idempotent_id('67929954-4551-4d22-b15a-27fb6e56b711')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:group_types_manage")
+        rules=["group:group_types_manage"])
     def test_update_group_type(self):
         group_type = self.create_group_type()
         update_params = {
@@ -199,7 +199,7 @@
     @decorators.idempotent_id('a5f88c26-df7c-4f21-a3ae-7a4c2d6212b4')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:access_group_types_specs")
+        rules=["group:access_group_types_specs"])
     def test_create_group_type_group_specs(self):
         # TODO(felipemonteiro): Combine with ``test_create_group_type``
         # once multiple policy testing is supported. This policy is
@@ -214,7 +214,7 @@
     @decorators.idempotent_id('8d9e2831-24c3-47b7-a76a-2e563287f12f')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="group:access_group_types_specs")
+        rules=["group:access_group_types_specs"])
     def test_show_group_type(self):
         group_type = self.create_group_type()
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/volume/test_limits_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_limits_rbac.py
index aec5cb1..3127d83 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_limits_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_limits_rbac.py
@@ -33,7 +33,7 @@
         '"limits_extension:used_limits" must be available in the cloud.')
     @decorators.idempotent_id('dab04510-5b86-4479-a633-6e496ff405af')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="limits_extension:used_limits")
+                                 rules=["limits_extension:used_limits"])
     def test_show_limits(self):
         # It is enough to check whether any of the following keys below
         # are in the response body under ['limits']['absolute'], but no harm
diff --git a/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py
index a62bbda..5664bf9 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_qos_rbac.py
@@ -39,31 +39,32 @@
         return qos_specs
 
     @rbac_rule_validation.action(
-        service="cinder", rule="volume_extension:qos_specs_manage:create")
+        service="cinder", rules=["volume_extension:qos_specs_manage:create"])
     @decorators.idempotent_id('4f9f45f0-b379-4577-a279-cec3e917cbec')
     def test_create_qos_with_consumer(self):
         with self.rbac_utils.override_role(self):
             self._create_test_qos_specs()
 
     @rbac_rule_validation.action(
-        service="cinder", rule="volume_extension:qos_specs_manage:delete")
+        service="cinder", rules=["volume_extension:qos_specs_manage:delete"])
     @decorators.idempotent_id('fbc8a77e-6b6d-45ae-bebe-c496eb8f06f7')
     def test_delete_qos_with_consumer(self):
         qos = self._create_test_qos_specs()
         with self.rbac_utils.override_role(self):
             self.qos_client.delete_qos(qos['id'])
 
-    @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:qos_specs_manage:get")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rules=["volume_extension:qos_specs_manage:get"])
     @decorators.idempotent_id('22aff0dd-0343-408d-ae80-e77551956e14')
     def test_show_qos(self):
         qos = self._create_test_qos_specs()
         with self.rbac_utils.override_role(self):
             self.qos_client.show_qos(qos['id'])['qos_specs']
 
-    @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:"
-                                      "qos_specs_manage:get_all")
+    @rbac_rule_validation.action(
+        service="cinder",
+        rules=["volume_extension:qos_specs_manage:get_all"])
     @decorators.idempotent_id('ff1e98f3-d456-40a9-96d4-c7e4a55dcffa')
     def test_get_association_qos(self):
         qos = self._create_test_qos_specs()
@@ -76,14 +77,14 @@
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:qos_specs_manage:get_all")
+        rules=["volume_extension:qos_specs_manage:get_all"])
     @decorators.idempotent_id('546b8bb1-04a4-4387-9506-a538a7f3cd6a')
     def test_list_qos(self):
         with self.rbac_utils.override_role(self):
             self.qos_client.list_qos()['qos_specs']
 
     @rbac_rule_validation.action(
-        service="cinder", rule="volume_extension:qos_specs_manage:update")
+        service="cinder", rules=["volume_extension:qos_specs_manage:update"])
     @decorators.idempotent_id('89b630b7-c170-47c3-ac80-50ed425c2d98')
     def test_set_qos_key(self):
         qos = self._create_test_qos_specs()
@@ -92,7 +93,7 @@
                 qos['id'], iops_bytes='500')['qos_specs']
 
     @rbac_rule_validation.action(
-        service="cinder", rule="volume_extension:qos_specs_manage:update")
+        service="cinder", rules=["volume_extension:qos_specs_manage:update"])
     @decorators.idempotent_id('6c50c837-de77-4dae-a2ec-30e05c62969c')
     def test_unset_qos_key(self):
         qos = self._create_test_qos_specs()
@@ -104,7 +105,7 @@
                                         'qos-key-unset', args=['iops_bytes'])
 
     @rbac_rule_validation.action(
-        service="cinder", rule="volume_extension:qos_specs_manage:update")
+        service="cinder", rules=["volume_extension:qos_specs_manage:update"])
     @decorators.idempotent_id('2047b347-8bbe-405e-bf5a-c75a0d7e3930')
     def test_associate_qos(self):
         qos = self._create_test_qos_specs()
@@ -117,7 +118,7 @@
             self.qos_client.disassociate_qos, qos['id'], vol_type)
 
     @rbac_rule_validation.action(
-        service="cinder", rule="volume_extension:qos_specs_manage:update")
+        service="cinder", rules=["volume_extension:qos_specs_manage:update"])
     @decorators.idempotent_id('f12aeca1-0c02-4f33-b805-014171e5b2d4')
     def test_disassociate_qos(self):
         qos = self._create_test_qos_specs()
@@ -132,7 +133,7 @@
                                         'disassociate', args=vol_type)
 
     @rbac_rule_validation.action(
-        service="cinder", rule="volume_extension:qos_specs_manage:update")
+        service="cinder", rules=["volume_extension:qos_specs_manage:update"])
     @decorators.idempotent_id('9f6e664d-a5d9-4e71-b122-73a3086be1b9')
     def test_disassociate_all_qos(self):
         qos = self._create_test_qos_specs()
diff --git a/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py
index dace257..dcc67f6 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_quota_classes_rbac.py
@@ -39,7 +39,7 @@
 
     @decorators.idempotent_id('1a060def-2b43-4534-97f5-5eadbbe8c726')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:quota_classes")
+                                 rules=["volume_extension:quota_classes"])
     def test_show_quota_class_set(self):
         with self.rbac_utils.override_role(self):
             self.quota_classes_client.show_quota_class_set(
@@ -47,7 +47,7 @@
 
     @decorators.idempotent_id('72159478-23a7-4c75-989f-6bac609eca62')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:quota_classes")
+                                 rules=["volume_extension:quota_classes"])
     def test_update_quota_class_set(self):
         quota_class_set = self.quota_classes_client.show_quota_class_set(
             self.quota_name)['quota_class_set']
diff --git a/patrole_tempest_plugin/tests/api/volume/test_scheduler_stats_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_scheduler_stats_rbac.py
index a243587..efcfdaf 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_scheduler_stats_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_scheduler_stats_rbac.py
@@ -37,7 +37,7 @@
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="scheduler_extension:scheduler_stats:get_pools")
+        rules=["scheduler_extension:scheduler_stats:get_pools"])
     @decorators.idempotent_id('5f800441-4d30-48ec-9e5b-0d55bc86acbb')
     def test_list_back_end_storage_pools(self):
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/volume/test_snapshot_manage_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_snapshot_manage_rbac.py
index d238045..e2887e0 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_snapshot_manage_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_snapshot_manage_rbac.py
@@ -51,7 +51,7 @@
     @decorators.idempotent_id('bd7d62f2-e485-4626-87ef-03b7f19ee1d0')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="snapshot_extension:snapshot_manage")
+        rules=["snapshot_extension:snapshot_manage"])
     def test_manage_snapshot_rbac(self):
         name = data_utils.rand_name(self.__class__.__name__ +
                                     '-Managed-Snapshot')
@@ -73,7 +73,7 @@
     @decorators.idempotent_id('4a2e8934-9c0b-434e-8f0b-e18b9aff126f')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="snapshot_extension:snapshot_unmanage")
+        rules=["snapshot_extension:snapshot_unmanage"])
     def test_unmanage_snapshot_rbac(self):
         with self.rbac_utils.override_role(self):
             self.snapshots_client.unmanage_snapshot(self.snapshot['id'])
diff --git a/patrole_tempest_plugin/tests/api/volume/test_snapshots_actions_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_snapshots_actions_rbac.py
index 65b7526..ed42b2d 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_snapshots_actions_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_snapshots_actions_rbac.py
@@ -49,7 +49,7 @@
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:snapshot_admin_actions:reset_status")
+        rules=["volume_extension:snapshot_admin_actions:reset_status"])
     @decorators.idempotent_id('ea430145-34ef-408d-b678-95d5ae5f46eb')
     def test_reset_snapshot_status(self):
         status = 'error'
@@ -61,7 +61,7 @@
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:snapshot_admin_actions:force_delete")
+        rules=["volume_extension:snapshot_admin_actions:force_delete"])
     @decorators.idempotent_id('a8b0f7d8-4c00-4645-b8d5-33ab4eecc6cb')
     def test_snapshot_force_delete(self):
         temp_snapshot = self.create_snapshot(self.volume['id'])
@@ -73,7 +73,7 @@
     @decorators.idempotent_id('a95eab2a-c441-4609-9235-f7478627da88')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="snapshot_extension:snapshot_actions:update_snapshot_status")
+        rules=["snapshot_extension:snapshot_actions:update_snapshot_status"])
     def test_update_snapshot_status(self):
         status = 'creating'
         self.snapshots_client.reset_snapshot_status(
diff --git a/patrole_tempest_plugin/tests/api/volume/test_snapshots_metadata_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_snapshots_metadata_rbac.py
index 1c5fb2e..1141b7e 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_snapshots_metadata_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_snapshots_metadata_rbac.py
@@ -48,7 +48,7 @@
             self.snapshot_id, metadata)['metadata']
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_snapshot_metadata")
+                                 rules=["volume:get_snapshot_metadata"])
     @decorators.idempotent_id('f6912bb1-62e6-483d-bcd0-e98c1641f4c3')
     def test_get_snapshot_metadata(self):
         # Create volume and snapshot metadata
@@ -60,7 +60,7 @@
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_tenant_attribute")
+        rules=["volume_extension:volume_tenant_attribute"])
     @decorators.idempotent_id('e2c73b00-0c19-4bb7-8c61-d84b1a223ed1')
     def test_get_snapshot_metadata_for_volume_tenant(self):
         # Create volume and snapshot metadata
@@ -72,7 +72,7 @@
 
     @decorators.idempotent_id('7ea597f6-c544-4b10-aab0-ff68f595fb06')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:update_snapshot_metadata")
+                                 rules=["volume:update_snapshot_metadata"])
     def test_update_snapshot_metadata(self):
         self._create_test_snapshot_metadata()
         with self.rbac_utils.override_role(self):
@@ -83,7 +83,7 @@
 
     @decorators.idempotent_id('93068d02-0131-4dd3-af16-fc40d7128d93')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_snapshot_metadata")
+                                 rules=["volume:get_snapshot_metadata"])
     def test_show_snapshot_metadata_item(self):
         self._create_test_snapshot_metadata()
         with self.rbac_utils.override_role(self):
@@ -92,7 +92,7 @@
 
     @decorators.idempotent_id('1f8f43e7-da31-4128-bb3c-73fc548650e3')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:update_snapshot_metadata")
+                                 rules=["volume:update_snapshot_metadata"])
     def test_update_snapshot_metadata_item(self):
         update_item = {"key3": "value3_update"}
         self._create_test_snapshot_metadata()
@@ -102,7 +102,7 @@
 
     @decorators.idempotent_id('3ec32516-f7cd-4f88-b78a-ddee67492071')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:delete_snapshot_metadata")
+                                 rules=["volume:delete_snapshot_metadata"])
     def test_delete_snapshot_metadata_item(self):
         self._create_test_snapshot_metadata()
         with self.rbac_utils.override_role(self):
diff --git a/patrole_tempest_plugin/tests/api/volume/test_user_messages_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_user_messages_rbac.py
index 56ee1e0..962a9b1 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_user_messages_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_user_messages_rbac.py
@@ -65,7 +65,7 @@
     @decorators.idempotent_id('bf7f31a1-509b-4a7d-a8a8-ad6ce68229c7')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="message:get_all")
+        rules=["message:get_all"])
     def test_list_messages(self):
         with self.rbac_utils.override_role(self):
             self.messages_client.list_messages()['messages']
@@ -73,7 +73,7 @@
     @decorators.idempotent_id('9cc1ad1e-68a2-4407-8b60-ea77909bce08')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="message:get")
+        rules=["message:get"])
     def test_show_message(self):
         message_id = self._create_user_message()
 
@@ -83,7 +83,7 @@
     @decorators.idempotent_id('65ca7fb7-7f2c-443e-b144-ac86973a97be')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="message:delete")
+        rules=["message:delete"])
     def test_delete_message(self):
         message_id = self._create_user_message()
 
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 a4fc3fd..2a5b9fe 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
@@ -76,7 +76,7 @@
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_actions:attach")
+        rules=["volume_extension:volume_actions:attach"])
     @decorators.idempotent_id('f97b10e4-2eed-4f8b-8632-71c02cb9fe42')
     def test_attach_volume_to_instance(self):
         server = self._create_server()
@@ -94,7 +94,7 @@
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_actions:detach")
+        rules=["volume_extension:volume_actions:detach"])
     @decorators.idempotent_id('5a042f6a-688b-42e6-a02e-fe5c47b89b07')
     def test_detach_volume_from_instance(self):
         server = self._create_server()
@@ -107,7 +107,7 @@
             self.volumes_client, volume_id, 'available')
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:update_readonly_flag")
+                                 rules=["volume:update_readonly_flag"])
     @decorators.idempotent_id('2750717a-f250-4e41-9e09-02624aad6ff8')
     def test_volume_readonly_update(self):
         with self.rbac_utils.override_role(self):
@@ -118,7 +118,7 @@
 
     @decorators.idempotent_id('59b783c0-f4ef-430c-8a90-1bad97d4ec5c')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:update")
+                                 rules=["volume:update"])
     def test_volume_set_bootable(self):
         with self.rbac_utils.override_role(self):
             self.volumes_client.set_bootable_volume(self.volume['id'],
@@ -132,7 +132,7 @@
     @decorators.idempotent_id('41566922-75a1-4484-99c7-9c8782ee99ac')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_actions:reserve")
+        rules=["volume_extension:volume_actions:reserve"])
     def test_volume_reserve(self):
         with self.rbac_utils.override_role(self):
             self.volumes_client.reserve_volume(self.volume['id'])
@@ -145,14 +145,14 @@
     @decorators.idempotent_id('e5fa9564-77d9-4e57-b0c0-3e0ae4d08535')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_actions:unreserve")
+        rules=["volume_extension:volume_actions:unreserve"])
     def test_volume_unreserve(self):
         with self.rbac_utils.override_role(self):
             self.volumes_client.unreserve_volume(self.volume['id'])
 
     @decorators.idempotent_id('c015c82f-7010-48cc-bd71-4ef542046f20')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:retype")
+                                 rules=["volume:retype"])
     def test_volume_retype(self):
         vol_type = self.create_volume_type()['name']
         volume = self.create_volume()
@@ -164,7 +164,7 @@
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_admin_actions:reset_status")
+        rules=["volume_extension:volume_admin_actions:reset_status"])
     @decorators.idempotent_id('4b3dad7d-0e73-4839-8781-796dd3d7af1d')
     def test_volume_reset_status(self):
         volume = self.create_volume()
@@ -175,7 +175,7 @@
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_admin_actions:force_delete")
+        rules=["volume_extension:volume_admin_actions:force_delete"])
     @decorators.idempotent_id('a312a937-6abf-4b91-a950-747086cbce48')
     def test_volume_force_delete(self):
         volume = self.create_volume()
@@ -189,7 +189,7 @@
     @utils.services('compute')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_admin_actions:force_detach")
+        rules=["volume_extension:volume_admin_actions:force_detach"])
     def test_force_detach_volume_from_instance(self):
         volume = self.create_volume()
         server = self._create_server()
@@ -227,7 +227,7 @@
     @utils.services('image')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_actions:upload_image")
+        rules=["volume_extension:volume_actions:upload_image"])
     @decorators.idempotent_id('b0d0da46-903c-4445-893e-20e680d68b50')
     def test_volume_upload_image(self):
         # TODO(felipemonteiro): The ``upload_volume`` endpoint also enforces
@@ -251,7 +251,7 @@
     @utils.services('image')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_actions:upload_public")
+        rules=["volume_extension:volume_actions:upload_public"])
     @decorators.idempotent_id('578a84dd-a6bd-4f97-a418-4a0c3c272c08')
     def test_volume_upload_public(self):
         # This also enforces "volume_extension:volume_actions:upload_image".
@@ -277,7 +277,7 @@
     max_microversion = 'latest'
 
     @decorators.idempotent_id('a654833d-4811-4acd-93ef-5ac4a34c75bc')
-    @rbac_rule_validation.action(service="cinder", rule="volume:get_all")
+    @rbac_rule_validation.action(service="cinder", rules=["volume:get_all"])
     def test_show_volume_summary(self):
         with self.rbac_utils.override_role(self):
             self.volumes_client.show_volume_summary()
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py
index 61532c6..ac2255a 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_basic_crud_rbac.py
@@ -13,12 +13,16 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from tempest.common import waiters
+from tempest import config
 from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
+CONF = config.CONF
+
 
 class VolumesBasicCrudV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
@@ -28,34 +32,66 @@
         cls.volume = cls.create_volume()
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:create")
+                                 rules=["volume:create"])
     @decorators.idempotent_id('426b08ef-6394-4d06-9128-965d5a6c38ef')
     def test_create_volume(self):
+        name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
+        size = CONF.volume.volume_size
+
         with self.rbac_utils.override_role(self):
-            self.create_volume()
+            volume = self.volumes_client.create_volume(name=name, size=size)[
+                'volume']
+        # Use helper in base Tempest volume class which waits for deletion.
+        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
+
+    @decorators.idempotent_id('a009e6dc-e8bf-4412-99f5-8e45cffcffec')
+    @rbac_rule_validation.action(service="cinder",
+                                 rules=["volume:create_from_image"])
+    def test_create_volume_from_image(self):
+        name = data_utils.rand_name(self.__class__.__name__ + '-Volume')
+        size = CONF.volume.volume_size
+        img_uuid = CONF.compute.image_ref
+
+        with self.rbac_utils.override_role(self):
+            volume = self.volumes_client.create_volume(
+                name=name, size=size, imageRef=img_uuid)['volume']
+        # Use helper in base Tempest volume class which waits for deletion.
+        self.addCleanup(self.delete_volume, self.volumes_client, volume['id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                volume['id'], 'available')
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:delete")
+                                 rules=["volume:delete"])
     @decorators.idempotent_id('6de9f9c2-509f-4558-867b-af21c7163be4')
     def test_delete_volume(self):
         volume = self.create_volume()
         with self.rbac_utils.override_role(self):
             self.volumes_client.delete_volume(volume['id'])
+        self.volumes_client.wait_for_resource_deletion(volume['id'])
 
-    @rbac_rule_validation.action(service="cinder", rule="volume:get")
+    @rbac_rule_validation.action(service="cinder", rules=["volume:get"])
     @decorators.idempotent_id('c4c3fdd5-b1b1-49c3-b977-a9f40ee9257a')
-    def test_get_volume(self):
+    def test_show_volume(self):
         with self.rbac_utils.override_role(self):
             self.volumes_client.show_volume(self.volume['id'])
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_all")
+                                 rules=["volume:get_all"])
     @decorators.idempotent_id('e3ab7906-b04b-4c45-aa11-1104d302f940')
-    def test_volume_list(self):
+    def test_list_volumes(self):
         with self.rbac_utils.override_role(self):
             self.volumes_client.list_volumes()
 
-    @rbac_rule_validation.action(service="cinder", rule="volume:update")
+    @decorators.idempotent_id('9b6d5beb-254f-4f1b-9906-0bdce4042f53')
+    @rbac_rule_validation.action(service="cinder",
+                                 rules=["volume:get_all"])
+    def test_list_volumes_with_details(self):
+        with self.rbac_utils.override_role(self):
+            self.volumes_client.list_volumes(detail=True)
+
+    @rbac_rule_validation.action(service="cinder", rules=["volume:update"])
     @decorators.idempotent_id('b751b889-9a9b-40d8-ae7d-4b0f65e71ac7')
     def test_update_volume(self):
         update_name = data_utils.rand_name(self.__class__.__name__ + 'volume')
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
index c21c40e..8e2e264 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_hosts_rbac.py
@@ -22,7 +22,7 @@
 class VolumeHostsV3RbacTest(rbac_base.BaseVolumeRbacTest):
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:hosts")
+                                 rules=["volume_extension:hosts"])
     @decorators.idempotent_id('64e837f5-5452-4e26-b934-c721ea7a8644')
     def test_list_hosts(self):
         with self.rbac_utils.override_role(self):
@@ -30,7 +30,7 @@
 
     @decorators.idempotent_id('9ddf321e-788f-4787-b8cc-dfa59e264143')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:hosts")
+                                 rules=["volume_extension:hosts"])
     def test_show_host(self):
         hosts = self.volume_hosts_client.list_hosts()['hosts']
         host_names = [host['host_name'] for host in hosts]
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_metadata_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_metadata_rbac.py
index 8a50141..6c2c84d 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_metadata_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_metadata_rbac.py
@@ -42,14 +42,14 @@
                         self.volume['id'], "key1")
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:create_volume_metadata")
+                                 rules=["volume:create_volume_metadata"])
     @decorators.idempotent_id('232bbb8b-4c29-44dc-9077-b1398c20b738')
     def test_create_volume_metadata(self):
         with self.rbac_utils.override_role(self):
             self._add_metadata(self.volume)
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_volume_metadata")
+                                 rules=["volume:get_volume_metadata"])
     @decorators.idempotent_id('87ea37d9-23ab-47b2-a59c-16fc4d2c6dfa')
     def test_show_volume_metadata(self):
         with self.rbac_utils.override_role(self):
@@ -58,7 +58,7 @@
 
     @decorators.idempotent_id('5c0f4c19-b448-4f51-9224-dad5faddc3bb')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_volume_metadata")
+                                 rules=["volume:get_volume_metadata"])
     def test_show_volume_metadata_item(self):
         self._add_metadata(self.volume)
 
@@ -67,7 +67,7 @@
                 self.volume['id'], "key1")
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:delete_volume_metadata")
+                                 rules=["volume:delete_volume_metadata"])
     @decorators.idempotent_id('7498dfc1-9db2-4423-ad20-e6dcb25d1beb')
     def test_delete_volume_metadata_item(self):
         self._add_metadata(self.volume)
@@ -77,7 +77,7 @@
                                                             "key1")
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:update_volume_metadata")
+                                 rules=["volume:update_volume_metadata"])
     @decorators.idempotent_id('8ce2ff80-99ba-49ae-9bb1-7e96729ee5af')
     def test_update_volume_metadata_item(self):
         self._add_metadata(self.volume)
@@ -88,7 +88,7 @@
 
     @decorators.idempotent_id('a231b445-97a5-4657-b05f-245895e88da9')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:update_volume_metadata")
+                                 rules=["volume:update_volume_metadata"])
     def test_update_volume_metadata(self):
         self._add_metadata(self.volume)
         updated_metadata = {"key1": "value1"}
@@ -99,7 +99,7 @@
     @decorators.idempotent_id('39e8f82c-f1fc-4905-bf47-177ce2f71bb9')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_image_metadata")
+        rules=["volume_extension:volume_image_metadata"])
     def test_list_volumes_details_image_metadata(self):
         self.volumes_client.update_volume_image_metadata(
             self.volume['id'], image_id=self.image_id)
@@ -117,7 +117,7 @@
     @decorators.idempotent_id('53f94d52-0dd5-42cf-a3a4-59b35150b3d5')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_image_metadata")
+        rules=["volume_extension:volume_image_metadata"])
     def test_show_volume_details_image_metadata(self):
         self.volumes_client.update_volume_image_metadata(
             self.volume['id'], image_id=self.image_id)
@@ -135,7 +135,7 @@
     @decorators.idempotent_id('a9d9e825-5ea3-42e6-96f3-7ac4e97b2ed0')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_image_metadata")
+        rules=["volume_extension:volume_image_metadata"])
     def test_update_volume_image_metadata(self):
         with self.rbac_utils.override_role(self):
             self.volumes_client.update_volume_image_metadata(
@@ -146,7 +146,7 @@
     @decorators.idempotent_id('a41c8eed-2051-4a25-b401-df036faacbdc')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_image_metadata")
+        rules=["volume_extension:volume_image_metadata"])
     def test_delete_volume_image_metadata(self):
         self.volumes_client.update_volume_image_metadata(
             self.volume['id'], image_id=self.image_id)
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 32cc48c..cd1fb6e 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
@@ -45,21 +45,21 @@
 
     @decorators.idempotent_id('427c9f0c-982e-403d-ae45-c05f4d6322ff')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:quotas:show")
+                                 rules=["volume_extension:quotas:show"])
     def test_list_quotas(self):
         with self.rbac_utils.override_role(self):
             self.quotas_client.show_quota_set(self.demo_tenant_id)
 
     @decorators.idempotent_id('e47cf444-2753-4983-be6d-fc0d6523720f')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:quotas:show")
+                                 rules=["volume_extension:quotas:show"])
     def test_list_quotas_usage_true(self):
         with self.rbac_utils.override_role(self):
             self.quotas_client.show_quota_set(self.demo_tenant_id,
                                               params={'usage': True})
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:quotas:show")
+                                 rules=["volume_extension:quotas:show"])
     @decorators.idempotent_id('b3c7177e-b6b1-4d0f-810a-fc95606964dd')
     def test_list_default_quotas(self):
         with self.rbac_utils.override_role(self):
@@ -67,7 +67,7 @@
                 self.demo_tenant_id)
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:quotas:update")
+                                 rules=["volume_extension:quotas:update"])
     @decorators.idempotent_id('60f8f421-1630-4953-b449-b22af32265c7')
     def test_update_quota_set(self):
         self._restore_default_quota_set()
@@ -81,7 +81,7 @@
 
     @decorators.idempotent_id('329bdb88-5132-4810-b1fc-350d181577e3')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume_extension:quotas:delete")
+                                 rules=["volume_extension:quotas:delete"])
     def test_delete_quota_set(self):
         self._restore_default_quota_set()
 
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_services_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_services_rbac.py
index 1711c88..9f97a82 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_services_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_services_rbac.py
@@ -41,7 +41,7 @@
     @decorators.idempotent_id('b9134f01-97c0-4abd-9455-fe2f03e3f966')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:services:index")
+        rules=["volume_extension:services:index"])
     def test_list_services(self):
         with self.rbac_utils.override_role(self):
             self.services_client.list_services()['services']
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 a69682d..b7e45f9 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
@@ -49,7 +49,7 @@
         return transfer
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:create_transfer")
+                                 rules=["volume:create_transfer"])
     @decorators.idempotent_id('25413af4-468d-48ff-94ca-4436f8526b3e')
     def test_create_volume_transfer(self):
         with self.rbac_utils.override_role(self):
@@ -58,7 +58,7 @@
             self.volumes_client, self.volume['id'], 'awaiting-transfer')
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_transfer")
+                                 rules=["volume:get_transfer"])
     @decorators.idempotent_id('7a0925d3-ed97-4c25-8299-e5cdabe2eb55')
     def test_get_volume_transfer(self):
         transfer = self._create_transfer()
@@ -69,7 +69,7 @@
             self.transfers_client.show_volume_transfer(transfer['id'])
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_all_transfers")
+                                 rules=["volume:get_all_transfers"])
     @decorators.idempotent_id('02a06f2b-5040-49e2-b2b7-619a7db59603')
     def test_list_volume_transfers(self):
         with self.rbac_utils.override_role(self):
@@ -77,13 +77,13 @@
 
     @decorators.idempotent_id('e84e45b0-9872-40bf-bf44-971266161a86')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_all_transfers")
+                                 rules=["volume:get_all_transfers"])
     def test_list_volume_transfers_details(self):
         with self.rbac_utils.override_role(self):
             self.transfers_client.list_volume_transfers(detail=True)
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:accept_transfer")
+                                 rules=["volume:accept_transfer"])
     @decorators.idempotent_id('987f2a11-d657-4984-a6c9-28f06c1cd014')
     def test_accept_volume_transfer(self):
         transfer = self._create_transfer()
@@ -97,7 +97,7 @@
                                                 self.volume['id'], 'available')
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:delete_transfer")
+                                 rules=["volume:delete_transfer"])
     @decorators.idempotent_id('4672187e-7fff-454b-832a-5c8865dda868')
     def test_delete_volume_transfer(self):
         transfer = self._create_transfer()
diff --git a/patrole_tempest_plugin/tests/api/volume/test_volume_types_access_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_types_access_rbac.py
index 89dc0a2..3aed54d 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_types_access_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_types_access_rbac.py
@@ -52,7 +52,7 @@
     @decorators.idempotent_id('af70e6ad-e931-419f-9200-8bcc284e4e47')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_type_access")
+        rules=["volume_extension:volume_type_access"])
     def test_list_type_access(self):
         self._add_type_access()
 
@@ -63,7 +63,7 @@
     @decorators.idempotent_id('b462eeba-45d0-4d6e-945a-a1d27708d367')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_type_access:addProjectAccess")
+        rules=["volume_extension:volume_type_access:addProjectAccess"])
     def test_add_type_access(self):
         with self.rbac_utils.override_role(self):
             self._add_type_access(ignore_not_found=True)
@@ -71,7 +71,7 @@
     @decorators.idempotent_id('8f848aeb-636a-46f1-aeeb-e2a60e9d2bfe')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_type_access:removeProjectAccess")
+        rules=["volume_extension:volume_type_access:removeProjectAccess"])
     def test_remove_type_access(self):
         self._add_type_access(ignore_not_found=True)
 
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 8d4c265..b610dde 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
@@ -55,7 +55,7 @@
     @decorators.idempotent_id('76c36be2-2b6c-4acf-9aac-c9dc5c17cdbe')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:types_extra_specs:index")
+        rules=["volume_extension:types_extra_specs:index"])
     def test_list_volume_types_extra_specs(self):
         with self.rbac_utils.override_role(self):
             self.volume_types_client.list_volume_types_extra_specs(
@@ -63,7 +63,7 @@
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:types_extra_specs:create")
+        rules=["volume_extension:types_extra_specs:create"])
     @decorators.idempotent_id('eea40251-990b-49b0-99ae-10e4585b479b')
     def test_create_volume_type_extra_specs(self):
         with self.rbac_utils.override_role(self):
@@ -72,7 +72,7 @@
     @decorators.idempotent_id('e2dcc9c6-2fef-431d-afaf-92b45bc76d1a')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:types_extra_specs:show")
+        rules=["volume_extension:types_extra_specs:show"])
     def test_show_volume_type_extra_specs(self):
         self._create_volume_type_extra_specs()
 
@@ -83,7 +83,7 @@
     @decorators.idempotent_id('93001912-f938-41c7-8787-62dc7010fd52')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:types_extra_specs:delete")
+        rules=["volume_extension:types_extra_specs:delete"])
     def test_delete_volume_type_extra_specs(self):
         self._create_volume_type_extra_specs(ignore_not_found=True)
 
@@ -94,7 +94,7 @@
     @decorators.idempotent_id('0a444437-7402-4fbe-a18a-93af2ee00618')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:types_extra_specs:update")
+        rules=["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_volume_types_rbac.py b/patrole_tempest_plugin/tests/api/volume/test_volume_types_rbac.py
index a37661e..a5bec1f 100644
--- a/patrole_tempest_plugin/tests/api/volume/test_volume_types_rbac.py
+++ b/patrole_tempest_plugin/tests/api/volume/test_volume_types_rbac.py
@@ -24,7 +24,7 @@
     @decorators.idempotent_id('e2bbf968-d947-4a15-a4da-a98c3069731e')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:types_manage")
+        rules=["volume_extension:types_manage"])
     def test_create_volume_type(self):
         with self.rbac_utils.override_role(self):
             self.create_volume_type()
@@ -32,7 +32,7 @@
     @decorators.idempotent_id('2b74ac82-e03e-4801-86f3-d05c9acfd66b')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:types_manage")
+        rules=["volume_extension:types_manage"])
     def test_update_volume_type(self):
         volume_type = self.create_volume_type()
         with self.rbac_utils.override_role(self):
@@ -42,7 +42,7 @@
     @decorators.idempotent_id('90aec0ef-4f9b-4170-be6b-a392c12540be')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:types_manage")
+        rules=["volume_extension:types_manage"])
     def test_delete_volume_type(self):
         volume_type = self.create_volume_type()
         with self.rbac_utils.override_role(self):
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 7f5f566..bf22341 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
@@ -41,6 +41,7 @@
     def resource_setup(cls):
         super(VolumesBackupsV3RbacTest, cls).resource_setup()
         cls.volume = cls.create_volume()
+        cls.backup = cls._create_backup(volume_id=cls.volume['id'])
 
     def _decode_url(self, backup_url):
         return json.loads(base64.decode_as_text(backup_url))
@@ -56,24 +57,30 @@
 
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="backup:create")
+                                 rules=["backup:create"])
     @decorators.idempotent_id('6887ec94-0bcf-4ab7-b30f-3808a4b5a2a5')
     def test_create_backup(self):
+        backup_name = data_utils.rand_name(self.__class__.__name__ + '-Backup')
+
         with self.rbac_utils.override_role(self):
-            self.create_backup(volume_id=self.volume['id'])
+            backup = self.backups_client.create_backup(
+                volume_id=self.volume['id'], name=backup_name)['backup']
+        self.addCleanup(self.backups_client.delete_backup, backup['id'])
+        waiters.wait_for_volume_resource_status(
+            self.backups_client, backup['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                self.volume['id'], 'available')
 
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="backup:get")
+                                 rules=["backup:get"])
     @decorators.idempotent_id('abd92bdd-b0fb-4dc4-9cfc-de9e968f8c8a')
     def test_show_backup(self):
-        backup = self.create_backup(volume_id=self.volume['id'])
-
         with self.rbac_utils.override_role(self):
-            self.backups_client.show_backup(backup['id'])
+            self.backups_client.show_backup(self.backup['id'])
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="backup:get_all")
+                                 rules=["backup:get_all"])
     @decorators.idempotent_id('4d18f0f0-7e01-4007-b622-dedc859b22f6')
     def test_list_backups(self):
         with self.rbac_utils.override_role(self):
@@ -81,7 +88,7 @@
 
     @decorators.idempotent_id('dbd69865-876f-4835-b70e-7341153fb162')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="backup:get_all")
+                                 rules=["backup:get_all"])
     def test_list_backups_with_details(self):
         with self.rbac_utils.override_role(self):
             self.backups_client.list_backups(detail=True)
@@ -90,9 +97,12 @@
     @decorators.idempotent_id('50f43bde-205e-438e-9a05-5eac07fc3d63')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:backup_admin_actions:reset_status")
+        rules=["volume_extension:backup_admin_actions:reset_status"])
     def test_reset_backup_status(self):
+        # Use instance-level create_backup for easier debugging.
         backup = self.create_backup(volume_id=self.volume['id'])
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                self.volume['id'], 'available')
 
         with self.rbac_utils.override_role(self):
             self.backups_client.reset_backup_status(backup_id=backup['id'],
@@ -102,20 +112,20 @@
 
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="backup:restore")
+                                 rules=["backup:restore"])
     @decorators.idempotent_id('9c794bf9-2446-4f41-8fe0-80b71e757f9d')
     def test_restore_backup(self):
-        backup = self.create_backup(volume_id=self.volume['id'])
-
         with self.rbac_utils.override_role(self):
             restore = self.backups_client.restore_backup(
-                backup['id'])['restore']
+                self.backup['id'])['restore']
+        self.addCleanup(self.volumes_client.delete_volume,
+                        restore['volume_id'])
         waiters.wait_for_volume_resource_status(
             self.backups_client, restore['backup_id'], 'available')
 
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="backup:delete")
+                                 rules=["backup:delete"])
     @decorators.idempotent_id('d5d0c6a2-413d-437e-a73f-4bf2b41a20ed')
     def test_delete_backup(self):
         # Do not call the create_backup in Tempest's base volume class, because
@@ -127,6 +137,8 @@
                         self.backups_client.delete_backup, backup['id'])
         waiters.wait_for_volume_resource_status(self.backups_client,
                                                 backup['id'], 'available')
+        waiters.wait_for_volume_resource_status(self.volumes_client,
+                                                self.volume['id'], 'available')
 
         with self.rbac_utils.override_role(self):
             self.backups_client.delete_backup(backup['id'])
@@ -135,22 +147,20 @@
 
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="backup:export-import")
+                                 rules=["backup:export-import"])
     @decorators.idempotent_id('e984ec8d-e8eb-485c-98bc-f1856020303c')
     def test_export_backup(self):
-        backup = self.create_backup(volume_id=self.volume['id'])
-
         with self.rbac_utils.override_role(self):
-            self.backups_client.export_backup(backup['id'])['backup-record']
+            self.backups_client.export_backup(self.backup['id'])[
+                'backup-record']
 
     @decorators.attr(type='slow')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="backup:backup-import")
+                                 rules=["backup:backup-import"])
     @decorators.idempotent_id('1e70f039-4556-44cc-9cc1-edf2b7ed648b')
     def test_import_backup(self):
-        backup = self.create_backup(volume_id=self.volume['id'])
         export_backup = self.backups_client.export_backup(
-            backup['id'])['backup-record']
+            self.backup['id'])['backup-record']
         new_id = data_utils.rand_uuid()
         new_url = self._modify_backup_url(
             export_backup['backup_url'], {'id': new_id})
@@ -160,9 +170,17 @@
                 backup_service=export_backup['backup_service'],
                 backup_url=new_url)['backup']
         self.addCleanup(self.backups_client.delete_backup, import_backup['id'])
+        waiters.wait_for_volume_resource_status(self.backups_client,
+                                                import_backup['id'],
+                                                'available')
 
 
 class VolumesBackupsV318RbacTest(rbac_base.BaseVolumeRbacTest):
+    """Validates that the ``GET /backups/{backup_id}`` and
+    ``GET /backups/details`` APIs inject the expected attribute
+    'os-backup-project-attr:project_id' into the response body following
+    successful authorization.
+    """
     _api_version = 3
     # The minimum microversion for showing 'os-backup-project-attr:project_id'
     # is 3.18.
@@ -175,22 +193,36 @@
         if not CONF.volume_feature_enabled.backup:
             raise cls.skipException("Cinder backup feature disabled")
 
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesBackupsV318RbacTest, cls).resource_setup()
+        cls.volume = cls.create_volume()
+        cls.backup = cls._create_backup(volume_id=cls.volume['id'])
+        cls.expected_attr = 'os-backup-project-attr:project_id'
+
     @decorators.idempotent_id('69801485-d5be-4e75-bbb4-168d50b5a8c2')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="backup:backup_project_attribute")
+                                 rules=["backup:backup_project_attribute"])
     def test_show_backup_project_attribute(self):
-        volume = self.create_volume()
-        backup = self.create_backup(volume_id=volume['id'])
-        expected_attr = 'os-backup-project-attr:project_id'
-
         with self.rbac_utils.override_role(self):
-            body = self.backups_client.show_backup(backup['id'])['backup']
+            body = self.backups_client.show_backup(self.backup['id'])['backup']
 
         # Show backup API attempts to inject the attribute below into the
         # response body but only if policy enforcement succeeds.
-        if expected_attr not in body:
+        if self.expected_attr not in body:
             raise rbac_exceptions.RbacMalformedResponse(
-                attribute=expected_attr)
+                attribute=self.expected_attr)
+
+    @decorators.idempotent_id('aa40b7c0-5974-48be-8cbc-e23cc61c4c68')
+    @rbac_rule_validation.action(service="cinder",
+                                 rules=["backup:backup_project_attribute"])
+    def test_list_backup_details_project_attribute(self):
+        with self.rbac_utils.override_role(self):
+            body = self.backups_client.list_backups(detail=True)['backups']
+
+        if self.expected_attr not in body[0]:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute=self.expected_attr)
 
 
 class VolumesBackupsV39RbacTest(rbac_base.BaseVolumeRbacTest):
@@ -204,18 +236,22 @@
         if not CONF.volume_feature_enabled.backup:
             raise cls.skipException("Cinder backup feature disabled")
 
+    @classmethod
+    def resource_setup(cls):
+        super(VolumesBackupsV39RbacTest, cls).resource_setup()
+        cls.volume = cls.create_volume()
+        cls.backup = cls._create_backup(volume_id=cls.volume['id'])
+
     @decorators.attr(type='slow')
     @decorators.idempotent_id('b45b0e98-6eb8-4c62-aa53-0f8c7c09faa6')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="backup:update")
+        rules=["backup:update"])
     def test_backup_update(self):
-        volume = self.create_volume()
-        backup = self.create_backup(volume_id=volume['id'])
         update_kwargs = {
             'name': data_utils.rand_name(self.__class__.__name__ + '-Backup'),
             'description': data_utils.rand_name("volume-backup-description")
         }
         with self.rbac_utils.override_role(self):
-            self.backups_client.update_backup(backup['id'],
+            self.backups_client.update_backup(self.backup['id'],
                                               **update_kwargs)
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 18883c9..ec6cf66 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
@@ -28,7 +28,7 @@
         # Create a test shared volume for tests
         cls.volume = cls.create_volume()
 
-    @rbac_rule_validation.action(service="cinder", rule="volume:extend")
+    @rbac_rule_validation.action(service="cinder", rules=["volume:extend"])
     @decorators.idempotent_id('1627b065-4081-4e14-8340-8e4fb02ceaf2')
     def test_volume_extend(self):
         # Extend volume test
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 852d81e..2782e22 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
@@ -66,7 +66,7 @@
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_manage")
+        rules=["volume_extension:volume_manage"])
     @decorators.idempotent_id('114f9708-939b-407e-aeac-d21ebfabaad3')
     def test_volume_manage(self):
         volume_id = self.create_volume()['id']
@@ -108,7 +108,7 @@
 
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:volume_unmanage")
+        rules=["volume_extension:volume_unmanage"])
     @decorators.idempotent_id('d5d72abe-60bc-45ac-a8f2-c21b24f0b5d6')
     def test_volume_unmanage(self):
         volume_id = self.create_volume()['id']
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 7d721c4..40469a2 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
@@ -46,7 +46,7 @@
             detail=with_detail, **params)['snapshots']
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:create_snapshot")
+                                 rules=["volume:create_snapshot"])
     @decorators.idempotent_id('ac7b2ee5-fbc0-4360-afc2-de8fa4881ede')
     def test_create_snapshot(self):
         # Create a temp snapshot
@@ -54,7 +54,7 @@
             self.create_snapshot(self.volume['id'])
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_snapshot")
+                                 rules=["volume:get_snapshot"])
     @decorators.idempotent_id('93a11b40-1ba8-44d6-a196-f8d97220f796')
     def test_show_snapshot(self):
         # Get the snapshot
@@ -65,7 +65,7 @@
     @decorators.idempotent_id('5d6f5f21-9293-4f2a-8f44-cabdc24d92cb')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:extended_snapshot_attributes")
+        rules=["volume_extension:extended_snapshot_attributes"])
     def test_show_snapshot_with_extended_attributes(self):
         """List snapshots with extended attributes."""
         expected_attrs = ('os-extended-snapshot-attributes:project_id',
@@ -80,7 +80,7 @@
                     attribute=expected_attr)
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:update_snapshot")
+                                 rules=["volume:update_snapshot"])
     @decorators.idempotent_id('53fe8ee3-3bea-4ae8-a979-3c98ea72f620')
     def test_update_snapshot(self):
         new_desc = 'This is the new description of snapshot.'
@@ -93,7 +93,7 @@
             self.snapshots_client, self.snapshot['id'], 'available')
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:delete_snapshot")
+                                 rules=["volume:delete_snapshot"])
     @decorators.idempotent_id('c7fe54ec-3b70-4772-ba11-f166d95888a3')
     def test_delete_snapshot(self):
         # Create a temp snapshot
@@ -105,7 +105,7 @@
             temp_snapshot['id'])
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_all_snapshots")
+                                 rules=["volume:get_all_snapshots"])
     @decorators.idempotent_id('e4edf0c0-2cd3-420f-b8ab-4d98a0718608')
     def test_list_snapshots(self):
         """List snapshots with params."""
@@ -115,7 +115,7 @@
 
     @decorators.idempotent_id('f3155d8e-45ee-45c9-910d-18c0242229e1')
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_all_snapshots")
+                                 rules=["volume:get_all_snapshots"])
     def test_list_snapshots_details(self):
         """List snapshots details with params."""
         params = {'name': self.snapshot['name']}
@@ -125,7 +125,7 @@
     @decorators.idempotent_id('dd37f388-2731-446d-a78f-676997ebb04a')
     @rbac_rule_validation.action(
         service="cinder",
-        rule="volume_extension:extended_snapshot_attributes")
+        rules=["volume_extension:extended_snapshot_attributes"])
     def test_list_snapshots_details_with_extended_attributes(self):
         """List snapshots details with extended attributes."""
         expected_attrs = ('os-extended-snapshot-attributes:project_id',
diff --git a/patrole_tempest_plugin/tests/unit/fixtures.py b/patrole_tempest_plugin/tests/unit/fixtures.py
index 4e3387e..aee36fa 100644
--- a/patrole_tempest_plugin/tests/unit/fixtures.py
+++ b/patrole_tempest_plugin/tests/unit/fixtures.py
@@ -16,6 +16,7 @@
 """Fixtures for Patrole tests."""
 from __future__ import absolute_import
 
+from contextlib import contextmanager
 import fixtures
 import mock
 import time
@@ -68,16 +69,19 @@
         self.useFixture(ConfPatcher(rbac_test_role='member', group='patrole'))
         self.useFixture(ConfPatcher(
             admin_role='admin', auth_version='v3', group='identity'))
+        self.useFixture(ConfPatcher(
+            api_v3=True, group='identity-feature-enabled'))
 
         test_obj_kwargs = {
             'os_primary.credentials.user_id': self.USER_ID,
             'os_primary.credentials.tenant_id': self.PROJECT_ID,
             'os_primary.credentials.project_id': self.PROJECT_ID,
-            'get_identity_version.return_value': 'v3'
         }
         self.mock_test_obj = mock.Mock(
             __name__='patrole_unit_test', spec=test.BaseTestCase,
-            os_primary=mock.Mock(), **test_obj_kwargs)
+            os_primary=mock.Mock(),
+            get_auth_providers=mock.Mock(return_value=[mock.Mock()]),
+            **test_obj_kwargs)
 
         # Mock out functionality that can't be used by unit tests. Mocking out
         # time.sleep is a test optimization.
@@ -115,6 +119,17 @@
             new_role = 'member' if role_toggle else 'admin'
             self.set_roles(['admin', 'member'], [new_role])
 
+    @contextmanager
+    def real_override_role(self, test_obj):
+        """Actual call to ``override_role``.
+
+        Useful for ensuring all the necessary mocks are performed before
+        the method in question is called.
+        """
+        _rbac_utils = rbac_utils.RbacUtils(test_obj)
+        with _rbac_utils.override_role(test_obj):
+            yield
+
     def set_roles(self, roles, roles_on_project=None):
         """Set the list of available roles in the system.
 
diff --git a/patrole_tempest_plugin/tests/unit/resources/custom_rbac_policy.yaml b/patrole_tempest_plugin/tests/unit/resources/custom_rbac_policy.yaml
new file mode 100644
index 0000000..444bd2e
--- /dev/null
+++ b/patrole_tempest_plugin/tests/unit/resources/custom_rbac_policy.yaml
@@ -0,0 +1,13 @@
+---
+even_rule: role:two or role:four or role:six or role:eight
+odd_rule: role:one or role:three or role:five or role:seven or role:nine
+zero_rule: role:zero
+prime_rule: role:one or role:two or role:three or role:five or role:seven
+all_rule: ''
+
+policy_action_1: rule:even_rule
+policy_action_2: rule:odd_rule
+policy_action_3: rule:zero_rule
+policy_action_4: rule:prime_rule
+policy_action_5: rule:all_rule
+policy_action_6: role:eight
diff --git a/patrole_tempest_plugin/tests/unit/test_hacking.py b/patrole_tempest_plugin/tests/unit/test_hacking.py
index 6096c24..d35b816 100644
--- a/patrole_tempest_plugin/tests/unit/test_hacking.py
+++ b/patrole_tempest_plugin/tests/unit/test_hacking.py
@@ -256,3 +256,53 @@
         self.assertTrue(checks.no_client_alias_in_test_cases(
             "  cls.client",
             "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+
+    def test_no_plugin_rbac_test_suffix_in_plugin_test_class_name(self):
+        check = checks.no_plugin_rbac_test_suffix_in_plugin_test_class_name
+
+        # Passing cases: these do not inherit from "PluginRbacTest" base class.
+        self.assertFalse(check(
+            "class FakeRbacTest(BaseFakeRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertFalse(check(
+            "class FakeRbacTest(base.BaseFakeRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+
+        # Passing cases: these **do** end in correct test class suffix.
+        self.assertFalse(check(
+            "class FakePluginRbacTest(BaseFakePluginRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertFalse(check(
+            "class FakePluginRbacTest(base.BaseFakePluginRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+
+        # Passing cases: plugin base class inherits from another base class.
+        self.assertFalse(check(
+            "class BaseFakePluginRbacTest(base.BaseFakeRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertFalse(check(
+            "class BaseFakePluginRbacTest(BaseFakeRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+
+        # Failing cases: these **do not** end in correct test class suffix.
+        # Case 1: RbacTest subclass doesn't end in PluginRbacTest.
+        self.assertTrue(check(
+            "class FakeRbacTest(base.BaseFakePluginRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertTrue(check(
+            "class FakeRbacTest(BaseFakePluginRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertTrue(check(
+            "class FakeRbacTest(BaseFakeNetworkPluginRbacTest)",
+            "./patrole_tempest_plugin/tests/api/network/fake_test_rbac.py"))
+        # Case 2: PluginRbacTest subclass doesn't inherit from
+        # BasePluginRbacTest.
+        self.assertTrue(check(
+            "class FakePluginRbacTest(base.BaseFakeRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertTrue(check(
+            "class FakePluginRbacTest(BaseFakeRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
+        self.assertTrue(check(
+            "class FakeNeutronPluginRbacTest(BaseFakeNeutronRbacTest)",
+            "./patrole_tempest_plugin/tests/api/fake_test_rbac.py"))
diff --git a/patrole_tempest_plugin/tests/unit/test_policy_authority.py b/patrole_tempest_plugin/tests/unit/test_policy_authority.py
index d396a29..624c0c5 100644
--- a/patrole_tempest_plugin/tests/unit/test_policy_authority.py
+++ b/patrole_tempest_plugin/tests/unit/test_policy_authority.py
@@ -13,7 +13,6 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import json
 import mock
 import os
 
@@ -61,11 +60,14 @@
         self.tenant_policy_file = os.path.join(current_directory,
                                                'resources',
                                                'tenant_rbac_policy.json')
-        self.conf_policy_path = os.path.join(
+        self.conf_policy_path_json = os.path.join(
             current_directory, 'resources', '%s.json')
 
+        self.conf_policy_path_yaml = os.path.join(
+            current_directory, 'resources', '%s.yaml')
+
         self.useFixture(fixtures.ConfPatcher(
-            custom_policy_files=[self.conf_policy_path], group='patrole'))
+            custom_policy_files=[self.conf_policy_path_json], group='patrole'))
         self.useFixture(fixtures.ConfPatcher(
             api_v3=True, api_v2=False, group='identity-feature-enabled'))
 
@@ -74,13 +76,18 @@
             if attr in dir(policy_authority.PolicyAuthority):
                 delattr(policy_authority.PolicyAuthority, attr)
 
-    def _get_fake_policy_rule(self, name, rule):
-        fake_rule = mock.Mock(check=rule, __name__='foo')
-        fake_rule.name = name
-        return fake_rule
+    @staticmethod
+    def _get_fake_policies(rules):
+        fake_rules = []
+        rules = policy_authority.policy.Rules.from_dict(rules)
+        for name, check in rules.items():
+            fake_rule = mock.Mock(check=check, __name__='foo')
+            fake_rule.name = name
+            fake_rules.append(fake_rule)
+        return fake_rules
 
     @mock.patch.object(policy_authority, 'LOG', autospec=True)
-    def test_custom_policy(self, m_log):
+    def _test_custom_policy(self, *args):
         default_roles = ['zero', 'one', 'two', 'three', 'four',
                          'five', 'six', 'seven', 'eight', 'nine']
 
@@ -105,6 +112,16 @@
             for role in set(default_roles) - set(role_list):
                 self.assertFalse(authority.allowed(rule, role))
 
+    def test_custom_policy_json(self):
+        # The CONF.patrole.custom_policy_files has a path to JSON file by
+        # default, so we don't need to use ConfPatcher here.
+        self._test_custom_policy()
+
+    def test_custom_policy_yaml(self):
+        self.useFixture(fixtures.ConfPatcher(
+            custom_policy_files=[self.conf_policy_path_yaml], group='patrole'))
+        self._test_custom_policy()
+
     def test_admin_policy_file_with_admin_role(self):
         test_tenant_id = mock.sentinel.tenant_id
         test_user_id = mock.sentinel.user_id
@@ -270,9 +287,9 @@
 
         fake_rule = 'fake_rule'
         expected_message = (
-            "Policy action \"{0}\" not found in policy file: {1} or among "
-            "registered policy in code defaults for service.").format(
-            fake_rule, self.custom_policy_file)
+            'Policy action "{0}" not found in policy files: {1} or among '
+            'registered policy in code defaults for {2} service.').format(
+            fake_rule, [self.custom_policy_file], "custom_rbac_policy")
 
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
                               authority.allowed, fake_rule, None)
@@ -292,9 +309,10 @@
                mock.sentinel.error)})
 
         expected_message = (
-            "Policy action \"{0}\" not found in policy file: {1} or among "
-            "registered policy in code defaults for service.").format(
-            mock.sentinel.rule, self.custom_policy_file)
+            'Policy action "{0}" not found in policy files: {1} or among '
+            'registered policy in code defaults for {2} service.').format(
+            mock.sentinel.rule, [self.custom_policy_file],
+            "custom_rbac_policy")
 
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
                               authority.allowed, mock.sentinel.rule, None)
@@ -302,18 +320,15 @@
         m_log.debug.assert_called_once_with(expected_message)
 
     @mock.patch.object(policy_authority, 'stevedore', autospec=True)
-    def test_get_policy_data_from_file_and_from_code(self, mock_stevedore):
-        fake_policy_rules = [
-            self._get_fake_policy_rule('code_policy_action_1',
-                                       'rule:code_rule_1'),
-            self._get_fake_policy_rule('code_policy_action_2',
-                                       'rule:code_rule_2'),
-            self._get_fake_policy_rule('code_policy_action_3',
-                                       'rule:code_rule_3'),
-        ]
+    def test_get_rules_from_file_and_from_code(self, mock_stevedore):
+        fake_policy_rules = self._get_fake_policies({
+            'code_policy_action_1': 'rule:code_rule_1',
+            'code_policy_action_2': 'rule:code_rule_2',
+            'code_policy_action_3': 'rule:code_rule_3',
+        })
 
         mock_manager = mock.Mock(obj=fake_policy_rules, __name__='foo')
-        mock_manager.configure_mock(name='fake_service')
+        mock_manager.configure_mock(name='tenant_rbac_policy')
         mock_stevedore.named.NamedExtensionManager.return_value = [
             mock_manager
         ]
@@ -323,10 +338,10 @@
         authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, "tenant_rbac_policy")
 
-        policy_data = authority._get_policy_data('fake_service')
-        self.assertIsInstance(policy_data, str)
+        rules = authority.get_rules()
+        self.assertIsInstance(rules, policy_authority.policy.Rules)
 
-        actual_policy_data = json.loads(policy_data)
+        actual_policy_data = {k: str(v) for k, v in rules.items()}
         expected_policy_data = {
             "code_policy_action_1": "rule:code_rule_1",
             "code_policy_action_2": "rule:code_rule_2",
@@ -335,26 +350,25 @@
             "rule2": "tenant_id:%(tenant_id)s",
             "rule3": "project_id:%(project_id)s",
             "rule4": "user_id:%(user_id)s",
-            "admin_tenant_rule": "role:admin and tenant_id:%(tenant_id)s",
-            "admin_user_rule": "role:admin and user_id:%(user_id)s"
+            "admin_tenant_rule": "(role:admin and tenant_id:%(tenant_id)s)",
+            "admin_user_rule": "(role:admin and user_id:%(user_id)s)"
         }
 
         self.assertEqual(expected_policy_data, actual_policy_data)
 
     @mock.patch.object(policy_authority, 'stevedore', autospec=True)
-    def test_get_policy_data_from_file_and_from_code_with_overwrite(
+    def test_get_rules_from_file_and_from_code_with_overwrite(
             self, mock_stevedore):
         # The custom policy file should overwrite default rules rule1 and rule2
         # that are defined in code.
-        fake_policy_rules = [
-            self._get_fake_policy_rule('rule1', 'rule:code_rule_1'),
-            self._get_fake_policy_rule('rule2', 'rule:code_rule_2'),
-            self._get_fake_policy_rule('code_policy_action_3',
-                                       'rule:code_rule_3'),
-        ]
+        fake_policy_rules = self._get_fake_policies({
+            'rule1': 'rule:code_rule_1',
+            'rule2': 'rule:code_rule_2',
+            'code_policy_action_3': 'rule:code_rule_3',
+        })
 
         mock_manager = mock.Mock(obj=fake_policy_rules, __name__='foo')
-        mock_manager.configure_mock(name='fake_service')
+        mock_manager.configure_mock(name='tenant_rbac_policy')
         mock_stevedore.named.NamedExtensionManager.return_value = [
             mock_manager
         ]
@@ -364,81 +378,51 @@
 
         authority = policy_authority.PolicyAuthority(
             test_tenant_id, test_user_id, 'tenant_rbac_policy')
-        policy_data = authority._get_policy_data('fake_service')
-        self.assertIsInstance(policy_data, str)
+        rules = authority.get_rules()
+        self.assertIsInstance(rules, policy_authority.policy.Rules)
 
-        actual_policy_data = json.loads(policy_data)
+        actual_policy_data = {k: str(v) for k, v in rules.items()}
         expected_policy_data = {
             "code_policy_action_3": "rule:code_rule_3",
             "rule1": "tenant_id:%(network:tenant_id)s",
             "rule2": "tenant_id:%(tenant_id)s",
             "rule3": "project_id:%(project_id)s",
             "rule4": "user_id:%(user_id)s",
-            "admin_tenant_rule": "role:admin and tenant_id:%(tenant_id)s",
-            "admin_user_rule": "role:admin and user_id:%(user_id)s"
+            "admin_tenant_rule": "(role:admin and tenant_id:%(tenant_id)s)",
+            "admin_user_rule": "(role:admin and user_id:%(user_id)s)"
         }
 
         self.assertEqual(expected_policy_data, actual_policy_data)
 
     @mock.patch.object(policy_authority, 'stevedore', autospec=True)
-    def test_get_policy_data_cannot_find_policy(self, mock_stevedore):
+    def test_get_rules_cannot_find_policy(self, mock_stevedore):
         mock_stevedore.named.NamedExtensionManager.return_value = None
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
                               policy_authority.PolicyAuthority,
                               None, None, 'test_service')
 
         expected_error = (
-            'Policy file for {0} service was not found among the registered '
+            'Policy files for {0} service were not found among the registered '
             'in-code policies or in any of the possible policy files: {1}.'
             .format('test_service',
                     [CONF.patrole.custom_policy_files[0] % 'test_service']))
         self.assertIn(expected_error, str(e))
 
-    @mock.patch.object(policy_authority, 'json', autospec=True)
+    @mock.patch.object(policy_authority.policy, 'parse_file_contents',
+                       autospec=True)
     @mock.patch.object(policy_authority, 'stevedore', autospec=True)
-    def test_get_policy_data_without_valid_policy(self, mock_stevedore,
-                                                  mock_json):
-        test_policy_action = mock.Mock(check='rule:bar', __name__='foo')
-        test_policy_action.configure_mock(name='foo')
-
-        test_policy = mock.Mock(obj=[test_policy_action], __name__='foo')
-        test_policy.configure_mock(name='test_service')
-
-        mock_stevedore.named.NamedExtensionManager\
-            .return_value = [test_policy]
-
-        mock_json.dumps.side_effect = ValueError
-
-        e = self.assertRaises(rbac_exceptions.RbacParsingException,
-                              policy_authority.PolicyAuthority,
-                              None, None, 'test_service')
-
-        expected_error = "Policy file for {0} service is invalid."\
-                         .format("test_service")
-        self.assertIn(expected_error, str(e))
-
-        mock_stevedore.named.NamedExtensionManager.assert_called_once_with(
-            'oslo.policy.policies',
-            names=['test_service'],
-            on_load_failure_callback=None,
-            invoke_on_load=True,
-            warn_on_missing_entrypoint=False)
-
-    @mock.patch.object(policy_authority, 'json', autospec=True)
-    @mock.patch.object(policy_authority, 'stevedore', autospec=True)
-    def test_get_policy_data_from_file_not_json(self, mock_stevedore,
-                                                mock_json):
+    def test_get_rules_without_valid_policy(self, mock_stevedore,
+                                            mock_parse_file_contents):
         mock_stevedore.named.NamedExtensionManager.return_value = None
-        mock_json.loads.side_effect = ValueError
+        mock_parse_file_contents.side_effect = ValueError
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
                               policy_authority.PolicyAuthority,
                               None, None, 'tenant_rbac_policy')
 
         expected_error = (
-            'Policy file for {0} service was not found among the registered '
-            'in-code policies or in any of the possible policy files: {1}.'
-            .format('tenant_rbac_policy', [CONF.patrole.custom_policy_files[0]
-                                           % 'tenant_rbac_policy']))
+            'Policy files for {0} service were not found among the registered '
+            'in-code policies or in any of the possible policy files:'
+            .format('tenant_rbac_policy'))
         self.assertIn(expected_error, str(e))
 
     def test_discover_policy_files(self):
@@ -450,56 +434,62 @@
                       dir(policy_authority.PolicyAuthority))
         self.assertIn('policy_files', dir(policy_parser))
         self.assertIn('tenant_rbac_policy', policy_parser.policy_files)
-        self.assertEqual(self.conf_policy_path % 'tenant_rbac_policy',
+        self.assertEqual([self.conf_policy_path_json % 'tenant_rbac_policy'],
                          policy_parser.policy_files['tenant_rbac_policy'])
 
     @mock.patch.object(policy_authority, 'policy', autospec=True)
-    @mock.patch.object(policy_authority.PolicyAuthority, '_get_policy_data',
+    @mock.patch.object(policy_authority.PolicyAuthority, 'get_rules',
                        autospec=True)
     @mock.patch.object(policy_authority, 'clients', autospec=True)
     @mock.patch.object(policy_authority, 'os', autospec=True)
-    def test_discover_policy_files_with_many_invalid_one_valid(self, m_os,
-                                                               m_creds, *args):
+    @mock.patch.object(policy_authority, 'glob', autospec=True)
+    def test_discover_policy_files_with_many_invalid_one_valid(self, m_glob,
+                                                               m_os, m_creds,
+                                                               *args):
+        service = 'test_service'
+        custom_policy_files = ['foo/%s', 'bar/%s', 'baz/%s']
+        m_glob.iglob.side_effect = [iter([path % service])
+                                    for path in custom_policy_files]
         # Only the 3rd path is valid.
-        m_os.path.isfile.side_effect = [False, False, True, False]
+        m_os.path.isfile.side_effect = [False, False, True]
 
         # Ensure the outer for loop runs only once in `discover_policy_files`.
         m_creds.Manager().identity_services_v3_client.\
             list_services.return_value = {
-                'services': [{'name': 'test_service'}]}
+                'services': [{'name': service}]}
 
         # The expected policy will be 'baz/test_service'.
         self.useFixture(fixtures.ConfPatcher(
-            custom_policy_files=['foo/%s', 'bar/%s', 'baz/%s'],
+            custom_policy_files=custom_policy_files,
             group='patrole'))
 
         policy_parser = policy_authority.PolicyAuthority(
-            None, None, 'test_service')
+            None, None, service)
 
         # Ensure that "policy_files" is set at class and instance levels.
-        self.assertIn('policy_files',
-                      dir(policy_authority.PolicyAuthority))
-        self.assertIn('policy_files', dir(policy_parser))
-        self.assertIn('test_service', policy_parser.policy_files)
-        self.assertEqual('baz/test_service',
-                         policy_parser.policy_files['test_service'])
+        self.assertTrue(hasattr(policy_authority.PolicyAuthority,
+                                'policy_files'))
+        self.assertTrue(hasattr(policy_parser, 'policy_files'))
+        self.assertEqual(['baz/%s' % service],
+                         policy_parser.policy_files[service])
 
     def test_discover_policy_files_with_no_valid_files(self):
         expected_error = (
-            'Policy file for {0} service was not found among the registered '
+            'Policy files for {0} service were not found among the registered '
             'in-code policies or in any of the possible policy files: {1}.'
-            .format('test_service', [self.conf_policy_path % 'test_service']))
+            .format('test_service',
+                    [self.conf_policy_path_json % 'test_service']))
 
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
                               policy_authority.PolicyAuthority,
                               None, None, 'test_service')
         self.assertIn(expected_error, str(e))
 
-        self.assertIn('policy_files',
-                      dir(policy_authority.PolicyAuthority))
-        self.assertNotIn(
-            'test_service',
-            policy_authority.PolicyAuthority.policy_files.keys())
+        self.assertTrue(hasattr(policy_authority.PolicyAuthority,
+                                'policy_files'))
+        self.assertEqual(
+            [],
+            policy_authority.PolicyAuthority.policy_files['test_service'])
 
     def _test_validate_service(self, v2_services, v3_services,
                                expected_failure=False, expected_services=None):
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py b/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
index 1bf5510..1a2c691 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
@@ -12,9 +12,13 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from __future__ import absolute_import
+
+import functools
 import mock
 from oslo_config import cfg
 
+import fixtures
 from tempest.lib import exceptions
 from tempest import manager
 from tempest import test
@@ -23,7 +27,7 @@
 from patrole_tempest_plugin import rbac_exceptions
 from patrole_tempest_plugin import rbac_rule_validation as rbac_rv
 from patrole_tempest_plugin import rbac_utils
-from patrole_tempest_plugin.tests.unit import fixtures
+from patrole_tempest_plugin.tests.unit import fixtures as patrole_fixtures
 
 CONF = cfg.CONF
 
@@ -43,10 +47,12 @@
         setattr(self.mock_test_args.os_primary, 'credentials', mock_creds)
 
         self.useFixture(
-            fixtures.ConfPatcher(rbac_test_role='Member', group='patrole'))
+            patrole_fixtures.ConfPatcher(rbac_test_role='Member',
+                                         group='patrole'))
         # Disable patrole log for unit tests.
         self.useFixture(
-            fixtures.ConfPatcher(enable_reporting=False, group='patrole_log'))
+            patrole_fixtures.ConfPatcher(enable_reporting=False,
+                                         group='patrole_log'))
 
 
 class RBACRuleValidationTest(BaseRBACRuleValidationTest):
@@ -54,6 +60,12 @@
     ``rbac_rule_validation`` decorator.
     """
 
+    def setUp(self):
+        super(RBACRuleValidationTest, self).setUp()
+        # This behavior is tested in separate test class below.
+        self.useFixture(fixtures.MockPatchObject(
+            rbac_rv, '_validate_override_role_called'))
+
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
     @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     def test_rule_validation_have_permission_no_exc(self, mock_authority,
@@ -69,7 +81,6 @@
             pass
 
         test_policy(self.mock_test_args)
-        mock_log.warning.assert_not_called()
         mock_log.error.assert_not_called()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
@@ -88,7 +99,6 @@
             raise exceptions.Forbidden()
 
         test_policy(self.mock_test_args)
-        mock_log.warning.assert_not_called()
         mock_log.error.assert_not_called()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
@@ -119,7 +129,8 @@
     @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     def test_rule_validation_rbac_malformed_response_positive(
             self, mock_authority, mock_log):
-        """Test RbacMalformedResponse error is thrown without permission passes.
+        """Test RbacMalformedResponse error is thrown without permission
+        passes.
 
         Positive test case: if RbacMalformedResponse is thrown and the user is
         not allowed to perform the action, then this is a success.
@@ -132,7 +143,6 @@
             raise rbac_exceptions.RbacMalformedResponse()
 
         mock_log.error.assert_not_called()
-        mock_log.warning.assert_not_called()
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
     @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
@@ -158,48 +168,6 @@
 
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
     @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
-    def test_rule_validation_rbac_conflicting_policies_positive(
-            self, mock_authority, mock_log):
-        """Test RbacConflictingPolicies error is thrown without permission passes.
-
-        Positive test case: if RbacConflictingPolicies is thrown and the user
-        is not allowed to perform the action, then this is a success.
-        """
-        mock_authority.PolicyAuthority.return_value.allowed.return_value =\
-            False
-
-        @rbac_rv.action(mock.sentinel.service, rules=[mock.sentinel.action])
-        def test_policy(*args):
-            raise rbac_exceptions.RbacConflictingPolicies()
-
-        mock_log.error.assert_not_called()
-        mock_log.warning.assert_not_called()
-
-    @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
-    def test_rule_validation_rbac_conflicting_policies_negative(self,
-                                                                mock_authority,
-                                                                mock_log):
-        """Test RbacConflictingPolicies error is thrown with permission fails.
-
-        Negative test case: if RbacConflictingPolicies is thrown and the user
-        is allowed to perform the action, then this is an expected failure.
-        """
-        mock_authority.PolicyAuthority.return_value.allowed.return_value = True
-
-        @rbac_rv.action(mock.sentinel.service, rules=[mock.sentinel.action])
-        def test_policy(*args):
-            raise rbac_exceptions.RbacConflictingPolicies()
-
-        test_re = ("Role Member was not allowed to perform the following "
-                   "actions: \[%s\].*" % (mock.sentinel.action))
-        self.assertRaisesRegex(
-            rbac_exceptions.RbacUnderPermissionException, test_re, test_policy,
-            self.mock_test_args)
-        self.assertRegex(mock_log.error.mock_calls[0][1][0], test_re)
-
-    @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     def test_expect_not_found_but_raises_forbidden(self, mock_authority,
                                                    mock_log):
         """Test that expecting 404 but getting 403 works for all scenarios.
@@ -211,7 +179,7 @@
            exception.
         """
         @rbac_rv.action(mock.sentinel.service, rules=[mock.sentinel.action],
-                        expected_error_code=404)
+                        expected_error_codes=[404])
         def test_policy(*args):
             raise exceptions.Forbidden('Test message')
 
@@ -241,7 +209,7 @@
         policy_names = ['foo:bar']
 
         @rbac_rv.action(mock.sentinel.service, rules=policy_names,
-                        expected_error_code=404)
+                        expected_error_codes=[404])
         def test_policy(*args):
             raise exceptions.NotFound()
 
@@ -269,7 +237,7 @@
             mock_log.warning.assert_called_with(
                 "NotFound exception was caught for test %s. Expected policies "
                 "which may have caused the error: %s. The service %s throws a "
-                "404 instead of a 403, which is irregular.",
+                "404 instead of a 403, which is irregular",
                 test_policy.__name__,
                 ', '.join(policy_names),
                 mock.sentinel.service)
@@ -294,7 +262,7 @@
             pass
 
         @rbac_rv.action(mock.sentinel.service, rules=[mock.sentinel.action],
-                        expected_error_code=404)
+                        expected_error_codes=[404])
         def test_policy_expect_not_found(*args):
             pass
 
@@ -334,7 +302,7 @@
         expected_irregular_msg = (
             "NotFound exception was caught for test %s. Expected policies "
             "which may have caused the error: %s. The service %s throws a "
-            "404 instead of a 403, which is irregular.")
+            "404 instead of a 403, which is irregular")
 
         actual_exception, actual_irregular_msg = \
             rbac_rv._get_exception_type(404)
@@ -385,6 +353,12 @@
     Patrole RBAC validation work flows.
     """
 
+    def setUp(self):
+        super(RBACRuleValidationLoggingTest, self).setUp()
+        # This behavior is tested in separate test class below.
+        self.useFixture(fixtures.MockPatchObject(
+            rbac_rv, '_validate_override_role_called'))
+
     @mock.patch.object(rbac_rv, 'RBACLOG', autospec=True)
     @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     def test_rbac_report_logging_disabled(self, mock_authority, mock_rbaclog):
@@ -392,7 +366,8 @@
         is False
         """
         self.useFixture(
-            fixtures.ConfPatcher(enable_reporting=False, group='patrole_log'))
+            patrole_fixtures.ConfPatcher(enable_reporting=False,
+                                         group='patrole_log'))
 
         mock_authority.PolicyAuthority.return_value.allowed.return_value = True
 
@@ -410,7 +385,8 @@
         True
         """
         self.useFixture(
-            fixtures.ConfPatcher(enable_reporting=True, group='patrole_log'))
+            patrole_fixtures.ConfPatcher(enable_reporting=True,
+                                         group='patrole_log'))
 
         mock_authority.PolicyAuthority.return_value.allowed.return_value = True
         policy_names = ['foo:bar', 'baz:qux']
@@ -429,9 +405,75 @@
             "Allowed",
             "Allowed")
 
+    @mock.patch.object(rbac_rv, 'LOG', autospec=True)
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_with_callable_rule(self, mock_authority,
+                                                mock_log):
+        """Test that a callable as the rule is evaluated correctly."""
+        mock_authority.PolicyAuthority.return_value.allowed.return_value = True
+
+        @rbac_rv.action(mock.sentinel.service,
+                        rules=[lambda: mock.sentinel.action])
+        def test_policy(*args):
+            pass
+
+        test_policy(self.mock_test_args)
+
+        policy_authority = mock_authority.PolicyAuthority.return_value
+        policy_authority.allowed.assert_called_with(
+            mock.sentinel.action,
+            CONF.patrole.rbac_test_role)
+
+        mock_log.error.assert_not_called()
+
+    @mock.patch.object(rbac_rv, 'LOG', autospec=True)
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_with_conditional_callable_rule(
+            self, mock_authority, mock_log):
+        """Test that a complex callable with conditional logic as the rule is
+        evaluated correctly.
+        """
+        mock_authority.PolicyAuthority.return_value.allowed.return_value = True
+
+        def partial_func(x):
+            return "foo" if x == "bar" else "qux"
+        foo_callable = functools.partial(partial_func, "bar")
+        bar_callable = functools.partial(partial_func, "baz")
+
+        @rbac_rv.action(mock.sentinel.service,
+                        rules=[foo_callable])
+        def test_foo_policy(*args):
+            pass
+
+        @rbac_rv.action(mock.sentinel.service,
+                        rules=[bar_callable])
+        def test_bar_policy(*args):
+            pass
+
+        test_foo_policy(self.mock_test_args)
+        policy_authority = mock_authority.PolicyAuthority.return_value
+        policy_authority.allowed.assert_called_with(
+            "foo",
+            CONF.patrole.rbac_test_role)
+        policy_authority.allowed.reset_mock()
+
+        test_bar_policy(self.mock_test_args)
+        policy_authority = mock_authority.PolicyAuthority.return_value
+        policy_authority.allowed.assert_called_with(
+            "qux",
+            CONF.patrole.rbac_test_role)
+
+        mock_log.error.assert_not_called()
+
 
 class RBACRuleValidationNegativeTest(BaseRBACRuleValidationTest):
 
+    def setUp(self):
+        super(RBACRuleValidationNegativeTest, self).setUp()
+        # This behavior is tested in separate test class below.
+        self.useFixture(fixtures.MockPatchObject(
+            rbac_rv, '_validate_override_role_called'))
+
     @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     def test_rule_validation_invalid_service_raises_exc(self, mock_authority):
         """Test that invalid service raises the appropriate exception."""
@@ -451,6 +493,12 @@
     ``rbac_rule_validation`` decorator.
     """
 
+    def setUp(self):
+        super(RBACRuleValidationTestMultiPolicy, self).setUp()
+        # This behavior is tested in separate test class below.
+        self.useFixture(fixtures.MockPatchObject(
+            rbac_rv, '_validate_override_role_called'))
+
     def _assert_policy_authority_called_with(self, rules, mock_authority):
         m_authority = mock_authority.PolicyAuthority.return_value
         m_authority.allowed.assert_has_calls([
@@ -641,31 +689,24 @@
         _do_test([True, False, False, True], 'mock.sentinel.action2')
         _do_test([True, False, True, False], 'mock.sentinel.action2')
 
-    @mock.patch.object(rbac_rv, 'LOG', autospec=True)
-    def test_prepare_multi_policy_allowed_usages(self, mock_log):
+    def test_prepare_multi_policy_allowed_usages(self):
 
-        def _do_test(rule, rules, ecode, ecodes, exp_rules, exp_ecodes):
-            rule_list, ec_list = rbac_rv._prepare_multi_policy(rule, rules,
-                                                               ecode, ecodes)
+        def _do_test(rules, ecodes, exp_rules, exp_ecodes):
+            rule_list, ec_list = rbac_rv._prepare_multi_policy(rules, ecodes)
             self.assertEqual(rule_list, exp_rules)
             self.assertEqual(ec_list, exp_ecodes)
 
-        # Validate that using deprecated values: rule and expected_error_code
-        # are converted into rules = [rule] and expected_error_codes =
-        # [expected_error_code]
-        _do_test("rule1", None, 403, None, ["rule1"], [403])
-
-        # Validate that rules = [rule] and expected_error_codes defaults to
-        # 403 when no values are provided.
-        _do_test("rule1", None, None, None, ["rule1"], [403])
+        # Validate that expected_error_codes defaults to 403 when no values
+        # are provided.
+        _do_test(["rule1"], None, ["rule1"], [403])
 
         # Validate that `len(rules) == len(expected_error_codes)` works when
         # both == 1.
-        _do_test(None, ["rule1"], None, [403], ["rule1"], [403])
+        _do_test(["rule1"], [403], ["rule1"], [403])
 
         # Validate that `len(rules) == len(expected_error_codes)` works when
         # both are > 1.
-        _do_test(None, ["rule1", "rule2"], None, [403, 404],
+        _do_test(["rule1", "rule2"], [403, 404],
                  ["rule1", "rule2"], [403, 404])
 
         # Validate that when only a default expected_error_code argument is
@@ -675,36 +716,210 @@
         #     @rbac_rv.action(service, rules=[<rule>, <rule>])
         #     def test_policy(*args):
         #        ...
-        _do_test(None, ["rule1", "rule2"], 403, None,
+        _do_test(["rule1", "rule2"], None,
                  ["rule1", "rule2"], [403, 403])
 
-        # Validate that the deprecated values are ignored when new values are
-        # provided.
-        _do_test("rule3", ["rule1", "rule2"], 404, [403, 403],
-                 ["rule1", "rule2"], [403, 403])
-        mock_log.debug.assert_any_call(
-            "The `rules` argument will be used instead of `rule`.")
-        mock_log.debug.assert_any_call(
-            "The `exp_error_codes` argument will be used instead of "
-            "`exp_error_code`.")
-
     @mock.patch.object(rbac_rv, 'LOG', autospec=True)
     def test_prepare_multi_policy_disallowed_usages(self, mock_log):
 
-        def _do_test(rule, rules, ecode, ecodes):
-            rule_list, ec_list = rbac_rv._prepare_multi_policy(rule, rules,
-                                                               ecode, ecodes)
+        def _do_test(rules, ecodes):
+            rule_list, ec_list = rbac_rv._prepare_multi_policy(rules, ecodes)
 
         error_re = ("The `expected_error_codes` list is not the same length"
                     " as the `rules` list.")
         # When len(rules) > 1 then len(expected_error_codes) must be same len.
         self.assertRaisesRegex(ValueError, error_re, _do_test,
-                               None, ["rule1", "rule2"], None, [403])
+                               ["rule1", "rule2"], [403])
         # When len(expected_error_codes) > 1 len(rules) must be same len.
-        self.assertRaisesRegex(ValueError, error_re, _do_test,
-                               None, ["rule1"], None, [403, 404])
+        self.assertRaisesRegex(ValueError, error_re, _do_test, ["rule1"],
+                               [403, 404])
         error_re = ("The `rules` list must be provided if using the "
                     "`expected_error_codes` list.")
         # When expected_error_codes is provided rules must be as well.
-        self.assertRaisesRegex(ValueError, error_re, _do_test,
-                               None, None, None, [404])
+        self.assertRaisesRegex(ValueError, error_re, _do_test, None, [404])
+
+
+class RBACOverrideRoleValidationTest(BaseRBACRuleValidationTest):
+    """Class for validating that untimely exceptions (outside
+    ``override_role`` is called) result in test failures.
+
+    This regression tests false positives caused by test exceptions matching
+    the expected exception before or after the ``override_role`` context is
+    called. Also tests case where ``override_role`` is never called which is
+    an invalid Patrole test.
+
+    """
+
+    def setUp(self):
+        super(RBACOverrideRoleValidationTest, self).setUp()
+
+        # Mixin automatically initializes __override_role_called to False.
+        class FakeRbacTest(rbac_utils.RbacUtilsMixin, test.BaseTestCase):
+            def runTest(self):
+                pass
+
+        # Stub out problematic function calls.
+        FakeRbacTest.os_primary = mock.Mock(spec=manager.Manager)
+        FakeRbacTest.rbac_utils = self.useFixture(
+            patrole_fixtures.RbacUtilsFixture())
+        mock_creds = mock.Mock(user_id=mock.sentinel.user_id,
+                               project_id=mock.sentinel.project_id)
+        setattr(FakeRbacTest.os_primary, 'credentials', mock_creds)
+        setattr(FakeRbacTest.os_primary, 'auth_provider', mock.Mock())
+
+        self.parent_class = FakeRbacTest
+
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_override_role_called_inside_ctx(self,
+                                                             mock_authority):
+        """Test success case when the expected exception is raised within the
+        override_role context.
+        """
+        mock_authority.PolicyAuthority.return_value.allowed.return_value =\
+            False
+
+        class ChildRbacTest(self.parent_class):
+
+            @rbac_rv.action(mock.sentinel.service, rules=["fake:rule"],
+                            expected_error_codes=[404])
+            def test_called(self_):
+                with self_.rbac_utils.real_override_role(self_):
+                    raise exceptions.NotFound()
+
+        child_test = ChildRbacTest()
+        child_test.test_called()
+
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_override_role_patrole_exception_ignored(
+            self, mock_authority):
+        """Test success case where Patrole exception is raised (which is
+        valid in case of e.g. RbacMalformedException) after override_role
+        passes.
+        """
+        mock_authority.PolicyAuthority.return_value.allowed.return_value =\
+            True
+
+        class ChildRbacTest(self.parent_class):
+
+            @rbac_rv.action(mock.sentinel.service, rules=["fake:rule"],
+                            expected_error_codes=[404])
+            def test_called(self_):
+                with self_.rbac_utils.real_override_role(self_):
+                    pass
+                # Instances of BasePatroleException don't count as they are
+                # part of the validation work flow.
+                raise rbac_exceptions.BasePatroleException()
+
+        child_test = ChildRbacTest()
+        self.assertRaises(rbac_exceptions.BasePatroleException,
+                          child_test.test_called)
+
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_override_role_called_before_ctx(self,
+                                                             mock_authority):
+        """Test failure case when an exception that happens before
+        ``override_role`` context, even if it is the expected exception,
+        raises ``RbacOverrideRoleException``.
+        """
+        mock_authority.PolicyAuthority.return_value.allowed.return_value =\
+            False
+
+        # This behavior should work for supported (NotFound/Forbidden) and
+        # miscellaneous exceptions alike.
+        for exception_type in (exceptions.NotFound,
+                               Exception):
+            class ChildRbacTest(self.parent_class):
+
+                @rbac_rv.action(mock.sentinel.service, rules=["fake:rule"],
+                                expected_error_codes=[404])
+                def test_called_before(self_):
+                    raise exception_type()
+
+            child_test = ChildRbacTest()
+            test_re = ".*before.*"
+            self.assertRaisesRegex(rbac_exceptions.RbacOverrideRoleException,
+                                   test_re, child_test.test_called_before)
+
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_override_role_called_after_ctx(self,
+                                                            mock_authority):
+        """Test failure case when an exception that happens before
+        ``override_role`` context, even if it is the expected exception,
+        raises ``RbacOverrideRoleException``.
+        """
+        mock_authority.PolicyAuthority.return_value.allowed.return_value =\
+            False
+
+        # This behavior should work for supported (NotFound/Forbidden) and
+        # miscellaneous exceptions alike.
+        for exception_type in (exceptions.NotFound,
+                               Exception):
+            class ChildRbacTest(self.parent_class):
+
+                @rbac_rv.action(mock.sentinel.service, rules=["fake:rule"],
+                                expected_error_codes=[404])
+                def test_called_after(self_):
+                    with self_.rbac_utils.real_override_role(self_):
+                        pass
+                    # Simulates a test tearDown failure or some such.
+                    raise exception_type()
+
+            child_test = ChildRbacTest()
+            test_re = ".*after.*"
+            self.assertRaisesRegex(rbac_exceptions.RbacOverrideRoleException,
+                                   test_re, child_test.test_called_after)
+
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_override_role_never_called(self, mock_authority):
+        """Test failure case where override_role is **never** called."""
+        mock_authority.PolicyAuthority.return_value.allowed.return_value =\
+            False
+
+        class ChildRbacTest(self.parent_class):
+
+            @rbac_rv.action(mock.sentinel.service, rules=["fake:rule"],
+                            expected_error_codes=[404])
+            def test_never_called(self_):
+                pass
+
+        child_test = ChildRbacTest()
+        test_re = ".*missing required `override_role` call.*"
+        self.assertRaisesRegex(rbac_exceptions.RbacOverrideRoleException,
+                               test_re, child_test.test_never_called)
+
+    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
+    def test_rule_validation_override_role_sequential_test_calls(
+            self, mock_authority):
+        """Test success/failure scenarios above across sequential test calls.
+        """
+        mock_authority.PolicyAuthority.return_value.allowed.return_value =\
+            False
+
+        class ChildRbacTest(self.parent_class):
+
+            @rbac_rv.action(mock.sentinel.service, rules=["fake:rule1"],
+                            expected_error_codes=[404])
+            def test_called(self_):
+                with self_.rbac_utils.real_override_role(self_):
+                    raise exceptions.NotFound()
+
+            @rbac_rv.action(mock.sentinel.service, rules=["fake:rule2"],
+                            expected_error_codes=[404])
+            def test_called_before(self_):
+                raise exceptions.NotFound()
+
+        test_re = ".*before.*"
+
+        # Test case where override role is called in first test but *not* in
+        # second test.
+        child_test1 = ChildRbacTest()
+        child_test1.test_called()
+        self.assertRaisesRegex(rbac_exceptions.RbacOverrideRoleException,
+                               test_re, child_test1.test_called_before)
+
+        # Test case where override role is *not* called in first test but is
+        # in second test.
+        child_test2 = ChildRbacTest()
+        self.assertRaisesRegex(rbac_exceptions.RbacOverrideRoleException,
+                               test_re, child_test2.test_called_before)
+        child_test2.test_called()
diff --git a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
index 4937318..5132079 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_utils.py
@@ -57,9 +57,9 @@
 
         roles_client.create_user_role_on_project.assert_called_once_with(
             self.rbac_utils.PROJECT_ID, self.rbac_utils.USER_ID, 'admin_id')
-        mock_test_obj.os_primary.auth_provider.clear_auth\
+        mock_test_obj.get_auth_providers()[0].clear_auth\
             .assert_called_once_with()
-        mock_test_obj.os_primary.auth_provider.set_auth\
+        mock_test_obj.get_auth_providers()[0].set_auth\
             .assert_called_once_with()
         mock_time.sleep.assert_called_once_with(1)
 
@@ -86,9 +86,9 @@
             mock.call(self.rbac_utils.PROJECT_ID, self.rbac_utils.USER_ID,
                       'member_id')
         ])
-        mock_test_obj.os_primary.auth_provider.clear_auth.assert_has_calls(
+        mock_test_obj.get_auth_providers()[0].clear_auth.assert_has_calls(
             [mock.call()] * 2)
-        mock_test_obj.os_primary.auth_provider.set_auth.assert_has_calls(
+        mock_test_obj.get_auth_providers()[0].set_auth.assert_has_calls(
             [mock.call()] * 2)
         mock_time.sleep.assert_has_calls([mock.call(1)] * 2)
 
@@ -120,9 +120,9 @@
             mock.call(self.rbac_utils.PROJECT_ID, self.rbac_utils.USER_ID,
                       'admin_id')
         ])
-        mock_test_obj.os_primary.auth_provider.clear_auth.assert_has_calls(
+        mock_test_obj.get_auth_providers()[0].clear_auth.assert_has_calls(
             [mock.call()] * 3)
-        mock_test_obj.os_primary.auth_provider.set_auth.assert_has_calls(
+        mock_test_obj.get_auth_providers()[0].set_auth.assert_has_calls(
             [mock.call()] * 3)
         mock_time.sleep.assert_has_calls([mock.call(1)] * 3)
 
@@ -208,11 +208,6 @@
         class FakeRbacTest(rbac_utils.RbacUtilsMixin, test.BaseTestCase):
 
             @classmethod
-            def skip_checks(cls):
-                super(FakeRbacTest, cls).skip_checks()
-                cls.skip_rbac_checks()
-
-            @classmethod
             def setup_clients(cls):
                 super(FakeRbacTest, cls).setup_clients()
                 cls.setup_rbac_utils()
@@ -237,21 +232,3 @@
 
         self.assertTrue(hasattr(child_test, 'rbac_utils'))
         self.assertIsInstance(child_test.rbac_utils, rbac_utils.RbacUtils)
-
-    def test_skip_rbac_checks(self):
-        """Validate that the child class is skipped if `[patrole] enable_rbac`
-        is False and that the child class's name is in the skip message.
-        """
-        self.useFixture(patrole_fixtures.ConfPatcher(enable_rbac=False,
-                                                     group='patrole'))
-
-        class ChildRbacTest(self.parent_class):
-            pass
-
-        child_test = ChildRbacTest()
-
-        with testtools.ExpectedException(
-                testtools.TestCase.skipException,
-                value_re=('Patrole testing not enabled so skipping %s.'
-                          % ChildRbacTest.__name__)):
-            child_test.setUpClass()
diff --git a/releasenotes/notes/add-neutron-tempest-plugin-clients-c031e232021b390c.yaml b/releasenotes/notes/add-neutron-tempest-plugin-clients-c031e232021b390c.yaml
new file mode 100644
index 0000000..91d3f20
--- /dev/null
+++ b/releasenotes/notes/add-neutron-tempest-plugin-clients-c031e232021b390c.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    In order to strive toward complete test coverage for the services it
+    tests, Patrole now offers RBAC coverage for the APIs included in
+    neutron-tempest-plugin. If this plugin is not installed or enabled, then
+    Patrole will skip those tests.
diff --git a/releasenotes/notes/check-expected-errors-only-in-override-role-f7109a73f5ff70e2.yaml b/releasenotes/notes/check-expected-errors-only-in-override-role-f7109a73f5ff70e2.yaml
new file mode 100644
index 0000000..e0ac744
--- /dev/null
+++ b/releasenotes/notes/check-expected-errors-only-in-override-role-f7109a73f5ff70e2.yaml
@@ -0,0 +1,19 @@
+---
+features:
+  - |
+    Add new exception called ``RbacOverrideRoleException``. Used for
+    safeguarding against false positives that might occur when the expected
+    exception isn't raised inside the ``override_role`` context. Specifically,
+    when:
+
+    * ``override_role`` isn't called
+    * an exception is raised before ``override_role`` context
+    * an exception is raised after ``override_role`` context
+fixes:
+  - |
+    Previously, the ``rbac_rule_validation.action`` decorator could catch
+    expected exceptions with no regard to where the error happened. Such
+    behavior could cause false-positive results. To prevent this from
+    happening from now on, if an exception happens outside of the
+    ``override_role`` context, it will cause
+    ``rbac_exceptions.RbacOverrideRoleException`` to be raised.
diff --git a/releasenotes/notes/deprecate-enable-rbac-option-1e499bb0914cdee8.yaml b/releasenotes/notes/deprecate-enable-rbac-option-1e499bb0914cdee8.yaml
new file mode 100644
index 0000000..7354c4f
--- /dev/null
+++ b/releasenotes/notes/deprecate-enable-rbac-option-1e499bb0914cdee8.yaml
@@ -0,0 +1,13 @@
+---
+deprecations:
+  - |
+    The ``[patrole].enable_rbac`` option is deprecated and will be removed
+    during the "S" release. This is a legacy option that was meaningful
+    downstream when Patrole was a suite of tests inside Tempest itself. Now
+    that Patrole exists upstream as a Tempest plugin, it is paradoxical to
+    install the Patrole plugin yet have an option that allows all Patrole
+    tests to be skipped. This option is at odds with current Patrole
+    architecture.
+
+    To skip RBAC tests going forward, with Patrole Tempest plugin already
+    installed, use an appropriate regex.
diff --git a/releasenotes/notes/deprecate-roles-client-in-rbac-utils-087eda0658d18fa9.yaml b/releasenotes/notes/deprecate-roles-client-in-rbac-utils-087eda0658d18fa9.yaml
new file mode 100644
index 0000000..c42da8a
--- /dev/null
+++ b/releasenotes/notes/deprecate-roles-client-in-rbac-utils-087eda0658d18fa9.yaml
@@ -0,0 +1,6 @@
+---
+deprecations:
+  - |
+    Patrole will only support the v3 Tempest roles client for role
+    overriding operations. Support for the v2 version has been dropped
+    because the Keystone v2 API is slated for removal.
diff --git a/releasenotes/notes/multiple-policy-files-9aa7f7583283739e.yaml b/releasenotes/notes/multiple-policy-files-9aa7f7583283739e.yaml
new file mode 100644
index 0000000..a3555e6
--- /dev/null
+++ b/releasenotes/notes/multiple-policy-files-9aa7f7583283739e.yaml
@@ -0,0 +1,17 @@
+---
+features:
+  - |
+    In order to implement the tests for plugins which do not maintain the
+    ``policy.json`` with full list of the policy rules and provide policy file
+    with only their own policy rules, the Patrole should be able to load and
+    merge multiple policy files for any of the services.
+
+    - Discovery all policy files for each of the services.
+      The updated ``discover_policy_files`` function picks all candidate paths
+      found out of the potential paths in the ``[patrole].custom_policy_files``
+      config option. Using ``glob.glob()`` function makes it possible to use
+      the patterns like '\*.json' to discover the policy files.
+
+    - Loading and merging a data from multiple policy files.
+      Patrole loads a data from each of the discovered policy files for a
+      service and merge the data from all files.
diff --git a/releasenotes/notes/patrole-rocky-release-e6f36691306bec7e.yaml b/releasenotes/notes/patrole-rocky-release-e6f36691306bec7e.yaml
new file mode 100644
index 0000000..22c4958
--- /dev/null
+++ b/releasenotes/notes/patrole-rocky-release-e6f36691306bec7e.yaml
@@ -0,0 +1,14 @@
+---
+prelude: >
+    This release is to tag the Patrole for OpenStack Rocky release.
+    After this release, Patrole will support below OpenStack Releases:
+
+      * Rocky
+      * Queens
+      * Pike
+
+    Current development of Patrole is for OpenStack Stein development
+    cycle. Every Patrole commit is also tested against master during
+    the Stein cycle. However, this does not necessarily mean that using
+    Patrole as of this tag will work against a Stein (or future release)
+    cloud.
diff --git a/releasenotes/notes/remove-deprecated-api-extensions-policies-fca3d31c7f5f1f6c.yaml b/releasenotes/notes/remove-deprecated-api-extensions-policies-fca3d31c7f5f1f6c.yaml
new file mode 100644
index 0000000..925791f
--- /dev/null
+++ b/releasenotes/notes/remove-deprecated-api-extensions-policies-fca3d31c7f5f1f6c.yaml
@@ -0,0 +1,23 @@
+---
+features:
+  - |
+    A new policy feature flag called
+    ``[policy_feature_flag].removed_nova_policies_stein`` has been added to
+    Patrole's config to handle Nova API extension policies removed in Stein.
+
+    The policy feature flag is applied to tests that validate response bodies
+    for expected attributes previously returned for the following policies
+    that passed authorization:
+
+      - os_compute_api:os-config-drive
+      - os_compute_api:os-extended-availability-zone
+      - os_compute_api:os-extended-status
+      - os_compute_api:os-extended-volumes
+      - os_compute_api:os-keypairs
+      - os_compute_api:os-server-usage
+      - os_compute_api:os-flavor-rxtx
+      - os_compute_api:os-flavor-access (only from /flavors APIs)
+      - os_compute_api:image-size
+
+    Note that not all removed policies are included above because test coverage
+    is missing for them (like os_compute_api:os-security-groups).
diff --git a/releasenotes/notes/remove-deprecated-enable-rbac-config-option-a5e46ce1053b7dea.yaml b/releasenotes/notes/remove-deprecated-enable-rbac-config-option-a5e46ce1053b7dea.yaml
new file mode 100644
index 0000000..53b1710
--- /dev/null
+++ b/releasenotes/notes/remove-deprecated-enable-rbac-config-option-a5e46ce1053b7dea.yaml
@@ -0,0 +1,5 @@
+---
+upgrade:
+  - |
+    Remove deprecated ``[patrole].enable_rbac`` configuration option. To skip
+    Patrole tests going forward, use an appropriate regex.
diff --git a/releasenotes/notes/remove-deprecated-rules-expected-error-codes-params-52071a83113934fd.yaml b/releasenotes/notes/remove-deprecated-rules-expected-error-codes-params-52071a83113934fd.yaml
new file mode 100644
index 0000000..16b2e03
--- /dev/null
+++ b/releasenotes/notes/remove-deprecated-rules-expected-error-codes-params-52071a83113934fd.yaml
@@ -0,0 +1,13 @@
+---
+upgrade:
+  - |
+    The following deprecated parameters in ``rbac_rule_validation.action``
+    decorator:
+
+    * ``rule``
+    * ``expected_error_code``
+
+    have been removed. Use the non-deprecated versions instead:
+
+    * ``rules``
+    * ``expected_error_codes``
diff --git a/releasenotes/notes/removed-keystone-policies-stein-feature-flag-6cfebbf64ed525d7.yaml b/releasenotes/notes/removed-keystone-policies-stein-feature-flag-6cfebbf64ed525d7.yaml
new file mode 100644
index 0000000..3bed287
--- /dev/null
+++ b/releasenotes/notes/removed-keystone-policies-stein-feature-flag-6cfebbf64ed525d7.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Added new feature flag called ``removed_keystone_policies_stein`` under
+    the configuration group ``[policy-feature-enabled]`` for skipping Keystone
+    tests whose policies were removed in Stein. This feature flag is currently
+    applied to credentials-related policies, e.g.:
+    identity:[create|update|get|delete]_credential
diff --git a/releasenotes/notes/volume-type-encryption-policy-granularity-141ac283b9c0778e.yaml b/releasenotes/notes/volume-type-encryption-policy-granularity-141ac283b9c0778e.yaml
new file mode 100644
index 0000000..4aeb107
--- /dev/null
+++ b/releasenotes/notes/volume-type-encryption-policy-granularity-141ac283b9c0778e.yaml
@@ -0,0 +1,19 @@
+---
+features:
+  - |
+    Added new Cinder feature flag (``CONF.policy_feature_enabled.added_cinder_policies_stein``)
+    for the following newly introduced granular Cinder policies:
+
+    - ``volume_extension:volume_type_encryption:create``
+    - ``volume_extension:volume_type_encryption:get``
+    - ``volume_extension:volume_type_encryption:update``
+    - ``volume_extension:volume_type_encryption:delete``
+
+    The corresponding Patrole test cases are modified to support
+    the granularity.  The test cases also support backward
+    compatibility with the old single rule:
+    ``volume_extension:volume_type_encryption``
+
+    The ``rules`` parameter in ``rbac_rule_validation.action``
+    decorator now also accepts a list of callables; each callable
+    should return a policy action (str).
diff --git a/releasenotes/notes/yaml-policy-file-support-278d3edf64f98d69.yaml b/releasenotes/notes/yaml-policy-file-support-278d3edf64f98d69.yaml
new file mode 100644
index 0000000..e333377
--- /dev/null
+++ b/releasenotes/notes/yaml-policy-file-support-278d3edf64f98d69.yaml
@@ -0,0 +1,7 @@
+---
+features:
+- |
+  Patrole now supports parsing custom YAML policy files, the new policy file
+  extension since Ocata. The function ``_get_policy_data`` has been renamed to
+  ``get_rules`` and been changed to re-use ``oslo_policy.policy.Rules.load``
+  function.
diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst
index ce29994..eb061a4 100644
--- a/releasenotes/source/index.rst
+++ b/releasenotes/source/index.rst
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   v0.4.0
    v0.3.0
    v0.2.0
    v0.1.0
diff --git a/releasenotes/source/v0.4.0.rst b/releasenotes/source/v0.4.0.rst
new file mode 100644
index 0000000..2ed32ff
--- /dev/null
+++ b/releasenotes/source/v0.4.0.rst
@@ -0,0 +1,6 @@
+====================
+v0.4.0 Release Notes
+====================
+
+.. release-notes:: 0.4.0 Release Notes
+   :version: 0.4.0
diff --git a/test-requirements.txt b/test-requirements.txt
index 9085c07..a08c27a 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -8,3 +8,4 @@
 nose>=1.3.7 # LGPL
 nosexcover>=1.0.10 # BSD
 oslotest>=3.2.0 # Apache-2.0
+bandit>=1.5 # Apache-2.0
diff --git a/tox.ini b/tox.ini
index a09822f..bc829d2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-minversion = 1.6
+minversion = 2.0
 envlist = pep8,py35,py27
 skipsdist = True
 
@@ -22,8 +22,12 @@
 
 [testenv:pep8]
 basepython = python3
-commands = flake8 {posargs}
-           check-uuid --package patrole_tempest_plugin.tests.api
+deps =
+    -r{toxinidir}/test-requirements.txt
+commands =
+    flake8 {posargs}
+    bandit -r patrole_tempest_plugin -x patrole_tempest_plugin/tests -n 5
+    check-uuid --package patrole_tempest_plugin.tests.api
 
 [testenv:uuidgen]
 basepython = python3