#!/usr/bin/python import logging from ldapuserdir import LdapUserDir, __version__ as libversion, LdapUserDirError 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 = 'grouplist' 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] Shows or changes members of a group in Active Directory. Also can be used to investigate users and their group memberships. User and group names can be given as full distinguished names or just as the short system names (the tool will try to figure out the full names based on the standard OU extensions in the config and a lookup) The configuration is read from a configuration file. Default locations: """ usage += "\n\t* " + "\n\t* ".join(cfgfile_loc) + '\n' usage_epilog = """ Examples: List group members %prog svc_ra_x06sa %prog 'svc_ra_*' Get group memberships for user mueller (optionally with a group filter) %prog -g mueller %prog -g mueller 'svc_ra_*' 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('-g', action = 'store', dest = 'user_to_group', help = 'get group memberships for this user', default = '' ) parser.add_option('-u', action = 'store', dest = 'userfilter', help = 'list all matching ldap users that have defined unix mappings (always implies -n)' ) parser.add_option('--dn', action = 'store_true', dest = 'flag_showdn', help = 'show full DNs in the output', default = False ) parser.add_option('-c', action = 'store', dest = 'cfgfile', help = 'path of a config file (else tries default locations)', default = '' ) 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('-v', action = 'store_true', dest = 'flag_verbose', help = 'use more verbose output (for group and user lists)', 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('-n', '--allow-no-mssfu', action = 'store_false', dest = 'flag_mssfu', help = 'do not restrict to entries with unix (msSFU) mappings', default = True ) parser.add_option('--debug', action = 'store_true', dest = 'flag_debug', help = 'debug mode: log messages at debug level', ) parser.add_option('--showconf', action = 'store_true', dest = 'flag_examplecfg', help = 'show an example configuration file', default= False ) parser.add_option('-V', action = 'store_true', dest = 'flag_version', help = 'show version information', default = False ) (options, args) = parser.parse_args() if options.flag_debug: ch.setLevel(logging.DEBUG) flag_verbose = options.flag_verbose userfilter = options.userfilter flag_mssfu = options.flag_mssfu flag_showdn = options.flag_showdn 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 break; 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: 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 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 # this we should actually do with systemuser2dn if ',' not in user_dn: user_dn = 'CN=' + user_dn + ',' + config['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) try: user_dn = l_unpriv.systemuser2dn(os.getlogin()) except LdapUserDirError, err: if str(err) == 'No such user': sys.stderr.write(''' Error: Need priviledged user and cannot map your system user "%s" to LDAP DN for binding (you may want to use the explicit -D user_dn option) ''' % os.getlogin() ) sys.exit(1) else: sys.stderr.write('Uncaught Error: %s' % str(err)) 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: ldapdir = LdapUserDir(config['serverurl'], user_dn, user_pw, group_ou = config['group_ou'], user_ou = config['user_ou'], logger=mylogger) if mode == 'grouplist': sfilter = config['default_group_filter'] if args: sfilter = args.pop(0) ldapdir.list_groups(sfilter, mssfu=flag_mssfu, returndn=flag_showdn, verbose=flag_verbose) elif mode == 'userlist': ldapdir.list_users_etcpwd(userfilter, verbose = flag_verbose) elif mode == 'user_to_group': sfilter = None if args: sfilter = args.pop(0) try: sys.stdout.write("\n".join(ldapdir.get_groups_for_user(user_to_group, gfilter=sfilter, mssfu=flag_mssfu, returndn=flag_showdn)) + '\n') #sys.stdout.write("\n".join(ldapdir.get_groups_for_user(user_to_group)) # + "\n") except LdapUserDirError, err: sys.stderr.write('Error: ' + str(err) +'\n') sys.exit(1) elif mode == 'add': if len(args) < 2: sys.stderr.write("Error: Not enough arguments\n") sys.exit(1) group = args.pop(0) ldapdir.add_groupmembers(group, args) elif mode == 'del': if len(args) < 2: sys.stderr.write("Error: Not enough arguments\n") sys.exit(1) group = args.pop(0) ldapdir.del_groupmembers(group, args) 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 Exception, err: sys.stderr.write('Unhandled Exception (%s): %s\n' % (type(err), str(err))) sys.exit(1) sys.exit(0)