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:
parent
63b8a82b31
commit
4f75f848a5
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue