From 8f5dacadddb345932df7b3f3c0a7b2511ae71268 Mon Sep 17 00:00:00 2001 From: "Jeroen van Meeuwen (Kolab Systems)" Date: Wed, 25 Jul 2012 09:47:12 +0200 Subject: Add syncrepl compatibility for OpenLDAP --- pykolab/auth/ldap/__init__.py | 75 +++++++++++++++++++++++----------- pykolab/auth/ldap/syncrepl.py | 94 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 24 deletions(-) create mode 100644 pykolab/auth/ldap/syncrepl.py diff --git a/pykolab/auth/ldap/__init__.py b/pykolab/auth/ldap/__init__.py index 71f16ce..8d82d6a 100644 --- a/pykolab/auth/ldap/__init__.py +++ b/pykolab/auth/ldap/__init__.py @@ -782,8 +782,12 @@ class LDAP(pykolab.base.Base): modlist.append((ldap.MOD_REPLACE, attribute, attrs[attribute])) dn = entry_dn + if len(modlist) > 0: - self.ldap.modify_s(dn, modlist) + try: + self.ldap.modify_s(dn, modlist) + except: + log.error(_("Could not update dn %r") % (dn)) def synchronize(self): """ @@ -911,6 +915,9 @@ class LDAP(pykolab.base.Base): """ pass + def _change_add_None(self, *args, **kw): + pass + def _change_add_resource(self, entry, change): """ An entry of type resource was added. @@ -1657,8 +1664,12 @@ class LDAP(pykolab.base.Base): # This entry was in the start result set eval("self._change_none_%s(entry, change_dict)" % (entry['type'])) else: - change = psearch.CHANGE_TYPES_STR[change_dict['change_type']] - change = change.lower() + if isinstance(change_dict['change_type'], int): + change = psearch.CHANGE_TYPES_STR[change_dict['change_type']] + change = change.lower() + else: + change = change_dict['change_type'] + eval( "self._change_%s_%s(entry, change_dict)" % ( change, @@ -1667,32 +1678,44 @@ class LDAP(pykolab.base.Base): ) # Typical for Paged Results Control - elif kw.has_key('user') and isinstance(kw['user'], list): - for entry_dn,entry_attrs in kw['user']: + elif kw.has_key('entry') and isinstance(kw['entry'], list): + for entry_dn,entry_attrs in kw['entry']: entry = { 'dn': entry_dn } + entry_attrs = utils.normalize(entry_attrs) for attr in entry_attrs.keys(): entry[attr.lower()] = entry_attrs[attr] unique_attr = self.config_get('unique_attribute') entry['id'] = entry[unique_attr] - result_attribute = conf.get('cyrus-sasl', 'result_attribute') - - rcpt_addrs = self.recipient_policy(entry) - - for key in rcpt_addrs.keys(): - entry[key] = rcpt_addrs[key] + try: + entry['type'] = self._entry_type(entry) + except: + entry['type'] = "unknown" - cache.get_entry(self.domain, entry) + log.debug(_("Entry type: %s") % (entry['type']), level=8) - self.imap.connect(domain=self.domain) + eval("self._change_none_%s(entry, None)" % (entry['type'])) - if not self.imap.user_mailbox_exists(entry[result_attribute]): - folder = self.imap.user_mailbox_create( - entry[result_attribute] - ) - - server = self.imap.user_mailbox_server(folder) +# result_attribute = conf.get('cyrus-sasl', 'result_attribute') +# +# rcpt_addrs = self.recipient_policy(entry) +# +# log.debug(_("Recipient Addresses: %r") % (rcpt_addrs), level=9) +# +# for key in rcpt_addrs.keys(): +# entry[key] = rcpt_addrs[key] +# +# cache.get_entry(self.domain, entry) +# +# self.imap.connect(domain=self.domain) +# +# if not self.imap.user_mailbox_exists(entry[result_attribute]): +# folder = self.imap.user_mailbox_create( +# entry[result_attribute] +# ) +# +# server = self.imap.user_mailbox_server(folder) def _unbind(self): """ @@ -1848,7 +1871,7 @@ class LDAP(pykolab.base.Base): break if callback: - callback(user=_result_data) + callback(entry=_result_data) _results.extend(_result_data) if (pages % 2) == 0: @@ -1911,20 +1934,24 @@ class LDAP(pykolab.base.Base): secondary_domains=[] ): + import ldapurl import syncrepl + ldap_url = ldapurl.LDAPUrl(self.config_get('ldap_uri')) + ldap_sync_conn = syncrepl.DNSync( - '/var/lib/pykolab/syncrepl.db', + '/var/lib/kolab/syncrepl_%s.db' % (self.domain), ldap_url.initializeUrl(), - trace_level=ldapmodule_trace_level, - trace_file=ldapmodule_trace_file + trace_level=2, + callback=self._synchronize_callback ) msgid = ldap_sync_conn.syncrepl_search( base_dn, scope, mode='refreshAndPersist', - filterstr=filterstr + filterstr=filterstr, + attrlist=attrlist, ) try: diff --git a/pykolab/auth/ldap/syncrepl.py b/pykolab/auth/ldap/syncrepl.py new file mode 100644 index 0000000..e02e086 --- /dev/null +++ b/pykolab/auth/ldap/syncrepl.py @@ -0,0 +1,94 @@ +#!/usr/bin/python + +import anydbm +import ldap +import ldap.syncrepl +import ldapurl + +from pykolab import utils + +class DNSync(ldap.ldapobject.LDAPObject,ldap.syncrepl.SyncreplConsumer): + + callback = None + + def __init__(self, filename, *args, **kwargs): + if kwargs.has_key('callback'): + self.callback = kwargs['callback'] + del kwargs['callback'] + + ldap.ldapobject.LDAPObject.__init__(self, *args, **kwargs) + self.__db = anydbm.open(filename, 'c', 0640) + self.__presentUUIDs = {} + + def syncrepl_set_cookie(self,cookie): + self.__db['cookie'] = cookie + + def syncrepl_get_cookie(self): + if 'cookie' in self.__db: + return self.__db['cookie'] + + def syncrepl_delete(self, uuids): + for uuid in uuids: + dn = self.__db[uuid] + + if not self.callback == None: + self.callback( + change_type='delete', + previous_dn=None, + change_number=None, + dn=dn, + entry={} + ) + + del self.__db[uuid] + + def syncrepl_present(self, uuids, refreshDeletes=False): + if uuids is None: + if refreshDeletes is False: + nonpresent = [] + for uuid in self.__db.keys(): + if uuid == 'cookie': continue + if uuid in self.__presentUUIDs: continue + nonpresent.append(uuid) + self.syncrepl_delete(nonpresent) + self.__presentUUIDs = {} + else: + for uuid in uuids: + self.__presentUUIDs[uuid] = True + + def syncrepl_entry(self, dn, attrs, uuid): + attrs = utils.normalize(attrs) + + if uuid in self.__db: + odn = self.__db[uuid] + if odn != dn: + if not self.callback == None: + self.callback( + change_type='moddn', + previous_dn=odn, + change_number=None, + dn=dn, + entry=attrs + ) + + else: + if not self.callback == None: + self.callback( + change_type='modify', + previous_dn=None, + change_number=None, + dn=self.__db[uuid], + entry=attrs + ) + + else: + if not self.callback == None: + self.callback( + change_type='add', + previous_dn=None, + change_number=None, + dn=dn, + entry=attrs + ) + + self.__db[uuid] = dn -- cgit v1.1