Add a dereference option for ldap

This allows proper dereferencing of aliased objects in an LDAP tree.

Fixes Bug #1153786

Change-Id: Ia09a99b7bca1ab055eb0c6dfa34138beca15bff0
This commit is contained in:
Allan Feid 2013-03-11 23:11:52 -04:00 committed by Dolph Mathews
parent 63b8a82b31
commit 4f75f848a5
5 changed files with 103 additions and 2 deletions

View File

@ -146,6 +146,11 @@
# Maximum results per page; a value of zero ('0') disables paging (default)
# page_size = 0
# The LDAP dereferencing option for queries. This can be either 'never',
# 'searching', 'always', 'finding' or 'default'. The 'default' option falls
# back to using default dereferencing configured by your ldap.conf.
# alias_dereferencing = default
# The LDAP scope for queries, this can be either 'one'
# (onelevel/singleLevel) or 'sub' (subtree/wholeSubtree)
# query_scope = one

View File

@ -271,6 +271,7 @@ def configure():
register_bool('allow_subtree_delete', group='ldap', default=False)
register_str('query_scope', group='ldap', default='one')
register_int('page_size', group='ldap', default=0)
register_str('alias_dereferencing', group='ldap', default='default')
register_str('user_tree_dn', group='ldap', default=None)
register_str('user_filter', group='ldap', default=None)

View File

@ -29,6 +29,11 @@ LDAP_VALUES = {'TRUE': True, 'FALSE': False}
CONTROL_TREEDELETE = '1.2.840.113556.1.4.805'
LDAP_SCOPES = {'one': ldap.SCOPE_ONELEVEL,
'sub': ldap.SCOPE_SUBTREE}
LDAP_DEREF = {'always': ldap.DEREF_ALWAYS,
'default': None,
'finding': ldap.DEREF_FINDING,
'never': ldap.DEREF_NEVER,
'searching': ldap.DEREF_SEARCHING}
def py2ldap(val):
@ -62,6 +67,14 @@ def safe_iter(attrs):
yield attrs
def parse_deref(opt):
try:
return LDAP_DEREF[opt]
except KeyError:
raise ValueError((_('Invalid LDAP deref option: %s. Choose one of: ') %
opt) + ', '.join(LDAP_DEREF.keys()))
def ldap_scope(scope):
try:
return LDAP_SCOPES[scope]
@ -91,6 +104,7 @@ class BaseLdap(object):
self.LDAP_USER = conf.ldap.user
self.LDAP_PASSWORD = conf.ldap.password
self.LDAP_SCOPE = ldap_scope(conf.ldap.query_scope)
self.alias_dereferencing = parse_deref(conf.ldap.alias_dereferencing)
self.page_size = conf.ldap.page_size
if self.options_name is not None:
@ -142,7 +156,8 @@ class BaseLdap(object):
conn = fakeldap.FakeLdap(self.LDAP_URL)
else:
conn = LdapWrapper(self.LDAP_URL,
self.page_size)
self.page_size,
alias_dereferencing=self.alias_dereferencing)
if user is None:
user = self.LDAP_USER
@ -348,9 +363,11 @@ class BaseLdap(object):
class LdapWrapper(object):
def __init__(self, url, page_size):
def __init__(self, url, page_size, alias_dereferencing=None):
LOG.debug(_("LDAP init: url=%s"), url)
self.conn = ldap.initialize(url)
if alias_dereferencing is not None:
self.conn.set_option(ldap.OPT_DEREF, alias_dereferencing)
self.page_size = page_size
def simple_bind_s(self, user, password):

View File

@ -19,7 +19,9 @@ import ldap.modlist
import nose.exc
import subprocess
from keystone.common import ldap as ldap_common
from keystone import config
from keystone import exception
from keystone.identity.backends import ldap as identity_ldap
from keystone import test
@ -92,3 +94,72 @@ class LiveLDAPIdentity(test_backend_ldap.LDAPIdentity):
def test_user_enable_attribute_mask(self):
raise nose.exc.SkipTest('Test is for Active Directory Only')
def test_ldap_dereferencing(self):
alt_users_ldif = {'objectclass': ['top', 'organizationalUnit'],
'ou': 'alt_users'}
alt_fake_user_ldif = {'objectclass': ['person', 'inetOrgPerson'],
'cn': 'alt_fake1',
'sn': 'alt_fake1'}
aliased_users_ldif = {'objectclass': ['alias', 'extensibleObject'],
'aliasedobjectname': "ou=alt_users,%s" %
CONF.ldap.suffix}
create_object("ou=alt_users,%s" % CONF.ldap.suffix, alt_users_ldif)
create_object("%s=alt_fake1,ou=alt_users,%s" %
(CONF.ldap.user_id_attribute, CONF.ldap.suffix),
alt_fake_user_ldif)
create_object("ou=alt_users,%s" % CONF.ldap.user_tree_dn,
aliased_users_ldif)
CONF.ldap.query_scope = 'sub'
CONF.ldap.alias_dereferencing = 'never'
self.identity_api = identity_ldap.Identity()
self.assertRaises(exception.UserNotFound,
self.identity_api.get_user,
'alt_fake1')
CONF.ldap.alias_dereferencing = 'searching'
self.identity_api = identity_ldap.Identity()
user_ref = self.identity_api.get_user('alt_fake1')
self.assertEqual(user_ref['id'], 'alt_fake1')
CONF.ldap.alias_dereferencing = 'always'
self.identity_api = identity_ldap.Identity()
user_ref = self.identity_api.get_user('alt_fake1')
self.assertEqual(user_ref['id'], 'alt_fake1')
def test_base_ldap_connection_deref_option(self):
deref = ldap_common.parse_deref('default')
ldap_wrapper = ldap_common.LdapWrapper(CONF.ldap.url,
CONF.ldap.page_size,
alias_dereferencing=deref)
self.assertEqual(ldap.get_option(ldap.OPT_DEREF),
ldap_wrapper.conn.get_option(ldap.OPT_DEREF))
deref = ldap_common.parse_deref('always')
ldap_wrapper = ldap_common.LdapWrapper(CONF.ldap.url,
CONF.ldap.page_size,
alias_dereferencing=deref)
self.assertEqual(ldap.DEREF_ALWAYS,
ldap_wrapper.conn.get_option(ldap.OPT_DEREF))
deref = ldap_common.parse_deref('finding')
ldap_wrapper = ldap_common.LdapWrapper(CONF.ldap.url,
CONF.ldap.page_size,
alias_dereferencing=deref)
self.assertEqual(ldap.DEREF_FINDING,
ldap_wrapper.conn.get_option(ldap.OPT_DEREF))
deref = ldap_common.parse_deref('never')
ldap_wrapper = ldap_common.LdapWrapper(CONF.ldap.url,
CONF.ldap.page_size,
alias_dereferencing=deref)
self.assertEqual(ldap.DEREF_NEVER,
ldap_wrapper.conn.get_option(ldap.OPT_DEREF))
deref = ldap_common.parse_deref('searching')
ldap_wrapper = ldap_common.LdapWrapper(CONF.ldap.url,
CONF.ldap.page_size,
alias_dereferencing=deref)
self.assertEqual(ldap.DEREF_SEARCHING,
ldap_wrapper.conn.get_option(ldap.OPT_DEREF))

View File

@ -357,6 +357,13 @@ class LDAPIdentity(test.TestCase, test_backend.IdentityTests):
'Invalid LDAP scope: %s. *' % CONF.ldap.query_scope,
identity.backends.ldap.Identity)
def test_wrong_alias_dereferencing(self):
CONF.ldap.alias_dereferencing = uuid.uuid4().hex
self.assertRaisesRegexp(
ValueError,
'Invalid LDAP deref option: %s\.' % CONF.ldap.alias_dereferencing,
identity.backends.ldap.Identity)
# TODO (henry-nash) These need to be removed when the full LDAP implementation
# is submitted - see Bugs 1092187, 1101287, 1101276, 1101289