- remove domain specific code from mail tools
- make mail aliases and pop3 accounts domain properties - add class for system users - move GNVDomain class to gnuviech package - add more logging - add password hashing, passwd and shadow functions to gnuviech.tools git-svn-id: file:///home/www/usr01/svn/gnuviechadmin/gnuviech.info/gnuviechadmin/trunk@87 a67ec6bc-e5d5-0310-a910-815c51eb3124
This commit is contained in:
		
							parent
							
								
									b9ec217d1e
								
							
						
					
					
						commit
						fa6961463e
					
				
					 6 changed files with 316 additions and 76 deletions
				
			
		| 
						 | 
				
			
			@ -8,7 +8,7 @@ from log4py import Logger, FileAppender, LOGLEVEL_DEBUG
 | 
			
		|||
 | 
			
		||||
class GNVPrefs:
 | 
			
		||||
    """This class has static variables for the settings of the GNUViech
 | 
			
		||||
    administration tool. These settings can be customized in the file
 | 
			
		||||
    administration tool. These settings may be customized in the file
 | 
			
		||||
    gvadm.preferences."""
 | 
			
		||||
    # define standard values
 | 
			
		||||
    PWDMINLENGTH = 6
 | 
			
		||||
| 
						 | 
				
			
			@ -31,10 +31,33 @@ class GNVPrefs:
 | 
			
		|||
    WEBSTATSDIR   = WEBHOMEDIR+"stats/"
 | 
			
		||||
    LOGDIR        = BASEPREFIX+"/var/log"
 | 
			
		||||
    LOGFILE       = LOGDIR+"/gnvadm.log"
 | 
			
		||||
    USERTYPES     = {
 | 
			
		||||
        "web" : {
 | 
			
		||||
        "minuid"   : 10000,
 | 
			
		||||
        "maxuid"   : 10999,
 | 
			
		||||
        "group"    : "wwwusers",
 | 
			
		||||
        "fullname" : "Webuser %s",
 | 
			
		||||
        "home"     : WEBHOMEDIR + "%s",
 | 
			
		||||
        "shell"    : "/bin/true",
 | 
			
		||||
        "nohome"   : 1,
 | 
			
		||||
        "disabledpass" : 1
 | 
			
		||||
        },
 | 
			
		||||
        "pop3" : {
 | 
			
		||||
        "minuid"   : 20000,
 | 
			
		||||
        "maxuid"   : 29999,
 | 
			
		||||
        "group"    : "poponly",
 | 
			
		||||
        "fullname" : "Popuser %s",
 | 
			
		||||
        "home"     : POPHOMEDIR + "%s",
 | 
			
		||||
        "shell"    : "/bin/true",
 | 
			
		||||
        "nohome"   : 1,
 | 
			
		||||
        "disabledpass" : 1
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
    # load custom settings
 | 
			
		||||
    execfile("gvadm.preferences")
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.logger = self.getLogger(self)
 | 
			
		||||
        self.setupDirs()
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +99,30 @@ class GNVPrefs:
 | 
			
		|||
        logger.set_loglevel(LOGLEVEL_DEBUG)
 | 
			
		||||
        return logger
 | 
			
		||||
 | 
			
		||||
    def getNextSysId(self, type):
 | 
			
		||||
        nextid = self.USERTYPES[type]["minuid"]
 | 
			
		||||
        file = open(self.BASEPREFIX+"/etc/passwd", "r")
 | 
			
		||||
        for line in file.readlines():
 | 
			
		||||
            pwdline = tools.splitPasswdLine(line)
 | 
			
		||||
            self.logger.debug(str(pwdline))
 | 
			
		||||
            uid = int(pwdline["uid"])
 | 
			
		||||
            if (uid in
 | 
			
		||||
                range(int(self.USERTYPES[type]["minuid"]),
 | 
			
		||||
                      int(self.USERTYPES[type]["maxuid"]))
 | 
			
		||||
                and nextid <= uid): nextid = uid+1
 | 
			
		||||
        return nextid
 | 
			
		||||
 | 
			
		||||
    def getGroupId(self, type): pass
 | 
			
		||||
 | 
			
		||||
    def getFullName(self, type, username):
 | 
			
		||||
        return self.USERTYPES[type]["fullname"] % username
 | 
			
		||||
 | 
			
		||||
    def getHomeDir(self, type, username):
 | 
			
		||||
        return self.USERTYPES[type]["home"] % username
 | 
			
		||||
 | 
			
		||||
    def getShell(self, type):
 | 
			
		||||
        return self.USERTYPES[type]["shell"]
 | 
			
		||||
 | 
			
		||||
class NoAdmDirError(Exception):
 | 
			
		||||
    """This exception is raised if the admin directory does'nt exist."""
 | 
			
		||||
    pass
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										152
									
								
								backend/gnuviech/gnvdomain.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								backend/gnuviech/gnvdomain.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,152 @@
 | 
			
		|||
"""Package for GNUViech Admin main types and functions
 | 
			
		||||
 | 
			
		||||
(c) Copyright 2004 Jan Dittberner, IT-Consulting & Solutions
 | 
			
		||||
    Germany
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import os, pwd
 | 
			
		||||
import gnuviech
 | 
			
		||||
from gnuviech import sysuser
 | 
			
		||||
 | 
			
		||||
class DomainNotExistentError(Exception): pass
 | 
			
		||||
 | 
			
		||||
class DomainFileNotExistentError(Exception): pass
 | 
			
		||||
 | 
			
		||||
class GNVDomain:
 | 
			
		||||
    """Represents a domain in the GNUViech admin tool"""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, domain, prefs):
 | 
			
		||||
        """Initializes the domain object"""
 | 
			
		||||
        self.logger = prefs.getLogger(self)
 | 
			
		||||
        self.prefs = prefs
 | 
			
		||||
        self.name = domain
 | 
			
		||||
        self.webaccount = None
 | 
			
		||||
        self.zone = None
 | 
			
		||||
        self.statsusers = {}
 | 
			
		||||
        self.mailaliases = {}
 | 
			
		||||
        self.pop3accounts = {}
 | 
			
		||||
        try:
 | 
			
		||||
            self.__findUser()
 | 
			
		||||
        except gnuviech.NoAdmDirError:
 | 
			
		||||
            prefs.setupDirs()
 | 
			
		||||
            self.__init__(domain)
 | 
			
		||||
        except DomainFileNotExistentError:
 | 
			
		||||
            self.__createDomainFile()
 | 
			
		||||
            self.__init__(domain, prefs)
 | 
			
		||||
        except DomainNotExistentError:
 | 
			
		||||
            self.__createUser()
 | 
			
		||||
        self.createWebUser()
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        retval = "Domain "+self.name
 | 
			
		||||
        if not self.username is None:
 | 
			
		||||
            retval += ", User "+self.username
 | 
			
		||||
        else:
 | 
			
		||||
            retval += ", new domain"
 | 
			
		||||
        return retval
 | 
			
		||||
    
 | 
			
		||||
    def __createDomainFile(self):
 | 
			
		||||
        """Create the domain user id map file."""
 | 
			
		||||
        file = open(gnuviech.GNVPrefs.GVADMDIR+"domains", "w")
 | 
			
		||||
        file.close()
 | 
			
		||||
 | 
			
		||||
    def __createUser(self):
 | 
			
		||||
        """Create a user for the domain."""
 | 
			
		||||
        file = open(self.prefs.GVADMDIR+"domains", "r")
 | 
			
		||||
        id = 0
 | 
			
		||||
        for line in file.readlines():
 | 
			
		||||
            (key, value) = line.split(":")
 | 
			
		||||
            if (int(value) > id): id = int(value)
 | 
			
		||||
        file.close()
 | 
			
		||||
        id += 1
 | 
			
		||||
        file = open(self.prefs.GVADMDIR+"domains", "a")
 | 
			
		||||
        file.write("%s:%d\n" % (self.name, id))
 | 
			
		||||
        file.close()
 | 
			
		||||
        self.__findUser()
 | 
			
		||||
 | 
			
		||||
    def __findUser(self):
 | 
			
		||||
        """Finds the user for the domain."""
 | 
			
		||||
        self.username = None
 | 
			
		||||
        if (os.access(self.prefs.GVADMDIR, os.R_OK)):
 | 
			
		||||
            try:
 | 
			
		||||
                domainsfile = open(self.prefs.GVADMDIR+"domains", "r")
 | 
			
		||||
                for line in domainsfile.readlines():
 | 
			
		||||
                    (key, value) = line.split(":")
 | 
			
		||||
                    if (key == self.name):
 | 
			
		||||
                        self.username = "%s%02d" % (
 | 
			
		||||
                            self.prefs.USERPREFIX,
 | 
			
		||||
                            int(value))
 | 
			
		||||
                domainsfile.close()
 | 
			
		||||
                if self.username is None:
 | 
			
		||||
                    raise DomainNotExistentError
 | 
			
		||||
            except IOError:
 | 
			
		||||
                raise DomainFileNotExistentError
 | 
			
		||||
        else:
 | 
			
		||||
            raise gnuviech.NoAdmDirError
 | 
			
		||||
 | 
			
		||||
    def getMaxPop3Id(self):
 | 
			
		||||
        maxid = 0
 | 
			
		||||
        try:
 | 
			
		||||
            passwdfile = open(gnuviech.GNVPrefs.BASEPREFIX+"/etc/passwd", "r")
 | 
			
		||||
            for line in passwdfile.readlines():
 | 
			
		||||
                (login, passwd, uid, gid, name, dir, shell) = line.split(":")
 | 
			
		||||
                if login.startswith(self.username + "p"):
 | 
			
		||||
                    id = int(login[len(self.username):])
 | 
			
		||||
                    print id
 | 
			
		||||
                    if (id > maxid): maxid = id
 | 
			
		||||
        except IOError:
 | 
			
		||||
            pass
 | 
			
		||||
        return maxid
 | 
			
		||||
 | 
			
		||||
    def getNextUser(self, usertype):
 | 
			
		||||
        """Gets the next user for the given type."""
 | 
			
		||||
        if (usertype == "web"):
 | 
			
		||||
            return self.username
 | 
			
		||||
        if (usertype == "pop3"):
 | 
			
		||||
            return "%sp%d" % (self.username, self.getMaxPop3Id()+1)
 | 
			
		||||
 | 
			
		||||
    def addPOP3Account(self, account):
 | 
			
		||||
        self.pop3accounts[account.localpart] = account
 | 
			
		||||
 | 
			
		||||
    def addMailAlias(self, alias):
 | 
			
		||||
        self.mailaliases[alias.localpart] = alias
 | 
			
		||||
 | 
			
		||||
    def createWebUser(self):
 | 
			
		||||
        try:
 | 
			
		||||
            self.webaccount = sysuser.SystemUser(self.prefs, self.username)
 | 
			
		||||
        except sysuser.UserNotInPasswdError:
 | 
			
		||||
            self.webaccount = sysuser.createUser(self.prefs, self.username,
 | 
			
		||||
                                                    "web")
 | 
			
		||||
        self.logger.debug(str(self.webaccount))
 | 
			
		||||
        
 | 
			
		||||
# #!/bin/sh
 | 
			
		||||
# . /usr/local/etc/preferences
 | 
			
		||||
# if [ -n $USERPREFIX ]; then
 | 
			
		||||
#         USERPREFIX="usr"
 | 
			
		||||
# fi
 | 
			
		||||
# if [ $1 == "" ]; then
 | 
			
		||||
#         echo "usage: $0 <usernum>"
 | 
			
		||||
#         exit
 | 
			
		||||
# fi
 | 
			
		||||
 | 
			
		||||
# NEWUSER="$USERPREFIX$1"
 | 
			
		||||
# NEWHOME="/home/www/$NEWUSER"
 | 
			
		||||
# LOGDIR="/home/www/logfiles/$NEWUSER"
 | 
			
		||||
 | 
			
		||||
# adduser --home "$NEWHOME" --shell /bin/true --no-create-home --firstuid 10000 --ingroup wwwusers --disabled-password --gecos "Webuser $NEWUSER" $NEWUSER
 | 
			
		||||
 | 
			
		||||
# echo "${NEWUSER}:${NEWPASS}" | chpasswd
 | 
			
		||||
# mkdir -p "$NEWHOME/"{html,cgi-bin}
 | 
			
		||||
# mkdir -p "$LOGDIR"
 | 
			
		||||
# chown -Rc www-data.www-data "$LOGDIR"
 | 
			
		||||
# chmod 0750 "$LOGDIR"
 | 
			
		||||
# chown -Rc $NEWUSER.wwwusers "$NEWHOME"
 | 
			
		||||
# mkdir -p "$NEWHOME/html/stats"
 | 
			
		||||
# chown modlogan.wwwusers "$NEWHOME/html/stats"
 | 
			
		||||
# htpasswd -bc "/home/www/${NEWUSER}stats" "${NEWUSER}" "${NEWPASS}"
 | 
			
		||||
 | 
			
		||||
# echo added new web user $NEWUSER with password $NEWPASS        
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    dom = GNVDomain("dittberner.info")
 | 
			
		||||
    print dom
 | 
			
		||||
							
								
								
									
										62
									
								
								backend/gnuviech/sysuser.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								backend/gnuviech/sysuser.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,62 @@
 | 
			
		|||
from gnuviech import tools
 | 
			
		||||
 | 
			
		||||
class UserNotInPasswdError(Exception): pass
 | 
			
		||||
 | 
			
		||||
class NoPasswordInShadowError(Exception): pass
 | 
			
		||||
 | 
			
		||||
class SystemUser:
 | 
			
		||||
    def __init__(self, prefs, username):
 | 
			
		||||
        self.prefs = prefs
 | 
			
		||||
        self.logger = prefs.getLogger(self)
 | 
			
		||||
        self.getUser(username)
 | 
			
		||||
        self.logger.debug(str(self))
 | 
			
		||||
 | 
			
		||||
    def getUser(self, username):
 | 
			
		||||
        pwdfile = open(self.prefs.BASEPREFIX+"/etc/passwd", "r")
 | 
			
		||||
        for line in pwdfile.readlines():
 | 
			
		||||
            pwdline = tools.splitPasswdLine(line)
 | 
			
		||||
            self.logger.debug("PWDLINE: %s" % pwdline)
 | 
			
		||||
            if pwdline["loginname"] == username:
 | 
			
		||||
                self.username = pwdline["loginname"]
 | 
			
		||||
                self.password = self.getPassword()
 | 
			
		||||
                self.uid = pwdline["uid"]
 | 
			
		||||
                self.gid = pwdline["gid"]
 | 
			
		||||
                self.fullname = pwdline["fullname"]
 | 
			
		||||
                self.homedir = pwdline["homedir"]
 | 
			
		||||
                self.shell = pwdline["shell"]
 | 
			
		||||
                return
 | 
			
		||||
        pwdfile.close()
 | 
			
		||||
        raise UserNotInPasswdError
 | 
			
		||||
 | 
			
		||||
    def getPassword(self):
 | 
			
		||||
        shadowfile = open(self.prefs.BASEPREFIX+"/etc/shadow", "r")
 | 
			
		||||
        for line in shadowfile.readlines():
 | 
			
		||||
            shadowline = tools.splitShadowLine(line)
 | 
			
		||||
            self.logger.debug("SHADOWLINE: %s" % shadowline)
 | 
			
		||||
            if shadowline["loginname"] == self.username:
 | 
			
		||||
                shadowfile.close()
 | 
			
		||||
                return shadowline["passwordhash"]
 | 
			
		||||
        shadowfile.close()
 | 
			
		||||
        raise NoPasswordInShadowError
 | 
			
		||||
 | 
			
		||||
def createUser(prefs, username, type):
 | 
			
		||||
    line = ":".join((username, "x",
 | 
			
		||||
                    str(prefs.getNextSysId(type)),
 | 
			
		||||
                    str(prefs.getGroupId(type)),
 | 
			
		||||
                    prefs.getFullName(type, username),
 | 
			
		||||
                    prefs.getHomeDir(type, username),
 | 
			
		||||
                    prefs.getShell(type)))
 | 
			
		||||
    passwdfile = open(prefs.BASEPREFIX+"/etc/passwd", "a")
 | 
			
		||||
    passwdfile.write("%s\n" % line)
 | 
			
		||||
    passwdfile.close()
 | 
			
		||||
    createShadowItem(prefs, username, type, tools.generatePassword())
 | 
			
		||||
    return SystemUser(prefs, username)
 | 
			
		||||
 | 
			
		||||
def createShadowItem(prefs, username, type, password):
 | 
			
		||||
    line = ":".join((username,
 | 
			
		||||
                    tools.hashPassword(password, "md5"),
 | 
			
		||||
                    str(tools.daysSince1970()),
 | 
			
		||||
                    "0", "99999", "7", "", "", ""))
 | 
			
		||||
    shadowfile = open(prefs.BASEPREFIX+"/etc/shadow", "a")
 | 
			
		||||
    shadowfile.write("%s\n" % line)
 | 
			
		||||
    shadowfile.close()
 | 
			
		||||
| 
						 | 
				
			
			@ -5,16 +5,62 @@
 | 
			
		|||
 | 
			
		||||
import random, re
 | 
			
		||||
from gnuviech import GNVPrefs
 | 
			
		||||
from crypt import crypt
 | 
			
		||||
from time import time
 | 
			
		||||
 | 
			
		||||
def generatePassword():
 | 
			
		||||
    """Generates a password from the chars in GNVPrefs.PWDCHARS with
 | 
			
		||||
    a length between GNVPrefs.PWDMINLENGTH and GNVPrefs.PWDMAXLENGTH."""
 | 
			
		||||
    return "".join([chr(char) for char in
 | 
			
		||||
                    random.sample(GNVPrefs.PWDCHARS,
 | 
			
		||||
                                  random.randint(GNVPrefs.PWDMINLENGTH,
 | 
			
		||||
                                                 GNVPrefs.PWDMAXLENGTH))])
 | 
			
		||||
 | 
			
		||||
def generateSalt():
 | 
			
		||||
    saltchars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
 | 
			
		||||
    salt = []
 | 
			
		||||
    for i in range(8):
 | 
			
		||||
        salt.append(saltchars[random.randint(0, len(saltchars) - 1)])
 | 
			
		||||
    return "".join(salt)
 | 
			
		||||
    
 | 
			
		||||
def checkEmail(email):
 | 
			
		||||
    """Returns a match object if the given email address is syntactically
 | 
			
		||||
    correct otherwise it returns None"""
 | 
			
		||||
    # regex for email check
 | 
			
		||||
    p = re.compile(r'^([a-zA-Z0-9_\-.]+)@([a-zA-Z0-9\-]+(\.|[a-zA-Z0-9\-]+)*\.[a-z]{2,5})$')
 | 
			
		||||
    return p.search(email)
 | 
			
		||||
 | 
			
		||||
def splitPasswdLine(line):
 | 
			
		||||
    loginname, password, uid, gid, fullname, directory, shell = line.split(":")
 | 
			
		||||
    return {
 | 
			
		||||
        "loginname" : loginname,
 | 
			
		||||
        "password" : password,
 | 
			
		||||
        "uid" : uid,
 | 
			
		||||
        "gid" : gid,
 | 
			
		||||
        "fullname" : fullname,
 | 
			
		||||
        "homedir" : directory,
 | 
			
		||||
        "shell" : shell
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
def splitShadowLine(line):
 | 
			
		||||
    (loginname, passwordhash, lastchange, maychange, mustchange, warnexpire,
 | 
			
		||||
     disabled, disabledsince, reserved) = line.split(":")
 | 
			
		||||
    return {
 | 
			
		||||
        "loginname" : loginname,
 | 
			
		||||
        "passwordhash" : passwordhash,
 | 
			
		||||
        "lastchange" : lastchange,
 | 
			
		||||
        "maychange" : maychange,
 | 
			
		||||
        "mustchange" : mustchange,
 | 
			
		||||
        "warnexpire" : warnexpire,
 | 
			
		||||
        "disabled" : disabled,
 | 
			
		||||
        "disabledsince" : disabledsince,
 | 
			
		||||
        "reserved" : reserved
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
def hashPassword(password, method="md5"):
 | 
			
		||||
    if (method == "md5"):
 | 
			
		||||
        return crypt(password, "$1$%s" % generateSalt())
 | 
			
		||||
    return crypt(password, generateSalt())
 | 
			
		||||
 | 
			
		||||
def daysSince1970():
 | 
			
		||||
    return int(time()/(3600*24))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue