refactor: Break up RbacMalformedException into discrete exceptions
This patch set breaks up RbacMalformedException into the following discrete exceptions: * RbacMissingAttributeResponseBody * RbacPartialResponseBody * RbacEmptyResponseBody Each of the exception classes deals with a different type of failure related to a soft authorization failure [0] which means that a failure occurred server side related to RBAC authorization, but the result of which an incomplete, partial or empty response body (with a 2xx status code). * incomplete means that the response body (for show or list) is missing certain attributes * partial means that a list response only returned a subset of the possible results available. * empty means that the show or list response body is entirely empty Because RbacMalformedException is not part of a stable library it is removed altogether; we do not need to deprecate it. [0] http://git.openstack.org/cgit/openstack/patrole/tree/doc/source/rbac-overview.rst#n232 Story: 2003843 Task: 26633 Change-Id: I2c76c3c4d226e4877fc9d1e93707edfc230a1be4
This commit is contained in:
parent
742b73767b
commit
74f8e7d97f
|
@ -20,16 +20,34 @@ class BasePatroleException(exceptions.TempestException):
|
|||
message = "An unknown RBAC exception occurred"
|
||||
|
||||
|
||||
class RbacMalformedResponse(BasePatroleException):
|
||||
message = ("The response body is missing the expected %(attribute)s due "
|
||||
"to policy enforcement failure.")
|
||||
class BasePatroleResponseBodyException(BasePatroleException):
|
||||
message = "Response body incomplete due to RBAC authorization failure"
|
||||
|
||||
def __init__(self, empty=False, **kwargs):
|
||||
if empty:
|
||||
self.message = ("The response body is empty due to policy "
|
||||
"enforcement failure.")
|
||||
kwargs = {}
|
||||
super(RbacMalformedResponse, self).__init__(**kwargs)
|
||||
|
||||
class RbacMissingAttributeResponseBody(BasePatroleResponseBodyException):
|
||||
"""Raised when a list or show action is missing an attribute following
|
||||
RBAC authorization failure.
|
||||
"""
|
||||
message = ("The response body is missing the expected %(attribute)s due "
|
||||
"to policy enforcement failure")
|
||||
|
||||
|
||||
class RbacPartialResponseBody(BasePatroleResponseBodyException):
|
||||
"""Raised when a list action only returns a subset of the available
|
||||
resources.
|
||||
|
||||
For example, admin can return more resources than member for a list action.
|
||||
"""
|
||||
message = ("The response body only lists a subset of the available "
|
||||
"resources due to partial policy enforcement failure. Response "
|
||||
"body: %(body)s")
|
||||
|
||||
|
||||
class RbacEmptyResponseBody(BasePatroleResponseBodyException):
|
||||
"""Raised when a list or show action is empty following RBAC authorization
|
||||
failure.
|
||||
"""
|
||||
message = ("The response body is empty due to policy enforcement failure.")
|
||||
|
||||
|
||||
class RbacResourceSetupFailed(BasePatroleException):
|
||||
|
|
|
@ -198,7 +198,8 @@ def action(service,
|
|||
test_status = ('Error, %s' % (msg))
|
||||
LOG.error(msg)
|
||||
except (expected_exception,
|
||||
rbac_exceptions.RbacMalformedResponse) as actual_exception:
|
||||
rbac_exceptions.BasePatroleResponseBodyException) \
|
||||
as actual_exception:
|
||||
caught_exception = actual_exception
|
||||
test_status = 'Denied'
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ class FlavorAccessRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
|
||||
expected_attr = 'os-flavor-access:is_public'
|
||||
if expected_attr not in body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -71,7 +71,7 @@ class FlavorAccessRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
# If the `expected_attr` was not found in any flavor, then policy
|
||||
# enforcement failed.
|
||||
if not public_flavors:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@decorators.idempotent_id('39cb5c8f-9990-436f-9282-fc76a41d9bac')
|
||||
|
|
|
@ -45,7 +45,7 @@ class FlavorRxtxRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
with self.rbac_utils.override_role(self):
|
||||
result = self.flavors_client.list_flavors(detail=True)['flavors']
|
||||
if 'rxtx_factor' not in result[0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='rxtx_factor')
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -59,5 +59,5 @@ class FlavorRxtxRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
result = self.flavors_client.show_flavor(
|
||||
CONF.compute.flavor_ref)['flavor']
|
||||
if 'rxtx_factor' not in result:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='rxtx_factor')
|
||||
|
|
|
@ -294,7 +294,7 @@ class ImageSizeRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
|
||||
expected_attr = 'OS-EXT-IMG-SIZE:size'
|
||||
if expected_attr not in body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -310,5 +310,5 @@ class ImageSizeRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
|
||||
expected_attr = 'OS-EXT-IMG-SIZE:size'
|
||||
if expected_attr not in body[0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
|
|
@ -403,5 +403,5 @@ class ServerActionsV216RbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
server = self.servers_client.show_server(self.server_id)['server']
|
||||
|
||||
if 'host_status' not in server:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='host_status')
|
||||
|
|
|
@ -143,7 +143,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
expected_attr = 'config_drive'
|
||||
# If the first server contains "config_drive", then all the others do.
|
||||
if expected_attr not in body[0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -159,7 +159,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
body = self.servers_client.show_server(self.server['id'])['server']
|
||||
expected_attr = 'config_drive'
|
||||
if expected_attr not in body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@utils.requires_ext(extension='os-deferred-delete', service='compute')
|
||||
|
@ -188,7 +188,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
body = self.servers_client.list_servers(detail=True)['servers']
|
||||
# If the first server contains `expected_attr`, then all the others do.
|
||||
if expected_attr not in body[0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -205,7 +205,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
with self.rbac_utils.override_role(self):
|
||||
body = self.servers_client.show_server(self.server['id'])['server']
|
||||
if expected_attr not in body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -229,7 +229,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
for attr in ('host', 'instance_name'):
|
||||
whole_attr = 'OS-EXT-SRV-ATTR:%s' % attr
|
||||
if whole_attr not in body[0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=whole_attr)
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -253,7 +253,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
for attr in ('host', 'instance_name'):
|
||||
whole_attr = 'OS-EXT-SRV-ATTR:%s' % attr
|
||||
if whole_attr not in body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=whole_attr)
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -272,7 +272,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
'OS-EXT-STS:power_state')
|
||||
for attr in expected_attrs:
|
||||
if attr not in body[0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=attr)
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -291,7 +291,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
'OS-EXT-STS:power_state')
|
||||
for attr in expected_attrs:
|
||||
if attr not in body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=attr)
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -310,7 +310,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
with self.rbac_utils.override_role(self):
|
||||
body = self.servers_client.list_servers(detail=True)['servers']
|
||||
if expected_attr not in body[0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -329,7 +329,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
with self.rbac_utils.override_role(self):
|
||||
body = self.servers_client.show_server(self.server['id'])['server']
|
||||
if expected_attr not in body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@utils.requires_ext(extension='os-instance-actions', service='compute')
|
||||
|
@ -360,12 +360,12 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
self.server['id'], request_id)['instanceAction']
|
||||
|
||||
if 'events' not in instance_action:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='events')
|
||||
# Microversion 2.51+ returns 'events' always, but not 'traceback'. If
|
||||
# 'traceback' is also present then policy enforcement passed.
|
||||
if 'traceback' not in instance_action['events'][0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='events.traceback')
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -379,7 +379,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
result = self.servers_client.show_server(self.server['id'])[
|
||||
'server']
|
||||
if 'key_name' not in result:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='key_name')
|
||||
|
||||
@testtools.skipIf(CONF.policy_feature_enabled.removed_nova_policies_stein,
|
||||
|
@ -392,7 +392,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
with self.rbac_utils.override_role(self):
|
||||
result = self.servers_client.list_servers(detail=True)['servers']
|
||||
if 'key_name' not in result[0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='key_name')
|
||||
|
||||
@rbac_rule_validation.action(
|
||||
|
@ -514,7 +514,7 @@ class MiscPolicyActionsRbacTest(rbac_base.BaseV2ComputeRbacTest):
|
|||
body = self.servers_client.show_server(self.server['id'])['server']
|
||||
for expected_attr in expected_attrs:
|
||||
if expected_attr not in body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@utils.requires_ext(extension='os-simple-tenant-usage', service='compute')
|
||||
|
|
|
@ -118,4 +118,4 @@ class NetworkSegmentsRbacTest(base.BaseNetworkRbacTest):
|
|||
LOG.info("NotFound or Forbidden exception are not thrown when "
|
||||
"role doesn't have access to the endpoint. Instead, "
|
||||
"the response will have an empty network body.")
|
||||
raise rbac_exceptions.RbacMalformedResponse(empty=True)
|
||||
raise rbac_exceptions.RbacEmptyResponseBody()
|
||||
|
|
|
@ -363,7 +363,7 @@ class NetworksRbacTest(base.BaseNetworkRbacTest):
|
|||
self.network['id'], **kwargs)['network']
|
||||
|
||||
if len(retrieved_network) == 0:
|
||||
raise rbac_exceptions.RbacMalformedResponse(empty=True)
|
||||
raise rbac_exceptions.RbacEmptyResponseBody()
|
||||
|
||||
@utils.requires_ext(extension='provider', service='network')
|
||||
@rbac_rule_validation.action(service="neutron",
|
||||
|
@ -384,7 +384,7 @@ class NetworksRbacTest(base.BaseNetworkRbacTest):
|
|||
self.network['id'], **kwargs)['network']
|
||||
|
||||
if len(retrieved_network) == 0:
|
||||
raise rbac_exceptions.RbacMalformedResponse(empty=True)
|
||||
raise rbac_exceptions.RbacEmptyResponseBody()
|
||||
|
||||
@utils.requires_ext(extension='provider', service='network')
|
||||
@rbac_rule_validation.action(
|
||||
|
@ -406,7 +406,7 @@ class NetworksRbacTest(base.BaseNetworkRbacTest):
|
|||
self.network['id'], **kwargs)['network']
|
||||
|
||||
if len(retrieved_network) == 0:
|
||||
raise rbac_exceptions.RbacMalformedResponse(empty=True)
|
||||
raise rbac_exceptions.RbacEmptyResponseBody()
|
||||
|
||||
@utils.requires_ext(extension='provider', service='network')
|
||||
@rbac_rule_validation.action(
|
||||
|
@ -428,7 +428,7 @@ class NetworksRbacTest(base.BaseNetworkRbacTest):
|
|||
self.network['id'], **kwargs)['network']
|
||||
|
||||
if len(retrieved_network) == 0:
|
||||
raise rbac_exceptions.RbacMalformedResponse(empty=True)
|
||||
raise rbac_exceptions.RbacEmptyResponseBody()
|
||||
|
||||
@rbac_rule_validation.action(service="neutron",
|
||||
rules=["get_network", "delete_network"],
|
||||
|
|
|
@ -183,7 +183,7 @@ class PortsRbacTest(base.BaseNetworkRbacTest):
|
|||
|
||||
# Rather than throwing a 403, the field is not present, so raise exc.
|
||||
if fields[0] not in retrieved_port:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='binding:vif_type')
|
||||
|
||||
@utils.requires_ext(extension='binding', service='network')
|
||||
|
@ -203,7 +203,7 @@ class PortsRbacTest(base.BaseNetworkRbacTest):
|
|||
|
||||
# Rather than throwing a 403, the field is not present, so raise exc.
|
||||
if fields[0] not in retrieved_port:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='binding:vif_details')
|
||||
|
||||
@utils.requires_ext(extension='binding', service='network')
|
||||
|
@ -226,7 +226,7 @@ class PortsRbacTest(base.BaseNetworkRbacTest):
|
|||
|
||||
# Rather than throwing a 403, the field is not present, so raise exc.
|
||||
if fields[0] not in retrieved_port:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='binding:host_id')
|
||||
|
||||
@utils.requires_ext(extension='binding', service='network')
|
||||
|
@ -250,7 +250,7 @@ class PortsRbacTest(base.BaseNetworkRbacTest):
|
|||
|
||||
# Rather than throwing a 403, the field is not present, so raise exc.
|
||||
if fields[0] not in retrieved_port:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='binding:profile')
|
||||
|
||||
@rbac_rule_validation.action(service="neutron",
|
||||
|
|
|
@ -179,7 +179,7 @@ class RouterRbacTest(base.BaseNetworkRbacTest):
|
|||
|
||||
# Rather than throwing a 403, the field is not present, so raise exc.
|
||||
if 'distributed' not in retrieved_fields:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='distributed')
|
||||
|
||||
@decorators.idempotent_id('defc502c-4159-4824-b4d9-3cdcc39015b2')
|
||||
|
@ -201,7 +201,7 @@ class RouterRbacTest(base.BaseNetworkRbacTest):
|
|||
|
||||
# Rather than throwing a 403, the field is not present, so raise exc.
|
||||
if 'ha' not in retrieved_fields:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='ha')
|
||||
|
||||
@rbac_rule_validation.action(service="neutron",
|
||||
|
|
|
@ -126,7 +126,7 @@ class SecGroupRbacTest(base.BaseNetworkRbacTest):
|
|||
|
||||
# Neutron may return an empty list if access is denied.
|
||||
if not security_groups['security_groups']:
|
||||
raise rbac_exceptions.RbacMalformedResponse(empty=True)
|
||||
raise rbac_exceptions.RbacEmptyResponseBody()
|
||||
|
||||
@rbac_rule_validation.action(service="neutron",
|
||||
rules=["create_security_group_rule"])
|
||||
|
@ -170,4 +170,4 @@ class SecGroupRbacTest(base.BaseNetworkRbacTest):
|
|||
|
||||
# Neutron may return an empty list if access is denied.
|
||||
if not security_rules['security_group_rules']:
|
||||
raise rbac_exceptions.RbacMalformedResponse(empty=True)
|
||||
raise rbac_exceptions.RbacEmptyResponseBody()
|
||||
|
|
|
@ -73,7 +73,7 @@ class SubnetsRbacTest(base.BaseNetworkRbacTest):
|
|||
|
||||
# Neutron may return an empty list if access is denied.
|
||||
if not subnets['subnets']:
|
||||
raise rbac_exceptions.RbacMalformedResponse(empty=True)
|
||||
raise rbac_exceptions.RbacEmptyResponseBody()
|
||||
|
||||
@decorators.idempotent_id('f36cd821-dd22-4bd0-b43d-110fc4b553eb')
|
||||
@rbac_rule_validation.action(service="neutron",
|
||||
|
|
|
@ -208,7 +208,7 @@ class GroupTypesV3RbacTest(rbac_base.BaseVolumeRbacTest):
|
|||
group_type = self.create_group_type(ignore_notfound=True)
|
||||
|
||||
if 'group_specs' not in group_type:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='group_specs')
|
||||
|
||||
@decorators.idempotent_id('8d9e2831-24c3-47b7-a76a-2e563287f12f')
|
||||
|
@ -221,5 +221,5 @@ class GroupTypesV3RbacTest(rbac_base.BaseVolumeRbacTest):
|
|||
resp_body = self.group_types_client.show_group_type(
|
||||
group_type['id'])['group_type']
|
||||
if 'group_specs' not in resp_body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute='group_specs')
|
||||
|
|
|
@ -51,4 +51,5 @@ class LimitsV3RbacTest(rbac_base.BaseVolumeRbacTest):
|
|||
'limits']['absolute']
|
||||
for key in expected_keys:
|
||||
if key not in absolute_limits:
|
||||
raise rbac_exceptions.RbacMalformedResponse(attribute=key)
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=key)
|
||||
|
|
|
@ -111,7 +111,7 @@ class VolumeMetadataV3RbacTest(rbac_base.BaseVolumeRbacTest):
|
|||
'volumes']
|
||||
expected_attr = 'volume_image_metadata'
|
||||
if expected_attr not in resp_body[0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@decorators.idempotent_id('53f94d52-0dd5-42cf-a3a4-59b35150b3d5')
|
||||
|
@ -129,7 +129,7 @@ class VolumeMetadataV3RbacTest(rbac_base.BaseVolumeRbacTest):
|
|||
'volume']
|
||||
expected_attr = 'volume_image_metadata'
|
||||
if expected_attr not in resp_body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@decorators.idempotent_id('a9d9e825-5ea3-42e6-96f3-7ac4e97b2ed0')
|
||||
|
|
|
@ -210,7 +210,7 @@ class VolumesBackupsV318RbacTest(rbac_base.BaseVolumeRbacTest):
|
|||
# Show backup API attempts to inject the attribute below into the
|
||||
# response body but only if policy enforcement succeeds.
|
||||
if self.expected_attr not in body:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=self.expected_attr)
|
||||
|
||||
@decorators.idempotent_id('aa40b7c0-5974-48be-8cbc-e23cc61c4c68')
|
||||
|
@ -221,7 +221,7 @@ class VolumesBackupsV318RbacTest(rbac_base.BaseVolumeRbacTest):
|
|||
body = self.backups_client.list_backups(detail=True)['backups']
|
||||
|
||||
if self.expected_attr not in body[0]:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=self.expected_attr)
|
||||
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ class VolumesSnapshotV3RbacTest(rbac_base.BaseVolumeRbacTest):
|
|||
self.snapshot['id'])['snapshot']
|
||||
for expected_attr in expected_attrs:
|
||||
if expected_attr not in resp:
|
||||
raise rbac_exceptions.RbacMalformedResponse(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
||||
@rbac_rule_validation.action(service="cinder",
|
||||
|
@ -136,5 +136,5 @@ class VolumesSnapshotV3RbacTest(rbac_base.BaseVolumeRbacTest):
|
|||
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(
|
||||
raise rbac_exceptions.RbacMissingAttributeResponseBody(
|
||||
attribute=expected_attr)
|
||||
|
|
|
@ -46,8 +46,9 @@ class BaseRBACRuleValidationTest(base.TestCase):
|
|||
project_id=mock.sentinel.project_id)
|
||||
setattr(self.mock_test_args.os_primary, 'credentials', mock_creds)
|
||||
|
||||
self.test_roles = ['member']
|
||||
self.useFixture(
|
||||
patrole_fixtures.ConfPatcher(rbac_test_roles=['member'],
|
||||
patrole_fixtures.ConfPatcher(rbac_test_roles=self.test_roles,
|
||||
group='patrole'))
|
||||
# Disable patrole log for unit tests.
|
||||
self.useFixture(
|
||||
|
@ -69,9 +70,10 @@ class BaseRBACMultiRoleRuleValidationTest(base.TestCase):
|
|||
project_id=mock.sentinel.project_id)
|
||||
setattr(self.mock_test_args.os_primary, 'credentials', mock_creds)
|
||||
|
||||
self.test_roles = ['member', 'anotherrole']
|
||||
self.useFixture(
|
||||
patrole_fixtures.ConfPatcher(
|
||||
rbac_test_roles=['member', 'anotherrole'], group='patrole'))
|
||||
patrole_fixtures.ConfPatcher(rbac_test_roles=self.test_roles,
|
||||
group='patrole'))
|
||||
# Disable patrole log for unit tests.
|
||||
self.useFixture(
|
||||
patrole_fixtures.ConfPatcher(enable_reporting=False,
|
||||
|
@ -150,43 +152,66 @@ class RBACRuleValidationTest(BaseRBACRuleValidationTest):
|
|||
|
||||
@mock.patch.object(rbac_rv, 'LOG', autospec=True)
|
||||
@mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
|
||||
def test_rule_validation_rbac_malformed_response_positive(
|
||||
def test_rule_validation_rbac_failed_response_body_positive(
|
||||
self, mock_authority, mock_log):
|
||||
"""Test RbacMalformedResponse error is thrown without permission
|
||||
passes.
|
||||
"""Test BasePatroleResponseBodyException 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.
|
||||
Positive test case: if subclass of BasePatroleResponseBodyException 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.RbacMalformedResponse()
|
||||
def _do_test(exception_cls, **kwargs):
|
||||
@rbac_rv.action(mock.sentinel.service,
|
||||
rules=[mock.sentinel.action])
|
||||
def test_policy(*args):
|
||||
raise exception_cls(**kwargs)
|
||||
|
||||
mock_log.error.assert_not_called()
|
||||
mock_log.error.assert_not_called()
|
||||
mock_log.warning.assert_not_called()
|
||||
|
||||
_do_test(rbac_exceptions.RbacMissingAttributeResponseBody,
|
||||
attribute=mock.sentinel.attr)
|
||||
_do_test(rbac_exceptions.RbacPartialResponseBody,
|
||||
body=mock.sentinel.body)
|
||||
_do_test(rbac_exceptions.RbacEmptyResponseBody)
|
||||
|
||||
@mock.patch.object(rbac_rv, 'LOG', autospec=True)
|
||||
@mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
|
||||
def test_rule_validation_rbac_malformed_response_negative(
|
||||
def test_rule_validation_soft_authorization_exceptions(
|
||||
self, mock_authority, mock_log):
|
||||
"""Test RbacMalformedResponse error is thrown with permission fails.
|
||||
"""Test RbacUnderPermissionException error is thrown when any of the
|
||||
soft authorization-related exceptions are raised by a test.
|
||||
|
||||
Negative test case: if RbacMalformedResponse is thrown and the user is
|
||||
allowed to perform the action, then this is an expected failure.
|
||||
Negative test case: if subclass of BasePatroleResponseBodyException 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.RbacMalformedResponse()
|
||||
def _do_test(exception_cls, **kwargs):
|
||||
@rbac_rv.action(mock.sentinel.service,
|
||||
rules=[mock.sentinel.action])
|
||||
def test_policy(*args):
|
||||
raise exception_cls(**kwargs)
|
||||
|
||||
test_re = ("User with roles \['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)
|
||||
test_re = (".*User with roles \[%s\] was not allowed to "
|
||||
"perform the following actions: \[%s\].*" % (
|
||||
', '.join("'%s'" % r for r in self.test_roles),
|
||||
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)
|
||||
|
||||
_do_test(rbac_exceptions.RbacMissingAttributeResponseBody,
|
||||
attribute=mock.sentinel.attr)
|
||||
_do_test(rbac_exceptions.RbacPartialResponseBody,
|
||||
body=mock.sentinel.body)
|
||||
_do_test(rbac_exceptions.RbacEmptyResponseBody)
|
||||
|
||||
@mock.patch.object(rbac_rv, 'LOG', autospec=True)
|
||||
@mock.patch.object(rbac_rv, 'policy_authority', autospec=True)
|
||||
|
@ -397,28 +422,6 @@ class RBACMultiRoleRuleValidationTest(BaseRBACMultiRoleRuleValidationTest,
|
|||
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_rule_validation_rbac_malformed_response_negative(
|
||||
self, mock_authority, mock_log):
|
||||
"""Test RbacMalformedResponse error is thrown with permission fails.
|
||||
|
||||
Negative test case: if RbacMalformedResponse 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.RbacMalformedResponse()
|
||||
|
||||
test_re = ("User with roles \['member', 'anotherrole'\] 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_and_raise_not_found(self, mock_authority,
|
||||
|
@ -960,7 +963,7 @@ class RBACOverrideRoleValidationTest(BaseRBACRuleValidationTest):
|
|||
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
|
||||
valid in case of e.g. RbacPartialResponseBody) after override_role
|
||||
passes.
|
||||
"""
|
||||
mock_authority.PolicyAuthority.return_value.allowed.return_value =\
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
The exception class ``RbacMalformedException`` has been broken up into the
|
||||
following discrete exceptions:
|
||||
|
||||
* ``RbacMissingAttributeResponseBody`` - incomplete means that the
|
||||
response body (for show or list) is missing certain attributes
|
||||
* ``RbacPartialResponseBody`` - partial means that a list response
|
||||
only returned a subset of the possible results available.
|
||||
* ``RbacEmptyResponseBody`` - empty means that the show or list
|
||||
response body is entirely empty
|
||||
|
||||
Each of the exception classes above deals with a different type of failure
|
||||
related to a soft authorization failure. This means that, rather than a
|
||||
403 error code getting returned by the server, the response body is
|
||||
incomplete in some way.
|
||||
upgrade:
|
||||
- |
|
||||
The exception class ``RbacMalformedException`` has been removed. Use one
|
||||
of the following exception classes instead:
|
||||
|
||||
* ``RbacMissingAttributeResponseBody``
|
||||
* ``RbacPartialResponseBody``
|
||||
* ``RbacEmptyResponseBody``
|
Loading…
Reference in New Issue