Improve test coverage for flavor_access nova policies

This commit improves test coverage for flavor_access policies, achieving
the the most amount of coverage in Patrole for these policies that is
currently possible.

The base policy "os_compute_api:os-flavor-access" is covered by
4 separate APIs, but currently Patrole only tests 1 one of those
APIs. This commit extends the existing tests to achieve almost
full test coverage, with the exception of this endpoint:

    POST /flavors

At present, it is impossible to test os-flavor-access for that
endpoint since it also enforces os-flavor-manage:create
(or os-flavor-manage) both of which require admin.

In addition, this commit fixes test_show_flavor always passing.
While policy enforcement happens in Nova when calling
`self.flavors_client.show_flavor`, no Forbidden exception
is raised following failure. Instead, the attribute
"os-flavor-access:is_public" is injected into the response
body following successful policy enforcement. So Patrole
checks for the attribute and, if not found, raises an
appropriate RbacMalformedResponse exception.

Reference: https://github.com/openstack/nova/blob/master/nova/policies/flavor_access.py

Change-Id: Icaf516f996ec088ce48bbfc768116b2d6994c336
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 26c9957..7503962 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
@@ -17,8 +17,8 @@
 
 from tempest.lib.common.utils import test_utils
 from tempest.lib import decorators
-from tempest import test
 
+from patrole_tempest_plugin import rbac_exceptions
 from patrole_tempest_plugin import rbac_rule_validation
 from patrole_tempest_plugin.tests.api.compute import rbac_base
 
@@ -28,14 +28,6 @@
 class FlavorAccessRbacTest(rbac_base.BaseV2ComputeRbacTest):
 
     @classmethod
-    def skip_checks(cls):
-        super(FlavorAccessRbacTest, cls).skip_checks()
-        if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
-            msg = "%s skipped as OS-FLV-EXT-DATA extension not enabled."\
-                  % cls.__name__
-            raise cls.skipException(msg)
-
-    @classmethod
     def resource_setup(cls):
         super(FlavorAccessRbacTest, cls).resource_setup()
         cls.flavor_id = cls.create_flavor(is_public=False)['id']
@@ -46,11 +38,36 @@
     @rbac_rule_validation.action(
         service="nova",
         rule="os_compute_api:os-flavor-access")
-    def test_show_flavor(self):
-        # NOTE(felipemonteiro): show_flavor enforces the specified policy
-        # action, but only works if a public flavor is passed.
+    def test_show_flavor_contains_is_public_key(self):
+        public_flavor_id = CONF.compute.flavor_ref
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
-        self.flavors_client.show_flavor(self.public_flavor_id)['flavor']
+        body = self.flavors_client.show_flavor(public_flavor_id)[
+            'flavor']
+
+        expected_attr = 'os-flavor-access:is_public'
+        if expected_attr not in body:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute=expected_attr)
+
+    @decorators.idempotent_id('dd388146-9750-4124-82ba-62deff1052bb')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="os_compute_api:os-flavor-access")
+    def test_list_flavors_details_contains_is_public_key(self):
+        expected_attr = 'os-flavor-access:is_public'
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        flavors = self.flavors_client.list_flavors(detail=True)['flavors']
+        # There should already be a public flavor available, namely
+        # `CONF.compute.flavor_ref`.
+        public_flavors = [f for f in flavors if expected_attr in f]
+
+        # If the `expected_attr` was not found in any flavor, then policy
+        # enforcement failed.
+        if not public_flavors:
+            raise rbac_exceptions.RbacMalformedResponse(
+                attribute=expected_attr)
 
     @decorators.idempotent_id('39cb5c8f-9990-436f-9282-fc76a41d9bac')
     @rbac_rule_validation.action(
@@ -74,6 +91,23 @@
         self.addCleanup(test_utils.call_and_ignore_notfound_exc,
                         self.flavors_client.remove_flavor_access,
                         flavor_id=self.flavor_id, tenant_id=self.tenant_id)
+
         self.rbac_utils.switch_role(self, toggle_rbac_role=True)
         self.flavors_client.remove_flavor_access(
             flavor_id=self.flavor_id, tenant_id=self.tenant_id)
+
+    @decorators.idempotent_id('e1cf59fb-7f32-40a1-96b9-248ab23dd581')
+    @rbac_rule_validation.action(
+        service="nova",
+        rule="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.
+        self.flavors_client.add_flavor_access(
+            flavor_id=self.flavor_id, tenant_id=self.tenant_id)[
+            'flavor_access']
+        self.addCleanup(self.flavors_client.remove_flavor_access,
+                        flavor_id=self.flavor_id, tenant_id=self.tenant_id)
+
+        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
+        self.flavors_client.list_flavor_access(self.flavor_id)