#!/usr/bin/python import logging from ldapuserdir import LdapUserDir, __version__ as libversion import ldap import sys import os from optparse import OptionParser import ConfigParser import getpass class MyOptParser(OptionParser): """OptionParser derived class for enabling us to give out a nicer help text where text can follow the option section""" def format_epilog(self, formatter): return self.epilog.replace('%prog',os.path.basename(sys.argv[0])) def read_cfg(filename): cfg = ConfigParser.ConfigParser() try: mylogger.debug('reading config from %s' % filename) cfg.read(filename) config = { 'serverurl' : cfg.get('Ldap','serverurl'), 'user_ou' : cfg.get('Ldap','user_ou'), 'group_ou' : cfg.get('Ldap','group_ou'), 'default_user_dn' : cfg.get('Ldap','default_user_dn'), 'default_user_pw' : cfg.get('Ldap','default_user_pw'), } except Exception, err: sys.stderr.write("Error in reading configuration from %s\n" % filename) sys.stderr.write(str(err)+'\n') sys.exit(1) try: config['default_group_filter'] = cfg.get('Ldap','default_group_filter') except: config['default_group_filter'] = '*' return config #Defaults cfgfile_loc = [os.path.expanduser('~/.ldapuserdir-ctl.cfg'), '/etc/ldapuserdir-ctl.cfg'] config = { 'serverurl' : 'ldaps://xyzdir.example.com:636', 'user_ou' : 'OU=Users,DC=example.com,DC=ch', 'group_ou' : 'OU=Groups,DC=example.com,DC=ch', 'default_user_dn' : 'CN=minpriv_user,OU=Services,DC=example.com,DC=ch', 'default_user_pw' : 'dummypwd', 'default_group_filter' : 'svc-ra*' } flag_needprivileges = False userfilter = '-' user_pw = '' mode = 'list' mylogger = logging.getLogger(os.path.basename(sys.argv[0])) mylogger.setLevel(logging.DEBUG) formatter = logging.Formatter('%(name)s %(levelname)s: %(message)s') ch = logging.StreamHandler() ch.setLevel(logging.WARNING) ch.setFormatter(formatter) mylogger.addHandler(ch) ################################################ # OPTION PARSING usage = """%prog [options] groupname [usernames] Used to inspect or change members of a group in Active Directory User names can be given as full distinguished names or just as the short names (in that case they will be extended by the standard OU extension) The configuration is read from a configuration file (default locations: """ usage += ", ".join(cfgfile_loc) + ')\n' usage_epilog = """ Examples: List group members %prog svc_ra_x06sa %prog 'svc_ra_*' Get group memberships for user mueller %prog -g mueller Add/delete users to/from a group (requires access rights!) %prog -a svc_ra_x06sa user1 user2 user3 %prog -d svc_ra_x06sa user1 user2 List users matching a pattern %prog -u 'mueller*' """ examplecfg = """# Configuration file example: [Ldap] # URL for contacting the LDAP server serverurl = ldaps://d.psi.ch:636 # base ldap path under which all users are found user_ou = OU=Users,OU=PSI,DC=d,DC=psi,DC=ch # base ldap path under which groups are found group_ou = ou=Groups,ou=PSI,dc=d,dc=psi,dc=ch # minimally privileged Ldap user and password for running normal # lookup queries default_user_dn = CN=linux_ldap,OU=Services,OU=IT,DC=d,DC=psi,DC=ch default_user_pw = DEFaultPassword # Optional: # default filter to be used for group searches default_group_filter = svc-ra* """ #parser = OptionParser(usage = usage, epilog = usage_epilog) parser = MyOptParser(usage = usage, epilog = usage_epilog) parser.add_option('-a', action = 'store_true', dest = 'flag_add', help = 'add group members', ) parser.add_option('-d', action = 'store_true', dest = 'flag_del', help = 'delete group members', ) parser.add_option('-c', action = 'store', dest = 'cfgfile', help = 'path of a config file', default = '' ) parser.add_option('--configfile', action = 'store_true', dest = 'flag_examplecfg', help = 'show an example configuration file', default= False ) parser.add_option('-u', action = 'store', dest = 'userfilter', help = 'list all matching ldap users that have defined unix mappings', ) parser.add_option('--debug', action = 'store_true', dest = 'flag_debug', help = 'debug mode: log messages at debug level', ) parser.add_option('-D', action = 'store', dest = 'user_dn', help = 'DN or CN of ldap user for binding to the AD server (%s)' % config['default_user_dn'], default = None ) parser.add_option('-f', action = 'store', dest = 'pwfile', help = 'path to password file (without this pwd will be prompted for)', default = '' ) parser.add_option('-g', action = 'store', dest = 'user_to_group', help = 'get group memberships for this user', default = '' ) parser.add_option('-v', action = 'store_true', dest = 'flag_verbose', help = 'use more verbose output', default = False ) parser.add_option('--user-ou', action = 'store', dest = 'user_ou', help = 'default OU for users (%s)' % config['user_ou'], default = None ) parser.add_option('--group-ou', action = 'store', dest = 'group_ou', help = 'default OU for groups (%s)' % config['group_ou'], default = None ) parser.add_option('--no-msSFU', action = 'store_true', dest = 'flag_nosfu', help = 'do not restrict to entries with unix (msSFU) mappings', default = False ) parser.add_option('-V', action = 'store_true', dest = 'flag_version', help = 'show version information', default = False ) (options, args) = parser.parse_args() group = None usernames = [] if len(args) > 0: group = args.pop(0) usernames = args if options.flag_debug: ch.setLevel(logging.DEBUG) if options.flag_version: sys.stdout.write('Library version: ' + libversion + '\n') sys.exit(0) if options.flag_examplecfg: sys.stdout.write(examplecfg) sys.exit(0) cfgfile = None if(options.cfgfile != ''): cfgfile = options.cfgfile else: for tmp in cfgfile_loc: if os.path.exists(tmp): cfgfile = tmp if cfgfile == None: sys.stderr.write('Error: You must provide a config file (default locations ' + ', '.join(cfgfile_loc) + ' or explicitely)\n') sys.exit(1) config = read_cfg(cfgfile) user_dn = config['default_user_dn'] if options.user_dn: config['user_dn'] = options.user_dn if options.group_ou: config['group_ou'] = options.group_ou if options.user_ou: config['user_ou'] = options.user_ou flag_verbose = options.flag_verbose flag_sfu = not options.flag_nosfu userfilter = options.userfilter if options.flag_del: mode = 'del' flag_needprivileges = True if options.flag_add: mode = 'add' flag_needprivileges = True if userfilter: mode = 'userlist' if options.user_to_group: mode = "user_to_group" user_to_group = options.user_to_group if (mode == 'add' or mode == 'del') and len(usernames) == 0: sys.stderr.write("Error: Not enough arguments\n") sys.exit(1) if ',' not in user_dn: user_dn = 'CN=' + user_dn + ',' + user_ou if flag_needprivileges and user_dn == config['default_user_dn']: try: l_unpriv = LdapUserDir(config['serverurl'], config['default_user_dn'], config['default_user_pw'], user_ou = config['user_ou'], logger = mylogger) user_dn = l_unpriv.systemuser2dn(os.getlogin()) if user_dn == '': sys.stderr.write('Error: Need priviledged user and cannot map your system user "%s" to LDAP DN for binding (you may want to use explicit -D user_dn option?)' % os.getlogin() ) sys.exit(1) except ldap.LDAPError, e: sys.stderr.write('LDAP error: %s\n' % str(e)) sys.exit(1) if user_dn == config['default_user_dn']: user_pw = config['default_user_pw'] if options.pwfile != '': pwf = open(options.pwfile) user_pw = pwf.readline().rstrip('\n') if user_pw == '': user_pw = getpass.getpass() if user_pw == '': sys.stderr.write('Error: No empty passwd allowed\n') # Note: AD accepts empty passwds, but will give anonymous access # So, we want to catch that case sys.exit(1) try: l = LdapUserDir(config['serverurl'], user_dn, user_pw, config['group_ou'], config['user_ou'], sfu = flag_sfu, logger=mylogger) if mode == 'list': sfilter = config['default_group_filter'] if group: sfilter = group l.list_groups(sfilter) elif mode == 'userlist': l.list_users_etcpwd(userfilter, verbose = flag_verbose) elif mode == 'user_to_group': sys.stdout.write("\n".join(l.get_groups_for_user(user_to_group)) + "\n") elif mode == 'add': l.add_groupmembers(group, usernames) elif mode == 'del': l.del_groupmembers(group, usernames) except ldap.INVALID_CREDENTIALS, e: sys.exit(1) except ldap.LDAPError, e: sys.stderr.write('Unhandled LDAP error: %s\n' % str(e)) sys.exit(1) except: sys.stderr.write('Unhandled Exception!!!!!!!!\n') raise sys.exit(0)