diff options
author | Petr Viktorin <pviktori@redhat.com> | 2013-01-17 08:07:06 -0500 |
---|---|---|
committer | Martin Kosek <mkosek@redhat.com> | 2013-03-01 16:59:42 +0100 |
commit | 49a0e3ac01e3abef2be324bc3689d1296762265a (patch) | |
tree | 5cc741903a7de52dc6cae641e6d7ebcc183536a0 | |
parent | a8c0bf0c85e5cc73a4d7035fdcf1386600911a9a (diff) | |
download | freeipa.git-49a0e3ac01e3abef2be324bc3689d1296762265a.tar.gz freeipa.git-49a0e3ac01e3abef2be324bc3689d1296762265a.tar.xz freeipa.git-49a0e3ac01e3abef2be324bc3689d1296762265a.zip |
Move LDAPEntry to ipaserver.ipaldap and derive Entry from it
Legacy Entry methods such as setValue are added to LDAPEntry directly,
so that we can use connection classes that return LDAPEntry with
code that expects Entries.
The Entry and its unique __init__ are still kept for compatibility.
Part of the work for: https://fedorahosted.org/freeipa/ticket/2660
-rw-r--r-- | ipaserver/ipaldap.py | 210 | ||||
-rw-r--r-- | ipaserver/plugins/ldap2.py | 102 | ||||
-rw-r--r-- | tests/test_ipaserver/test_ldap.py | 3 |
3 files changed, 147 insertions, 168 deletions
diff --git a/ipaserver/ipaldap.py b/ipaserver/ipaldap.py index c944f037..ddf34cd3 100644 --- a/ipaserver/ipaldap.py +++ b/ipaserver/ipaldap.py @@ -23,11 +23,8 @@ import sys import os import os.path import socket -import ldif -import re import string import ldap -import cStringIO import time import struct import ldap.sasl @@ -38,10 +35,10 @@ from ipapython import ipautil from ipalib import errors from ipapython.ipautil import format_netloc, wait_for_open_socket, wait_for_open_ports from ipapython.dn import DN -from ipaserver.plugins.ldap2 import IPASimpleLDAPObject, LDAPEntry +from ipaserver.plugins.ldap2 import IPASimpleLDAPObject # Global variable to define SASL auth -SASL_AUTH = ldap.sasl.sasl({},'GSSAPI') +SASL_AUTH = ldap.sasl.sasl({}, 'GSSAPI') DEFAULT_TIMEOUT = 10 class IPAEntryLDAPObject(IPASimpleLDAPObject): @@ -85,59 +82,111 @@ class IPAEntryLDAPObject(IPASimpleLDAPObject): else: return IPASimpleLDAPObject.add_ext_s(self, dn, modlist, serverctrls, clientctrls) -class Entry: - """ - This class represents an LDAP Entry object. An LDAP entry consists of - a DN and a list of attributes. Each attribute consists of a name and - a list of values. In python-ldap, entries are returned as a list of - 2-tuples. Instance variables: - * dn - DN object - the DN of the entry - * data - CIDict - case insensitive dict of the attributes and values - """ - def __init__(self,entrydata): - """data is the raw data returned from the python-ldap result method, which is - a search result entry or a reference or None. - If creating a new empty entry, data is the string DN.""" - if entrydata: - if isinstance(entrydata,LDAPEntry): - self.dn = entrydata.dn - self.data = entrydata - elif isinstance(entrydata,tuple): - self.dn = entrydata[0] - self.data = ipautil.CIDict(entrydata[1]) - elif isinstance(entrydata,DN): - self.dn = entrydata - self.data = ipautil.CIDict() - elif isinstance(entrydata, basestring): - self.dn = DN(entrydata) - self.data = ipautil.CIDict() - elif isinstance(entrydata, dict): - if hasattr(entrydata, 'dn'): - entrydata['dn'] = entrydata.dn - self.dn = entrydata['dn'] - del entrydata['dn'] - self.data = ipautil.CIDict(entrydata) - else: - raise TypeError("entrydata must be 2-tuple, DN, or basestring, got %s" % type(entrydata)) +# Make python-ldap tuple style result compatible with Entry and Entity +# objects by allowing access to the dn (tuple index 0) via the 'dn' +# attribute name and the attr dict (tuple index 1) via the 'data' +# attribute name. Thus: +# r = result[0] +# r[0] == r.dn +# r[1] == r.data +class LDAPEntry(dict): + __slots__ = ('_dn',) + + def __init__(self, _dn=None, _obj=None, **kwargs): + if isinstance(_dn, LDAPEntry): + assert _obj is None + _obj = _dn + self._dn = DN(_obj._dn) #pylint: disable=E1103 else: - self.dn = DN() - self.data = ipautil.CIDict() + assert isinstance(_dn, DN) + if _obj is None: + _obj = {} + self._dn = _dn + + super(LDAPEntry, self).__init__(self._init_iter(_obj, **kwargs)) + + # properties for Entry and Entity compatibility + @property + def dn(self): + return self._dn + + @dn.setter + def dn(self, value): + assert isinstance(value, DN) + self._dn = value + + @property + def data(self): + # FIXME: for backwards compatibility only + return self + + def _attr_name(self, name): + if not isinstance(name, basestring): + raise TypeError( + "attribute name must be unicode or str, got %s object %r" % ( + name.__class__.__name__, name)) + if isinstance(name, str): + name = name.decode('ascii') + return name.lower() + + def _init_iter(self, _obj, **kwargs): + _obj = dict(_obj, **kwargs) + for (k, v) in _obj.iteritems(): + yield (self._attr_name(k), v) - assert isinstance(self.dn, DN) + def __repr__(self): + dict_repr = super(LDAPEntry, self).__repr__() + return '%s(%s, %s)' % (type(self).__name__, repr(self._dn), dict_repr) + + def copy(self): + return LDAPEntry(self) + + def __setitem__(self, name, value): + super(LDAPEntry, self).__setitem__(self._attr_name(name), value) + + def setdefault(self, name, default): + return super(LDAPEntry, self).setdefault(self._attr_name(name), default) + + def update(self, _obj={}, **kwargs): + super(LDAPEntry, self).update(self._init_iter(_obj, **kwargs)) + + def __getitem__(self, name): + # for python-ldap tuple compatibility + if name == 0: + return self._dn + elif name == 1: + return self + + return super(LDAPEntry, self).__getitem__(self._attr_name(name)) - dn = ipautil.dn_attribute_property('_dn') + def get(self, name, default=None): + return super(LDAPEntry, self).get(self._attr_name(name), default) - def __nonzero__(self): - """This allows us to do tests like if entry: returns false if there is no data, - true otherwise""" - return self.data != None and len(self.data) > 0 + def __delitem__(self, name): + super(LDAPEntry, self).__delitem__(self._attr_name(name)) - def getValues(self,name): + def pop(self, name, *default): + return super(LDAPEntry, self).pop(self._attr_name(name), *default) + + def __contains__(self, name): + return super(LDAPEntry, self).__contains__(self._attr_name(name)) + + def has_key(self, name): + return super(LDAPEntry, self).has_key(self._attr_name(name)) + + # for python-ldap tuple compatibility + def __iter__(self): + yield self._dn + yield self + + def getValues(self, name): + # FIXME: for backwards compatibility only """Get the list (array) of values for the attribute named name""" return self.data.get(name) - def getValue(self,name, default=None): + def getValue(self, name, default=None): + # FIXME: for backwards compatibility only """Get the first value for the attribute named name""" value = self.data.get(name, default) if isinstance(value, (list, tuple)): @@ -145,6 +194,7 @@ class Entry: return value def setValue(self, name, *value): + # FIXME: for backwards compatibility only """ Set a value on this entry. @@ -167,6 +217,7 @@ class Entry: setValues = setValue def toTupleList(self): + # FIXME: for backwards compatibility only """Convert the attrs and values to a list of 2-tuples. The first element of the tuple is the attribute name. The second element is either a single value or a list of values.""" @@ -177,6 +228,7 @@ class Entry: return r def toDict(self): + # FIXME: for backwards compatibility only """Convert the attrs and values to a dict. The dict is keyed on the attribute name. The value is either single value or a list of values.""" assert isinstance(self.dn, DN) @@ -186,29 +238,47 @@ class Entry: result['dn'] = self.dn return result - def __str__(self): - """Convert the Entry to its LDIF representation""" - return self.__repr__() - # the ldif class base64 encodes some attrs which I would rather see in - # raw form - to encode specific attrs as base64, add them to the list below - ldif.safe_string_re = re.compile('^$') - base64_attrs = ['nsstate', 'krbprincipalkey', 'krbExtraData'] +class Entry(LDAPEntry): + """For compatibility with old code only - def __repr__(self): - """Convert the Entry to its LDIF representation""" - sio = cStringIO.StringIO() - # what's all this then? the unparse method will currently only accept - # a list or a dict, not a class derived from them. self.data is a - # cidict, so unparse barfs on it. I've filed a bug against python-ldap, - # but in the meantime, we have to convert to a plain old dict for - # printing - # I also don't want to see wrapping, so set the line width really high - # (1000) - newdata = {} - newdata.update(self.data) - ldif.LDIFWriter(sio,Entry.base64_attrs,1000).unparse(str(self.dn),newdata) - return sio.getvalue() + This class represents an LDAP Entry object. An LDAP entry consists of + a DN and a list of attributes. Each attribute consists of a name and + a list of values. In python-ldap, entries are returned as a list of + 2-tuples. Instance variables: + + * dn - DN object - the DN of the entry + * data - CIDict - case insensitive dict of the attributes and values + """ + def __init__(self, entrydata): + """data is the raw data returned from the python-ldap result method, which is + a search result entry or a reference or None. + If creating a new empty entry, data is the string DN.""" + if entrydata: + if isinstance(entrydata, (tuple, LDAPEntry)): + dn = entrydata[0] + data = ipautil.CIDict(entrydata[1]) + elif isinstance(entrydata, DN): + dn = entrydata + data = ipautil.CIDict() + elif isinstance(entrydata, basestring): + dn = DN(entrydata) + data = ipautil.CIDict() + elif isinstance(entrydata, dict): + if hasattr(entrydata, 'dn'): + entrydata['dn'] = entrydata.dn + dn = entrydata['dn'] + del entrydata['dn'] + data = ipautil.CIDict(entrydata) + else: + raise TypeError( + "entrydata must be 2-tuple, DN, or basestring, got %s" % + type(entrydata)) + else: + dn = DN() + data = ipautil.CIDict() + + super(Entry, self).__init__(dn, data) class IPAdmin(IPAEntryLDAPObject): diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py index be6eb4ba..16e7e55e 100644 --- a/ipaserver/plugins/ldap2.py +++ b/ipaserver/plugins/ldap2.py @@ -69,103 +69,6 @@ from ipalib.request import context _debug_log_ldap = False -# Make python-ldap tuple style result compatible with Entry and Entity -# objects by allowing access to the dn (tuple index 0) via the 'dn' -# attribute name and the attr dict (tuple index 1) via the 'data' -# attribute name. Thus: -# r = result[0] -# r[0] == r.dn -# r[1] == r.data -class LDAPEntry(dict): - __slots__ = ('_dn',) - - def __init__(self, _dn=None, _obj=None, **kwargs): - if isinstance(_dn, LDAPEntry): - assert _obj is None - _obj = _dn - self._dn = DN(_obj._dn) #pylint: disable=E1103 - else: - assert isinstance(_dn, DN) - if _obj is None: - _obj = {} - self._dn = _dn - - super(LDAPEntry, self).__init__(self._init_iter(_obj, **kwargs)) - - # properties for Entry and Entity compatibility - @property - def dn(self): - return self._dn - - @dn.setter - def dn(self, value): - assert isinstance(value, DN) - self._dn = value - - @property - def data(self): - return self - - def _attr_name(self, name): - if not isinstance(name, basestring): - raise TypeError( - "attribute name must be unicode or str, got %s object %r" % ( - name.__class__.__name__, name)) - if isinstance(name, str): - name = name.decode('ascii') - return name.lower() - - def _init_iter(self, _obj, **kwargs): - _obj = dict(_obj, **kwargs) - for (k, v) in _obj.iteritems(): - yield (self._attr_name(k), v) - - def __repr__(self): - dict_repr = super(LDAPEntry, self).__repr__() - return '%s(%s, %s)' % (type(self).__name__, repr(self._dn), dict_repr) - - def copy(self): - return LDAPEntry(self) - - def __setitem__(self, name, value): - super(LDAPEntry, self).__setitem__(self._attr_name(name), value) - - def setdefault(self, name, default): - return super(LDAPEntry, self).setdefault(self._attr_name(name), default) - - def update(self, _obj={}, **kwargs): - super(LDAPEntry, self).update(self._init_iter(_obj, **kwargs)) - - def __getitem__(self, name): - # for python-ldap tuple compatibility - if name == 0: - return self._dn - elif name == 1: - return self - - return super(LDAPEntry, self).__getitem__(self._attr_name(name)) - - def get(self, name, default=None): - return super(LDAPEntry, self).get(self._attr_name(name), default) - - def __delitem__(self, name): - super(LDAPEntry, self).__delitem__(self._attr_name(name)) - - def pop(self, name, *default): - return super(LDAPEntry, self).pop(self._attr_name(name), *default) - - def __contains__(self, name): - return super(LDAPEntry, self).__contains__(self._attr_name(name)) - - def has_key(self, name): - return super(LDAPEntry, self).has_key(self._attr_name(name)) - - # for python-ldap tuple compatibility - def __iter__(self): - yield self._dn - yield self - - # Group Member types MEMBERS_ALL = 0 MEMBERS_DIRECT = 1 @@ -332,6 +235,7 @@ class SchemaCache(object): schema_cache = SchemaCache() + class IPASimpleLDAPObject(object): ''' The purpose of this class is to provide a boundary between IPA and @@ -538,6 +442,10 @@ class IPASimpleLDAPObject(object): been converted to it's preferred IPA python type. ''' + # FIXME: Temporarily import here to prevent import loops + # Once ipaldap does not depend on ldap2, move the import to the top + from ipaserver.ipaldap import LDAPEntry + ipa_result = [] for dn_tuple in result: original_dn = dn_tuple[0] diff --git a/tests/test_ipaserver/test_ldap.py b/tests/test_ipaserver/test_ldap.py index 872d69f0..06d5d4ec 100644 --- a/tests/test_ipaserver/test_ldap.py +++ b/tests/test_ipaserver/test_ldap.py @@ -27,7 +27,8 @@ import nose import os -from ipaserver.plugins.ldap2 import ldap2, LDAPEntry +from ipaserver.plugins.ldap2 import ldap2 +from ipaserver.ipaldap import LDAPEntry from ipalib.plugins.service import service, service_show from ipalib.plugins.host import host import nss.nss as nss |