Merge "Update docs building"
diff --git a/.zuul.yaml b/.zuul.yaml
index 949ad2f..089ba6e 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -84,7 +84,7 @@
         # Use Member for py3 because arguably negative testing is more
         # important than admin, which is already covered by patrole-admin job.
         RBAC_TEST_ROLE: Member
-        USE_PYTHON3: True
+        USE_PYTHON3: true
       devstack_services:
         s-account: false
         s-container: false
@@ -101,8 +101,10 @@
         - patrole-py35-member
         - patrole-multinode-admin
         - patrole-multinode-member
+        - openstack-tox-lower-constraints
     gate:
       jobs:
         - patrole-admin
         - patrole-member
         - patrole-py35-member
+        - openstack-tox-lower-constraints
diff --git a/HACKING.rst b/HACKING.rst
index a94b47c..056c86a 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -39,25 +39,17 @@
 - [P103] ``self.client`` must not be used as a client alias; this allows for
          code that is more maintainable and easier to read
 
-Role Switching
---------------
+Role Overriding
+---------------
 
-Correct role switching is vital to correct RBAC testing within Patrole. If a
-test does not call ``rbac_utils.switch_role`` with ``toggle_rbac_role=True``
-within the RBAC test, then the test is *not* a valid RBAC test: The API
-endpoint under test will be performed with admin credentials, which is always
-wrong unless ``CONF.patrole.rbac_test_role`` is admin.
+Correct role overriding is vital to correct RBAC testing within Patrole. If a
+test does not call ``rbac_utils.override_role`` within the RBAC test, followed
+by the API endpoint that enforces the expected policy action, then the test is
+**not** a valid Patrole test: The API endpoint under test will be performed
+with admin role, which is always wrong unless ``CONF.patrole.rbac_test_role``
+is also admin.
 
-.. note::
+.. todo::
 
-    Switching back to the admin role for setup and clean up is automatically
-    performed. Toggling ``switch_role`` with ``toggle_rbac_role=False`` within
-    the context of a test should *never* be performed and doing so will likely
-    result in an error being thrown.
-..
-
-Patrole does not have a hacking check for role switching, but does use a
-built-in mechanism for verifying that role switching is being correctly
-executed across tests. If a test does not call ``switch_role`` with
-``toggle_rbac_role=True``, then an ``RbacResourceSetupFailed`` exception
-will be raised.
+    Patrole does not have a hacking check for role overriding, but one may be
+    added in the future.
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 8ec0013..ce799ad 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -35,21 +35,6 @@
 Given the value of ``enable_rbac``, enables or disables Patrole tests. If
 ``enable_rbac`` is ``False``, then Patrole tests are skipped.
 
-Strict Policy Check
--------------------
-
-Currently, many services define their "default" rule to be "anyone allowed".
-If a policy action is not explicitly defined in a policy file, then
-``oslo.policy`` will fall back to the "default" rule. This implies that if
-there's a typo in a policy action specified in a Patrole test, ``oslo.policy``
-can report that the ``rbac_test_role`` will be able to perform the
-non-existent policy action. For a testing framework, this is undesirable
-behavior.
-
-Hence, ``strict_policy_check``, if ``True``, will throw an error in the event
-that a non-existent or bogus policy action is passed to a Patrole test. If
-``False``, however, a ``self.skipException`` will be raised.
-
 Custom Policy Files
 -------------------
 
@@ -70,4 +55,3 @@
 
     Patrole currently does not support policy files located on a host different
     than the one on which it is running.
-..
diff --git a/etc/patrole.conf.sample b/etc/patrole.conf.sample
index cafdf8a..ed2b07c 100644
--- a/etc/patrole.conf.sample
+++ b/etc/patrole.conf.sample
@@ -14,18 +14,6 @@
 # Enables RBAC tests. (boolean value)
 #enable_rbac = true
 
-# DEPRECATED: If true, throws RbacParsingException for policies which
-# don't exist or are not included in the service's policy file. If
-# false, throws
-# skipException. (boolean value)
-# This option is deprecated for removal.
-# Its value may be silently ignored in the future.
-# Reason: This option allows for the possibility
-# of false positives. As a testing framework, Patrole should fail any
-# test that
-# passes in an invalid policy.
-#strict_policy_check = true
-
 # List of the paths to search for policy files. Each
 # policy path assumes that the service name is included in the path
 # once. Also
diff --git a/lower-constraints.txt b/lower-constraints.txt
new file mode 100644
index 0000000..a5ff1ca
--- /dev/null
+++ b/lower-constraints.txt
@@ -0,0 +1,84 @@
+alabaster==0.7.10
+appdirs==1.4.3
+asn1crypto==0.24.0
+Babel==2.5.3
+bcrypt==3.1.4
+certifi==2018.1.18
+cffi==1.11.5
+chardet==3.0.4
+cliff==2.11.0
+cmd2==0.8.1
+coverage==4.5.1
+cryptography==2.1.4
+debtcollector==1.19.0
+docutils==0.14
+dulwich==0.19.0
+extras==1.0.0
+fasteners==0.14.1
+fixtures==3.0.0
+flake8==2.5.5
+future==0.16.0
+hacking==1.0.0
+idna==2.6
+imagesize==1.0.0
+iso8601==0.1.12
+Jinja2==2.10
+jsonschema==2.6.0
+keystoneauth1==3.4.0
+linecache2==1.0.0
+MarkupSafe==1.0
+mccabe==0.2.1
+mock==2.0.0
+monotonic==1.4
+mox3==0.25.0
+msgpack==0.5.6
+netaddr==0.7.19
+netifaces==0.10.6
+nose==1.3.7
+nosexcover==1.0.11
+openstackdocstheme==1.20.0
+os-client-config==1.29.0
+oslo.concurrency==3.26.0
+oslo.config==5.2.0
+oslo.context==2.20.0
+oslo.i18n==3.20.0
+oslo.log==3.37.0
+oslo.policy==1.34.0
+oslo.serialization==2.25.0
+oslo.utils==3.36.0
+oslotest==3.3.0
+paramiko==2.4.1
+pbr==3.1.1
+pep8==1.5.7
+prettytable==0.7.2
+pyasn1==0.4.2
+pycparser==2.18
+pyflakes==0.8.1
+Pygments==2.2.0
+pyinotify==0.9.6
+PyNaCl==1.2.1
+pyparsing==2.2.0
+pyperclip==1.6.0
+python-dateutil==2.7.0
+python-mimeparse==1.6.0
+python-subunit==1.2.0
+pytz==2018.3
+PyYAML==3.12
+reno==2.7.0
+requests==2.18.4
+requestsexceptions==1.4.0
+rfc3986==1.1.0
+six==1.11.0
+snowballstemmer==1.2.1
+Sphinx==1.6.5
+sphinxcontrib-websupport==1.0.1
+stestr==2.0.0
+stevedore==1.28.0
+tempest==18.0.0
+testrepository==0.0.20
+testtools==2.3.0
+traceback2==1.4.0
+unittest2==1.1.0
+urllib3==1.22
+voluptuous==0.11.1
+wrapt==1.10.11
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index 8ac2a20..0077d19 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -27,15 +27,6 @@
     cfg.BoolOpt('enable_rbac',
                 default=True,
                 help="Enables RBAC tests."),
-    cfg.BoolOpt('strict_policy_check',
-                default=True,
-                deprecated_for_removal=True,
-                deprecated_reason="""This option allows for the possibility
-of false positives. As a testing framework, Patrole should fail any test that
-passes in an invalid policy.""",
-                help="""If true, throws RbacParsingException for policies which
-don't exist or are not included in the service's policy file. If false, throws
-skipException."""),
     # 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.
     cfg.ListOpt('custom_policy_files',
diff --git a/patrole_tempest_plugin/policy_authority.py b/patrole_tempest_plugin/policy_authority.py
index 6851942..99348b9 100644
--- a/patrole_tempest_plugin/policy_authority.py
+++ b/patrole_tempest_plugin/policy_authority.py
@@ -158,6 +158,8 @@
 
         :param string rule_name: Rule to be checked using ``oslo.policy``.
         :param bool is_admin: Whether admin context is used.
+        :raises RbacParsingException: If `rule_name`` does not exist in the
+            cloud (in policy file or among registered in-code policy defaults).
         """
         is_admin_context = self._is_admin_context(role)
         is_allowed = self._allowed(
@@ -215,9 +217,11 @@
             policy_data = mgr_policy_data
         else:
             error_message = (
-                'Policy file for {0} service neither found in code nor at {1}.'
-                .format(service, [loc % service for loc in
-                                  CONF.patrole.custom_policy_files])
+                'Policy file for {0} service was 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)
 
diff --git a/patrole_tempest_plugin/rbac_rule_validation.py b/patrole_tempest_plugin/rbac_rule_validation.py
index 97d246f..d3213cf 100644
--- a/patrole_tempest_plugin/rbac_rule_validation.py
+++ b/patrole_tempest_plugin/rbac_rule_validation.py
@@ -13,9 +13,9 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import functools
 import logging
 import sys
-import testtools
 
 from oslo_utils import excutils
 import six
@@ -105,9 +105,9 @@
         @rbac_rule_validation.action(
             service="nova", rule="os_compute_api:os-agents")
         def test_list_agents_rbac(self):
-            # The call to `switch_role` is mandatory.
-            self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-            self.agents_client.list_agents()
+            # The call to `override_role` is mandatory.
+            with self.rbac_utils.override_role(self):
+                self.agents_client.list_agents()
     """
 
     if extra_target_data is None:
@@ -116,6 +116,7 @@
     def decorator(test_func):
         role = CONF.patrole.rbac_test_role
 
+        @functools.wraps(test_func)
         def wrapper(*args, **kwargs):
             if args and isinstance(args[0], test.BaseTestCase):
                 test_obj = args[0]
@@ -163,8 +164,7 @@
                     LOG.error(msg)
             else:
                 if not allowed:
-                    LOG.error("Role %s was allowed to perform %s",
-                              role, rule)
+                    LOG.error("Role %s was allowed to perform %s", role, rule)
                     raise rbac_exceptions.RbacOverPermission(
                         "OverPermission: Role %s was allowed to perform %s" %
                         (role, rule))
@@ -177,8 +177,7 @@
                         "Allowed" if allowed else "Denied",
                         test_status)
 
-        _wrapper = testtools.testcase.attr(role)(wrapper)
-        return _wrapper
+        return wrapper
     return decorator
 
 
@@ -200,10 +199,6 @@
 
     :raises RbacResourceSetupFailed: If `project_id` or `user_id` are missing
         from the `auth_provider` attribute in `test_obj`.
-    :raises RbacParsingException: if ``[patrole] strict_policy_check`` is True
-        and the ``rule`` does not exist in the system.
-    :raises skipException: If ``[patrole] strict_policy_check`` is False and
-        the ``rule`` does not exist in the system.
     """
 
     try:
@@ -215,33 +210,27 @@
         LOG.error(msg)
         raise rbac_exceptions.RbacResourceSetupFailed(msg)
 
-    try:
-        role = CONF.patrole.rbac_test_role
-        # Test RBAC against custom requirements. Otherwise use oslo.policy.
-        if CONF.patrole.test_custom_requirements:
-            authority = requirements_authority.RequirementsAuthority(
-                CONF.patrole.custom_requirements_file, service)
-        else:
-            formatted_target_data = _format_extra_target_data(
-                test_obj, extra_target_data)
-            authority = policy_authority.PolicyAuthority(
-                project_id, user_id, service,
-                extra_target_data=formatted_target_data)
-        is_allowed = authority.allowed(rule, role)
+    role = CONF.patrole.rbac_test_role
+    # Test RBAC against custom requirements. Otherwise use oslo.policy.
+    if CONF.patrole.test_custom_requirements:
+        authority = requirements_authority.RequirementsAuthority(
+            CONF.patrole.custom_requirements_file, service)
+    else:
+        formatted_target_data = _format_extra_target_data(
+            test_obj, extra_target_data)
+        authority = policy_authority.PolicyAuthority(
+            project_id, user_id, service,
+            extra_target_data=formatted_target_data)
+    is_allowed = authority.allowed(rule, role)
 
-        if is_allowed:
-            LOG.debug("[Action]: %s, [Role]: %s is allowed!", rule,
-                      role)
-        else:
-            LOG.debug("[Action]: %s, [Role]: %s is NOT allowed!",
-                      rule, role)
-        return is_allowed
-    except rbac_exceptions.RbacParsingException as e:
-        if CONF.patrole.strict_policy_check:
-            raise e
-        else:
-            raise testtools.TestCase.skipException(str(e))
-    return False
+    if is_allowed:
+        LOG.debug("[Action]: %s, [Role]: %s is allowed!", rule,
+                  role)
+    else:
+        LOG.debug("[Action]: %s, [Role]: %s is NOT allowed!",
+                  rule, role)
+
+    return is_allowed
 
 
 def _get_exception_type(expected_error_code=403):
diff --git a/patrole_tempest_plugin/rbac_utils.py b/patrole_tempest_plugin/rbac_utils.py
index 49cb5e1..1ada69b 100644
--- a/patrole_tempest_plugin/rbac_utils.py
+++ b/patrole_tempest_plugin/rbac_utils.py
@@ -15,7 +15,6 @@
 
 import abc
 from contextlib import contextmanager
-import debtcollector.removals
 import six
 import time
 
@@ -107,20 +106,6 @@
             # up.
             self._override_role(test_obj, False)
 
-    @debtcollector.removals.remove(removal_version='Rocky')
-    def switch_role(self, test_obj, toggle_rbac_role):
-        """Switch the role used by `os_primary` Tempest credentials.
-
-        Switch the role used by `os_primary` credentials to:
-
-        * admin if `toggle_rbac_role` is False
-        * `CONF.patrole.rbac_test_role` if `toggle_rbac_role` is True
-
-        :param test_obj: instance of :py:class:`tempest.test.BaseTestCase`
-        :param toggle_rbac_role: role to switch `os_primary` Tempest creds to
-        """
-        self._override_role(test_obj, toggle_rbac_role)
-
     def _override_role(self, test_obj, toggle_rbac_role=False):
         """Private helper for overriding ``os_primary`` Tempest credentials.
 
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 226411f..1c5fb2e 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
@@ -47,15 +47,6 @@
         self.snapshots_client.create_snapshot_metadata(
             self.snapshot_id, metadata)['metadata']
 
-    @rbac_rule_validation.action(
-        service="cinder",
-        rule="volume_extension:extended_snapshot_attributes")
-    @decorators.idempotent_id('c9cbec1c-edfe-46b8-825b-7b6ac0a58c25')
-    def test_create_snapshot_metadata(self):
-        # Create metadata for the snapshot
-        with self.rbac_utils.override_role(self):
-            self._create_test_snapshot_metadata()
-
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:get_snapshot_metadata")
     @decorators.idempotent_id('f6912bb1-62e6-483d-bcd0-e98c1641f4c3')
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 df4fd10..7d721c4 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
@@ -17,6 +17,7 @@
 from tempest import config
 from tempest.lib import decorators
 
+from patrole_tempest_plugin import rbac_exceptions
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.volume import rbac_base
 
@@ -39,20 +40,15 @@
         # Create a test shared snapshot for tests
         cls.snapshot = cls.create_snapshot(cls.volume['id'])
 
-    def _list_by_param_values(self, params, with_detail=False):
-        # Perform list or list_details action with given params
-        # and validates result.
-        if with_detail:
-            self.snapshots_client.list_snapshots(
-                detail=True, params=params)['snapshots']
-        else:
-            self.snapshots_client.list_snapshots(
-                params=params)['snapshots']
+    def _list_by_param_values(self, with_detail=False, **params):
+        # Perform list or list_details action with given params.
+        return self.snapshots_client.list_snapshots(
+            detail=with_detail, **params)['snapshots']
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:create_snapshot")
     @decorators.idempotent_id('ac7b2ee5-fbc0-4360-afc2-de8fa4881ede')
-    def test_snapshot_create(self):
+    def test_create_snapshot(self):
         # Create a temp snapshot
         with self.rbac_utils.override_role(self):
             self.create_snapshot(self.volume['id'])
@@ -60,16 +56,33 @@
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:get_snapshot")
     @decorators.idempotent_id('93a11b40-1ba8-44d6-a196-f8d97220f796')
-    def test_snapshot_get(self):
+    def test_show_snapshot(self):
         # Get the snapshot
         with self.rbac_utils.override_role(self):
-            self.snapshots_client.show_snapshot(self.snapshot
-                                                ['id'])['snapshot']
+            self.snapshots_client.show_snapshot(
+                self.snapshot['id'])['snapshot']
+
+    @decorators.idempotent_id('5d6f5f21-9293-4f2a-8f44-cabdc24d92cb')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="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',
+                          'os-extended-snapshot-attributes:progress')
+
+        with self.rbac_utils.override_role(self):
+            resp = self.snapshots_client.show_snapshot(
+                self.snapshot['id'])['snapshot']
+        for expected_attr in expected_attrs:
+            if expected_attr not in resp:
+                raise rbac_exceptions.RbacMalformedResponse(
+                    attribute=expected_attr)
 
     @rbac_rule_validation.action(service="cinder",
                                  rule="volume:update_snapshot")
     @decorators.idempotent_id('53fe8ee3-3bea-4ae8-a979-3c98ea72f620')
-    def test_snapshot_update(self):
+    def test_update_snapshot(self):
         new_desc = 'This is the new description of snapshot.'
         params = {'description': new_desc}
         # Updates snapshot with new values
@@ -80,19 +93,9 @@
             self.snapshots_client, self.snapshot['id'], 'available')
 
     @rbac_rule_validation.action(service="cinder",
-                                 rule="volume:get_all_snapshots")
-    @decorators.idempotent_id('e4edf0c0-2cd3-420f-b8ab-4d98a0718608')
-    def test_snapshots_get_all(self):
-        """list snapshots with params."""
-        # Verify list snapshots by display_name filter
-        params = {'name': self.snapshot['name']}
-        with self.rbac_utils.override_role(self):
-            self._list_by_param_values(params)
-
-    @rbac_rule_validation.action(service="cinder",
                                  rule="volume:delete_snapshot")
     @decorators.idempotent_id('c7fe54ec-3b70-4772-ba11-f166d95888a3')
-    def test_snapshot_delete(self):
+    def test_delete_snapshot(self):
         # Create a temp snapshot
         temp_snapshot = self.create_snapshot(self.volume['id'])
         with self.rbac_utils.override_role(self):
@@ -100,3 +103,38 @@
             self.snapshots_client.delete_snapshot(temp_snapshot['id'])
         self.snapshots_client.wait_for_resource_deletion(
             temp_snapshot['id'])
+
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume:get_all_snapshots")
+    @decorators.idempotent_id('e4edf0c0-2cd3-420f-b8ab-4d98a0718608')
+    def test_list_snapshots(self):
+        """List snapshots with params."""
+        params = {'name': self.snapshot['name']}
+        with self.rbac_utils.override_role(self):
+            self._list_by_param_values(**params)
+
+    @decorators.idempotent_id('f3155d8e-45ee-45c9-910d-18c0242229e1')
+    @rbac_rule_validation.action(service="cinder",
+                                 rule="volume:get_all_snapshots")
+    def test_list_snapshots_details(self):
+        """List snapshots details with params."""
+        params = {'name': self.snapshot['name']}
+        with self.rbac_utils.override_role(self):
+            self._list_by_param_values(with_detail=True, **params)
+
+    @decorators.idempotent_id('dd37f388-2731-446d-a78f-676997ebb04a')
+    @rbac_rule_validation.action(
+        service="cinder",
+        rule="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',
+                          'os-extended-snapshot-attributes:progress')
+        params = {'name': self.snapshot['name']}
+
+        with self.rbac_utils.override_role(self):
+            resp = self._list_by_param_values(with_detail=True, **params)
+        for expected_attr in expected_attrs:
+            if expected_attr not in resp[0]:
+                raise rbac_exceptions.RbacMalformedResponse(
+                    attribute=expected_attr)
diff --git a/patrole_tempest_plugin/tests/unit/test_policy_authority.py b/patrole_tempest_plugin/tests/unit/test_policy_authority.py
index d2074e7..3e2cc4c 100644
--- a/patrole_tempest_plugin/tests/unit/test_policy_authority.py
+++ b/patrole_tempest_plugin/tests/unit/test_policy_authority.py
@@ -387,12 +387,11 @@
                               policy_authority.PolicyAuthority,
                               None, None, 'test_service')
 
-        expected_error = \
-            'Policy file for {0} service neither found in code '\
-            'nor at {1}.'.format(
-                'test_service',
-                [CONF.patrole.custom_policy_files[0] % 'test_service'])
-
+        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('test_service',
+                    [CONF.patrole.custom_policy_files[0] % 'test_service']))
         self.assertIn(expected_error, str(e))
 
     @mock.patch.object(policy_authority, 'json', autospec=True)
@@ -436,7 +435,8 @@
                               None, None, 'tenant_rbac_policy')
 
         expected_error = (
-            'Policy file for {0} service neither found in code nor at {1}.'
+            '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']))
         self.assertIn(expected_error, str(e))
@@ -485,9 +485,10 @@
                          policy_parser.policy_files['test_service'])
 
     def test_discover_policy_files_with_no_valid_files(self):
-        expected_error = ("Policy file for test_service service neither found "
-                          "in code nor at %s." %
-                          [self.conf_policy_path % 'test_service'])
+        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('test_service', [self.conf_policy_path % 'test_service']))
 
         e = self.assertRaises(rbac_exceptions.RbacParsingException,
                               policy_authority.PolicyAuthority,
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 82f0428..85547e1 100644
--- a/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
+++ b/patrole_tempest_plugin/tests/unit/test_rbac_rule_validation.py
@@ -13,7 +13,6 @@
 #    under the License.
 
 import mock
-import testtools
 
 from tempest.lib import exceptions
 from tempest import manager
@@ -297,12 +296,8 @@
     @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     def test_invalid_policy_rule_raises_parsing_exception(
             self, mock_authority):
-        """Test that invalid policy action causes test to be fail with
-        ``[patrole] strict_policy_check`` set to True.
+        """Test that invalid policy action causes test to raise an exception.
         """
-        self.useFixture(
-            fixtures.ConfPatcher(strict_policy_check=True, group='patrole'))
-
         mock_authority.PolicyAuthority.return_value.allowed.\
             side_effect = rbac_exceptions.RbacParsingException
 
@@ -319,30 +314,6 @@
             mock.sentinel.service, extra_target_data={})
 
     @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
-    def test_invalid_policy_rule_raises_skip_exception(
-            self, mock_authority):
-        """Test that invalid policy action causes test to be skipped with
-        ``[patrole] strict_policy_check`` set to False.
-        """
-        self.useFixture(
-            fixtures.ConfPatcher(strict_policy_check=False, group='patrole'))
-
-        mock_authority.PolicyAuthority.return_value.allowed.side_effect = (
-            rbac_exceptions.RbacParsingException)
-
-        @rbac_rv.action(mock.sentinel.service, mock.sentinel.action)
-        def test_policy(*args):
-            pass
-
-        error_re = 'Attempted to test an invalid policy file or action'
-        self.assertRaisesRegex(testtools.TestCase.skipException, error_re,
-                               test_policy, self.mock_test_args)
-
-        mock_authority.PolicyAuthority.assert_called_once_with(
-            mock.sentinel.project_id, mock.sentinel.user_id,
-            mock.sentinel.service, extra_target_data={})
-
-    @mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
     def test_get_exception_type_404(self, _):
         """Test that getting a 404 exception type returns NotFound."""
         expected_exception = exceptions.NotFound
diff --git a/releasenotes/notes/remove-deprecated-switch-role-148c9a5c6796857f.yaml b/releasenotes/notes/remove-deprecated-switch-role-148c9a5c6796857f.yaml
new file mode 100644
index 0000000..b303d09
--- /dev/null
+++ b/releasenotes/notes/remove-deprecated-switch-role-148c9a5c6796857f.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    The ``switch_role`` method in ``rbac_utils`` module has been removed
+    because it is a clunky way of manipulating Tempest roles to achieve
+    RBAC testing. Use ``override_role`` instead.
diff --git a/releasenotes/notes/remove-strict-policy-check-480e3d664f7b2d96.yaml b/releasenotes/notes/remove-strict-policy-check-480e3d664f7b2d96.yaml
new file mode 100644
index 0000000..37c5e1e
--- /dev/null
+++ b/releasenotes/notes/remove-strict-policy-check-480e3d664f7b2d96.yaml
@@ -0,0 +1,6 @@
+---
+upgrade:
+  - |
+    The ``[patrole].strict_policy_check`` was deprecated during the Queens
+    release cycle. It is removed in this release cycle because Patrole should
+    always fail on invalid policies.
diff --git a/tox.ini b/tox.ini
index 6445c64..cca09d0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -91,3 +91,10 @@
 
 [hacking]
 local-check-factory = patrole_tempest_plugin.hacking.checks.factory
+
+[testenv:lower-constraints]
+basepython = python3
+deps =
+  -c{toxinidir}/lower-constraints.txt
+  -r{toxinidir}/test-requirements.txt
+  -r{toxinidir}/requirements.txt