summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2012-10-09 18:51:39 +0000
committerGerrit Code Review <review@openstack.org>2012-10-09 18:51:39 +0000
commitb0eb94dbc0aff690fcbde6d49c3ad5c6578eb7b5 (patch)
tree2443d6dc8b723f90dec4e7251f5c01cca9f5e943
parent8236d3b4f6945f6057252e6bc195bec103e9b12d (diff)
parentee48c24184462724aa85b603296adb9f3f68934e (diff)
Merge "Unable to delete tenant if contains roles in LDAP backend (bug 1057407)"
-rw-r--r--etc/keystone.conf.sample1
-rw-r--r--keystone/common/ldap/core.py15
-rw-r--r--keystone/common/ldap/fakeldap.py14
-rw-r--r--keystone/config.py1
-rw-r--r--keystone/identity/backends/ldap/core.py20
-rw-r--r--tests/test_backend.py10
6 files changed, 60 insertions, 1 deletions
diff --git a/etc/keystone.conf.sample b/etc/keystone.conf.sample
index 3d330ba6..82bd757c 100644
--- a/etc/keystone.conf.sample
+++ b/etc/keystone.conf.sample
@@ -107,6 +107,7 @@
# password = None
# suffix = cn=example,cn=com
# use_dumb_member = False
+# allow_subtree_delete = False
# user_tree_dn = ou=Users,dc=example,dc=com
# user_objectclass = inetOrgPerson
diff --git a/keystone/common/ldap/core.py b/keystone/common/ldap/core.py
index a8b8e970..027bc1be 100644
--- a/keystone/common/ldap/core.py
+++ b/keystone/common/ldap/core.py
@@ -25,6 +25,7 @@ LOG = logging.getLogger(__name__)
LDAP_VALUES = {'TRUE': True, 'FALSE': False}
+CONTROL_TREEDELETE = '1.2.840.113556.1.4.805'
def py2ldap(val):
@@ -103,6 +104,8 @@ class BaseLdap(object):
self.structural_classes = self.DEFAULT_STRUCTURAL_CLASSES
self.use_dumb_member = getattr(conf.ldap, 'use_dumb_member') or True
+ self.subtree_delete_enabled = getattr(conf.ldap,
+ 'allow_subtree_delete')
def get_connection(self, user=None, password=None):
if self.LDAP_URL.startswith('fake://'):
@@ -309,6 +312,14 @@ class BaseLdap(object):
conn = self.get_connection()
conn.delete_s(self._id_to_dn(id))
+ def deleteTree(self, id):
+ conn = self.get_connection()
+ tree_delete_control = ldap.controls.LDAPControl(CONTROL_TREEDELETE,
+ 0,
+ None)
+ conn.delete_ext_s(self._id_to_dn(id),
+ serverctrls=[tree_delete_control])
+
class LdapWrapper(object):
def __init__(self, url):
@@ -362,3 +373,7 @@ class LdapWrapper(object):
def delete_s(self, dn):
LOG.debug("LDAP delete: dn=%s", dn)
return self.conn.delete_s(dn)
+
+ def delete_ext_s(self, dn, serverctrls):
+ LOG.debug("LDAP delete_ext: dn=%s, serverctrls=%s", dn, serverctrls)
+ return self.conn.delete_ext_s(dn, serverctrls)
diff --git a/keystone/common/ldap/fakeldap.py b/keystone/common/ldap/fakeldap.py
index 77d2bfe4..bfbefd78 100644
--- a/keystone/common/ldap/fakeldap.py
+++ b/keystone/common/ldap/fakeldap.py
@@ -212,6 +212,20 @@ class FakeLdap(object):
raise ldap.NO_SUCH_OBJECT
self.db.sync()
+ def delete_ext_s(self, dn, serverctrls):
+ """Remove the ldap object at specified dn."""
+ if server_fail:
+ raise ldap.SERVER_DOWN
+
+ key = '%s%s' % (self.__prefix, dn)
+ LOG.debug('FakeLdap delete item: dn=%s', dn)
+ try:
+ del self.db[key]
+ except KeyError:
+ LOG.error('FakeLdap delete item failed: dn=%s not found.', dn)
+ raise ldap.NO_SUCH_OBJECT
+ self.db.sync()
+
def modify_s(self, dn, attrs):
"""Modify the object at dn using the attribute list.
diff --git a/keystone/config.py b/keystone/config.py
index 37ce6644..a6d5f0c6 100644
--- a/keystone/config.py
+++ b/keystone/config.py
@@ -164,6 +164,7 @@ register_str('password', group='ldap', default='freeipa4all')
register_str('suffix', group='ldap', default='cn=example,cn=com')
register_bool('use_dumb_member', group='ldap', default=False)
register_str('user_name_attribute', group='ldap', default='sn')
+register_bool('allow_subtree_delete', group='ldap', default=False)
register_str('user_tree_dn', group='ldap', default=None)
register_str('user_objectclass', group='ldap', default='inetOrgPerson')
diff --git a/keystone/identity/backends/ldap/core.py b/keystone/identity/backends/ldap/core.py
index 21b7f1d5..deb78e43 100644
--- a/keystone/identity/backends/ldap/core.py
+++ b/keystone/identity/backends/ldap/core.py
@@ -572,7 +572,11 @@ class TenantApi(common_ldap.BaseLdap, ApiShimMixin):
return list(res)
def delete(self, id):
- super(TenantApi, self).delete(id)
+ if self.subtree_delete_enabled:
+ super(TenantApi, self).deleteTree(id)
+ else:
+ self.role_api.roles_delete_subtree_by_tenant(id)
+ super(TenantApi, self).delete(id)
def update(self, id, values):
try:
@@ -894,6 +898,20 @@ class RoleApi(common_ldap.BaseLdap, ApiShimMixin):
tenant_id=tenant_id))
return res
+ def roles_delete_subtree_by_tenant(self, tenant_id):
+ conn = self.get_connection()
+ query = '(objectClass=%s)' % self.object_class
+ tenant_dn = self.tenant_api._id_to_dn(tenant_id)
+ try:
+ roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query)
+ for role_dn, _ in roles:
+ try:
+ conn.delete_s(role_dn)
+ except Exception as inst:
+ raise inst
+ except ldap.NO_SUCH_OBJECT:
+ pass
+
def rolegrant_get_by_ids(self, user_id, role_id, tenant_id):
conn = self.get_connection()
user_dn = self.user_api._id_to_dn(user_id)
diff --git a/tests/test_backend.py b/tests/test_backend.py
index e60a4e1c..eb6fa671 100644
--- a/tests/test_backend.py
+++ b/tests/test_backend.py
@@ -626,6 +626,16 @@ class IdentityTests(object):
for test_tenant in default_fixtures.TENANTS:
self.assertTrue(x for x in tenants if x['id'] == test_tenant['id'])
+ def test_delete_tenant_with_role_assignments(self):
+ tenant = {'id': 'fake1', 'name': 'fake1'}
+ self.identity_api.create_tenant('fake1', tenant)
+ self.identity_api.add_role_to_user_and_tenant(
+ self.user_foo['id'], tenant['id'], 'useless')
+ self.identity_api.delete_tenant(tenant['id'])
+ self.assertRaises(exception.NotFound,
+ self.identity_api.get_tenant,
+ tenant['id'])
+
class TokenTests(object):
def test_token_crud(self):