aboutsummaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/common/ldap/core.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/common/ldap/core.py')
-rw-r--r--keystone-moon/keystone/common/ldap/core.py171
1 files changed, 90 insertions, 81 deletions
diff --git a/keystone-moon/keystone/common/ldap/core.py b/keystone-moon/keystone/common/ldap/core.py
index 144c0cfd..0bb3830c 100644
--- a/keystone-moon/keystone/common/ldap/core.py
+++ b/keystone-moon/keystone/common/ldap/core.py
@@ -24,11 +24,13 @@ import ldap.filter
import ldappool
from oslo_log import log
import six
+from six.moves import map, zip
from keystone import exception
from keystone.i18n import _
from keystone.i18n import _LW
+
LOG = log.getLogger(__name__)
LDAP_VALUES = {'TRUE': True, 'FALSE': False}
@@ -159,7 +161,7 @@ def convert_ldap_result(ldap_result):
at_least_one_referral = True
continue
- for kind, values in six.iteritems(attrs):
+ for kind, values in attrs.items():
try:
val2py = enabled2py if kind == 'enabled' else ldap2py
ldap_attrs[kind] = [val2py(x) for x in values]
@@ -327,7 +329,7 @@ def dn_startswith(descendant_dn, dn):
@six.add_metaclass(abc.ABCMeta)
class LDAPHandler(object):
- '''Abstract class which defines methods for a LDAP API provider.
+ """Abstract class which defines methods for a LDAP API provider.
Native Keystone values cannot be passed directly into and from the
python-ldap API. Type conversion must occur at the LDAP API
@@ -415,7 +417,8 @@ class LDAPHandler(object):
method to any derivations of the abstract class the code will fail
to load and run making it impossible to forget updating all the
derived classes.
- '''
+
+ """
@abc.abstractmethod
def __init__(self, conn=None):
self.conn = conn
@@ -481,13 +484,13 @@ class LDAPHandler(object):
class PythonLDAPHandler(LDAPHandler):
- '''Implementation of the LDAPHandler interface which calls the
- python-ldap API.
+ """LDAPHandler implementation which calls the python-ldap API.
- Note, the python-ldap API requires all string values to be UTF-8
- encoded. The KeystoneLDAPHandler enforces this prior to invoking
- the methods in this class.
- '''
+ Note, the python-ldap API requires all string values to be UTF-8 encoded.
+ The KeystoneLDAPHandler enforces this prior to invoking the methods in this
+ class.
+
+ """
def __init__(self, conn=None):
super(PythonLDAPHandler, self).__init__(conn=conn)
@@ -569,10 +572,7 @@ class PythonLDAPHandler(LDAPHandler):
def _common_ldap_initialization(url, use_tls=False, tls_cacertfile=None,
tls_cacertdir=None, tls_req_cert=None,
debug_level=None):
- '''Method for common ldap initialization between PythonLDAPHandler and
- PooledLDAPHandler.
- '''
-
+ """LDAP initialization for PythonLDAPHandler and PooledLDAPHandler."""
LOG.debug("LDAP init: url=%s", url)
LOG.debug('LDAP init: use_tls=%s tls_cacertfile=%s tls_cacertdir=%s '
'tls_req_cert=%s tls_avail=%s',
@@ -616,7 +616,7 @@ def _common_ldap_initialization(url, use_tls=False, tls_cacertfile=None,
"or is not a directory") %
tls_cacertdir)
ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, tls_cacertdir)
- if tls_req_cert in LDAP_TLS_CERTS.values():
+ if tls_req_cert in list(LDAP_TLS_CERTS.values()):
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, tls_req_cert)
else:
LOG.debug("LDAP TLS: invalid TLS_REQUIRE_CERT Option=%s",
@@ -624,15 +624,16 @@ def _common_ldap_initialization(url, use_tls=False, tls_cacertfile=None,
class MsgId(list):
- '''Wrapper class to hold connection and msgid.'''
+ """Wrapper class to hold connection and msgid."""
pass
def use_conn_pool(func):
- '''Use this only for connection pool specific ldap API.
+ """Use this only for connection pool specific ldap API.
This adds connection object to decorated API as next argument after self.
- '''
+
+ """
def wrapper(self, *args, **kwargs):
# assert isinstance(self, PooledLDAPHandler)
with self._get_pool_connection() as conn:
@@ -642,8 +643,7 @@ def use_conn_pool(func):
class PooledLDAPHandler(LDAPHandler):
- '''Implementation of the LDAPHandler interface which uses pooled
- connection manager.
+ """LDAPHandler implementation which uses pooled connection manager.
Pool specific configuration is defined in [ldap] section.
All other LDAP configuration is still used from [ldap] section
@@ -663,8 +663,8 @@ class PooledLDAPHandler(LDAPHandler):
Note, the python-ldap API requires all string values to be UTF-8
encoded. The KeystoneLDAPHandler enforces this prior to invoking
the methods in this class.
- '''
+ """
# Added here to allow override for testing
Connector = ldappool.StateConnector
auth_pool_prefix = 'auth_pool_'
@@ -737,7 +737,7 @@ class PooledLDAPHandler(LDAPHandler):
# if connection has a lifetime, then it already has options specified
if conn.get_lifetime() > 30:
return
- for option, invalue in six.iteritems(self.conn_options):
+ for option, invalue in self.conn_options.items():
conn.set_option(option, invalue)
def _get_pool_connection(self):
@@ -745,9 +745,8 @@ class PooledLDAPHandler(LDAPHandler):
def simple_bind_s(self, who='', cred='',
serverctrls=None, clientctrls=None):
- '''Not using use_conn_pool decorator here as this API takes cred as
- input.
- '''
+ # Not using use_conn_pool decorator here as this API takes cred as
+ # input.
self.who = who
self.cred = cred
with self._get_pool_connection() as conn:
@@ -773,16 +772,17 @@ class PooledLDAPHandler(LDAPHandler):
filterstr='(objectClass=*)', attrlist=None, attrsonly=0,
serverctrls=None, clientctrls=None,
timeout=-1, sizelimit=0):
- '''This API is asynchoronus API which returns MsgId instance to be used
- in result3 call.
+ """Asynchronous API to return a ``MsgId`` instance.
+
+ The ``MsgId`` instance can be safely used in a call to ``result3()``.
- To work with result3 API in predicatable manner, same LDAP connection
- is needed which provided msgid. So wrapping used connection and msgid
- in MsgId class. The connection associated with search_ext is released
- once last hard reference to MsgId object is freed. This will happen
- when the method is done with returned MsgId usage.
- '''
+ To work with ``result3()`` API in predictable manner, the same LDAP
+ connection is needed which originally provided the ``msgid``. So, this
+ method wraps the existing connection and ``msgid`` in a new ``MsgId``
+ instance. The connection associated with ``search_ext`` is released
+ once last hard reference to the ``MsgId`` instance is freed.
+ """
conn_ctxt = self._get_pool_connection()
conn = conn_ctxt.__enter__()
try:
@@ -800,11 +800,12 @@ class PooledLDAPHandler(LDAPHandler):
def result3(self, msgid, all=1, timeout=None,
resp_ctrl_classes=None):
- '''This method is used to wait for and return the result of an
- operation previously initiated by one of the LDAP asynchronous
- operation routines (eg search_ext()) It returned an invocation
- identifier (a message id) upon successful initiation of their
- operation.
+ """This method is used to wait for and return result.
+
+ This method returns the result of an operation previously initiated by
+ one of the LDAP asynchronous operation routines (eg search_ext()). It
+ returned an invocation identifier (a message id) upon successful
+ initiation of their operation.
Input msgid is expected to be instance of class MsgId which has LDAP
session/connection used to execute search_ext and message idenfier.
@@ -812,7 +813,8 @@ class PooledLDAPHandler(LDAPHandler):
The connection associated with search_ext is released once last hard
reference to MsgId object is freed. This will happen when function
which requested msgId and used it in result3 exits.
- '''
+
+ """
conn, msg_id = msgid
return conn.result3(msg_id, all, timeout)
@@ -831,7 +833,7 @@ class PooledLDAPHandler(LDAPHandler):
class KeystoneLDAPHandler(LDAPHandler):
- '''Convert data types and perform logging.
+ """Convert data types and perform logging.
This LDAP inteface wraps the python-ldap based interfaces. The
python-ldap interfaces require string values encoded in UTF-8. The
@@ -854,7 +856,8 @@ class KeystoneLDAPHandler(LDAPHandler):
Data returned from the LDAP call is converted back from UTF-8
encoded strings into the Python data type used internally in
OpenStack.
- '''
+
+ """
def __init__(self, conn=None):
super(KeystoneLDAPHandler, self).__init__(conn=conn)
@@ -938,7 +941,7 @@ class KeystoneLDAPHandler(LDAPHandler):
if attrlist is None:
attrlist_utf8 = None
else:
- attrlist_utf8 = map(utf8_encode, attrlist)
+ attrlist_utf8 = list(map(utf8_encode, attrlist))
ldap_result = self.conn.search_s(base_utf8, scope,
filterstr_utf8,
attrlist_utf8, attrsonly)
@@ -989,7 +992,7 @@ class KeystoneLDAPHandler(LDAPHandler):
attrlist_utf8 = None
else:
attrlist = [attr for attr in attrlist if attr is not None]
- attrlist_utf8 = map(utf8_encode, attrlist)
+ attrlist_utf8 = list(map(utf8_encode, attrlist))
msgid = self.conn.search_ext(base_utf8,
scope,
filterstr_utf8,
@@ -1083,7 +1086,7 @@ def register_handler(prefix, handler):
def _get_connection(conn_url, use_pool=False, use_auth_pool=False):
- for prefix, handler in six.iteritems(_HANDLERS):
+ for prefix, handler in _HANDLERS.items():
if conn_url.startswith(prefix):
return handler()
@@ -1109,7 +1112,6 @@ def filter_entity(entity_ref):
class BaseLdap(object):
- DEFAULT_SUFFIX = "dc=example,dc=com"
DEFAULT_OU = None
DEFAULT_STRUCTURAL_CLASSES = None
DEFAULT_ID_ATTR = 'cn'
@@ -1156,8 +1158,6 @@ class BaseLdap(object):
if self.options_name is not None:
self.suffix = conf.ldap.suffix
- if self.suffix is None:
- self.suffix = self.DEFAULT_SUFFIX
dn = '%s_tree_dn' % self.options_name
self.tree_dn = (getattr(conf.ldap, dn)
or '%s,%s' % (self.DEFAULT_OU, self.suffix))
@@ -1169,7 +1169,7 @@ class BaseLdap(object):
self.object_class = (getattr(conf.ldap, objclass)
or self.DEFAULT_OBJECTCLASS)
- for k, v in six.iteritems(self.attribute_options_names):
+ for k, v in self.attribute_options_names.items():
v = '%s_%s_attribute' % (self.options_name, v)
self.attribute_mapping[k] = getattr(conf.ldap, v)
@@ -1318,7 +1318,7 @@ class BaseLdap(object):
# in a case-insensitive way. We use the case specified in the
# mapping for the model to ensure we have a predictable way of
# retrieving values later.
- lower_res = {k.lower(): v for k, v in six.iteritems(res[1])}
+ lower_res = {k.lower(): v for k, v in res[1].items()}
id_attrs = lower_res.get(self.id_attr.lower())
if not id_attrs:
@@ -1404,7 +1404,7 @@ class BaseLdap(object):
self.affirm_unique(values)
object_classes = self.structural_classes + [self.object_class]
attrs = [('objectClass', object_classes)]
- for k, v in six.iteritems(values):
+ for k, v in values.items():
if k in self.attribute_ignore:
continue
if k == 'id':
@@ -1416,7 +1416,7 @@ class BaseLdap(object):
if attr_type is not None:
attrs.append((attr_type, [v]))
extra_attrs = [attr for attr, name
- in six.iteritems(self.extra_attr_mapping)
+ in self.extra_attr_mapping.items()
if name == k]
for attr in extra_attrs:
attrs.append((attr, [v]))
@@ -1439,8 +1439,8 @@ class BaseLdap(object):
with self.get_connection() as conn:
try:
attrs = list(set(([self.id_attr] +
- self.attribute_mapping.values() +
- self.extra_attr_mapping.keys())))
+ list(self.attribute_mapping.values()) +
+ list(self.extra_attr_mapping.keys()))))
res = conn.search_s(self.tree_dn,
self.LDAP_SCOPE,
query,
@@ -1453,14 +1453,15 @@ class BaseLdap(object):
return None
def _ldap_get_all(self, ldap_filter=None):
- query = u'(&%s(objectClass=%s))' % (ldap_filter or
- self.ldap_filter or
- '', self.object_class)
+ query = u'(&%s(objectClass=%s)(%s=*))' % (
+ ldap_filter or self.ldap_filter or '',
+ self.object_class,
+ self.id_attr)
with self.get_connection() as conn:
try:
attrs = list(set(([self.id_attr] +
- self.attribute_mapping.values() +
- self.extra_attr_mapping.keys())))
+ list(self.attribute_mapping.values()) +
+ list(self.extra_attr_mapping.keys()))))
return conn.search_s(self.tree_dn,
self.LDAP_SCOPE,
query,
@@ -1479,7 +1480,7 @@ class BaseLdap(object):
query = (u'(&%s%s)' %
(query, ''.join([calc_filter(k, v) for k, v in
- six.iteritems(query_params)])))
+ query_params.items()])))
with self.get_connection() as conn:
return conn.search_s(search_base, scope, query, attrlist)
@@ -1509,7 +1510,7 @@ class BaseLdap(object):
old_obj = self.get(object_id)
modlist = []
- for k, v in six.iteritems(values):
+ for k, v in values.items():
if k == 'id':
# id can't be modified.
continue
@@ -1648,7 +1649,7 @@ class BaseLdap(object):
(query, ''.join(['(%s=%s)'
% (k, ldap.filter.escape_filter_chars(v))
for k, v in
- six.iteritems(query_params)])))
+ query_params.items()])))
not_deleted_nodes = []
with self.get_connection() as conn:
try:
@@ -1738,6 +1739,11 @@ class BaseLdap(object):
return query_term
+ if query is None:
+ # make sure query is a string so the ldap filter is properly
+ # constructed from filter_list later
+ query = ''
+
if hints is None:
return query
@@ -1799,25 +1805,24 @@ class EnabledEmuMixIn(BaseLdap):
utf8_decode(naming_rdn[1]))
self.enabled_emulation_naming_attr = naming_attr
- def _get_enabled(self, object_id):
+ def _get_enabled(self, object_id, conn):
dn = self._id_to_dn(object_id)
query = '(member=%s)' % dn
- with self.get_connection() as conn:
- try:
- enabled_value = conn.search_s(self.enabled_emulation_dn,
- ldap.SCOPE_BASE,
- query, ['cn'])
- except ldap.NO_SUCH_OBJECT:
- return False
- else:
- return bool(enabled_value)
+ try:
+ enabled_value = conn.search_s(self.enabled_emulation_dn,
+ ldap.SCOPE_BASE,
+ query, attrlist=DN_ONLY)
+ except ldap.NO_SUCH_OBJECT:
+ return False
+ else:
+ return bool(enabled_value)
def _add_enabled(self, object_id):
- if not self._get_enabled(object_id):
- modlist = [(ldap.MOD_ADD,
- 'member',
- [self._id_to_dn(object_id)])]
- with self.get_connection() as conn:
+ with self.get_connection() as conn:
+ if not self._get_enabled(object_id, conn):
+ modlist = [(ldap.MOD_ADD,
+ 'member',
+ [self._id_to_dn(object_id)])]
try:
conn.modify_s(self.enabled_emulation_dn, modlist)
except ldap.NO_SUCH_OBJECT:
@@ -1851,10 +1856,12 @@ class EnabledEmuMixIn(BaseLdap):
return super(EnabledEmuMixIn, self).create(values)
def get(self, object_id, ldap_filter=None):
- ref = super(EnabledEmuMixIn, self).get(object_id, ldap_filter)
- if 'enabled' not in self.attribute_ignore and self.enabled_emulation:
- ref['enabled'] = self._get_enabled(object_id)
- return ref
+ with self.get_connection() as conn:
+ ref = super(EnabledEmuMixIn, self).get(object_id, ldap_filter)
+ if ('enabled' not in self.attribute_ignore and
+ self.enabled_emulation):
+ ref['enabled'] = self._get_enabled(object_id, conn)
+ return ref
def get_all(self, ldap_filter=None):
if 'enabled' not in self.attribute_ignore and self.enabled_emulation:
@@ -1862,8 +1869,10 @@ class EnabledEmuMixIn(BaseLdap):
tenant_list = [self._ldap_res_to_model(x)
for x in self._ldap_get_all(ldap_filter)
if x[0] != self.enabled_emulation_dn]
- for tenant_ref in tenant_list:
- tenant_ref['enabled'] = self._get_enabled(tenant_ref['id'])
+ with self.get_connection() as conn:
+ for tenant_ref in tenant_list:
+ tenant_ref['enabled'] = self._get_enabled(
+ tenant_ref['id'], conn)
return tenant_list
else:
return super(EnabledEmuMixIn, self).get_all(ldap_filter)