summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--etc/keystone.conf.sample5
-rw-r--r--keystone/common/config.py1
-rw-r--r--keystone/common/ldap/core.py21
-rw-r--r--tests/_ldap_livetest.py71
-rw-r--r--tests/test_backend_ldap.py7
5 files changed, 103 insertions, 2 deletions
diff --git a/etc/keystone.conf.sample b/etc/keystone.conf.sample
index 426e3b24..b39eb926 100644
--- a/etc/keystone.conf.sample
+++ b/etc/keystone.conf.sample
@@ -140,6 +140,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
diff --git a/keystone/common/config.py b/keystone/common/config.py
index e60385cc..2fd20b99 100644
--- a/keystone/common/config.py
+++ b/keystone/common/config.py
@@ -268,6 +268,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)
diff --git a/keystone/common/ldap/core.py b/keystone/common/ldap/core.py
index 865c90e7..b06f2277 100644
--- a/keystone/common/ldap/core.py
+++ b/keystone/common/ldap/core.py
@@ -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):
diff --git a/tests/_ldap_livetest.py b/tests/_ldap_livetest.py
index d6a7a63d..5d9e96d0 100644
--- a/tests/_ldap_livetest.py
+++ b/tests/_ldap_livetest.py
@@ -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))
diff --git a/tests/test_backend_ldap.py b/tests/test_backend_ldap.py
index b0749d9e..6db626d8 100644
--- a/tests/test_backend_ldap.py
+++ b/tests/test_backend_ldap.py
@@ -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