* bin/ldapuserdir-ctl: improved documentation * ldapuserdir/ldapuserdir.py: fixed error when search ldap base contained only 'dc=d,dc=psi,dc=ch'
394 lines
12 KiB
Python
Executable File
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)
|