Various prototypes for Kodama and Shiido, two of our projects, will be released. They represent useful utilities that will be combined into larger libraries and toolsets.
Our first prototype available is addluser, a command line tool which helps users create and delete users in the directory server. It follows the release often, release early mantra, with increased functionality commonplace daily. In the prototype directory is the python-ldap RPM required for the use of this script. I’ve built it against OpenLDAP 2.0.x libraries.
The current version of addluser.py is .05. This version, dated 1/4/2001, provides a better salt function than the static one in previous versions (added .04). Thanks to Luke Howard for the sample code which this salt is based off of. Version .05 fixes a python-ldap naming issue, and adds password confirmation when adding new users.
#!/usr/bin/python
# addluser v.05
# created by Joe Little
# provides useradd style additions to an LDAP directory service
# This prototype is PublicDomain at this point (heh its a prototype!)
# It represents one piece from an upcoming suite of tools, which combined
# should be GPLed as a package.
# please change the base_dn below before using.
# It currently connects to localhost as the LDAP server (s/ localhost)
# It also depends upon python-ldap (http://python-ldap.sourceforge.net)
#
# CHANGELOG
# v.01 -- Initial release
# v.02 -- added -r (remove) functionality, fixed default cn behavior
# v.03 -- uid checking added. Use with -n flag. It will find the next
# UID/GID after the uid and gid number provided by the user
# via the command line, or after the default uid/gid number,
# if no uid/gid is provided as an argument
# Separately, it uses uidcheck to make sure there is no duplicate
# UID in system. Skips GID check since having same GID is allowed
# v.04 -- added SALT routine as provided by Luke Howard. Much better..
# v.05 -- changed importation to ldap instead of _ldap
# Added password confirmation block to ensure correct user password
import sys
import getpass
import crypt
import ldap
import whrandom
base_dn = "dc=open-it,dc=org"
#mail_domain = "open-it.org"
login_dn = "cn=Manager," + base_dn
# This is the ldap server, set to localhost by default. Currently NOT secure!
ldap_host = "localhost"
# def usage statment
def print_usage(errormsg=''):
print """
usage: %s [options] username
Options:
-h or -? Print out this message
-u uid uid number to use
-g gid gid number to use
-d home home directory of user
-s shell path of shell to use
-c comment GECOS field entry
-r remove user from LDAP (other options ignored)
-n Check for next available uid/gid; uses -u and -g value as offset
""" % (sys.argv[0])
if errormsg:
print ' Error: %s' % (errormsg)
sys.exit(1)
def getsalt():
salt = ""
for j in range(2):
i = whrandom.randint(0,9) % 3
if i == 0 :
i = (whrandom.randint(0,9) % 11)
elif i == 1 :
i = (whrandom.randint(0,9) % 25)
elif i == 2 :
i = (whrandom.randint(0,9) % 25)
salt = salt + str(i)
return (salt)
# RFC2307 ldif creation def
def BuildUser(user,uid,gid,gecos,homedir,shell,cn,userpass):
user_ldif = [
("uid", [user]),
("cn", [cn]),
("objectclass", ["account",
"posixAccount", "shadowAccount",
"top"]),
("shadowLastChange", ["12000"]),
("shadowMax", ["99999"]),
("shadowWarning", ["7"]),
("shadowInactive", ["-1"]),
("shadowExpire", ["-1"]),
("shadowFlag", ["123456789"]),
("userPassword", [userpass]),
("loginshell", [shell]),
("uidNumber", [str(uid)]),
("gidNumber", [str(gid)]),
("homeDirectory", [homedir]),
("gecos", [gecos])
]
return (user_ldif)
# command line parsing
def GetSysArgs(user,uid,gid,gecos,homedir,shell,delete,uidcheck):
import getopt
try:
optlist, args=getopt.getopt(sys.argv[1:], "?hu:g:d:s:c:f:l:rn")
except getopt.error,e:
print_usage(str(e))
sys.exit(1)
for key,value in optlist:
if key == "-r":
delete = 1
if key == "-n":
uidcheck = 1
if key == "-u":
try:
uid = int(value)
except ValueError:
print 'uid must be a number'
sys.exit(1)
if key == "-g":
try:
gid = int(value)
except ValueError:
print 'gid must be a number'
sys.exit(1)
if key == "-d":
try:
homedir = str(value)
except ValueError:
print 'home directory must be a valid string'
sys.exit(1)
if key == "-s":
try:
shell = str(value)
except ValueError:
print 'shell must be a valid string'
sys.exit(1)
if key == "-c":
try:
gecos = str(value)
except ValueError:
print 'comment field must be a valid string'
sys.exit(1)
if (key == "-h") or (key == "-?"):
print_usage()
sys.exit(1)
if len(sys.argv) == 1:
print_usage()
sys.exit(1)
else:
username = sys.argv[-1:]
user = username[0]
return (
user,uid,gid,gecos,homedir,shell,delete,uidcheck
)
# checkuid function. connects with server and finds next available uidnumber
# or gidnumber to use. Uses current uid/gid value (or default) as offset
def check_id (base_dn, id, searchfield):
idlist = []
nid = 60000
searchstr = searchfield + "=*"
res = l.search_s(base_dn, ldap.SCOPE_SUBTREE, searchstr)
for i in res:
j = i[1]
idlist.append (int(j[searchfield][0]))
idlist.sort()
testid = id
while 1:
if idlist.count(testid) == 0:
nid = testid
break
testid = testid + 1
return (nid)
# actual main
l = ldap.open(ldap_host)
# default defs
user = "guest"
uid = 500
gid = 500
gecos = "Guest User"
homedir = "/home/guest"
shell = "/bin/bash"
delete = 0
uidcheck = 0
# get command line arguments
user,uid,gid,gecos,homedir,shell,delete,uidcheck = GetSysArgs (user,uid,gid,gecos,homedir,shell,delete,uidcheck)
if not delete:
while 1:
# get user pass
pass1 = getpass.getpass ("Password for %s: " %user)
pass2 = getpass.getpass ("Confirm password: ")
if (pass1 == pass2) :
userpass_entry = pass1
break
else:
print "Passwords do not match. Please retry password."
userpass = "{crypt}" + crypt.crypt(userpass_entry, getsalt())
# get password for root dn
login_pw = getpass.getpass ("Password for %s: " % login_dn)
# bind as root dn
l.simple_bind_s (login_dn, login_pw)
# if checking of uid select, establish new uid now
if uidcheck:
cuid = check_id (base_dn, uid, "uidNumber")
uid = cuid
cuid = check_id (base_dn, gid, "gidNumber")
gid = cuid
print "UID=%d GID=%d" % (uid,gid)
# build ldif to add, if thats our operation
if not delete:
cn = user
user_info = BuildUser (user,uid,gid,gecos,homedir,shell,cn,userpass)
# check if UID is already in use on server
if not uidcheck:
cuid = check_id (base_dn, uid, "uidNumber")
if (cuid != uid):
print "UID already in use, consider using -n flag"
sys.exit(1)
# build user dn
dn = "uid=" + user + ",ou=People," + base_dn
# check for removal
if delete:
try:
l.delete_s(dn)
print "Deleted " + user
sys.exit(0)
except ldap.LDAPError:
print "Error in deleting entry"
print ' Error: %s' % (sys.exc_type)
sys.exit(1)
# add user otherwise
print "Adding " + user
try:
l.add_s (dn, user_info)
except ldap.LDAPError:
print "error in creating entry"
print ' Error: %s' % (sys.exc_type)
sys.exit(1)
print "Success"
# end connection
l.unbind()
