1
0
Fork 0

- 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:
Jan Dittberner 2004-12-26 19:29:32 +00:00
parent b9ec217d1e
commit fa6961463e
6 changed files with 316 additions and 76 deletions

View file

@ -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

View file

@ -4,8 +4,9 @@
Germany
"""
import os
import os, pwd
import gnuviech
from gnuviech import sysuser
class DomainNotExistentError(Exception): pass
@ -14,19 +15,27 @@ class DomainFileNotExistentError(Exception): pass
class GNVDomain:
"""Represents a domain in the GNUViech admin tool"""
def __init__(self, domain):
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()
self.__findUser()
except gnuviech.NoAdmDirError:
gnuviech.setupDirs()
prefs.setupDirs()
self.__init__(domain)
except DomainFileNotExistentError:
self.createDomainFile()
self.__init__(domain)
self.__createDomainFile()
self.__init__(domain, prefs)
except DomainNotExistentError:
self.createUser()
self.__createUser()
self.createWebUser()
def __repr__(self):
retval = "Domain "+self.name
@ -36,36 +45,36 @@ class GNVDomain:
retval += ", new domain"
return retval
def createDomainFile(self):
def __createDomainFile(self):
"""Create the domain user id map file."""
file = open(gnuviech.GNVPrefs.GVADMDIR+"domains", "w")
file.close()
def createUser(self):
def __createUser(self):
"""Create a user for the domain."""
file = open(gnuviech.GNVPrefs.GVADMDIR+"domains", "r")
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(gnuviech.GNVPrefs.GVADMDIR+"domains", "a")
file = open(self.prefs.GVADMDIR+"domains", "a")
file.write("%s:%d\n" % (self.name, id))
file.close()
self.findUser()
self.__findUser()
def findUser(self):
def __findUser(self):
"""Finds the user for the domain."""
self.username = None
if (os.access(gnuviech.GNVPrefs.GVADMDIR, os.R_OK)):
if (os.access(self.prefs.GVADMDIR, os.R_OK)):
try:
domainsfile = open(gnuviech.GNVPrefs.GVADMDIR+"domains", "r")
domainsfile = open(self.prefs.GVADMDIR+"domains", "r")
for line in domainsfile.readlines():
(key, value) = line.split(":")
if (key == self.name):
self.username = "%s%02d" % (
gnuviech.GNVPrefs.USERPREFIX,
self.prefs.USERPREFIX,
int(value))
domainsfile.close()
if self.username is None:
@ -96,6 +105,48 @@ class GNVDomain:
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

View 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()

View file

@ -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))

View file

@ -2,7 +2,9 @@
import os, string
import gnuviech
from GNVAdm import GNVDomain
#from GNVAdm import GNVDomain
from gnuviech import GNVPrefs, tools
from gnuviech.gnvdomain import GNVDomain
# if [ -n $USERPREFIX ]; then
# USERPREFIX="usr"
@ -27,81 +29,107 @@ from GNVAdm import GNVDomain
# echo "Herzlich willkommen auf dem GNU-Viech" | mail -s "Willkommen auf dem GNU-Viech" ${NEWUSER}
# echo added new pop3 user $NEWUSER with password $NEWPASS
class MailAliasExists(Exception): pass
class POP3AccountExists(Exception): pass
class MailAccount:
def __init__(self, domain):
def __init__(self, domain, localpart):
"Initialize a MailAccount instance for a given domain"
if (not os.access(gnuviech.GNVPrefs.VIRTUALDOMDIR, os.R_OK & os.X_OK)):
self.setupDirs()
self.domain = domain
self.localpart = localpart
self.prefs = domain.prefs
self.logger = domain.prefs.getLogger(self)
def setupDirs(self):
os.mkdir(gnuviech.GNVPrefs.VIRTUALDOMDIR)
def __repr__(self):
return "%s@%s" % (self.localpart, self.domain.name)
class MailAlias(MailAccount):
"""This represents a mail alias"""
def __init__(self, domain):
def __init__(self, domain, localpart, target):
"Initialize the POPAccount class for a given domain"
MailAccount.__init__(self, domain)
self.aliases = {}
self.readAll()
if localpart in domain.mailaliases.keys():
raise MailAliasExists
MailAccount.__init__(self, domain, localpart)
self.setTarget(target)
def readAll(self):
"""reads the aliasfile for the given domain"""
self.aliases = {}
if (os.access(gnuviech.GNVPrefs.VIRTUALDOMDIR, os.R_OK)):
try:
aliasfile = open(gnuviech.GNVPrefs.VIRTUALDOMDIR+self.domain.name , 'r')
for line in aliasfile.readlines():
keyvals = string.split(line,":",1)
self.aliases[keyvals[0]] = keyvals[1].strip()
aliasfile.close()
except IOError:
print "couldn't read the aliasfile for "+self.domain.name+"."
else:
print "couldn't read from "+gnuviech.GNVPrefs.VIRTUALDOMDIR+"."
def setTarget(self, target):
self.target = target
self.logger.debug("setting target for alias %s to %s." %
(str(self), self.target))
# self.aliases = {}
# self.readAll()
def writeAll(self):
"""writes the aliasfile for the given domain with the aliases defined
in the dictionary object aliases"""
if (os.access(gnuviech.GNVPrefs.VIRTUALDOMDIR, os.W_OK)):
try:
aliasfile = open(gnuviech.GNVPrefs.VIRTUALDOMDIR+self.domain.name, 'w')
keys = self.aliases.keys();
keys.sort();
for key in keys:
aliasfile.write("%s:%s" % (key, self.aliases[key]) + "\n")
aliasfile.close()
except IOError:
print "writing to aliasfile failed."
else:
print "no write access to directory "+gnuviech.GNVPrefs.VIRTUALDOMDIR+"."
# def readAll():
# """reads the aliasfile for the given domain"""
# self.aliases = {}
# if (os.access(gnuviech.GNVPrefs.VIRTUALDOMDIR, os.R_OK)):
# try:
# aliasfile = open(gnuviech.GNVPrefs.VIRTUALDOMDIR+self.domain.name , 'r')
# for line in aliasfile.readlines():
# keyvals = string.split(line,":",1)
# self.aliases[keyvals[0]] = keyvals[1].strip()
# aliasfile.close()
# except IOError:
# self.logger.error("couldn't read the aliasfile for "+self.domain.name+".")
# else:
# self.logger.error("couldn't read from "+gnuviech.GNVPrefs.VIRTUALDOMDIR+".")
def setAlias(self, alias, target):
"""sets a mail alias for given domain which directs the MTA to the
given target
"""
self.readAll()
self.aliases[alias]=target
self.writeAll()
# def writeAll(self):
# """writes the aliasfile for the given domain with the aliases defined
# in the dictionary object aliases"""
# if (os.access(gnuviech.GNVPrefs.VIRTUALDOMDIR, os.W_OK)):
# try:
# aliasfile = open(gnuviech.GNVPrefs.VIRTUALDOMDIR+self.domain.name, 'w')
# keys = self.aliases.keys();
# keys.sort();
# for key in keys:
# aliasfile.write("%s:%s" % (key, self.aliases[key]) + "\n")
# aliasfile.close()
# except IOError:
# self.logger.error("writing to aliasfile failed.")
# else:
# self.logger.error("no write access to directory "+gnuviech.GNVPrefs.VIRTUALDOMDIR+".")
# def setAlias(self, alias, target):
# """sets a mail alias for given domain which directs the MTA to the
# given target
# """
# self.readAll()
# self.aliases[alias]=target
# self.writeAll()
class POP3Account(MailAccount):
"""This represents a pop 3 account"""
def create(self, address):
"""Creates a pop3/imap account for the domain"""
print self
print "adding address "+address
alias = MailAlias(self.domain)
alias.setAlias(address, self.domain.getNextUser("pop3"))
print alias
def __init__(self, domain, localpart):
"""Creates a new pop3 mail account"""
if localpart in domain.pop3accounts.keys():
raise POP3AccountExists
MailAccount.__init__(self, domain, localpart)
self.logger.debug("adding address %s@%s." % (self.localpart,
self.domain.name))
self.setPassword(tools.generatePassword())
self.setSysUser(domain.getNextUser("pop3"))
self.domain.addMailAlias(MailAlias(self.domain,
self.localpart, self.sysuser))
def setPassword(self, newpassword):
self.password = newpassword
self.logger.debug("set password for %s to %s." %
(str(self), self.password))
def setSysUser(self, username):
self.sysuser = username
self.logger.debug("set system user for %s to %s" %
(str(self), self.sysuser))
if __name__ == "__main__":
popacc = POP3Account(GNVDomain("dittberner.info"))
print popacc
popacc.create("test")
alias = MailAlias(GNVDomain("dittberner.info"))
print alias
alias.setAlias("klaus", "klaus@dittberner.info")
print alias
prefs = GNVPrefs()
domain = GNVDomain("dittberner.info", prefs)
domain.addPOP3Account(POP3Account(domain, "test"))
domain.addMailAlias(MailAlias(domain, "klaus", "klaus@test.de"))

View file

@ -1,5 +1,6 @@
import gnuviech, sys
import gnuviech.tools
from gnuviech.gnvdomain import GNVDomain
class Test:
def __init__(self, prefs):
@ -30,6 +31,11 @@ maximum password length: %d""" % (avglen, minlen, maxlen))
else:
self.logger.debug("%s is an invalid email address." % address)
domain = GNVDomain("dittberner.info", self.prefs)
self.logger.debug("Domain %s." % domain)
domain = GNVDomain("jesusgemeindesohland.de", self.prefs)
self.logger.debug("Domain %s." % domain)
if __name__ == "__main__":
prefs = gnuviech.GNVPrefs()
Test(prefs).doTest()