diff options
Diffstat (limited to 'userconfig/unixauthdb.py')
-rwxr-xr-x | userconfig/unixauthdb.py | 1154 |
1 files changed, 1154 insertions, 0 deletions
diff --git a/userconfig/unixauthdb.py b/userconfig/unixauthdb.py new file mode 100755 index 0000000..92ffcc8 --- /dev/null +++ b/userconfig/unixauthdb.py @@ -0,0 +1,1154 @@ +#!/usr/bin/python +########################################################################### +# Copyright (C) 2004-2006 by Simon Edwards +# <simon@simonzone.com> +# +# Copyright: See COPYING file that comes with this distribution +# +########################################################################### +# An API for querying and modifying the authorisation database on Unix systems. +# +# The first function that you need to use is getContext(). It returns a +# Context object that contains all relevant information concerning +# the current authorisation database on this machine. + +import crypt +import random +import fcntl +import time +import os +import os.path +import stat +import shutil +import codecs +import locale +import tempfile + +ldaperror = "" +try: + import ldap +except ImportError: + ldaperror = "The LDAP Python Module is not installed, but needed to use LDAP. Install it." + +def createTempFile(origfile): + origstat = os.stat(origfile) + tmp_prefix = os.path.basename(origfile) + "." + tmp_dir = os.path.dirname(origfile) + try: + ret = tempfile.mkstemp(prefix=tmp_prefix, dir=tmp_dir) + except: + raise IOError, "Unable to create a new temporary file for " + origfile + (fd, tmpfile) = ret + shutil.copymode(origfile, tmpfile) + os.chown(tmpfile, origstat.st_uid, origstat.st_gid) + + return ret + +def getContext(editmode=False): + """Get a Context object describing the system's authorisation database. + + Parameters: + + editmode - Set to true if you also wish change the information in this + context. Root access is required. Defaults to false. + + Returns a Context object. + + If the environmental variable "USERCONFIG_USES_LDAP" is set to "true", + userconfig will use LDAP as the backend. This feature is in development + and using it is not recommended, it won't work. + """ + + # Detect what kind of auth system we are running on and create + # and initialise the corresponding Context object type. + + # Check for Mandrake + + # Check libuser.conf + try: + if os.environ["USERCONFIG_USES_LDAP"].lower() == "true": + use_ldap = True + except KeyError,e: + use_ldap = False + if not use_ldap: + return PwdContext(editmode) + else: + print "===================================================================" + print "Warning:" + print "\tYou are using LDAP as backend. This feature is under development" + print "\tand it is currently not recommended to use it." + print "\tIf you do not want to use LDAP as backend, set the environmental" + print "\tvariabale 'USERCONFIG_USES_LDAP' to 'False'." + print "===================================================================" + return LdapContext(editmode) + +########################################################################### +# Base classes. +# +class Context(object): + """Contains all of the information about the current authorisation + database, plus some useful methods for modify this information. + + """ + def __init__(self): + self._users = [] + self._groups = [] + self._shells = None + self._setDefaultValues() + + def newUser(self,defaults=False,systemuser=False): + """Create a new UnixUser object. + + Creates a new blank UnixUser object. The object is not part of the + current Context. You need to add it yourself using addUser(). + + Newly allocated UIDs are unique with respect to the list of UnixUser + objects in the Context. + + Keyword arguments: + defaults -- Set to true if the new object should be filled in with + reasonable default values for the UID and username. + (default False) + systemuser -- Should the new user be allocated a UID from the system + range of UIDs. (default is False) + + Returns a new UnixUser object. + """ + newuserobj = self._createUser() + if defaults: + if systemuser: + r = xrange(0,self.last_system_uid) + else: + r = xrange(self.first_uid,self.last_uid) + for candiate in r: + for u in self._users: + if u.getUID()==candiate: + break + else: + newuserobj.setUID(candiate) + break + + if self.lookupUsername(u'new_user') is None: + newuserobj.setUsername(u'new_user') + else: + i = 1 + while 1: + if self.lookupUsername(u'new_user_'+str(i)) is None: + newuserobj.setUsername(u'new_user_'+str(i)) + break + i += 1 + return newuserobj + + def getUsers(self): + """Get a list of all existing users. + + Returns an array of UnixUser objects. + """ + #print "USERS:", self._users + return self._users[:] + + def getGroups(self): + """Get a list of all existing groups. + + Returns an array of UnixGroup objects. + """ + try: + self._groups.remove("new_user") + except ValueError: + print "no user removed" + pass + return self._groups[:] + + def newGroup(self,defaults=False,systemgroup=False): + """Create a new UnixGroup object. + + Creates a new blank UnixGroup object. The object is not part of the + current Context. You need to add it yourself using addGroup(). + + Newly allocated GIDs are unique with respect to the list of UnixGroup + objects in the Context. + + Keyword arguments: + defaults -- Set to true if the new object should be filled in with + reasonable default values for the GID and groupname. + (default False) + systemgroup -- Set to True if the newly allocated GID should come + from the pool of system group IDs. (default False) + + Returns a new UnixGroup object. + """ + newgroupobj = self._createGroup() + if defaults: + if systemgroup: + r = xrange(0,self.last_system_gid) + else: + r = xrange(self.first_gid,self.last_gid) + for candiate in r: + for u in self._groups: + if u.getGID()==candiate: + break + else: + newgroupobj.setGID(candiate) + break + if self.lookupGroupname(u'new_group') is None: + newgroupobj.setGroupname(u'new_group') + else: + i = 1 + while 1: + if self.lookupGroupname(u'new_user_'+str(i)) is None: + newgroupobj.setGroupname(u'new_user_'+str(i)) + break + i += 1 + return newgroupobj + + def _createGroup(self): + raise NotImplementedError, "Context.newGroup()" + + def addUser(self,userobj): + """Adds the given user to the authorisation database. + + This change only takes effect after calling context.save(). + + Keyword arguments: + userobj -- The UnixUser object to add. + """ + self._users.append(userobj) + + def addGroup(self,groupobj): + """Adds the given group to the authorisation database. + + This change only takes effect after calling context.save(). + + Keyword arguments: + groupobj -- The UnixGroup object to add. + """ + if groupobj not in self._groups: + self._groups.append(groupobj) + + def removeUser(self,userobj): + """Removes the given user object from the authorisation database. + + The user is also removed from all groups. + + This change only takes effect after calling context.save(). + """ + for g in userobj.getGroups(): + userobj.removeFromGroup(g) + + self._users.remove(userobj) + + def removeGroup(self,groupobj): + """Removes the given group object from the authorisation database. + + All users are removed from the group. + + This change only takes effect after calling context.save(). + """ + for u in groupobj.getUsers(): + u.removeFromGroup(groupobj) + + self._groups.remove(groupobj) + + def lookupUID(self,uid): + """Lookup a UnixUser object by its numeric user ID. + + Keyword arguments: + uid -- User ID to lookup, integer. + + Returns the matching UnixUser object or None if it was not found. + """ + for user in self._users: + if user.getUID()==uid: + return user + return None + + def lookupUsername(self,username): + """Lookup a UnixUser object by username. + + Keyword arguments: + username -- Username to lookup, string. + + Returns the matching UnixUser object or None if it was not found. + """ + for user in self._users: + if user.getUsername()==username: + return user + return None + + def lookupGID(self,gid): + """Lookup a UnixGroup object by its numeric group ID. + + Keyword arguments: + gid -- Group ID to lookup, integer. + + Returns the matching UnixGroup object or None if it was not found. + """ + for group in self._groups: + if group.getGID()==gid: + return group + return None + + def lookupGroupname(self,groupname): + """Lookup a UnixGroup object by groupname. + + Returns the matching UnixGroup object or None if it was not found. + """ + for group in self._groups: + if group.getGroupname()==groupname: + return group + return None + + def getUserShells(self): + """Get the list of available login shells. + + Returns an array of strings. + """ + if self._shells is None: + self._shells = [] + fhandle = codecs.open('/etc/shells','r',locale.getpreferredencoding()) + for l in fhandle.readlines(): + # TODO: strangely this lets some comented lines slip through + if len(l.strip()) > 1 and l.strip()[0] is not "#": + # Only show existing shells + if os.path.isfile(l.strip()): + self._shells.append(l.strip()) + fhandle.close() + return self._shells[:] + + def save(self): + """Synchronises the Context with the underlying operating system. + + After a successful save, any changes to the Context will be reflected + system wide. + """ + raise NotImplementedError, "Context.save()" + + def createHomeDirectory(self,userobj): + if os.path.exists(userobj.getHomeDirectory()): + raise IOError, u"Home directory %s already exists." % userobj.getHomeDirectory() + + # Copy the skeleton directory over + shutil.copytree(self._getSkeletonDirectory(),userobj.getHomeDirectory(),True) + + # Fix the file ownership stuff + uid = userobj.getUID() + gid = userobj.getPrimaryGroup().getGID() + os.chmod(userobj.getHomeDirectory(),self.dir_mode) + #os.system("chmod "+self.dir_mode+" "+userobj.getHomeDirectory()) + #print "Setting permissions:", userobj.getHomeDirectory(),self.dir_mode + os.lchown(userobj.getHomeDirectory(),uid,gid) + for root,dirs,files in os.walk(userobj.getHomeDirectory()): + for d in dirs: + os.lchown(os.path.join(root,d),uid,gid) + for f in files: + os.lchown(os.path.join(root,f),uid,gid) + + def removeHomeDirectory(self,userobj): + if os.path.exists(userobj.getHomeDirectory()): + shutil.rmtree(userobj.getHomeDirectory()) + + def _createUser(self): + raise NotImplementedError, "Context._createUser()" + + def _sanityCheck(self): + userids = [] + for u in self._users: + if isinstance(u,UnixUser)==False: + raise TypeError,"Found an object in the list of users that is not a UnixUser object." + uid = u.getUID() + if uid in userids: + raise ValueError, "User ID %i appears more than once." % uid + userids.append(uid) + u._sanityCheck() + + groupids = [] + for g in self._groups: + if isinstance(g,UnixGroup)==False: + raise TypeError,"Found an object in the list of groups that is not a UnixGroup object." + gid = g.getGID() + if gid in groupids: + raise ValueError, "Group ID %i appears more than once." % gid + groupids.append(gid) + g._sanityCheck() + + def _getSkeletonDirectory(self): + return self.skel + + def _readAdduserConf(self): + """ Fill a dictionary with the values from /etc/adduser.conf + which then can be used as default values, if the file exists + at least. + Attention: We're not validating!""" + self.defaults = {} + self.adduserconf = '/etc/adduser.conf' + if not os.path.isfile(self.adduserconf): + return + fhandle = codecs.open(self.adduserconf,'r',locale.getpreferredencoding()) + for line in fhandle.readlines(): + line = line.strip() + parts = line.split("=") + if len(parts) == 2: + self.defaults[str(parts[0].strip())] = parts[1].strip() + + def _setDefaultValues(self): + """ Set a lot of default values for UIDs and GIDs, try to use the values + from /etc/adduser.conf.""" + self._readAdduserConf() + + try: + self.skel = self.defaults["SKEL"] + except KeyError: + self.skel = '/etc/skel' + + # IDs for new users and groups. + try: + self.first_uid = int(self.defaults['FIRST_UID']) + except (KeyError,ValueError): + self.first_uid = 1000 + + try: + self.last_uid = int(self.defaults["LAST_UID"]) + except (KeyError,ValueError): + self.last_uid = 29999 + + try: + self.first_gid = int(self.defaults["FIRST_GID"]) + except (KeyError,ValueError): + self.first_gid = 1000 + + try: + self.last_gid = int(self.defaults["LAST_GID"]) + except (KeyError,ValueError): + self.last_gid = 65534 + + # Which IDs are system user and system groups? + try: + self.first_system_uid = int(self.defaults["FIRST_SYSTEM_UID"]) + except (KeyError,ValueError): + self.first_system_uid = 500 + + try: + self.last_system_uid = int(self.defaults["LAST_SYSTEM_UID"]) + except (KeyError,ValueError): + self.last_system_uid = 65534 + + try: + self.first_system_gid = int(self.defaults["FIRST_SYSTEM_GID"]) + except (KeyError,ValueError): + self.first_system_gid = 500 + + try: + self.last_system_gid = int(self.defaults["LAST_SYSTEM_GID"]) + except (KeyError,ValueError): + self.last_system_gid = 65534 + + # More defaults which might make sense. + try: + self.dir_mode = int(self.defaults["DIR_MODE"],8) + except (KeyError,ValueError): + self.dir_mode = int("0755",8) + print "Didn't read default DIR_MODE" + + try: + self.dhome = self.defaults["DHOME"] + except KeyError: + self.dhome = "/home" + + try: + self.dshell = self.defaults["DSHELL"] + except KeyError: + # Will be set in showNewUser() + self.dshell = None + +########################################################################### +class UnixUser(object): + def __init__(self,context): + self._context = context + self._uid = None + self._username = None + + # UnixGroup object. + self._primarygroup = None + + # List of UnixGroup objects. + self._groups = [] + + self._gecos = None + self._homedirectory = None + self._loginshell = None + + self._islocked = False + + self._encpass = "" + + # FIXME : This should actually be days since epoch or something like this + self._passlastchange = 0 + self._passminimumagebeforechange = 0 + self._passmaximumage = None + self._passexpirewarn = 7 + self._passexpiredisabledays = None + self._disableddays = None + + def polish(self): + primary_group = self._context.lookupGID(self._gid) + if primary_group is None: + # The GID didn't match an existing group. Quickly make a new group. + new_group = self._context.newGroup() + new_group.setGID(self._gid) + + new_group_name = u"group%i" % self._gid + i = 0 + while self._context.lookupGroupname(new_group_name) is not None: + i += 1 + new_group_name = u"group%i_%i" % (self._gid,i) + new_group.setGroupname(new_group_name) + + self._context.addGroup(new_group) + primary_group = new_group + + self.setPrimaryGroup(primary_group) + for group in self._context._groups: + if group.contains(self): + self._groups.append(group) + + def getUID(self): + """Get the unix user ID. + + Returns the integer. + """ + return self._uid + + def setUID(self,uid): + """Set the unix user ID. + + Keyword arguments: + uid -- Integer user id. + """ + uid = int(uid) + if uid<0: + raise ValueError, "User ID (%i) is a negative number." % uid + self._uid = uid + + def isSystemUser(self): + """See if this user is a system user. + + Returns True or False. + """ + return not (self._context.first_uid <= self._uid < self._context.last_uid) + + def getUsername(self): return self._username + + def setUsername(self,username): self._username = username + + def getPrimaryGroup(self): + """Get the primary group for this user. + + Returns a UnixGroup object. + """ + return self._primarygroup + + def setPrimaryGroup(self,groupobj): + """Set the primary group for this user. + + If the given group is not part of this user's list of groups, then + it will be added. + + Keyword arguments: + groupobj -- The group to set as the primary group. + """ + self.addToGroup(groupobj) + self._primarygroup = groupobj + + def getGroups(self): + """Get the list of groups that this user belongs to. + + The user's primary group is also included in the returned list. + + Returns a list of UnixGroup objects. Modify the list does not affect + this UnixUser object. + """ + return self._groups[:] + + def addToGroup(self,groupobj): + """Add this user to the given group. + + Keyword arguments: + groupobj -- UnixGroup object. + """ + groupobj._addUser(self) + if groupobj not in self._groups: + self._groups.append(groupobj) + + def removeFromGroup(self,groupobj): + """Remove this user from the given group. + + If group is current this user's primary group, then + + Keyword arguments: + groupobj -- UnixGroup object. + """ + groupobj._removeUser(self) + try: + self._groups.remove(groupobj) + except ValueError: + pass + if self._primarygroup is groupobj: + if len(self._groups)==0: + self._primarygroup = None + else: + self._primarygroup = self._groups[0] + + def setPassword(self,password): + # Make some salt. + space = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQSRTUVWXYZ0123456789./' + salt = "" + for x in range(8): + salt += space[random.randint(0,len(space)-1)] + self._encpass = crypt.crypt(password,'$1$'+salt+'$') + + def isLocked(self): return self._islocked + def setLocked(self,locked): self._islocked = locked + + def getRealName(self): + if not self._gecos: + return "" + try: + return self._gecos.split(",")[0] + except AttributeError: + return self._gecos + + def setRealName(self,realname): self._gecos = realname + def getHomeDirectory(self): return self._homedirectory + def setHomeDirectory(self,homedirectory): self._homedirectory = homedirectory + def getLoginShell(self): return self._loginshell + def setLoginShell(self,loginshell): self._loginshell = loginshell + + # 'None' means that there is no maximum password age. + def getMaximumPasswordAge(self): return self._passmaximumage + def setMaximumPasswordAge(self,days): self._passmaximumage = days + + def getMinimumPasswordAgeBeforeChange(self): return self._passminimumagebeforechange + def setMinimumPasswordAgeBeforeChange(self,days): self._passminimumagebeforechange = days + def getPasswordDisableAfterExpire(self): return self._passexpiredisabledays + def setPasswordDisableAfterExpire(self,days): self._passexpiredisabledays = days + def getPasswordExpireWarning(self): return self._passexpirewarn + def setPasswordExpireWarning(self,days): self._passexpirewarn = days + def getLastPasswordChange(self): return self._passlastchange + def getExpirationDate(self): return self._disableddays + def setExpirationDate(self,unixdate): self._disableddays = unixdate + + def __str__(self): + return "%s(%i)" % (self._username,self._uid) + + def _sanityCheck(self): + if self._primarygroup is None: + raise ValueError,"Userobj has no primary group!" + if self._uid is None: + raise ValueError,"Userobj has no UID!" + +########################################################################### +class UnixGroup(object): + def __init__(self,context): + self._context = context + + # List of UnixUser objects. + self._members = [] + + self._gid = None + self._groupname = None + + def contains(self,userobj): + """Check if a the given user is a member of this group. + + Returns True or False. + """ + return userobj in self._members + + def polish(self): pass + def isSystemGroup(self): + """Check if this group is a system group. + + Returns True or False. + """ + return not (self._context.first_gid <= self._gid < self._context.last_gid) + #return not (500 <= self._gid < 65534) + + def getGID(self): + """Get the unix group ID. + + Returns the integer group id. + """ + return self._gid + + def setGID(self,gid): + """Set the unix group ID. + + Keyword arguments: + gid -- new group id, integer. + """ + self._gid = gid + + def getGroupname(self): return self._groupname + def setGroupname(self,groupname): self._groupname = groupname + def getUsers(self): return self._members[:] + def _addUser(self,userobj): + if not self.contains(userobj): + self._members.append(userobj) + + def _removeUser(self,userobj): + try: + self._members.remove(userobj) + except ValueError: + pass + + def __str__(self): + # FIXME encoding + return str(self._groupname) + " (" + str(self._gid) + ") " + str([str(u) for u in self._members]) + + def _sanityCheck(self): + pass + +########################################################################### +class PwdContext(Context): + #def __init__(self,editmode,passwordfile="etc-passwd",groupfile='etc-group',shadowfile="etc-shadow"): + def __init__(self,editmode,passwordfile="/etc/passwd",groupfile='/etc/group',shadowfile="/etc/shadow"): + Context.__init__(self) + self.__editmode = editmode + self.__passwordfile = passwordfile + self.__groupfile = groupfile + self.__shadowfile = shadowfile + self._setDefaultValues() + + # Read in the password file + fhandle = codecs.open(passwordfile,'r',locale.getpreferredencoding()) + if LockFDRead(fhandle.fileno())==False: + raise IOError,"Unable to lock the "+passwordfile+" file." + try: + for line in fhandle.readlines(): + if line.strip()!="": + newuserobj = self.newUser(False) + newuserobj._initString(line) + self._users.append(newuserobj) + finally: + UnlockFD(fhandle.fileno()) + fhandle.close() + + # Read the group file + fhandle = codecs.open(groupfile,'r',locale.getpreferredencoding()) + if LockFDRead(fhandle.fileno())==False: + raise IOError,"Unable to lock the "+groupfile+" file." + try: + for line in fhandle.readlines(): + if line.strip()!="": + newgroupobj = self.newGroup(False) + newgroupobj._initString(line) + self._groups.append(newgroupobj) + finally: + UnlockFD(fhandle.fileno()) + fhandle.close() + + if self.__editmode: + # Load up the info from the shadow file too. + fhandle = codecs.open(shadowfile,'r',locale.getpreferredencoding()) + if LockFDRead(fhandle.fileno())==False: + raise IOError,"Unable to lock the "+shadowfile+" file." + try: + for line in fhandle.readlines(): + if line.strip()!="": + try: + (username,encpass,passlastchange,passminimumagebeforechange,passmaximumage, \ + passexpirewarn,passexpiredisabledays,disableddays,reserve) = \ + tuple(line.strip().split(":")) + userobj = self.lookupUsername(username) + if userobj is not None: + if encpass=="": + encpass = u"*" + userobj._encpass = encpass + if userobj._encpass[0]=='!': + userobj._islocked = True + userobj._encpass = userobj._encpass[1:] + else: + userobj._islocked = False + # FIXME : set time + if passlastchange and passlastchange!=u"None": + userobj._passlastchange = int(passlastchange) + else: + passlastchange = 0 + + if passminimumagebeforechange=="": + passminimumagebeforechange = None + else: + passminimumagebeforechange = int(passminimumagebeforechange) + if passminimumagebeforechange>=99999: + passminimumagebeforechange = None + userobj._passminimumagebeforechange = passminimumagebeforechange + + if passmaximumage=="": + passmaximumage = None + else: + passmaximumage = int(passmaximumage) + if passmaximumage>=99999: + passmaximumage = None + userobj._passmaximumage = passmaximumage + + if passexpirewarn=="": + passexpirewarn = None + else: + passexpirewarn = int(passexpirewarn) + if passexpirewarn>=99999: + passexpirewarn = None + userobj._passexpirewarn = passexpirewarn + + if passexpiredisabledays=="": + userobj._passexpiredisabledays = None + else: + userobj._passexpiredisabledays = int(passexpiredisabledays) + + if disableddays=="" or disableddays==u"99999": + userobj._disableddays = None + else: + userobj._disableddays = int(disableddays) + + userobj._reserve = reserve + else: + print "Couldn't find",username + except ValueError: + pass + finally: + UnlockFD(fhandle.fileno()) + fhandle.close() + + for group in self._groups: + group.polish() + for user in self._users: + user.polish() + + def _createUser(self): + return PwdUser(self) + + def _createGroup(self): + return PwdGroup(self) + + def save(self): + if self.__editmode==False: + raise IOError, "Can't save, the context was created Read only." + + self._sanityCheck() + + # Write out the new password file. + (fd, tmpname) = createTempFile(self.__passwordfile) + for u in self._users: + os.write(fd, u._getPasswdEntry().encode(locale.getpreferredencoding(),'replace')) + #print u._getPasswdEntry() + os.close(fd) + + # Update the passwd file + passwordlock = os.open(self.__passwordfile, os.O_WRONLY) # FIXME encoding + if LockFDWrite(passwordlock)==False: + raise IOError,"Couldn't get a write lock on "+self.__passwordfile + try: + os.rename(tmpname, self.__passwordfile) + finally: + UnlockFD(passwordlock) + os.close(passwordlock) + + # Write out the new group file + (fd, tmpname) = createTempFile(self.__groupfile) + origstat = os.stat(self.__groupfile) + for g in self._groups: + os.write(fd,g._getGroupFileEntry().encode(locale.getpreferredencoding())) + #print g._getGroupFileEntry()[:-1] + os.close(fd) + os.chown(tmpname, origstat.st_uid, origstat.st_gid) + + # Update the group file. + grouplock = os.open(self.__groupfile, os.O_WRONLY) + if LockFDWrite(grouplock)==False: + raise IOError,"Couldn't get write lock on "+self.__groupfile + try: + os.rename(tmpname, self.__groupfile) + finally: + UnlockFD(grouplock) + os.close(grouplock) + + # Write out the new shadow file + origstat = os.stat(self.__shadowfile) + (fd, tmpname) = createTempFile(self.__shadowfile) + for u in self._users: + os.write(fd,u._getShadowEntry().encode(locale.getpreferredencoding())) + #print u._getShadowEntry()[:-1] + os.close(fd) + + # Update the shadow file. + + # Make sure that it is writable. + if (origstat.st_mode & stat.S_IWUSR)==0: + os.chmod(self.__shadowfile,origstat.st_mode|stat.S_IWUSR) + + shadowlock = os.open(self.__shadowfile, os.O_WRONLY) + if LockFDWrite(shadowlock)==False: + raise IOError,"Couldn't get write lock on "+self.__shadowfile + try: + os.rename(tmpname, self.__shadowfile) + finally: + UnlockFD(shadowlock) + os.close(shadowlock) + + # set the permissions back to thier default. + if (origstat.st_mode & stat.S_IWUSR)==0: + os.chmod(self.__shadowfile,origstat.st_mode) + +########################################################################### +class LdapContext(Context): + + def __init__(self,editmode,server="localhost",admin_dn="",admin_pass=""): + """ Connect to the LDAP server and invoke further actions. + """ + Context.__init__(self) + # admin_dn is DistinguishedName? (or dn, for short) + self.server = server + self.baseDN = "dc=vizZzion,dc=net" + + self.url = "ldap://"+self.server + + self.ldapserver = ldap.initialize(self.url) + self.ldapserver.protocol_version = ldap.VERSION3 + + self.editmode = editmode + if not self.editmode: + self.ldapserver.simple_bind("admin",admin_pass) + print "Connected to ", self.url + + self._users = self._getUsers() + + def _getUsers(self): + """ Retrieve a list of users from the LDAP server. + """ + _users = [] + print "LdapContext._getUsers" + searchScope = ldap.SCOPE_SUBTREE + retrieveAttributes = None + searchFilter = "cn=*" + try: + ldap_result_id = self.ldapserver.search(self.baseDN, searchScope, searchFilter, retrieveAttributes) + result_set = [] + while 1: + result_type, result_data = self.ldapserver.result(ldap_result_id, 0) + if (result_data == []): + break + else: + if result_type == ldap.RES_SEARCH_ENTRY: + #print result_data[0][1] + #print " --------------------- " + result_set.append(result_data[0][1]) + #print result_set + except ldap.LDAPError, e: + print "ERROR: ",e + + if len(result_set) == 0: + print "No Results." + return + count = 0 + """ + for entry in result_set: + for d in entry.keys(): + print d, "::", entry[d] + print "======== Next User ==============" + """ + # Walk through result_set and create users. + for entry in result_set: + try: + name = entry['cn'][0] + login = entry['uid'][0] + loginshell = entry['loginShell'][0] + homedirectory = entry['homeDirectory'][0] + uid = entry['uidNumber'][0] + gid = entry['gidNumber'][0] + count = count + 1 + #print "\n%d. User: %s\n\tName: %s\n\tShell: %s\n\tHomeDir: %s\n\tUID: %s\n\tGID: %s\n" %\ + # (count, login, name, loginshell, homedirectory, uid, gid) + # Create a new userobject + new_user = self._createUser() + new_user.setHomeDirectory(homedirectory) + new_user.setUID(uid) + new_user.setRealName(name) + new_user.setLoginShell(loginshell) + new_user.setUsername(login) + _users.append(new_user) + print "Number of Users:", len(self._users) + + except KeyError, e: + # Debugging output... + print "ERR:: ",e + print 'err:: ',entry + return _users + + def _createUser(self): + return LdapUser(self) + + def _createGroup(self): + return LdapGroup(self) + + def save(self): + print "LdapContext.save() does nothing yet." + +########################################################################### +class LdapUser(UnixUser): + + def __str__(self): + return "LdapUser: %s(%i)" % (self._username,self._uid) + + +########################################################################### +class LdapGroup(UnixGroup): + + def __str__(self): + return "LdapGroup: %s(%i)" % (self._username,self._uid) + + +########################################################################### +class PwdUser(UnixUser): + def __init__(self,context): + UnixUser.__init__(self,context) + self._reserve = u"" + + def _initString(self,line): + (self._username,x,self._uid,self._gid,self._gecos,self._homedirectory, \ + self._loginshell) = tuple(line.strip().split(":")) + self._uid = int(self._uid) + self._gid = int(self._gid) + + def _getPasswdEntry(self): + return u":".join( [self._username, + u"x", + unicode(self._uid), + unicode(self._primarygroup.getGID()), + self._gecos, + self._homedirectory, + self._loginshell ] ) + u"\n" + + def _getShadowEntry(self): + if self._islocked: + encpass = u'!' + self._encpass + else: + encpass = self._encpass + + if self._passminimumagebeforechange==None: + passminimumagebeforechange = "" + else: + passminimumagebeforechange = str(self._passminimumagebeforechange) + + if self._passmaximumage==None: + passmaximumage = u"99999" + else: + passmaximumage = unicode(self._passmaximumage) + + if self._disableddays==None: + disableddays = u"" + else: + disableddays = unicode(self._disableddays) + + if self._passexpiredisabledays==None: + passexpiredisabledays = u"" + else: + passexpiredisabledays = unicode(self._passexpiredisabledays) + + if self._passexpirewarn==None: + passexpirewarn = u"" + else: + passexpirewarn = unicode(self._passexpirewarn) + + return u":".join( [self._username, + encpass, + unicode(self._passlastchange), + passminimumagebeforechange, + passmaximumage, + passexpirewarn, + passexpiredisabledays, + disableddays, + self._reserve ])+ u"\n" + +########################################################################### +class PwdGroup(UnixGroup): + def __init__(self,context): + UnixGroup.__init__(self,context) + self._memberids = u"" + self._encpass = u"" + + def _initString(self,line): + (self._groupname,self._encpass,self._gid,self._memberids) = tuple(line.strip().split(":")) + self._gid = int(self._gid) + + def polish(self): + membernames = self._memberids.split(",") + for username in membernames: + userobj = self._context.lookupUsername(username) + if userobj!=None: + self._members.append(userobj) + + def _getGroupFileEntry(self): + return u":".join( [ self._groupname, + self._encpass, + unicode(self._gid), + u",".join([u.getUsername() for u in self._members if u.getPrimaryGroup() is not self])]) + u"\n" + +########################################################################### +def LockFDRead(fd): + retries = 6 + while retries!=0: + try: + fcntl.lockf(fd,fcntl.LOCK_SH | fcntl.LOCK_NB) + return True + except IOError: + # Wait a moment + time.sleep(1) + return False + +def LockFDWrite(fd): + retries = 6 + while retries!=0: + try: + fcntl.lockf(fd,fcntl.LOCK_EX | fcntl.LOCK_NB) + return True + except IOError: + # Wait a moment + time.sleep(1) + return False + +def UnlockFD(fd): + fcntl.lockf(fd,fcntl.LOCK_UN) + +########################################################################### + +if __name__=='__main__': + print "Testing" + context = getContext(True) + + print "Stopping here..." + #import sys + #sys.exit(0) ## Remove. + #print "Users:" + #for user in context.getUsers(): + for user in context._users: + print "--------------------------------------------------" + print "UID:",user.getUID() + print "Is system user:",user.isSystemUser() + print "Username:",user.getUsername() + print "Primary Group:",str(user.getPrimaryGroup()) + print "Groups:",[str(u) for u in user.getGroups()] + print "Is locked:",user.isLocked() + print "Real name:",user.getRealName() + print "Home Dir:",user.getHomeDirectory() + print "Maximum password age:",user.getMaximumPasswordAge() + print "Minimum password age before change:",user.getMinimumPasswordAgeBeforeChange() + print "Expire warning:",user.getPasswordExpireWarning() + print "Disable after Expire:",user.getPasswordDisableAfterExpire() + #print user._getPasswdEntry() + + print "Groups" + for group in context.getGroups(): + print str(group) + #print group._getGroupFileEntry() + + print "Saving" + context.save() |