Merge "Replace cls.__class__.__name__ with cls.__name__"
diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index 6b95182..f60f0f4 100644
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -37,6 +37,12 @@
         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
+
+       # TODO(rb560u): Remove this once stable/pike becomes EOL.
+       # Make the 'test_list_trusts' test backwards compatible.
+       # The Keystone Trust API is enforced differently depending on passed
+       # arguments
+       iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
     fi
 
     if [[ ${DEVSTACK_SERIES} == 'queens' ]]; then
@@ -54,12 +60,32 @@
         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
+
+       # TODO(rb560u): Remove this once stable/queens becomes EOL.
+       # Make the 'test_list_trusts' test backwards compatible.
+       # The Keystone Trust API is enforced differently depending on passed
+       # arguments
+       iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
     fi
 
     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
+
+       # TODO(rb560u): Remove this once stable/rocky becomes EOL.
+       # Make the 'test_list_trusts' test backwards compatible.
+       # The Keystone Trust API is enforced differently depending on passed
+       # arguments
+       iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
+    fi
+
+    if [[ ${DEVSTACK_SERIES} == 'stein' ]]; then
+       # TODO(rb560u): Remove this once stable/stein becomes EOL.
+       # Make the 'test_list_trusts' test backwards compatible.
+       # The Keystone Trust API is enforced differently depending on passed
+       # arguments
+       iniset $TEMPEST_CONFIG policy-feature-enabled keystone_policy_enforcement_train False
     fi
 
     iniset $TEMPEST_CONFIG patrole rbac_test_roles $RBAC_TEST_ROLES
diff --git a/doc/source/_static/.keep b/doc/source/_static/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/doc/source/_static/.keep
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 57d763e..1d82bc0 100755
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -81,7 +81,6 @@
 # The theme to use for HTML and HTML Help pages.  Major themes that come with
 # Sphinx are currently 'default' and 'sphinxdoc'.
 # html_theme_path = ["."]
-html_static_path = ['_static']
 html_theme = 'openstackdocs'
 
 # openstackdocstheme options
diff --git a/patrole_tempest_plugin/config.py b/patrole_tempest_plugin/config.py
index 63f0a8a..5eab0e3 100644
--- a/patrole_tempest_plugin/config.py
+++ b/patrole_tempest_plugin/config.py
@@ -184,7 +184,12 @@
                 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.""")
+in Stein."""),
+    cfg.BoolOpt('keystone_policy_enforcement_train',
+                default=True,
+                help="""Is the cloud running the Train release or newer? If
+so, the Keystone Trust API is enforced differently depending on passed
+arguments""")
 ]
 
 
diff --git a/patrole_tempest_plugin/tests/api/identity/rbac_base.py b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
index e3fac27..6e76a72 100644
--- a/patrole_tempest_plugin/tests/api/identity/rbac_base.py
+++ b/patrole_tempest_plugin/tests/api/identity/rbac_base.py
@@ -28,30 +28,6 @@
                            base.BaseIdentityTest):
 
     @classmethod
-    def setup_test_endpoint(cls, service=None):
-        """Creates a service and an endpoint for test."""
-        interface = 'public'
-        url = data_utils.rand_url()
-        region_name = data_utils.rand_name(
-            cls.__name__ + '-region')
-        # Endpoint creation requires a service
-        if service is None:
-            service = cls.setup_test_service()
-        params = {
-            'service_id': service['id'],
-            'region': region_name,
-            'interface': interface,
-            'url': url
-        }
-
-        endpoint = cls.endpoints_client.create_endpoint(**params)['endpoint']
-        cls.addClassResourceCleanup(
-            test_utils.call_and_ignore_notfound_exc,
-            cls.endpoints_client.delete_endpoint, endpoint['id'])
-
-        return endpoint
-
-    @classmethod
     def setup_test_role(cls):
         """Set up a test role."""
         name = data_utils.rand_name(cls.__name__ + '-test_role')
@@ -182,6 +158,33 @@
         super(BaseIdentityV3RbacTest, cls).resource_cleanup()
 
     @classmethod
+    def setup_test_endpoint(cls, service=None):
+        """Creates a service and an endpoint for test."""
+        interface = 'public'
+        url = data_utils.rand_url()
+        region_name = data_utils.rand_name(
+            cls.__name__ + '-region')
+        # Endpoint creation requires a service
+        if service is None:
+            service = cls.setup_test_service()
+        params = {
+            'service_id': service['id'],
+            'region': region_name,
+            'interface': interface,
+            'url': url
+        }
+
+        endpoint = cls.endpoints_client.create_endpoint(**params)['endpoint']
+        cls.addClassResourceCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            cls.regions_client.delete_region, endpoint['region'])
+        cls.addClassResourceCleanup(
+            test_utils.call_and_ignore_notfound_exc,
+            cls.endpoints_client.delete_endpoint, endpoint['id'])
+
+        return endpoint
+
+    @classmethod
     def setup_test_credential(cls, user=None):
         """Creates a credential for test."""
         keys = [data_utils.rand_uuid_hex(),
@@ -249,8 +252,10 @@
         """Creates a region for test."""
         description = data_utils.rand_name(
             cls.__name__ + '-test_region_desc')
+        id = data_utils.rand_name(cls.__name__)
 
         region = cls.regions_client.create_region(
+            id=id,
             description=description)['region']
         cls.regions.append(region)
 
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 c8db04e..41c9bf5 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
@@ -94,7 +94,10 @@
     @decorators.idempotent_id('d9a6fd06-08f6-462c-a86c-ce009adf1230')
     @rbac_rule_validation.action(
         service="keystone",
-        rules=["identity:delete_trust"])
+        rules=["identity:delete_trust"],
+        extra_target_data={
+            "target.trust.trustor_user_id": "os_primary.credentials.user_id"
+        })
     def test_delete_trust(self):
         trust = self.setup_test_trust(trustor_user_id=self.trustor_user_id,
                                       trustee_user_id=self.trustee_user_id)
@@ -107,14 +110,24 @@
         service="keystone",
         rules=["identity:list_trusts"])
     def test_list_trusts(self):
+        # Depending on the passed arguments to the list trusts API, different
+        # policy actions are enforced.
+        feature_flag = \
+            CONF.policy_feature_enabled.keystone_policy_enforcement_train
         with self.override_role():
-            self.trusts_client.list_trusts(
-                trustor_user_id=self.trustor_user_id)
+            if feature_flag:
+                self.trusts_client.list_trusts()
+            else:
+                self.trusts_client.list_trusts(
+                    trustor_user_id=self.trustor_user_id)
 
     @decorators.idempotent_id('3c9ff92f-a73e-4f9b-8865-e017f38c70f5')
     @rbac_rule_validation.action(
         service="keystone",
-        rules=["identity:list_roles_for_trust"])
+        rules=["identity:list_roles_for_trust"],
+        extra_target_data={
+            "target.trust.trustor_user_id": "os_primary.credentials.user_id"
+        })
     def test_list_roles_for_trust(self):
         with self.override_role():
             self.trusts_client.list_trust_roles(self.trust['id'])
@@ -122,7 +135,10 @@
     @decorators.idempotent_id('3bb4f97b-cecd-4c7d-ad10-b88ee6c5d573')
     @rbac_rule_validation.action(
         service="keystone",
-        rules=["identity:get_role_for_trust"])
+        rules=["identity:get_role_for_trust"],
+        extra_target_data={
+            "target.trust.trustor_user_id": "os_primary.credentials.user_id"
+        })
     def test_show_trust_role(self):
         with self.override_role():
             self.trusts_client.show_trust_role(
@@ -131,7 +147,10 @@
     @decorators.idempotent_id('0184e0fb-641e-4b52-ab73-81c1ce6ca5c1')
     @rbac_rule_validation.action(
         service="keystone",
-        rules=["identity:get_trust"])
+        rules=["identity:get_trust"],
+        extra_target_data={
+            "target.trust.trustor_user_id": "os_primary.credentials.user_id"
+        })
     def test_show_trust(self):
         with self.override_role():
             self.trusts_client.show_trust(self.trust['id'])
diff --git a/releasenotes/notes/keystone_policy_enforcement_rocky-b52fb471ac31189b.yaml b/releasenotes/notes/keystone_policy_enforcement_rocky-b52fb471ac31189b.yaml
new file mode 100644
index 0000000..1520cab
--- /dev/null
+++ b/releasenotes/notes/keystone_policy_enforcement_rocky-b52fb471ac31189b.yaml
@@ -0,0 +1,7 @@
+---
+features:
+  - |
+    Added new feature flag called ``keystone_policy_enforcement_train`` under
+    the configuration group ``[policy-feature-enabled]`` to make ``test_list_trusts``
+    test backwards compatible, test the current release, and test the correct policy
+    action. The Keystone Trust API is enforced differently depending on passed arguments