Guest User

dl2groups.py

a guest
Apr 13th, 2010
407
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.37 KB | None | 0 0
  1. #!/usr/bin/env python
  2. #
  3. # dl2groups.py A script to automatic mirroring of Zimbra distribution
  4. # lists to classical LDAP groups structure.
  5. #
  6. #This script based on script written by Carlos Vidal <[email protected]>
  7. #and found on Zimbra support forum
  8. #
  9. #
  10.  
  11. HELP = "\
  12. Usage: zdl2groups [flags]\n\
  13. \n\
  14. This command connects to Zimbra's LDAP server and creates classical LDAP groups according all existing Distribution Lists.\n\
  15. It's quite useful for connecting 3rd party sw like Alfresco or Openfire Jabber Server.\n\
  16. \n\
  17. Parameters: \n\
  18. -a, --add  :add group CN records according DistributionLists \n\
  19. -d, --del  :remove all existing group CN records \n\
  20. -p, --print : print proposed changes\n\
  21. \n\
  22. \n\
  23. -u=name, --user=name :LDAP login as uid=name,cn=admins,cn=zimbra\n\
  24.                     :defaulf login is uid=zimbra,cn=admins,cn=zimbra \n\
  25. -p=, --pass=, --passwd=, password=  :LDAP password\n\
  26. -b=, --base=  :LDAP search base\n\
  27. -h, --help   : this help screen message\n\
  28. -l, --list    :list possible changes (for tesing)\n\
  29. \n\
  30. use -l, -l -d, -a, -d , -a -d\n\
  31. "
  32.  
  33. LDAP_SERVER = 'ldap://m.hansa.com.ua:389'
  34. #LDAP_BASE = 'dc=com,dc=ua'
  35. LDAP_BASE = 'dc=ua'
  36. LDAP_USER = 'uid=zimbra,cn=admins,cn=zimbra'
  37. LDAP_PASS = 'password' #use in terminal zmlocalconfig -s -m nokey zimbra_ldap_password
  38. OUT_FILE = '/tmp/updategroups.zm'
  39. MAIL = ''
  40. # LDAP_USER=''
  41. # LDAP_PASS=''
  42. import getopt
  43. import sys
  44. import ldap
  45.  
  46. class Person:
  47.     #re_groups = re.compile(".*groupe?s:(.*)", re.M | re.I)
  48.     def __init__(self, mail):
  49.         self.mail = mail
  50.         self.alias = []
  51.         self.dn = ''
  52.         self.sn = ''
  53.         self.givenName = ''
  54.         self.displayName = ''
  55.         self.zimbraNotes = ''
  56.         self.description = ''
  57.         self.co = ''
  58.         self.company = ''
  59.  
  60.  
  61. class Group:
  62.     def __init__(self, mail):
  63.         self.dn = ''
  64.         self.gdn = ''
  65.         self.mail = mail
  66.         self.alias = []
  67.         self.members = []
  68.         self.realmembers = []
  69.         self.cn=''
  70.         self.description=''
  71.         self.zimbraNotes=''
  72.         self.ldif=''
  73.  
  74.  
  75. def getListsData(l):
  76.     allGroups.clear()
  77.    
  78.     # Get all DLs by LDAP search
  79.     for dn, g in l.search_s(LDAP_BASE, ldap.SCOPE_SUBTREE, "ObjectClass=zimbraDistributionList"):
  80.         #print dn
  81.         if 'mail' in g:
  82.             grp = Group(g['mail'][0])
  83.             grp.alias = g['mail'][1:]
  84.             grp.dn = dn
  85.             #convert list dn like uid=grp_name, ou=peole to gdn  (like cn=list_name,ou=people,dc=domain,dc=com )
  86.             grp.gdn = dn.replace(',ou=people,',',ou=groups,').replace('uid=','cn=')
  87.             if 'cn' in g: grp.cn = g['cn']
  88.             if 'description' in g: grp.description = g['description']
  89.             if 'zimbraNotes' in g: grp.zimbraNotes = g['zimbraNotes']
  90.         else:
  91.             continue
  92.        
  93.         if 'zimbraMailForwardingAddress' in g:
  94.             grp.members = g['zimbraMailForwardingAddress']
  95.        
  96.         allGroups[grp.mail] = grp
  97.        
  98. def getPersonsData(l):
  99.     allPersons.clear()
  100.     # Get all persons and their description fields
  101.     for dn, p in l.search_s(LDAP_BASE, ldap.SCOPE_SUBTREE,"(&(objectClass=organizationalPerson)(zimbraMailStatus=enabled)(zimbraAccountStatus=active)(!(|(uid=wiki)(uid=admin)(uid=spam.*)(uid=ham.*)(uid=galsync))))"):
  102.         #print p,"\n"
  103.         if 'zimbraMailDeliveryAddress' in p:
  104.             per = Person(p['zimbraMailDeliveryAddress'][0])
  105.             per.alias = p['mail']
  106.             per.dn = dn
  107.             if 'cn' in p: per.cn = p['cn']
  108.             if 'givenName' in p: per.givenName = p['givenName'][0].strip()
  109.             if 'sn' in p: per.sn = p['sn'][0].strip()
  110.             if 'displayName' in p: per.displayName = p['displayName'][0]
  111.             if 'zimbraNotes' in p: per.zimbraNotes = p['zimbraNotes'][0]
  112.             if 'description' in p: per.description = p['description'][0]
  113.             if 'co' in p: per.co = p['co'][0]
  114.             if 'company' in p: per.company = p['company'][0]  
  115.         else:
  116.             continue
  117.        
  118.         allPersons[per.mail] = per        
  119.  
  120. def resolveDLUsers(grp):
  121.     # this function resolves members like alias or another list and returns sorted list of real users (those main mails) w/o duplicates
  122.     realmembers = []
  123.     #print "\nProgessing group ",grp.mail,'members:', grp.members
  124.     for addr in grp.members:
  125.         if addr in allPersons:
  126.             if addr in allPersons[addr].mail:
  127.                 #print addr, 'It is a real acc'
  128.                 realmembers.append (addr)  
  129.                 continue
  130.         elif addr in allGroups:
  131.             if addr in allGroups[addr].mail:
  132.                 #print addr, ' Its a list. We have to expand it.'
  133.                 for z in resolveDLUsers(allGroups[addr]):
  134.                     realmembers.append(z)
  135.                 continue
  136.         else:
  137.             for g in allGroups:
  138.                 if addr in allGroups[g].alias:
  139.                     #print addr, "Group alias. Will be expanded ", allGroups[g].mail
  140.                     for z in resolveDLUsers(allGroups[allGroups[g].mail]):
  141.                         realmembers.append(z)
  142.                     continue
  143.                
  144.             for m in allPersons:
  145.                 if addr in allPersons[m].alias:
  146.                     #print addr, "Users alias. Real account is ",allPersons[m].mail
  147.                     realmembers.append(allPersons[m].mail)
  148.                     continue                        
  149.     #sort and remove duplicates
  150.     if realmembers:
  151.         realmembers.sort()
  152.         last=realmembers[-1]
  153.         for i in range(len(realmembers)-2, -1, -1):
  154.             if last==realmembers[i]: del realmembers[i]
  155.             else: last=realmembers[i]
  156.     grp.realmembers=realmembers
  157.     return realmembers
  158.  
  159. # this functions checks if required ou=groups leafs exist for zimbra domains
  160. def getRequiredOuGroupsDNs(l):
  161.     # Get all DNs for ou=people for all domains and subdomains
  162.     res=[]
  163.     for dn, p in l.search_s(LDAP_BASE, ldap.SCOPE_SUBTREE,"(ou=people)"):
  164.         res.append(dn.replace('ou=people,','ou=groups,'))
  165.     return res
  166.  
  167. def getExistingOuGroupsDNs(l):
  168.     # Get all DNs for ou=groups for all domains and subdomains
  169.     res=[]
  170.     for dn, p in l.search_s(LDAP_BASE, ldap.SCOPE_SUBTREE,"(ou=groups)"):
  171.         res.append(dn)
  172.     return res
  173.  
  174. def createOuGroupsDN(l,d):
  175.     add_record = [ ('objectClass', ['top','organizationalUnit']), ('ou', ['groups']) ]
  176.     try:
  177.         l.add_s(d, add_record)
  178.     except ldap.LDAPError, e:
  179.         print ' createOuGroupsDN: error for ', d
  180.         if type(e.message) == dict and e.message.has_key('desc'):
  181.             print 'Error - ' + e.message['desc']
  182.         else:
  183.             print 'Error - ' + str(e)
  184.  
  185. def deleteOuGroupsDN(l,d):
  186.     try:
  187.         l.delete_s(d)
  188.     except ldap.LDAPError, e:
  189.         print ' deleteOuGroupsDN: error for ', d
  190.         if type(e.message) == dict and e.message.has_key('desc'):
  191.             print 'Error - ' + e.message['desc']
  192.         else:
  193.             print 'Error - ' + str(e)
  194.  
  195.  
  196. # this function checks is certain groups CN exists
  197. def existCnGroup(l,grp):
  198.     gdn=allGroups[grp].gdn
  199.     try:
  200.         l.search_s(gdn, ldap.SCOPE_SUBTREE,"(objectClass=*)")
  201.         return 1
  202.     except ldap.LDAPError, e:
  203.         return 0
  204.  
  205. def getExistingCnGroup(l):
  206.     # Get all DNs for ou=groups for all domains and subdomains
  207.     res=[]
  208.     for dn, p in l.search_s(LDAP_BASE, ldap.SCOPE_SUBTREE,"(objectClass=groupOfNames)"):
  209.         res.append(dn)
  210.     return res
  211.  
  212. def createCnGroup(l,grp,members):
  213.     if len(members):
  214.         #convert group mail=grp to gdn  (like cn=list_name,ou=people,dc=domain,dc=com )
  215.         gdn=allGroups[grp].gdn
  216.         #print '  Processing ',grp, ' will create CN record ', gdn
  217.        
  218.         dnmembers=[]
  219.         for memb in members:
  220.             dnmembers.append(allPersons[memb].dn)
  221.    
  222.         add_record = [ ('objectClass', ['top','groupOfNames']), ('member',dnmembers)]
  223.  
  224.         if len(allGroups[grp].description):
  225.             add_record.append(('description',allGroups[grp].description))
  226.        
  227.         if len(allGroups[grp].cn):
  228.             add_record.append(('o',allGroups[grp].cn))
  229.        
  230.         try:
  231.             l.add_s(gdn, add_record)
  232.         except ldap.LDAPError, e:
  233.             print ' createCnGroup: error for ', gdn
  234.             if type(e.message) == dict and e.message.has_key('desc'):
  235.                 print 'Error - ' + e.message['desc']
  236.             else:
  237.                 print 'Error - ' + str(e)
  238.  
  239. #delete certain groups by it's corresponding DL's email
  240. def deleteCnGroupByMail(l,grp):
  241.     #convert group mail=grp to gdn  (like cn=list_name,ou=people,dc=domain,dc=com )
  242.     gdn=allGroups[grp].gdn
  243.     #print "   deleteCnGroupByMail: will be deleted ",gdn
  244.     try:
  245.         l.delete_s(gdn)
  246.         return 0
  247.     except ldap.LDAPError, e:
  248.         if type(e.message) == dict and e.message.has_key('desc'):
  249.             #print 'Error - ' + e.message['desc']
  250.             return e.message['desc']
  251.         else:
  252.             #print 'Unknown error - ' + str(e)
  253.             return str(e)
  254.                        
  255.  
  256. #---------------------------  M A I N  ---------------------------
  257. allPersons = {}
  258. allGroups = {}
  259.  
  260. if __name__ == '__main__':
  261.     # Parse command line options
  262.     # If a program wants to take 2 options, -a, and -b with the b option requiring an argument, the value should be "ab:".
  263.     try:
  264.         opts, args = getopt.getopt(sys.argv[1:], 'hadls:u:p:b:', ['help', 'del', 'list', 'server=', 'user=', 'password=', 'passwd=', 'pass=', 'base='])
  265.     except:
  266.         print "Wrong parameters.\n"
  267.         print HELP
  268.         sys.exit(1)
  269.    
  270.     listp = 0
  271.     addp = 0
  272.     delp = 0
  273.  
  274.    
  275.     if len(opts) > 0 :
  276.         for o, a in opts:
  277.             if o in ('-l', '--list'):
  278.                 listp = 1
  279.             elif o in ('-d', '--del'):
  280.                 delp = 1
  281.             elif o in ('-a', '--add'):
  282.                 addp = 1
  283.             elif o in ('-l', '--list'):
  284.                 listp = 1
  285.             elif o in ('-s', '--server'):
  286.                 LDAP_SERVER = a
  287.             elif o in ('-u', '--user'):
  288.                 LDAP_USER = 'uid=%s,cn=admins,cn=zimbra' % a
  289.             elif o in ('-p', '--pass', '--passwd', '--password'):
  290.                 LDAP_PASS = a
  291.             elif o in ('-b', '--base'):
  292.                 LDAP_BASE = a
  293.            
  294.             else:
  295.                 print "Something is wrong in arguments.\n"
  296.                 #print HELP
  297.                 sys.exit(0)
  298.     else:
  299.         print "No arguments given.\n"
  300.         print HELP
  301.         sys.exit(0)
  302.  
  303.     # Connect to the LDAP server
  304.     try:
  305.         l = ldap.initialize(LDAP_SERVER)
  306.         l.simple_bind_s(LDAP_USER, LDAP_PASS)
  307.     except:
  308.         print 'Cannot connect to LDAP server ' + LDAP_SERVER
  309.         sys.exit(1)
  310.  
  311.     # Get the list of all DLs
  312.     getListsData(l)
  313.     getPersonsData(l)
  314.     #del l
  315.    
  316.     # Normalize ou=groups leafs
  317.     # ou=groups magic is here
  318.     existGroupsDNs = getExistingOuGroupsDNs (l)
  319.     existGroupsDNs.sort()
  320.     #print '\n    Exists in Zimbra ou-groups DNs:\n', existGroupsDNs
  321.    
  322.     reqGroupsDNs = getRequiredOuGroupsDNs (l)
  323.     reqGroupsDNs.sort()
  324.     #print "    According Zimbra DistributionLists we need following group DNs:\n", reqGroupsDNs
  325.    
  326.     # create if need additional Groups DNs
  327.     for d in reqGroupsDNs:
  328.         if d not in existGroupsDNs:
  329.             print '\nGroups DN: we have to create ', d
  330.             if not listp:
  331.                 createOuGroupsDN(l,d)
  332.    
  333.     # delete unused Groups DNs
  334.     for d in existGroupsDNs:
  335.         if d not in reqGroupsDNs:
  336.             print '\nGroups DN: we have to delete ', d
  337.             if not listp:
  338.                 deleteOuGroupsDN(l,d)
  339.    
  340.     #   ====  main group CN section ========
  341.    
  342.     #remove group CNs from LDAP
  343.     if delp:
  344.         #get list of all group CNs from LDAP
  345.         exist_grp_CNs = getExistingCnGroup(l)    
  346.        
  347.         if not listp:
  348.             print 'Removing all existing group CNs from LDAP'
  349.         for g in exist_grp_CNs:
  350.             if listp:
  351.                 print '  Delete CN: ',g
  352.             else:
  353.                 try:
  354.                     l.delete_s(g)
  355.                 except ldap.LDAPError, e:
  356.                     if type(e.message) == dict and e.message.has_key('desc'):
  357.                         print 'Error - ' + e.message['desc']
  358.                     else:
  359.                         print 'Unknown error - ' + str(e)
  360.  
  361.     #add group CNs to LDAP according existing DistributionLists
  362.     if addp and not listp:
  363.         # Expanding DistributionList members
  364.         print "Expanding and Processing DistributionLists and it's members "      
  365.         for grp in allGroups:
  366.             realmembs = resolveDLUsers(allGroups[grp])
  367.             createCnGroup(l,grp,realmembs)
  368.    
  369.     if listp:
  370.         print '\nLDAP_BASE: ',LDAP_BASE
  371.         print 'LDAP_SERVER: ',LDAP_SERVER
  372.         print "\nIf you remove -l parameter this script will make following changes in LDAP groups:"
  373.         print "DistributionList    = Group (members)           "      
  374.         for grp in allGroups:
  375.             realmembs = resolveDLUsers(allGroups[grp])
  376.             print grp,' = ', allGroups[grp].gdn
  377.             for m in realmembs:
  378.                 print '   ',m, ' = ', allPersons[m].dn
  379.             print
  380.            
  381.  
  382.     print 'Done.'
  383.    
  384.     l.unbind_s()
  385.     sys.exit(0)
Advertisement
Add Comment
Please, Sign In to add comment