# core of LDAP Filter Methods. from Globals import HTMLFile, HTML __version__ = "$Revision: 1.10 $"[11:-2] try: import ldap from ldap import modlist # see if it's on a regular path except ImportError: from Products.ZLDAPConnection import ldap from Products.ZLDAPConnection.ldap import modlist import string from Shared.DC.ZRDB import Aqueduct from Shared.DC.ZRDB.Aqueduct import parse, decodestring, default_input_form from Shared.DC.ZRDB.Results import Results import Acquisition, Globals, AccessControl.Role, OFS.SimpleItem from Globals import HTMLFile, MessageDialog, Persistent import DocumentTemplate import ExtensionClass import sys from zLOG import LOG from ldif import LDIFRecordList, is_dn, valid_changetype_dict, CHANGE_TYPES import ldifvar from AccessControl.DTML import RestrictedDTML try: from AccessControl import getSecurityManager except ImportError: getSecurityManager = None MODIFY_MAPPING_DICT = {'add' : ldap.MOD_ADD, #'replace' : ldap.MOD_REPLACE, 'delete' : ldap.MOD_DELETE} class ERP5LDIFRecordList(LDIFRecordList): def parse(self): """ Continously read and parse LDIF records """ self._line = self._input_file.readline() while self._line and \ (not self._max_entries or self.records_read<self._max_entries): # Reset record version = None; dn = None; changetype = None; modop = None; entry = {}; attr_type,attr_value = self._parseAttrTypeandValue() while attr_type!=None and attr_value!=None: if attr_type=='dn': # attr type and value pair was DN of LDIF record if dn!=None: raise ValueError, 'Two lines starting with dn: in one record.' if not is_dn(attr_value): raise ValueError, 'No valid string-representation of distinguished name %s.' % (repr(attr_value)) dn = attr_value elif attr_type=='version' and dn is None: version = 1 elif attr_type=='changetype': # attr type and value pair was DN of LDIF record if dn is None: raise ValueError, 'Read changetype: before getting valid dn: line.' if changetype!=None: raise ValueError, 'Two lines starting with changetype: in one record.' if not valid_changetype_dict.has_key(attr_value): raise ValueError, 'changetype value %s is invalid.' % (repr(attr_value)) changetype = attr_value attr_type, attr_value = self._parseAttrTypeandValue() modify_list = [] entry[changetype] = [] while (attr_type and attr_value) is not None: mod_op = MODIFY_MAPPING_DICT[attr_type] mod_type = attr_value multivalued_list = [] while attr_value is not None: attr_type, attr_value = self._parseAttrTypeandValue() if attr_value is not None: multivalued_list.append(attr_value) modify_list.append((mod_op, mod_type, multivalued_list)) entry[changetype] = [modify_list] attr_type, attr_value = self._parseAttrTypeandValue() #don't add new entry for the same dn break elif attr_value not in (None, '') and \ not self._ignored_attr_types.has_key(string.lower(attr_type)): # Add the attribute to the entry if not ignored attribute if entry.has_key(attr_type): entry[attr_type].append(attr_value) else: entry[attr_type]=[attr_value] # Read the next line within an entry attr_type, attr_value = self._parseAttrTypeandValue() if entry: # append entry to result list self.handle(dn, entry) self.records_read = self.records_read+1 return # parse() class Filter(DocumentTemplate.HTML): """ Subclass of DocumentTemplate.HTML for Variable Interpolation. Special LDAP Specific tags would go here. Since there aren't any (like ldapvar or ldaptest or whatever), we don't have to worry. It's just nice to have a nice name that reflects what this is. :) """ pass class nvLDIF(DocumentTemplate.HTML): # Non-validating Ldif Template for use by LDIFFiles. commands={} for k, v in DocumentTemplate.HTML.commands.items(): commands[k] = v commands['ldifvar' ] = ldifvar.LDIFVar commands['ldifline' ] = ldifvar.LDIFLine _proxy_roles=() class Ldif(RestrictedDTML, ExtensionClass.Base, nvLDIF): pass def LDAPConnectionIDs(self): """find LDAP connections in the current folder and parents Returns list of ids. """ ids={} StringType = type('') have_key = ids.has_key while self is not None: if hasattr(self, 'objectValues'): for o in self.objectValues(): if (hasattr(o,'_isAnLDAPConnection') and o._isAnLDAPConnection() and hasattr(o,'id')): id=o.id if type(id) is not StringType: id=id() if not ids.has_key(id): if hasattr(o,'title_and_id'): o=o.title_and_id() else: o=id ids[id]=id if hasattr(self, 'aq_parent'): self=self.aq_parent else: self=None ids=map(lambda item: (item[1], item[0]), ids.items()) ids.sort() return ids manage_addZLDAPMethodForm = HTMLFile('add', globals()) def manage_addZLDAPMethod(self, id, title, connection_id, scope, basedn, filters, arguments, getfromconnection=0, REQUEST=None, submit=None): """Add an LDAP Method """ l=LDAPMethod(id, title, connection_id, scope, basedn, arguments, filters) self._setObject(id, l) if getfromconnection: getattr(self,id).recomputeBaseDN() if REQUEST is not None: u=REQUEST['URL1'] if submit==" Add and Edit ": u="%s/%s/manage_main" % (u,id) elif submit==" Add and Test ": u="%s/%s/manage_testForm" % (u,id) else: u=u+'/manage_main' REQUEST.RESPONSE.redirect(u) return '' _ldapScopes = { "ONELEVEL": ldap.SCOPE_ONELEVEL, "SUBTREE": ldap.SCOPE_SUBTREE, "BASE": ldap.SCOPE_BASE } class LDAPMethod(Aqueduct.BaseQuery, Acquisition.Implicit, Globals.Persistent, AccessControl.Role.RoleManager, OFS.SimpleItem.Item, ): 'LDAP Method' meta_type = 'LDAP Method' manage_main = HTMLFile('edit', globals()) manage_options = ( {'label':'Edit', 'action':'manage_main'}, {'label':'Test', 'action':'manage_testForm'}, {'label':'Security', 'action':'manage_access'}, ) __ac_permissions__=( ('View management screens', ('manage_tabs','manage_main',),), ('Change LDAP Methods', ('manage_edit', 'manage_testForm','manage_test')), ('Use LDAP Methods', ('__call__',''), ('Anonymous','Manager')), ) #manage_testForm = HTMLFile("testForm", globals()) def manage_testForm(self, REQUEST): " " input_src=default_input_form(self.title_or_id(), self._arg, 'manage_test', '<!--#var manage_tabs-->') return DocumentTemplate.HTML(input_src)(self,REQUEST,HTTP_REFERER='') def __init__(self, id, title, connection_id, scope, basedn, arguments, filters): """ init method """ self.id = id self.title = title self.connection_id = connection_id self._scope = _ldapScopes[scope] self.scope = scope self.basedn = basedn self.arguments_src=self.arguments=arguments self._arg=parse(arguments) self.filters = filters def recomputeBaseDN(self): ' recompute base DN based on connection ' cdn=self._connection().dn if self.basedn: self.basedn='%s, %s' % (self.basedn, cdn) else: self.basedn=cdn return self.basedn def manage_edit(self, title, connection_id, scope, basedn, arguments, filters, REQUEST=None): """ commit changes """ self.title = title self.connection_id = connection_id self._scope = _ldapScopes[scope] self.scope = scope self.basedn = basedn self.arguments_src=self.arguments=arguments self._arg=parse(arguments) self.filters = filters if REQUEST is not None: return MessageDialog( title='Edited', message='<strong>%s</strong> has been changed.' % self.id, action ='./manage_main', ) def cleanse(self,s): import string # kill line breaks &c. s = string.join(string.split(s)) return s def _connection(self): ' return actual ZLDAP Connection Object ' if hasattr(self,'connection_id') and hasattr(self,self.connection_id): return getattr(self, self.connection_id) def _getConn(self): return self._connection().GetConnection() # Hacky, Hacky GetConnection=_getConn def manage_test(self, REQUEST): """ do the test query """ src="Could not render the filter template!" res=() t=v=tb=None try: try: src=self(REQUEST, src__=1) res=self(REQUEST, tst__=1) r=self.prettyResults(res) except: t, v, tb = sys.exc_info() r='<strong>Error, <em>%s</em>:</strong> %s' % (t,v) report=DocumentTemplate.HTML( '<html><body bgcolor="#ffffff">\n' '<!--#var manage_tabs-->\n<hr>%s\n\n' '<hr><strong>Filter used:</strong><br>\n<pre>\n%s\n</pre>\n<hr>\n' '</body></html>' % (r, src) ) report=apply(report,(self,REQUEST),{self.id:res}) if tb is not None: self.raise_standardErrorMessage( None, REQUEST, t, v, tb, None, report) return report finally: tb=None def prettyResults(self, res): s = "" if not res or not len(res): s = "no results" else: for dn,attrs in res: s = s + ('<ul><li><b>DN: %s</b></li>\n<ul>' % dn) s = s + str(pretty_results(attrs=attrs.items())) s = s + '</ul></ul>' return s def __call__(self, REQUEST=None, src__=0, tst__=0, **kw): """ call the object """ if REQUEST is None: if kw: REQUEST = kw else: if hasattr(self, 'REQUEST'): REQUEST=self.REQUEST else: REQUEST={} c = self._getConn() if not c: raise "LDAPError", "LDAP Connection not open" if hasattr(self, 'aq_parent'): p = self.aq_parent else: p = None argdata = self._argdata(REQUEST) #use our BaseQuery's magic. :) # Also need the authenticated user. auth_user = REQUEST.get('AUTHENTICATED_USER', None) if auth_user is None: auth_user = getattr(self, 'REQUEST', None) if auth_user is not None: try: auth_user = auth_user.get('AUTHENTICATED_USER', None) except: auth_user = None if auth_user is not None: if getSecurityManager is None: # working in a pre-Zope 2.2.x instance from AccessControl.User import verify_watermark verify_watermark(auth_user) argdata['AUTHENTICATED_USER'] = auth_user f = Filter(self.filters) # make a FilterTemplate f.cook() if getSecurityManager is None: # working in a pre-Zope 2.2 instance f = apply(f, (p,argdata)) #apply the template else: # Working with the new security manager (Zope 2.2.x ++) security = getSecurityManager() security.addContext(self) try: f = apply(f, (p,), argdata) # apply the template finally: security.removeContext(self) f = str(f) #ensure it's a string if src__: return f #return the rendered source f = self.cleanse(f) ### run the search res = c.search_s(self.basedn, self._scope, f) if tst__: return res #return test-friendly data ### instantiate Entry objects based on results l = [] #list of entries to return conn=self._connection() #ZLDAPConnection Entry = conn._EntryFactory() for dn, attrdict in res: e = Entry(dn, attrdict, conn).__of__(self) l.append(e) return l manage_addZLDIFMethodForm = HTMLFile('addLdif', globals()) def manage_addZLDIFMethod(self, id, title, connection_id, basedn, arguments, ldif, getfromconnection=0, REQUEST=None, submit=None): """Add an LDIF Method """ l=LDIFMethod(id, title, connection_id, basedn, arguments, ldif) self._setObject(id, l) if getfromconnection: getattr(self,id).recomputeBaseDN() if REQUEST is not None: u=REQUEST['URL1'] if submit == " Add and Edit ": u = "%s/%s/manage_main" % (u, id) elif submit == " Add and Test ": u = "%s/%s/manage_testForm" % (u, id) else: u = u + '/manage_main' REQUEST.RESPONSE.redirect(u) return '' class LDIFMethod(LDAPMethod): 'LDIF Method' meta_type = 'LDIF Method' manage_main = HTMLFile('editLdif', globals()) manage_options = ( {'label':'Edit', 'action':'manage_main'}, {'label':'Test', 'action':'manage_testForm'}, {'label':'Security', 'action':'manage_access'}, ) __ac_permissions__=( ('View management screens', ('manage_tabs', 'manage_main',),), ('Change LDAP Methods', ('manage_edit', 'manage_testForm', 'manage_test')), ('Use LDAP Methods', ('__call__', ''), ('Anonymous', 'Manager')), ) #manage_testForm = HTMLFile("testLdifForm", globals()) def __init__(self, id, title, connection_id, basedn, arguments, ldif, **kw): """ init method """ self.id = id self.title = title self.connection_id = connection_id self.basedn = basedn self.arguments_src=self.arguments=arguments self._arg=parse(arguments) self.ldif = str(ldif) def manage_edit(self, title, connection_id, basedn, arguments, ldif, REQUEST=None, **kw): """ commit changes """ self.title = title self.connection_id = connection_id self.basedn = basedn self.arguments_src = self.arguments = arguments self._arg = parse(arguments) self.ldif = str(ldif) if REQUEST is not None: return MessageDialog( title = 'Edited', message = '<strong>%s</strong> has been changed.' % self.id, action = './manage_main', ) def __call__(self, REQUEST=None, src__=0, tst__=0, **kw): """ call the object """ if REQUEST is None: if kw: REQUEST = kw else: if hasattr(self, 'REQUEST'): REQUEST = self.REQUEST else: REQUEST={} c = self._connection().GetConnection() if not c: raise "LDAPError", "LDAP Connection not open" if hasattr(self, 'aq_parent'): p = self.aq_parent else: p = None argdata = self._argdata(REQUEST) #use our BaseQuery's magic. :) # Also need the authenticated user. auth_user = REQUEST.get('AUTHENTICATED_USER', None) if auth_user is None: auth_user = getattr(self, 'REQUEST', None) if auth_user is not None: try: auth_user = auth_user.get('AUTHENTICATED_USER', None) except: auth_user = None if auth_user is not None: if getSecurityManager is None: # working in a pre-Zope 2.2.x instance from AccessControl.User import verify_watermark verify_watermark(auth_user) argdata['AUTHENTICATED_USER'] = auth_user ldif = Ldif(self.ldif) # make a FilterTemplate ldif.cook() if getSecurityManager is None: # working in a pre-Zope 2.2 instance ldif = apply(ldif, (p, argdata)) #apply the template else: # Working with the new security manager (Zope 2.2.x ++) security = getSecurityManager() security.addContext(self) try: ldif = apply(ldif, (p,), argdata) # apply the template finally: security.removeContext(self) ldif = str(ldif) #ensure it's a string #LOG('ldif', 0, ldif) if src__: return ldif #return the rendered source ### Apply Query from cStringIO import StringIO file = StringIO(ldif) l = ERP5LDIFRecordList(file) l.parse() res = l.all_records for record in res: dn = record[0] entry = record[1] if type(entry) == type({}): authorized_modify_key = [key for key in entry.keys() if key in CHANGE_TYPES] if len(authorized_modify_key): for key in authorized_modify_key: tuple_list = entry[key] if key == 'delete': try: c.delete_s(dn) except ldap.NO_SUCH_OBJECT: pass #LOG('LDIFMethod can\'t delete NO SUCH OBJECT',0,dn) else: for mod_tuple in tuple_list: c.modify_s(dn, mod_tuple) else: mod_list = modlist.addModlist(entry) try: c.add_s(dn, mod_list) except ldap.ALREADY_EXISTS: pass #LOG('LDIFMethod can\'t add, entry allready exists',0,dn) #except ldap.SERVER_DOWN: #c = self._connection().GetConnection() #try: #c.add_s(dn, mod_list) #except ldap.ALREADY_EXISTS: #pass else: LOG('LDIFMethod Type unknow',0,'') return res class LDAP(LDAPMethod): "backwards compatibility. blech. XXX Delete Me!" pretty_results=DocumentTemplate.HTML("""\ <table border="1" cellpadding="2" cellspacing="0" rules="rows" frame="void"> <dtml-in attrs> <tr valign="top"> <th align="left">&dtml-sequence-key;</th> <td><dtml-in name="sequence-item">&dtml-sequence-item;<br /></dtml-in></td> </tr> </dtml-in> </table>""") import Globals Globals.default__class_init__(LDAPMethod) Globals.default__class_init__(LDIFMethod)