Fix project-related forbidden response messages

When attempting to delete a project that has a child, the operation
is rejected as expected, but the message said it was rejected
because of an authority problem:

 You are not authorized to perform the requested action: cannot
 delete the project ... since it is not a leaf in the hierarchy.

This is misleading since the problem has nothing to do with
authority and granting more authority isn't going to allow the
operation to work.

There are several operations like this that are fixed.

ForbiddenAction is meant to be used to reference the action in the
policy file.

Change-Id: Ibe8c1ee8e5cac7c135d0b84c5e59b630e3d91d20
Closes-Bug: 1548562
This commit is contained in:
Brant Knudson 2016-02-22 18:12:35 -06:00
parent d2af8e0d6a
commit 8e2bba4829
3 changed files with 45 additions and 40 deletions

View File

@ -140,27 +140,33 @@ class CircularRegionHierarchyError(Error):
title = 'Bad Request'
class PasswordVerificationError(Error):
class ForbiddenNotSecurity(Error):
"""When you want to return a 403 Forbidden response but not security.
Use this for errors where the message is always safe to present to the user
and won't give away extra information.
"""
code = 403
title = 'Forbidden'
class PasswordVerificationError(ForbiddenNotSecurity):
message_format = _("The password length must be less than or equal "
"to %(size)i. The server could not comply with the "
"request because the password is invalid.")
code = 403
title = 'Forbidden'
class RegionDeletionError(Error):
class RegionDeletionError(ForbiddenNotSecurity):
message_format = _("Unable to delete region %(region_id)s because it or "
"its child regions have associated endpoints.")
code = 403
title = 'Forbidden'
class PKITokenExpected(Error):
class PKITokenExpected(ForbiddenNotSecurity):
message_format = _('The certificates you requested are not available. '
'It is likely that this server does not use PKI tokens '
'otherwise this is the result of misconfiguration.')
code = 403
title = 'Forbidden'
class SecurityError(Error):

View File

@ -87,9 +87,8 @@ class Manager(manager.Manager):
parents_list = self.list_project_parents(project_id)
max_depth = CONF.max_project_tree_depth
if self._get_hierarchy_depth(parents_list) > max_depth:
raise exception.ForbiddenAction(
action=_('max hierarchy depth reached for '
'%s branch.') % project_id)
raise exception.ForbiddenNotSecurity(
_('Max hierarchy depth reached for %s branch.') % project_id)
def _assert_is_domain_project_constraints(self, project_ref, parent_ref):
"""Enforces specific constraints of projects that act as domains
@ -292,9 +291,9 @@ class Manager(manager.Manager):
parents_list = self.list_project_parents(project_id)
for project in parents_list:
if not project.get('enabled', True):
raise exception.ForbiddenAction(
action=_('cannot enable project %s since it has '
'disabled parents') % project_id)
raise exception.ForbiddenNotSecurity(
_('Cannot enable project %s since it has disabled '
'parents') % project_id)
def _check_whole_subtree_is_disabled(self, project_id, subtree_list=None):
if not subtree_list:
@ -317,8 +316,8 @@ class Manager(manager.Manager):
parent_id = original_project.get('parent_id')
if 'parent_id' in project and project.get('parent_id') != parent_id:
raise exception.ForbiddenAction(
action=_('Update of `parent_id` is not allowed.'))
raise exception.ForbiddenNotSecurity(
_('Update of `parent_id` is not allowed.'))
if ('is_domain' in project and
project['is_domain'] != original_project['is_domain']):
@ -375,9 +374,9 @@ class Manager(manager.Manager):
# effectively disables its children.
if (not original_project.get('is_domain') and not cascade and not
self._check_whole_subtree_is_disabled(project_id)):
raise exception.ForbiddenAction(
action=_('cannot disable project %(project_id)s since its '
'subtree contains enabled projects.')
raise exception.ForbiddenNotSecurity(
_('Cannot disable project %(project_id)s since its '
'subtree contains enabled projects.')
% {'project_id': project_id})
self._disable_project(project_id)
@ -461,10 +460,10 @@ class Manager(manager.Manager):
% project.get('id'))
if not self.is_leaf_project(project_id) and not cascade:
raise exception.ForbiddenAction(
action=_('cannot delete the project %s since it is not '
'a leaf in the hierarchy. Use the cascade option '
'if you want to delete a whole subtree.')
raise exception.ForbiddenNotSecurity(
_('Cannot delete the project %s since it is not a leaf in the '
'hierarchy. Use the cascade option if you want to delete a '
'whole subtree.')
% project_id)
if cascade:
@ -474,9 +473,9 @@ class Manager(manager.Manager):
subtree_list.reverse()
if not self._check_whole_subtree_is_disabled(
project_id, subtree_list=subtree_list):
raise exception.ForbiddenAction(
action=_('cannot delete project %(project_id)s since its '
'subtree contains enabled projects.')
raise exception.ForbiddenNotSecurity(
_('Cannot delete project %(project_id)s since its subtree '
'contains enabled projects.')
% {'project_id': project_id})
project_list = subtree_list + [project]
@ -705,9 +704,9 @@ class Manager(manager.Manager):
# their own domain since, once it is disabled, they won't be able
# to get a valid token to issue this delete.
if domain['enabled']:
raise exception.ForbiddenAction(
action=_('cannot delete a domain that is enabled, '
'please disable it first.'))
raise exception.ForbiddenNotSecurity(
_('Cannot delete a domain that is enabled, please disable it '
'first.'))
self._delete_domain_contents(domain_id)
# Delete any database stored domain config

View File

@ -2974,7 +2974,7 @@ class IdentityTests(AssignmentTestHelperMixin):
self.assertFalse(subtree[0]['enabled'])
parent['enabled'] = True
self.assertRaises(exception.ForbiddenAction,
self.assertRaises(exception.ForbiddenNotSecurity,
self.resource_api.update_project,
parent['id'],
parent,
@ -3643,7 +3643,7 @@ class IdentityTests(AssignmentTestHelperMixin):
self.resource_api.update_project(project2['id'], project2)
# Cannot cascade delete root_project, since project1 is enabled
self.assertRaises(exception.ForbiddenAction,
self.assertRaises(exception.ForbiddenNotSecurity,
self.resource_api.delete_project,
root_project['id'],
cascade=True)
@ -3671,7 +3671,7 @@ class IdentityTests(AssignmentTestHelperMixin):
# update the parent_id is not allowed
leaf_project['parent_id'] = root_project1['id']
self.assertRaises(exception.ForbiddenAction,
self.assertRaises(exception.ForbiddenNotSecurity,
self.resource_api.update_project,
leaf_project['id'],
leaf_project)
@ -3683,7 +3683,7 @@ class IdentityTests(AssignmentTestHelperMixin):
root_project1['id'])
# delete root_project2 is not allowed since it is not a leaf project
self.assertRaises(exception.ForbiddenAction,
self.assertRaises(exception.ForbiddenNotSecurity,
self.resource_api.delete_project,
root_project2['id'])
@ -3729,7 +3729,7 @@ class IdentityTests(AssignmentTestHelperMixin):
projects_hierarchy = self._create_projects_hierarchy()
root_project = projects_hierarchy[0]
self.assertRaises(exception.ForbiddenAction,
self.assertRaises(exception.ForbiddenNotSecurity,
self.resource_api.delete_project,
root_project['id'])
@ -3744,7 +3744,7 @@ class IdentityTests(AssignmentTestHelperMixin):
# try to update project3 parent to parent1
project3['parent_id'] = project1['id']
self.assertRaises(exception.ForbiddenAction,
self.assertRaises(exception.ForbiddenNotSecurity,
self.resource_api.update_project,
project3['id'],
project3)
@ -3779,7 +3779,7 @@ class IdentityTests(AssignmentTestHelperMixin):
root_project = projects_hierarchy[0]
root_project['enabled'] = False
self.assertRaises(exception.ForbiddenAction,
self.assertRaises(exception.ForbiddenNotSecurity,
self.resource_api.update_project,
root_project['id'],
root_project)
@ -3798,7 +3798,7 @@ class IdentityTests(AssignmentTestHelperMixin):
# Try to enable the leaf project, it's not possible since it has
# a disabled parent
leaf_project['enabled'] = True
self.assertRaises(exception.ForbiddenAction,
self.assertRaises(exception.ForbiddenNotSecurity,
self.resource_api.update_project,
leaf_project['id'],
leaf_project)
@ -3818,7 +3818,7 @@ class IdentityTests(AssignmentTestHelperMixin):
# Creating another project in the hierarchy shouldn't be allowed
project = unit.new_project_ref(domain_id=DEFAULT_DOMAIN_ID,
parent_id=leaf_project['id'])
self.assertRaises(exception.ForbiddenAction,
self.assertRaises(exception.ForbiddenNotSecurity,
self.resource_api.create_project,
project['id'],
project)
@ -3863,7 +3863,7 @@ class IdentityTests(AssignmentTestHelperMixin):
self.assertDictEqual(domain, domain_ref)
# Ensure an 'enabled' domain cannot be deleted
self.assertRaises(exception.ForbiddenAction,
self.assertRaises(exception.ForbiddenNotSecurity,
self.resource_api.delete_domain,
domain_id=domain['id'])