Files
ldapuserdir/bin/ldapuserdir-ctl
Derek Feichtinger f952fd4979 fixed bug when search pattern only contains dc
* bin/ldapuserdir-ctl: improved documentation

* ldapuserdir/ldapuserdir.py: fixed error when search
  ldap base contained only 'dc=d,dc=psi,dc=ch'
2016-02-16 17:32:44 +01:00

394 lines
12 KiB
Python
Executable File

#!/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 for the file:
"""
usage += "\n\t* " + "\n\t* ".join(cfgfile_loc) + '\n'
usage += """
Accessing the user information in AD requires an account with
limited permissions that needs to be set in the configuration file's
default_user_dn and default_user_pw options."""
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*'
List users matching a mail address pattern
%prog -m '*mueller@psi*
"""
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 ldap users matching the filter expression'
)
parser.add_option('-m', '--mail',
action = 'store',
dest = 'mailfilter',
help = 'list all ldap users based on the mail address filter expression'
)
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
mailfilter = options.mailfilter
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 mailfilter:
mode = 'maillist'
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':
records = ldapdir.get_users(userfilter, config['user_ou'], mssfu=flag_mssfu)
ldapdir.list_users_etcpwd(records, verbose = flag_verbose)
elif mode == 'maillist':
records = ldapdir.get_users_by_mailaddr(mailfilter, config['user_ou'], mssfu=flag_mssfu)
ldapdir.list_users_etcpwd(records, 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:
if str(err) == "No such user":
sys.stderr.write('Error: No such user (%s)\n' % user_to_group)
else:
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)