Except forbidden when clearing default project IDs

The identity backend registers a callback that listens for when a
project is deleted. When it receives a notification, it uses the
project ID send in the notification and removes all references to it
from the identity backend, where users might have it referenced in
their `default_project_id` attribute. The original fix for this did
not account for LDAP backends being read-only. This caused an issue
where DELETE /v3/projects/{project_id} actually caused an HTTP 403
Forbidden exception because the LDAP backend wasn't writeable,
despite that project actually being deleted.

This change makes the identity API manager handle the exception
and tests it specifically for LDAP, or read-only, backends.

Change-Id: I16f4fcb289dad2fe752f3188476329c95cf777c9
Closes-Bug: 1705081
This commit is contained in:
Lance Bragstad 2017-08-07 20:29:08 +00:00
parent 9cbd6bc0eb
commit b068d71b59
2 changed files with 29 additions and 1 deletions

View File

@ -530,7 +530,15 @@ class Manager(manager.Manager):
"""
project_id = payload['resource_info']
self.driver.unset_default_project_id(project_id)
try:
self.driver.unset_default_project_id(project_id)
except exception.Forbidden:
# NOTE(lbragstad): If the driver throws a Forbidden, it's because
# the driver doesn't support writes. This is the case with the
# in-tree LDAP implementation since it is read-only. This also
# ensures consistency for out-of-tree backends that might be
# read-only.
pass
# Domain ID normalization methods
def _set_domain_id_and_mapping(self, ref, domain_id, driver,

View File

@ -31,6 +31,7 @@ from keystone.common import driver_hints
import keystone.conf
from keystone import exception
from keystone import identity
from keystone.identity.backends import ldap as ldap_identity
from keystone.identity.backends.ldap import common as common_ldap
from keystone.identity.mapping_backends import mapping as map
from keystone.tests import unit
@ -1723,6 +1724,25 @@ class LDAPIdentity(BaseLDAPIdentity, unit.TestCase):
self.assertEqual('crap', user_ref['id'])
self.assertEqual('Foo Bar', user_ref['name'])
def test_identity_manager_catches_forbidden_when_deleting_a_project(self):
# The identity API registers a callback that listens for notifications
# that a project has been deleted. When it receives one, it uses the ID
# and attempts to clear any users who have `default_project_id`
# attributes associated to that project. Since the LDAP backend is
# read-only, clearing the `default_project_id` requires a write which
# isn't possible.
project = unit.new_project_ref(
domain_id=CONF.identity.default_domain_id
)
project = self.resource_api.create_project(project['id'], project)
with mock.patch.object(
ldap_identity.Identity, '_disallow_write'
) as mocked:
mocked.side_effect = exception.Forbidden()
self.resource_api.delete_project(project['id'])
mocked.assert_called_once()
class LDAPLimitTests(unit.TestCase, identity_tests.LimitTests):
def setUp(self):