The ldap.ReconnectLDAPObject will reconnect to the ldap server if the connection is dropped. Small fix that caused wrong config to be read when multiple locations were available.
331 lines
10 KiB
Python
Executable File
331 lines
10 KiB
Python
Executable File
#!/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 (with user list only)',
|
|
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
|
|
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:
|
|
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)
|