- mail related tables in database schema
- gpg encryption for mails - domain creation and deletion completed - logging - use pwd and grp git-svn-id: file:///home/www/usr01/svn/gnuviechadmin/gnuviech.info/gnuviechadmin/trunk@230 a67ec6bc-e5d5-0310-a910-815c51eb3124
This commit is contained in:
parent
3f4457bdca
commit
fdea3217c8
28 changed files with 877 additions and 323 deletions
7
bin/gva
7
bin/gva
|
@ -24,7 +24,12 @@ import gnuviechadmin.cli.client
|
||||||
import gnuviechadmin.cli.sysuser
|
import gnuviechadmin.cli.sysuser
|
||||||
import gnuviechadmin.cli.domain
|
import gnuviechadmin.cli.domain
|
||||||
import gnuviechadmin.cli.record
|
import gnuviechadmin.cli.record
|
||||||
import sys
|
import sys, os, logging.config
|
||||||
|
|
||||||
|
logcfgs = ('gnuviechadmin/logging.cfg', '/etc/gnuviechadmin/logging.cfg',
|
||||||
|
os.path.expanduser('~/.gva-logging.cfg'))
|
||||||
|
for cfg in [x for x in logcfgs if os.path.exists(x)]:
|
||||||
|
logging.config.fileConfig(cfg)
|
||||||
|
|
||||||
commands = [gnuviechadmin.cli.client.ClientCli,
|
commands = [gnuviechadmin.cli.client.ClientCli,
|
||||||
gnuviechadmin.cli.sysuser.SysuserCli,
|
gnuviechadmin.cli.sysuser.SysuserCli,
|
||||||
|
|
49
data/dbrepo/versions/3/3.py
Normal file
49
data/dbrepo/versions/3/3.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
from sqlalchemy import *
|
||||||
|
from migrate import *
|
||||||
|
from gnuviechadmin.backend.settings import dbschema
|
||||||
|
|
||||||
|
meta = BoundMetaData(migrate_engine)
|
||||||
|
domain = Table('domain', meta, schema = dbschema, autoload = True)
|
||||||
|
mailaccount = Table(
|
||||||
|
'mailaccount', meta,
|
||||||
|
Column('mailaccountid', Integer, primary_key = True),
|
||||||
|
Column('domainid', Integer, ForeignKey('domain.domainid'),
|
||||||
|
nullable = False),
|
||||||
|
Column('mailaccount', String(12), nullable = False, unique = True),
|
||||||
|
Column('clearpass', String(64)),
|
||||||
|
Column('cryptpass', String(34)),
|
||||||
|
Column('uid', Integer, nullable = False),
|
||||||
|
Column('gid', Integer, nullable = False),
|
||||||
|
Column('home', String(128), nullable = False),
|
||||||
|
Column('spamcheck', Boolean, nullable = False, default = False),
|
||||||
|
Column('sajunkscore', Integer),
|
||||||
|
schema = dbschema
|
||||||
|
)
|
||||||
|
mailaddress = Table(
|
||||||
|
'mailaddress', meta,
|
||||||
|
Column('mailaddressid', Integer, primary_key = True),
|
||||||
|
Column('domainid', Integer, ForeignKey('domain.domainid'),
|
||||||
|
nullable = False),
|
||||||
|
Column('email', String(255), nullable = False),
|
||||||
|
UniqueConstraint('email', 'domainid'),
|
||||||
|
schema = dbschema
|
||||||
|
)
|
||||||
|
mailtarget = Table(
|
||||||
|
'mailtarget', meta,
|
||||||
|
Column('mailtargetid', Integer, primary_key = True),
|
||||||
|
Column('mailaddressid', Integer, ForeignKey('mailaddress.mailaddressid'),
|
||||||
|
nullable = False),
|
||||||
|
Column('target', String(128), nullable = False),
|
||||||
|
UniqueConstraint('target', 'mailaddressid'),
|
||||||
|
schema = dbschema
|
||||||
|
)
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
mailaccount.create()
|
||||||
|
mailaddress.create()
|
||||||
|
mailtarget.create()
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
mailtarget.drop()
|
||||||
|
mailaddress.drop()
|
||||||
|
mailaccount.drop()
|
6
data/templates/domain/index.html
Normal file
6
data/templates/domain/index.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<html>
|
||||||
|
<head><title>Hier entsteht ein neuer Internetauftritt</title></head>
|
||||||
|
<body>
|
||||||
|
<p>Hier entsteht der Internetauftritt für www.${domain}.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
4
data/templates/domainconf/htaccess-stats
Normal file
4
data/templates/domainconf/htaccess-stats
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
AuthType Basic
|
||||||
|
AuthName "Statistics for ${domain}"
|
||||||
|
AuthUserFile "${userfile}"
|
||||||
|
require valid-user
|
42
data/templates/domainconf/modlogan.conf
Normal file
42
data/templates/domainconf/modlogan.conf
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
[global]
|
||||||
|
includepath = /etc/modlogan
|
||||||
|
include = modlogan.def.conf,global
|
||||||
|
|
||||||
|
loadplugin = input_clf
|
||||||
|
loadplugin = processor_web
|
||||||
|
loadplugin = output_modlogan
|
||||||
|
|
||||||
|
statedir = ${statsdir}
|
||||||
|
|
||||||
|
incremental = 1
|
||||||
|
enable_resolver = 1
|
||||||
|
|
||||||
|
read_ahead_limit = 100
|
||||||
|
|
||||||
|
[processor_web]
|
||||||
|
# to group known bot attacks like Nimda and CodeRed
|
||||||
|
include = group.url.conf,group_exploits
|
||||||
|
# to use the german file extension descriptions uncomment the following
|
||||||
|
#include = group.extension.conf,groupext_german
|
||||||
|
# include the default config
|
||||||
|
include = modlogan.def.conf,processor_web
|
||||||
|
|
||||||
|
# to only have pages listed, not files
|
||||||
|
#hideurl="\.(?i:gif|png|jpe?g|css|js|class|mid|swf|mp3|mpg)$$"
|
||||||
|
# to not show people they're reading the stats more often than people their page...
|
||||||
|
hideurl="^/stats"
|
||||||
|
|
||||||
|
debug_searchengines = 1
|
||||||
|
hidereferrer = "^http://([^.]+\.)*${domainesc}/"
|
||||||
|
|
||||||
|
## configure the output generator
|
||||||
|
[output_modlogan]
|
||||||
|
include = modlogan.def.conf,output_modlogan
|
||||||
|
hostname = ${domain}
|
||||||
|
|
||||||
|
outputdir = ${statsdir}
|
||||||
|
|
||||||
|
## configure the parser
|
||||||
|
[input_clf]
|
||||||
|
include = modlogan.def.conf,input_clf
|
||||||
|
inputfile = -
|
10
data/templates/domainconf/vhost.conf
Normal file
10
data/templates/domainconf/vhost.conf
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<VirtualHost ${ipaddr}:80>
|
||||||
|
ServerName www.${domain}
|
||||||
|
ServerAlias ${domain}
|
||||||
|
|
||||||
|
Alias /stats ${statsdir}
|
||||||
|
DocumentRoot ${docroot}
|
||||||
|
|
||||||
|
ErrorLog ${logdir}/error.log
|
||||||
|
CustomLog ${logdir}/access.log combined
|
||||||
|
</VirtualHost>
|
8
data/templates/mails/create_client.txt
Normal file
8
data/templates/mails/create_client.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
A new client with the following data has been created:
|
||||||
|
|
||||||
|
Firstname : ${firstname}
|
||||||
|
Lastname : ${lastname}
|
||||||
|
Email : ${email}
|
||||||
|
Address : ${address1}
|
||||||
|
: ${zipcode} ${city}
|
||||||
|
Phone : ${phone}
|
14
data/templates/mails/create_domain.txt
Normal file
14
data/templates/mails/create_domain.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
A new domain with the following data has been created:
|
||||||
|
|
||||||
|
System user : ${sysuser}
|
||||||
|
Domain name : ${domain}
|
||||||
|
Document root : ${docroot}
|
||||||
|
Statistics password : ${statspass}
|
||||||
|
|
||||||
|
To enable the domain in apache use
|
||||||
|
|
||||||
|
sudo a2ensite ${domain}
|
||||||
|
|
||||||
|
You can access statistics for the domain at
|
||||||
|
|
||||||
|
http://www.${domain}/stats/
|
8
data/templates/mails/create_sysuser.txt
Normal file
8
data/templates/mails/create_sysuser.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
A new system user has been created
|
||||||
|
|
||||||
|
UID : ${uid}
|
||||||
|
Client : ${firstname} ${lastname} <${email}>
|
||||||
|
Username : ${username}
|
||||||
|
Password : ${password}
|
||||||
|
Home : ${home}
|
||||||
|
Shell : ${shell}
|
|
@ -19,41 +19,35 @@
|
||||||
#
|
#
|
||||||
# Version: $Id$
|
# Version: $Id$
|
||||||
|
|
||||||
import ConfigParser, os
|
import smtplib, os, logging, tempfile
|
||||||
from subprocess import *
|
from email.MIMEText import MIMEText
|
||||||
from sqlalchemy import *
|
from pyme import core
|
||||||
|
from pyme.constants.sig import mode
|
||||||
|
|
||||||
|
from settings import config
|
||||||
from gnuviechadmin.exceptions import *
|
from gnuviechadmin.exceptions import *
|
||||||
|
from subprocess import *
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
class BackendEntity(object):
|
class BackendEntity(object):
|
||||||
"""This is the abstract base class for all backend entity classes."""
|
"""This is the abstract base class for all backend entity classes."""
|
||||||
|
|
||||||
def __init__(self, verbose = False):
|
def __init__(self, delegateto, verbose = False):
|
||||||
|
self.logger = logging.getLogger("%s.%s" % (
|
||||||
|
self.__class__.__module__, self.__class__.__name__))
|
||||||
|
self.delegateto = delegateto
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
self.config = ConfigParser.ConfigParser()
|
|
||||||
self.config.readfp(open('gnuviechadmin/defaults.cfg'))
|
|
||||||
self.config.read(['gnuviechadmin/gva.cfg',
|
|
||||||
os.path.expanduser('~/.gva.cfg')])
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.verbose:
|
return self.delegateto.__repr__(verbose = self.verbose)
|
||||||
cols = [col for col in \
|
|
||||||
object_mapper(self).local_table.columns.keys()]
|
|
||||||
format = "%(class)s:"
|
|
||||||
format = format + ", ".join([col + "=%(" + col + ")s" for col in \
|
|
||||||
cols])
|
|
||||||
data = {'class' : self.__class__.__name__}
|
|
||||||
else:
|
|
||||||
cols = self._shortkeys
|
|
||||||
format = ",".join("%(" + col + ")s" for col in cols)
|
|
||||||
data = {}
|
|
||||||
data.update(dict([(col, self.__getattribute__(col)) for col in cols]))
|
|
||||||
return format % data
|
|
||||||
|
|
||||||
def sucommand(self, cmdline, pipedata = None):
|
def sucommand(self, cmdline, pipedata = None):
|
||||||
"""Executes a command as root using the configured suwrapper
|
"""Executes a command as root using the configured suwrapper
|
||||||
command. If a pipe is specified it is used as stdin of the
|
command. If a pipe is specified it is used as stdin of the
|
||||||
subprocess."""
|
subprocess."""
|
||||||
suwrapper = self.config.get('common', 'suwrapper')
|
self.logger.debug("sucommand called: %s (pipedata=%s)", cmdline,
|
||||||
|
str(pipedata))
|
||||||
|
suwrapper = config.get('common', 'suwrapper')
|
||||||
toexec = "%s %s" % (suwrapper, cmdline)
|
toexec = "%s %s" % (suwrapper, cmdline)
|
||||||
if pipedata:
|
if pipedata:
|
||||||
p = Popen(toexec, shell = True, stdin=PIPE)
|
p = Popen(toexec, shell = True, stdin=PIPE)
|
||||||
|
@ -63,21 +57,80 @@ class BackendEntity(object):
|
||||||
sts = os.waitpid(p.pid, 0)
|
sts = os.waitpid(p.pid, 0)
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print "%s|%s: %d" % (pipedata, toexec, sts[1])
|
print "%s|%s: %d" % (pipedata, toexec, sts[1])
|
||||||
|
self.logger.info("%s|%s: %d", pipedata, toexec, sts[1])
|
||||||
else:
|
else:
|
||||||
p = Popen(toexec, shell = True)
|
p = Popen(toexec, shell = True)
|
||||||
sts = os.waitpid(p.pid, 0)
|
sts = os.waitpid(p.pid, 0)
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print "%s: %s" % (toexec, sts[1])
|
print "%s: %s" % (toexec, sts[1])
|
||||||
|
self.logger.info("%s: %s", toexec, sts[1])
|
||||||
return sts[1]
|
return sts[1]
|
||||||
|
|
||||||
|
def send_mail(self, subject, text):
|
||||||
|
"""This method sends a mail with the given text and subject
|
||||||
|
and signs it usign GnuPG. If a public key of the recipient is
|
||||||
|
available the mail is encrypted."""
|
||||||
|
plain = core.Data(text)
|
||||||
|
sig = core.Data()
|
||||||
|
c = core.Context()
|
||||||
|
signer = config.get('common', 'mailfrom')
|
||||||
|
rcpt = config.get('common', 'mailto')
|
||||||
|
c.signers_clear()
|
||||||
|
for sigkey in [x for x in c.op_keylist_all(signer, 1)]:
|
||||||
|
if sigkey.can_sign:
|
||||||
|
c.signers_add(sigkey)
|
||||||
|
if not c.signers_enum(0):
|
||||||
|
raise Exception("No secret keys for signing available for %s." % (
|
||||||
|
signer))
|
||||||
|
keylist = []
|
||||||
|
for key in [x for x in c.op_keylist_all(rcpt, 1)]:
|
||||||
|
valid = 0
|
||||||
|
subkey = key.subkeys
|
||||||
|
while subkey:
|
||||||
|
keyid = subkey.keyid
|
||||||
|
if keyid == None:
|
||||||
|
break
|
||||||
|
can_encrypt = subkey.can_encrypt
|
||||||
|
valid += can_encrypt
|
||||||
|
subkey = subkey.next
|
||||||
|
if valid:
|
||||||
|
keylist.append(key)
|
||||||
|
if keylist:
|
||||||
|
c.set_armor(1)
|
||||||
|
c.op_encrypt_sign(keylist, 1, plain, sig)
|
||||||
|
else:
|
||||||
|
c.op_sign(plain, sig, mode.CLEAR)
|
||||||
|
sig.seek(0,0)
|
||||||
|
|
||||||
|
msg = MIMEText(sig.read())
|
||||||
|
msg['Subject'] = subject
|
||||||
|
msg['From'] = signer
|
||||||
|
msg['To'] = rcpt
|
||||||
|
|
||||||
|
s = smtplib.SMTP()
|
||||||
|
s.connect()
|
||||||
|
s.sendmail(signer, [rcpt], msg.as_string())
|
||||||
|
s.close()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
"""Validates whether all mandatory fields of the entity have
|
"""Validates whether all mandatory fields of the entity have
|
||||||
values."""
|
values."""
|
||||||
missingfields = []
|
missingfields = []
|
||||||
for key in [col.name for col in \
|
for key in [col.name for col in \
|
||||||
object_mapper(self).local_table.columns \
|
sqlalchemy.object_mapper(
|
||||||
|
self.delegateto).local_table.columns \
|
||||||
if not col.primary_key and not col.nullable]:
|
if not col.primary_key and not col.nullable]:
|
||||||
if self.__getattribute__(key) is None:
|
if self.delegateto.__getattribute__(key) is None:
|
||||||
missingfields.append(key)
|
missingfields.append(key)
|
||||||
if missingfields:
|
if missingfields:
|
||||||
raise MissingFieldsError(missingfields)
|
raise MissingFieldsError(missingfields)
|
||||||
|
|
||||||
|
def write_to_file(self, filename, template):
|
||||||
|
"""Write the data from template to the specified file."""
|
||||||
|
tmp = tempfile.NamedTemporaryFile()
|
||||||
|
tmp.write(template)
|
||||||
|
tmp.flush()
|
||||||
|
cmd = 'cp "%s" "%s"' % (tmp.name, filename)
|
||||||
|
self.sucommand(cmd)
|
||||||
|
tmp.close()
|
||||||
|
|
||||||
|
|
|
@ -19,51 +19,68 @@
|
||||||
#
|
#
|
||||||
# Version: $Id$
|
# Version: $Id$
|
||||||
|
|
||||||
from sqlalchemy import *
|
import sqlalchemy, logging
|
||||||
from gnuviechadmin.exceptions import *
|
from gnuviechadmin.exceptions import *
|
||||||
from BackendEntity import *
|
from BackendEntity import *
|
||||||
|
|
||||||
class BackendEntityHandler(object):
|
class BackendEntityHandler(object):
|
||||||
def __init__(self, entityclass, verbose = False):
|
def __init__(self, entityclass, toclass, verbose = False):
|
||||||
|
self.logger = logging.getLogger("%s.%s" % (
|
||||||
|
self.__class__.__module__, self.__class__.__name__))
|
||||||
self.entityclass = entityclass
|
self.entityclass = entityclass
|
||||||
|
self.toclass = toclass
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
|
|
||||||
def create(self, **kwargs):
|
def create(self, **kwargs):
|
||||||
# try:
|
"""Create a new entity of the managed type with the fields set
|
||||||
sess = create_session()
|
to the values in kwargs."""
|
||||||
entity = self.entityclass(self.verbose, **kwargs)
|
self.logger.debug("create with params %s", str(kwargs))
|
||||||
try:
|
sess = sqlalchemy.create_session()
|
||||||
entity.create_hook()
|
transaction = sess.create_transaction()
|
||||||
sess.save(entity)
|
delegate = self.toclass(**kwargs)
|
||||||
sess.flush()
|
entity = self.entityclass(delegate, self.verbose)
|
||||||
except:
|
try:
|
||||||
sess.delete(entity)
|
sess.save(delegate)
|
||||||
sess.flush()
|
sess.flush()
|
||||||
raise
|
sess.refresh(delegate)
|
||||||
# except Exception, e:
|
entity.create_hook(sess)
|
||||||
# raise CreationFailedError(self.entityclass.__name__, e)
|
sess.flush()
|
||||||
|
transaction.commit()
|
||||||
|
except:
|
||||||
|
transaction.rollback()
|
||||||
|
self.logger.exception("Exception in create.")
|
||||||
|
raise
|
||||||
|
|
||||||
def fetchall(self):
|
def fetchall(self, **kwargs):
|
||||||
"""Fetches all entities of the managed entity type."""
|
"""Fetches all entities of the managed entity type."""
|
||||||
session = create_session()
|
self.logger.debug("fetchall with params %s", str(kwargs))
|
||||||
query = session.query(self.entityclass)
|
session = sqlalchemy.create_session()
|
||||||
allentities = query.select()
|
query = session.query(self.toclass)
|
||||||
for entity in allentities:
|
if kwargs:
|
||||||
BackendEntity.__init__(entity, self.verbose)
|
allentities = query.select_by(**kwargs)
|
||||||
return allentities
|
else:
|
||||||
|
allentities = query.select()
|
||||||
|
return [self.entityclass(entity, self.verbose) \
|
||||||
|
for entity in allentities]
|
||||||
|
|
||||||
def delete(self, pkvalue):
|
def delete(self, pkvalue):
|
||||||
"""Deletes the entity of the managed entity type that has the
|
"""Deletes the entity of the managed entity type that has the
|
||||||
specified primary key value."""
|
specified primary key value."""
|
||||||
|
self.logger.debug("delete with primary key %s", str(pkvalue))
|
||||||
|
sess = sqlalchemy.create_session()
|
||||||
|
transaction = sess.create_transaction()
|
||||||
try:
|
try:
|
||||||
sess = create_session()
|
to = sess.query(self.toclass).get(pkvalue)
|
||||||
entity = sess.query(self.entityclass).get(pkvalue)
|
if to:
|
||||||
if entity:
|
entity = self.entityclass(to, self.verbose)
|
||||||
BackendEntity.__init__(entity, self.verbose)
|
self.logger.info("delete %s", str(entity))
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print "delete %s" % (str(entity))
|
print "delete %s" % (str(entity))
|
||||||
entity.delete_hook()
|
entity.delete_hook(sess)
|
||||||
sess.delete(entity)
|
sess.delete(to)
|
||||||
sess.flush()
|
sess.flush()
|
||||||
|
transaction.commit()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
raise DeleteFailedError(self.entityclass.__name__, e)
|
transaction.rollback()
|
||||||
|
self.logger.exception("Exception in delete.")
|
||||||
|
raise
|
||||||
|
|
73
gnuviechadmin/backend/BackendTo.py
Normal file
73
gnuviechadmin/backend/BackendTo.py
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# -*- coding: UTF-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright (C) 2007 by Jan Dittberner.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||||
|
# USA.
|
||||||
|
#
|
||||||
|
# Version: $Id$
|
||||||
|
|
||||||
|
from sqlalchemy import object_mapper
|
||||||
|
|
||||||
|
from tables import *
|
||||||
|
|
||||||
|
class BackendTo(object):
|
||||||
|
"""Backend transfer object class."""
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
for (key, value) in kwargs.items():
|
||||||
|
self.__setattr__(key, value)
|
||||||
|
|
||||||
|
def __repr__(self, **kwargs):
|
||||||
|
if 'verbose' in kwargs and kwargs['verbose']:
|
||||||
|
cols = [col for col in \
|
||||||
|
object_mapper(self).local_table.columns.keys()]
|
||||||
|
format = "%(class)s:"
|
||||||
|
format = format + ", ".join([col + "=%(" + col + ")s" for col in \
|
||||||
|
cols])
|
||||||
|
data = {'class' : self.__class__.__name__}
|
||||||
|
else:
|
||||||
|
cols = self._shortkeys
|
||||||
|
format = ",".join("%(" + col + ")s" for col in cols)
|
||||||
|
data = {}
|
||||||
|
data.update(dict([(col, self.__getattribute__(col)) for col in cols]))
|
||||||
|
return format % data
|
||||||
|
|
||||||
|
class Client(BackendTo):
|
||||||
|
"""Transfer object class for clients."""
|
||||||
|
_shortkeys = ('clientid', 'firstname', 'lastname', 'email')
|
||||||
|
|
||||||
|
class Sysuser(BackendTo):
|
||||||
|
"""Transfer object class for system users."""
|
||||||
|
_shortkeys = ("sysuserid", "clientid", "username", "home", "shell")
|
||||||
|
|
||||||
|
class Domain(BackendTo):
|
||||||
|
"""Transfer object class for DNS domains."""
|
||||||
|
_shortkeys = ("domainid", "sysuserid", "name", "type")
|
||||||
|
|
||||||
|
class Record(BackendTo):
|
||||||
|
"""Transfer object class for DNS domain records."""
|
||||||
|
_shortkeys = ("recordid", "domainid", "name", "type", "content")
|
||||||
|
|
||||||
|
client_mapper = mapper(Client, client_table)
|
||||||
|
sysuser_mapper = mapper(Sysuser, sysuser_table)
|
||||||
|
domain_mapper = mapper(Domain, domain_table)
|
||||||
|
record_mapper = mapper(Record, record_table)
|
||||||
|
|
||||||
|
client_mapper.add_property("sysusers", relation(Sysuser, backref = 'client'))
|
||||||
|
|
||||||
|
sysuser_mapper.add_property("domains", relation(Domain, backref = 'sysuser'))
|
||||||
|
|
||||||
|
domain_mapper.add_property("records", relation(Record, cascade = 'all',
|
||||||
|
backref = 'domain'))
|
|
@ -19,42 +19,58 @@
|
||||||
#
|
#
|
||||||
# Version: $Id$
|
# Version: $Id$
|
||||||
|
|
||||||
from sqlalchemy import *
|
|
||||||
from tables import client_table
|
|
||||||
from gnuviechadmin.exceptions import *
|
from gnuviechadmin.exceptions import *
|
||||||
|
from settings import config
|
||||||
import sysuser
|
from BackendTo import *
|
||||||
from BackendEntity import *
|
from BackendEntity import *
|
||||||
from BackendEntityHandler import *
|
from BackendEntityHandler import *
|
||||||
|
|
||||||
class Client(BackendEntity):
|
class ClientEntity(BackendEntity):
|
||||||
"""Entity class for clients."""
|
"""Entity class for clients."""
|
||||||
|
|
||||||
_shortkeys = ('clientid', 'firstname', 'lastname', 'email')
|
def __init__(self, delegate, verbose = False, **kwargs):
|
||||||
|
BackendEntity.__init__(self, delegate, verbose)
|
||||||
def __init__(self, verbose = False, **kwargs):
|
|
||||||
BackendEntity.__init__(self, verbose)
|
|
||||||
self.clientid = None
|
|
||||||
self.country = 'de'
|
|
||||||
self.title = None
|
|
||||||
self.address2 = None
|
|
||||||
self.mobile = None
|
|
||||||
self.fax = None
|
|
||||||
for (key, value) in kwargs.items():
|
for (key, value) in kwargs.items():
|
||||||
self.__setattr__(key, value)
|
self.__setattr__(key, value)
|
||||||
|
if not self.delegateto.country:
|
||||||
|
self.delegateto.country = self._get_default_country()
|
||||||
self.validate()
|
self.validate()
|
||||||
|
|
||||||
def create_hook(self):
|
def _client_mail(self):
|
||||||
pass
|
text = get_template(config.get('common', 'mailtemplates'),
|
||||||
|
config.get('client', 'create.mail')).substitute({
|
||||||
|
'firstname' : self.delegateto.firstname,
|
||||||
|
'lastname' : self.delegateto.lastname,
|
||||||
|
'email' : self.delegateto.email,
|
||||||
|
'address1' : self.delegateto.address1,
|
||||||
|
'zipcode' : self.delegateto.zip,
|
||||||
|
'city' : self.delegateto.city,
|
||||||
|
'phone' : self.delegateto.phone})
|
||||||
|
subject = get_template_string(
|
||||||
|
config.get('client', 'create_subject')).substitute({
|
||||||
|
'firstname' : self.delegateto.firstname,
|
||||||
|
'lastname' : self.delegateto.lastname})
|
||||||
|
self.send_mail(subject, text)
|
||||||
|
|
||||||
def delete_hook(self):
|
def create_hook(self, session):
|
||||||
pass
|
"""Actions to perform when a client is created."""
|
||||||
|
self._client_mail()
|
||||||
|
|
||||||
client_mapper = mapper(Client, client_table)
|
def delete_hook(self, session):
|
||||||
client_mapper.add_property("sysusers", relation(sysuser.Sysuser))
|
"""Actions to perform when a client is deleted."""
|
||||||
|
if self.delegateto.sysusers:
|
||||||
|
raise CannotDeleteError(
|
||||||
|
self.delegateto,
|
||||||
|
"it still has the following system users assigned: %s" % (
|
||||||
|
", ".join([sysuser.username for sysuser in \
|
||||||
|
self.delegateto.sysusers])))
|
||||||
|
|
||||||
|
def _get_default_country(self):
|
||||||
|
"""Gets the default country."""
|
||||||
|
return config.get('common', 'defaultcountry')
|
||||||
|
|
||||||
class ClientHandler(BackendEntityHandler):
|
class ClientHandler(BackendEntityHandler):
|
||||||
"""BackendEntityHandler for Client entities."""
|
"""BackendEntityHandler for Client entities."""
|
||||||
|
|
||||||
def __init__(self, verbose = False):
|
def __init__(self, verbose = False):
|
||||||
BackendEntityHandler.__init__(self, Client, verbose)
|
BackendEntityHandler.__init__(self, ClientEntity, Client, verbose)
|
||||||
|
|
|
@ -19,37 +19,29 @@
|
||||||
#
|
#
|
||||||
# Version: $Id: client.py 1101 2007-02-28 21:15:20Z jan $
|
# Version: $Id: client.py 1101 2007-02-28 21:15:20Z jan $
|
||||||
|
|
||||||
from sqlalchemy import *
|
import datetime, os
|
||||||
from tables import domain_table
|
|
||||||
from gnuviechadmin.exceptions import *
|
from gnuviechadmin.exceptions import *
|
||||||
|
from settings import *
|
||||||
|
from BackendTo import Record, Domain
|
||||||
|
from BackendEntity import BackendEntity
|
||||||
|
from BackendEntityHandler import BackendEntityHandler
|
||||||
|
|
||||||
from record import Record
|
class DomainEntity(BackendEntity):
|
||||||
import datetime
|
|
||||||
from BackendEntity import *
|
|
||||||
from BackendEntityHandler import *
|
|
||||||
from settings import config
|
|
||||||
|
|
||||||
class Domain(BackendEntity):
|
|
||||||
"""Entity class for DNS domains."""
|
"""Entity class for DNS domains."""
|
||||||
|
|
||||||
_shortkeys = ("domainid", "sysuserid", "name", "type")
|
|
||||||
_valid_domain_types = ("MASTER", "SLAVE")
|
_valid_domain_types = ("MASTER", "SLAVE")
|
||||||
|
|
||||||
def __init__(self, verbose = False, **kwargs):
|
def __init__(self, delegate, verbose = False, **kwargs):
|
||||||
BackendEntity.__init__(self, verbose)
|
BackendEntity.__init__(self, delegate, verbose)
|
||||||
self.domainid = None
|
|
||||||
self.sysuserid = None
|
|
||||||
self.name = None
|
|
||||||
self.type = None
|
|
||||||
self.master = None
|
|
||||||
self.ns1 = None
|
self.ns1 = None
|
||||||
self.ns2 = None
|
self.ns2 = None
|
||||||
self.mx = None
|
self.mx = None
|
||||||
self.ipaddr = None
|
self.ipaddr = None
|
||||||
for (key, value) in kwargs.items():
|
for (key, value) in kwargs.items():
|
||||||
self.__setattr__(key, value)
|
self.__setattr__(key, value)
|
||||||
if not self.type:
|
if not self.delegateto.type:
|
||||||
self.type = self.getdefaultdomaintype()
|
self.delegateto.type = self.getdefaultdomaintype()
|
||||||
if not self.ns1:
|
if not self.ns1:
|
||||||
self.ns1 = config.get('domain', 'defaultns1')
|
self.ns1 = config.get('domain', 'defaultns1')
|
||||||
if not self.ns2:
|
if not self.ns2:
|
||||||
|
@ -58,7 +50,7 @@ class Domain(BackendEntity):
|
||||||
self.mx = config.get('domain', 'defaultmx')
|
self.mx = config.get('domain', 'defaultmx')
|
||||||
if not self.ipaddr:
|
if not self.ipaddr:
|
||||||
self.ipaddr = config.get('domain', 'defaultip')
|
self.ipaddr = config.get('domain', 'defaultip')
|
||||||
self.type = self.type.upper()
|
self.delegateto.type = self.delegateto.type.upper()
|
||||||
self.validate()
|
self.validate()
|
||||||
|
|
||||||
def getdefaultdomaintype(self):
|
def getdefaultdomaintype(self):
|
||||||
|
@ -66,13 +58,13 @@ class Domain(BackendEntity):
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
BackendEntity.validate(self)
|
BackendEntity.validate(self)
|
||||||
if not self.type in self._valid_domain_types:
|
if not self.delegateto.type in self._valid_domain_types:
|
||||||
raise ValidationFailedError(
|
raise ValidationFailedError(
|
||||||
self, "invalid domain type %s" % (self.type))
|
self, "invalid domain type %s" % (self.delegateto.type))
|
||||||
if self.type == 'SLAVE' and not self.master:
|
if self.delegateto.type == 'SLAVE' and not self.delegateto.master:
|
||||||
raise ValidationFailedError(
|
raise ValidationFailedError(
|
||||||
self, "you have to specify a master for slave domains.")
|
self, "you have to specify a master for slave domains.")
|
||||||
if self.type == 'MASTER':
|
if self.delegateto.type == 'MASTER':
|
||||||
if not self.ns1 or not self.ns2:
|
if not self.ns1 or not self.ns2:
|
||||||
raise ValidationFailedError(
|
raise ValidationFailedError(
|
||||||
self, "two nameservers must be specified.")
|
self, "two nameservers must be specified.")
|
||||||
|
@ -80,10 +72,17 @@ class Domain(BackendEntity):
|
||||||
raise ValidationFailedError(
|
raise ValidationFailedError(
|
||||||
self, "a primary mx host must be specified.")
|
self, "a primary mx host must be specified.")
|
||||||
|
|
||||||
def _getnewserial(self):
|
def _getnewserial(self, oldserial = None):
|
||||||
current = datetime.datetime.now()
|
current = datetime.datetime.now()
|
||||||
return int("%04d%02d%02d01" % \
|
datepart = "%04d%02d%02d" % \
|
||||||
(current.year, current.month, current.day))
|
(current.year, current.month, current.day)
|
||||||
|
retval = None
|
||||||
|
if oldserial:
|
||||||
|
if str(oldserial)[:len(datepart)] == datepart:
|
||||||
|
retval = oldserial + 1
|
||||||
|
if not retval:
|
||||||
|
retval = int("%s01" % (datepart))
|
||||||
|
return retval
|
||||||
|
|
||||||
def _getnewsoa(self):
|
def _getnewsoa(self):
|
||||||
return '%s %s %d %d %d %d %d' % \
|
return '%s %s %d %d %d %d %d' % \
|
||||||
|
@ -95,36 +94,228 @@ class Domain(BackendEntity):
|
||||||
config.getint('domain', 'defaultexpire'),
|
config.getint('domain', 'defaultexpire'),
|
||||||
config.getint('domain', 'defaultminimumttl'))
|
config.getint('domain', 'defaultminimumttl'))
|
||||||
|
|
||||||
def create_hook(self):
|
def update_serial(self, session):
|
||||||
self.records.append(Record(
|
query = session.query(Record)
|
||||||
name = self.name, type = 'SOA',
|
soarecord = query.get_by(Record.c.type == 'SOA',
|
||||||
|
Record.c.domainid == self.delegateto.domainid)
|
||||||
|
parts = soarecord.content.split(" ")
|
||||||
|
parts[2] = str(self._getnewserial(int(parts[2])))
|
||||||
|
soarecord.content = " ".join(parts)
|
||||||
|
session.save(soarecord)
|
||||||
|
session.flush()
|
||||||
|
|
||||||
|
def _get_vhost_dir(self):
|
||||||
|
return os.path.join(self.delegateto.sysuser.home,
|
||||||
|
self.delegateto.name,
|
||||||
|
config.get('domain', 'htdir'))
|
||||||
|
|
||||||
|
def _get_log_dir(self):
|
||||||
|
return os.path.join(config.get('domain', 'logpath'),
|
||||||
|
self.delegateto.name)
|
||||||
|
|
||||||
|
def _get_stats_dir(self):
|
||||||
|
return os.path.join(config.get('domain', 'statspath'),
|
||||||
|
self.delegateto.name)
|
||||||
|
|
||||||
|
def _create_vhost_dir(self):
|
||||||
|
vhostdir = self._get_vhost_dir()
|
||||||
|
cmd = 'mkdir -p "%s"' % (vhostdir)
|
||||||
|
self.sucommand(cmd)
|
||||||
|
for tpl in [tpl for tpl in os.listdir(
|
||||||
|
get_template_dir(config.get('domain', 'htdocstemplate'))) \
|
||||||
|
if not tpl.startswith('.')]:
|
||||||
|
template = get_template(config.get('domain', 'htdocstemplate'),
|
||||||
|
tpl)
|
||||||
|
template = template.substitute({
|
||||||
|
'domain' : self.delegateto.name})
|
||||||
|
self.write_to_file(os.path.join(vhostdir, tpl), template)
|
||||||
|
cmd = 'chown -R %(username)s:%(group)s "%(dir)s"' % {
|
||||||
|
'username' : self.delegateto.sysuser.username,
|
||||||
|
'group' : config.get('sysuser', 'defaultgroup'),
|
||||||
|
'dir' : vhostdir}
|
||||||
|
self.sucommand(cmd)
|
||||||
|
|
||||||
|
def _create_log_dir(self):
|
||||||
|
cmd = 'mkdir -p "%s"' % (self._get_log_dir())
|
||||||
|
self.sucommand(cmd)
|
||||||
|
|
||||||
|
def _get_auth_userfile(self):
|
||||||
|
authdir = config.get('domain', 'authdir')
|
||||||
|
if not os.path.isdir(authdir):
|
||||||
|
cmd = 'mkdir -p "%s"' % (authdir)
|
||||||
|
self.sucommand(cmd)
|
||||||
|
return os.path.join(authdir, '%s.passwd' % (self.delegateto.name))
|
||||||
|
|
||||||
|
def _create_stats_dir(self):
|
||||||
|
"""Creates the statistics directory for the domain and sets
|
||||||
|
Apache .htaccess password protection."""
|
||||||
|
statsdir = self._get_stats_dir()
|
||||||
|
authfile = self._get_auth_userfile()
|
||||||
|
cmd = 'htpasswd -m -c -b "%s" "%s" "%s"' % (
|
||||||
|
authfile,
|
||||||
|
self.delegateto.sysuser.username,
|
||||||
|
self.delegateto.sysuser.clearpass)
|
||||||
|
self.sucommand(cmd)
|
||||||
|
cmd = 'mkdir -p "%s"' % (statsdir)
|
||||||
|
self.sucommand(cmd)
|
||||||
|
template = get_template(config.get('domain', 'conftemplates'),
|
||||||
|
config.get('domain', 'statshtaccesstemplate'))
|
||||||
|
template = template.substitute({
|
||||||
|
'domain' : self.delegateto.name,
|
||||||
|
'userfile' : authfile})
|
||||||
|
self.write_to_file(os.path.join(self._get_stats_dir(),
|
||||||
|
'.htaccess'), template)
|
||||||
|
|
||||||
|
def _create_stats_conf(self):
|
||||||
|
modlogandir = os.path.join(config.get('domain',
|
||||||
|
'modlogandir'),
|
||||||
|
self.delegateto.sysuser.username)
|
||||||
|
cmd = 'mkdir -p "%s"' % (modlogandir)
|
||||||
|
self.sucommand(cmd)
|
||||||
|
template = get_template(config.get('domain', 'conftemplates'),
|
||||||
|
config.get('domain', 'modlogantemplate'))
|
||||||
|
template = template.substitute({
|
||||||
|
'statsdir' : self._get_stats_dir(),
|
||||||
|
'logdir' : self._get_log_dir(),
|
||||||
|
'domain' : self.delegateto.name,
|
||||||
|
'domainesc' : self.delegateto.name.replace('.', '\.')})
|
||||||
|
self.write_to_file(os.path.join(modlogandir,
|
||||||
|
self.delegateto.name + '.conf'),
|
||||||
|
template)
|
||||||
|
|
||||||
|
def _create_apache_conf(self):
|
||||||
|
template = get_template(config.get('domain', 'conftemplates'),
|
||||||
|
config.get('domain', 'apachetemplate'))
|
||||||
|
template = template.substitute({
|
||||||
|
'ipaddr' : self.ipaddr,
|
||||||
|
'statsdir' : self._get_stats_dir(),
|
||||||
|
'logdir' : self._get_log_dir(),
|
||||||
|
'domain' : self.delegateto.name,
|
||||||
|
'docroot' : self._get_vhost_dir()})
|
||||||
|
self.write_to_file(os.path.join(config.get('domain', 'sitesdir'),
|
||||||
|
self.delegateto.name), template)
|
||||||
|
|
||||||
|
def _mail_domain(self):
|
||||||
|
template = get_template(config.get('common', 'mailtemplates'),
|
||||||
|
config.get('domain', 'create.mail'))
|
||||||
|
text = template.substitute({
|
||||||
|
'sysuser' : self.delegateto.sysuser.username,
|
||||||
|
'domain' : self.delegateto.name,
|
||||||
|
'docroot' : self._get_vhost_dir(),
|
||||||
|
'statspass' : self.delegateto.sysuser.clearpass})
|
||||||
|
template = get_template_string(config.get('domain', 'create_subject'))
|
||||||
|
subject = template.substitute({
|
||||||
|
'domain' : self.delegateto.name})
|
||||||
|
self.send_mail(subject, text)
|
||||||
|
|
||||||
|
def create_hook(self, session):
|
||||||
|
self.delegateto.records.append(Record(
|
||||||
|
name = self.delegateto.name, type = 'SOA',
|
||||||
content = self._getnewsoa(),
|
content = self._getnewsoa(),
|
||||||
ttl = config.getint('domain', 'defaultttl')))
|
ttl = config.getint('domain', 'defaultttl')))
|
||||||
self.records.append(Record(
|
self.delegateto.records.append(Record(
|
||||||
name = self.name, type = 'NS', content = self.ns1,
|
name = self.delegateto.name, type = 'NS', content = self.ns1,
|
||||||
ttl = config.getint('domain', 'defaultttl')))
|
ttl = config.getint('domain', 'defaultttl')))
|
||||||
self.records.append(Record(
|
self.delegateto.records.append(Record(
|
||||||
name = self.name, type = 'NS', content = self.ns2,
|
name = self.delegateto.name, type = 'NS', content = self.ns2,
|
||||||
ttl = config.getint('domain', 'defaultttl')))
|
ttl = config.getint('domain', 'defaultttl')))
|
||||||
self.records.append(Record(
|
self.delegateto.records.append(Record(
|
||||||
name = self.name, type = 'MX', content = self.mx,
|
name = self.delegateto.name, type = 'MX', content = self.mx,
|
||||||
ttl = config.getint('domain', 'defaultttl'),
|
ttl = config.getint('domain', 'defaultttl'),
|
||||||
prio = config.getint('domain', 'defaultmxprio')))
|
prio = config.getint('domain', 'defaultmxprio')))
|
||||||
self.records.append(Record(
|
self.delegateto.records.append(Record(
|
||||||
name = self.name, type = 'A', content = self.ipaddr,
|
name = self.delegateto.name, type = 'A', content = self.ipaddr,
|
||||||
ttl = config.getint('domain', 'defaultttl')))
|
ttl = config.getint('domain', 'defaultttl')))
|
||||||
self.records.append(Record(
|
self.delegateto.records.append(Record(
|
||||||
name = "www.%s" % (self.name), type = 'A', content = self.ipaddr,
|
name = "www.%s" % (self.delegateto.name), type = 'A',
|
||||||
|
content = self.ipaddr,
|
||||||
ttl = config.getint('domain', 'defaultttl')))
|
ttl = config.getint('domain', 'defaultttl')))
|
||||||
|
session.save(self.delegateto)
|
||||||
|
session.flush()
|
||||||
|
self._create_vhost_dir()
|
||||||
|
self._create_log_dir()
|
||||||
|
self._create_stats_dir()
|
||||||
|
self._create_stats_conf()
|
||||||
|
self._create_apache_conf()
|
||||||
|
self._mail_domain()
|
||||||
|
|
||||||
def delete_hook(self):
|
def _delete_apache_conf(self):
|
||||||
pass
|
cmd = 'a2dissite %s' % (self.delegateto.name)
|
||||||
|
self.sucommand(cmd)
|
||||||
|
cmd = 'rm "%s"' % (os.path.join(config.get('domain', 'sitesdir'),
|
||||||
|
self.delegateto.name))
|
||||||
|
self.sucommand(cmd)
|
||||||
|
|
||||||
domain_mapper = mapper(Domain, domain_table)
|
def _delete_stats_conf(self):
|
||||||
domain_mapper.add_property("records", relation(Record))
|
cmd = 'rm "%s"' % (os.path.join(config.get('domain', 'modlogandir'),
|
||||||
|
self.delegateto.sysuser.username,
|
||||||
|
self.delegateto.name + '.conf'))
|
||||||
|
self.sucommand(cmd)
|
||||||
|
|
||||||
|
def _archive_stats_dir(self):
|
||||||
|
archive = os.path.join(self.delegateto.sysuser.home,
|
||||||
|
'%(domain)s-stats.tar.gz' % {
|
||||||
|
'domain' : self.delegateto.name})
|
||||||
|
cmd = 'tar czf "%(archive)s" --directory="%(statsbase)s" "%(statsdir)s"' % {
|
||||||
|
'archive' : archive,
|
||||||
|
'statsbase' : config.get('domain', 'statspath'),
|
||||||
|
'statsdir' : self.delegateto.name}
|
||||||
|
self.sucommand(cmd)
|
||||||
|
cmd = 'rm -r "%(statsdir)s"' % {
|
||||||
|
'statsdir' : self._get_stats_dir()}
|
||||||
|
self.sucommand(cmd)
|
||||||
|
cmd = 'chown "%(username)s:%(group)s" "%(archive)s"' % {
|
||||||
|
'username' : self.delegateto.sysuser.username,
|
||||||
|
'group' : config.get('sysuser', 'defaultgroup'),
|
||||||
|
'archive' : archive}
|
||||||
|
self.sucommand(cmd)
|
||||||
|
|
||||||
|
def _archive_log_dir(self):
|
||||||
|
archive = os.path.join(self.delegateto.sysuser.home,
|
||||||
|
'%(domain)s-logs.tar.gz' % {
|
||||||
|
'domain' : self.delegateto.name})
|
||||||
|
cmd = 'tar czf "%(archive)s" --directory="%(logbase)s" "%(logdir)s"' % {
|
||||||
|
'archive' : archive,
|
||||||
|
'logbase' : config.get('domain', 'logpath'),
|
||||||
|
'logdir' : self.delegateto.name}
|
||||||
|
self.sucommand(cmd)
|
||||||
|
cmd = 'rm -r "%(logdir)s"' % {
|
||||||
|
'logdir' : self._get_log_dir()}
|
||||||
|
self.sucommand(cmd)
|
||||||
|
cmd = 'chown "%(username)s:%(group)s" "%(archive)s"' % {
|
||||||
|
'username' : self.delegateto.sysuser.username,
|
||||||
|
'group' : config.get('sysuser', 'defaultgroup'),
|
||||||
|
'archive' : archive}
|
||||||
|
self.sucommand(cmd)
|
||||||
|
|
||||||
|
def _archive_vhost_dir(self):
|
||||||
|
archive = os.path.join(self.delegateto.sysuser.home,
|
||||||
|
'%(domain)s-vhost.tar.gz' % {
|
||||||
|
'domain' : self.delegateto.name})
|
||||||
|
cmd = 'tar czf "%(archive)s" --directory="%(vhostbase)s" "%(vhostdir)s"' % {
|
||||||
|
'archive' : archive,
|
||||||
|
'vhostbase' : self.delegateto.sysuser.home,
|
||||||
|
'vhostdir' : self.delegateto.name}
|
||||||
|
self.sucommand(cmd)
|
||||||
|
cmd = 'rm -r "%(vhostdir)s"' % {
|
||||||
|
'vhostdir' : os.path.join(self.delegateto.sysuser.home,
|
||||||
|
self.delegateto.name)}
|
||||||
|
self.sucommand(cmd)
|
||||||
|
cmd = 'chown "%(username)s:%(group)s" "%(archive)s"' % {
|
||||||
|
'username' : self.delegateto.sysuser.username,
|
||||||
|
'group' : config.get('sysuser', 'defaultgroup'),
|
||||||
|
'archive' : archive}
|
||||||
|
self.sucommand(cmd)
|
||||||
|
|
||||||
|
def delete_hook(self, session):
|
||||||
|
self._delete_apache_conf()
|
||||||
|
self._delete_stats_conf()
|
||||||
|
self._archive_stats_dir()
|
||||||
|
self._archive_log_dir()
|
||||||
|
self._archive_vhost_dir()
|
||||||
|
|
||||||
class DomainHandler(BackendEntityHandler):
|
class DomainHandler(BackendEntityHandler):
|
||||||
"""BackendEntityHandler for Domain entities."""
|
"""BackendEntityHandler for Domain entities."""
|
||||||
|
|
||||||
def __init__(self, verbose = False):
|
def __init__(self, verbose = False):
|
||||||
BackendEntityHandler.__init__(self, Domain, verbose)
|
BackendEntityHandler.__init__(self, DomainEntity, Domain, verbose)
|
||||||
|
|
|
@ -19,37 +19,24 @@
|
||||||
#
|
#
|
||||||
# Version: $Id$
|
# Version: $Id$
|
||||||
|
|
||||||
from sqlalchemy import *
|
|
||||||
from tables import record_table
|
|
||||||
from gnuviechadmin.exceptions import *
|
from gnuviechadmin.exceptions import *
|
||||||
|
from BackendTo import Record, Domain
|
||||||
|
from domain import DomainEntity
|
||||||
from BackendEntity import *
|
from BackendEntity import *
|
||||||
from BackendEntityHandler import *
|
from BackendEntityHandler import *
|
||||||
|
|
||||||
class Record(object):
|
class RecordEntity(BackendEntity):
|
||||||
"""Entity class for DNS domain records."""
|
"""Entity class for DNS domain records."""
|
||||||
def __init__(self, **kwargs):
|
def create_hook(self, session):
|
||||||
for (key, value) in kwargs.items():
|
domain = session.load(Domain, self.delegateto.domainid)
|
||||||
self.__setattr__(key, value)
|
DomainEntity(domain).update_serial(session)
|
||||||
|
|
||||||
#_shortkeys = ("recordid", "domainid", "name", "type", "content")
|
def delete_hook(self, session):
|
||||||
|
domain = session.load(Domain, self.delegateto.domainid)
|
||||||
#def __init__(self, verbose = False, **kwargs):
|
DomainEntity(domain).update_serial(session)
|
||||||
# BackendEntity.__init__(self, verbose)
|
|
||||||
# self.recordid = None
|
|
||||||
# self.domainid = None
|
|
||||||
# self.name = None
|
|
||||||
# self.type = None
|
|
||||||
# self.content = None
|
|
||||||
# self.ttl = None
|
|
||||||
# self.prio = None
|
|
||||||
# self.change_date = None
|
|
||||||
# self.validate()
|
|
||||||
|
|
||||||
record_mapper = mapper(Record, record_table)
|
|
||||||
|
|
||||||
class RecordHandler(BackendEntityHandler):
|
class RecordHandler(BackendEntityHandler):
|
||||||
"""BackendEntityHandler for Record entities."""
|
"""BackendEntityHandler for Record entities."""
|
||||||
|
|
||||||
def __init__(self, verbose = False):
|
def __init__(self, verbose = False):
|
||||||
BackendEntityHandler.__init__(self, Record, verbose)
|
BackendEntityHandler.__init__(self, RecordEntity, Record, verbose)
|
||||||
|
|
|
@ -19,13 +19,28 @@
|
||||||
#
|
#
|
||||||
# Version: $Id$
|
# Version: $Id$
|
||||||
|
|
||||||
import ConfigParser, os
|
import ConfigParser, os, string, logging.config
|
||||||
|
|
||||||
# global settings which must not be user configurable
|
# global settings which must not be user configurable
|
||||||
required_version = 2
|
required_version = 3
|
||||||
dbschema = 'gva'
|
dbschema = 'gva'
|
||||||
|
|
||||||
# load user configuration
|
# load user configuration
|
||||||
config = ConfigParser.ConfigParser()
|
config = ConfigParser.ConfigParser()
|
||||||
config.readfp(open('gnuviechadmin/defaults.cfg'))
|
config.readfp(open('gnuviechadmin/defaults.cfg'))
|
||||||
config.read(['gnuviechadmin/gva.cfg', os.path.expanduser('~/.gva.cfg')])
|
config.read(['gnuviechadmin/gva.cfg', os.path.expanduser('~/.gva.cfg')])
|
||||||
|
|
||||||
|
def get_template_dir(dirname):
|
||||||
|
"""Returns the template directory for the given directory."""
|
||||||
|
templatepath = config.get('common', 'templatedir')
|
||||||
|
return os.path.join(templatepath, dirname)
|
||||||
|
|
||||||
|
def get_template(dirname, filename):
|
||||||
|
"""Returns the template data from the given template file."""
|
||||||
|
templatefile = file(os.path.join(get_template_dir(dirname),
|
||||||
|
filename))
|
||||||
|
return string.Template(templatefile.read())
|
||||||
|
|
||||||
|
def get_template_string(templatestring):
|
||||||
|
"""Returns a template object for the given template string."""
|
||||||
|
return string.Template(templatestring)
|
||||||
|
|
|
@ -19,120 +19,123 @@
|
||||||
#
|
#
|
||||||
# Version: $Id$
|
# Version: $Id$
|
||||||
|
|
||||||
from sqlalchemy import *
|
|
||||||
from tables import sysuser_table
|
|
||||||
from gnuviechadmin.exceptions import *
|
from gnuviechadmin.exceptions import *
|
||||||
from gnuviechadmin.util import passwordutils, getenttools
|
from gnuviechadmin.util import passwordutils, getenttools
|
||||||
|
from settings import config
|
||||||
|
from BackendTo import *
|
||||||
from BackendEntity import *
|
from BackendEntity import *
|
||||||
from BackendEntityHandler import *
|
from BackendEntityHandler import *
|
||||||
|
|
||||||
class Sysuser(BackendEntity):
|
class SysuserEntity(BackendEntity):
|
||||||
"""Entity class for system users."""
|
"""Entity class for system users."""
|
||||||
|
|
||||||
_shortkeys = ("sysuserid", "clientid", "username", "home", "shell")
|
def __init__(self, delegate, verbose = False, **kwargs):
|
||||||
|
BackendEntity.__init__(self, delegate, verbose)
|
||||||
def __init__(self, verbose = False, **kwargs):
|
|
||||||
BackendEntity.__init__(self, verbose)
|
|
||||||
self.sysuserid = None
|
|
||||||
self.username = None
|
|
||||||
self.usertype = None
|
|
||||||
self.home = None
|
|
||||||
self.shell = None
|
|
||||||
self.clearpass = None
|
|
||||||
self.md5pass = None
|
|
||||||
self.clientid = None
|
|
||||||
self.sysuid = None
|
|
||||||
for (key, value) in kwargs.items():
|
for (key, value) in kwargs.items():
|
||||||
self.__setattr__(key, value)
|
self.__setattr__(key, value)
|
||||||
if not self.username:
|
if not self.delegateto.username:
|
||||||
self.username = self.getnextsysusername()
|
self.delegateto.username = self._get_next_sysusername()
|
||||||
if not self.usertype:
|
if not self.delegateto.usertype:
|
||||||
self.usertype = self.getdefaultsysusertype()
|
self.delegateto.usertype = self._get_default_sysusertype()
|
||||||
if not self.home:
|
if not self.delegateto.home:
|
||||||
self.home = self.gethome(self.username)
|
self.delegateto.home = self._get_home(self.delegateto.username)
|
||||||
if not self.shell:
|
if not self.delegateto.shell:
|
||||||
self.shell = self.getdefaultshell()
|
self.delegateto.shell = self._get_default_shell()
|
||||||
(self.clearpass, self.md5pass) = \
|
(self.delegateto.clearpass, self.delegateto.md5pass) = \
|
||||||
passwordutils.get_pw_tuple(self.clearpass)
|
passwordutils.get_pw_tuple(self.delegateto.clearpass)
|
||||||
if not self.sysuid:
|
if not self.delegateto.sysuid:
|
||||||
self.sysuid = self.getnextsysuid()
|
self.delegateto.sysuid = self._get_next_sysuid()
|
||||||
self.validate()
|
self.validate()
|
||||||
|
|
||||||
def getnextsysusername(self):
|
def _get_next_sysusername(self):
|
||||||
prefix = self.config.get('sysuser', 'nameprefix')
|
prefix = config.get('sysuser', 'nameprefix')
|
||||||
usernames = [user.username for user in \
|
usernames = [user.username for user in \
|
||||||
getenttools.finduserbyprefix(prefix)]
|
getenttools.find_user_by_prefix(prefix)]
|
||||||
maxid = max([int(username[len(prefix):]) for username in usernames])
|
maxid = max([int(username[len(prefix):]) for username in usernames])
|
||||||
for num in range(1, maxid + 1):
|
for num in range(1, maxid + 1):
|
||||||
username = "%s%02d" % (prefix, num)
|
username = "%s%02d" % (prefix, num)
|
||||||
if not username in usernames:
|
if not username in usernames:
|
||||||
return username
|
return username
|
||||||
|
|
||||||
def getdefaultsysusertype(self):
|
def _get_default_sysusertype(self):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def gethome(self, sysusername):
|
def _get_home(self, sysusername):
|
||||||
"""Gets a valid home directory for the given user name."""
|
"""Gets a valid home directory for the given user name."""
|
||||||
return os.path.join(self.config.get('sysuser', 'homedirbase'),
|
return os.path.join(config.get('sysuser', 'homedirbase'),
|
||||||
sysusername)
|
sysusername)
|
||||||
|
|
||||||
def getdefaultshell(self):
|
def _get_default_shell(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def getshellbinary(self):
|
def _get_shell_binary(self):
|
||||||
if self.shell:
|
if self.delegateto.shell:
|
||||||
return self.config.get('sysuser', 'shellyes')
|
return config.get('sysuser', 'shellyes')
|
||||||
return self.config.get('sysuser', 'shellno')
|
return config.get('sysuser', 'shellno')
|
||||||
|
|
||||||
def getnextsysuid(self):
|
def _get_next_sysuid(self):
|
||||||
uid = int(self.config.get('sysuser', 'minuid'))
|
uid = int(config.get('sysuser', 'minuid'))
|
||||||
muid = getenttools.getmaxuid(int(self.config.get('sysuser',
|
muid = getenttools.get_max_uid(int(config.get('sysuser', 'maxuid')))
|
||||||
'maxuid')))
|
|
||||||
if muid >= uid:
|
if muid >= uid:
|
||||||
uid = muid + 1
|
uid = muid + 1
|
||||||
return uid
|
return uid
|
||||||
|
|
||||||
def populate_home(self):
|
def _populate_home(self):
|
||||||
templatedir = self.config.get('sysuser', 'hometemplate')
|
templatedir = get_template_dir(config.get('sysuser', 'hometemplate'))
|
||||||
cmdline = 'install -d --owner="%(username)s" --group="%(group)s" "%(home)s"' % {
|
|
||||||
'username' : self.username,
|
|
||||||
'group' : self.config.get('sysuser', 'defaultgroup'),
|
|
||||||
'home' : self.home }
|
|
||||||
self.sucommand(cmdline)
|
|
||||||
cmdline = 'cp -R "%(template)s" "%(home)s"' % {
|
cmdline = 'cp -R "%(template)s" "%(home)s"' % {
|
||||||
'template' : templatedir,
|
'template' : templatedir,
|
||||||
'home' : self.home }
|
'home' : self.delegateto.home }
|
||||||
self.sucommand(cmdline)
|
self.sucommand(cmdline)
|
||||||
cmdline = 'chown -R "%(username)s":"%(group)s" %(home)s' % {
|
cmdline = 'chown -R "%(username)s":"%(group)s" %(home)s' % {
|
||||||
'username' : self.username,
|
'username' : self.delegateto.username,
|
||||||
'group' : self.config.get('sysuser', 'defaultgroup'),
|
'group' : config.get('sysuser', 'defaultgroup'),
|
||||||
'home' : self.home }
|
'home' : self.delegateto.home }
|
||||||
self.sucommand(cmdline)
|
self.sucommand(cmdline)
|
||||||
|
|
||||||
def create_hook(self):
|
def _mail_sysuser(self):
|
||||||
gecos = self.config.get('sysuser', 'gecos')
|
template = get_template(config.get('common', 'mailtemplates'),
|
||||||
gecos = gecos % (self.username)
|
config.get('sysuser', 'create.mail'))
|
||||||
|
text = template.substitute({
|
||||||
|
'uid' : self.delegateto.sysuid,
|
||||||
|
'firstname' : self.delegateto.client.firstname,
|
||||||
|
'lastname' : self.delegateto.client.lastname,
|
||||||
|
'email' : self.delegateto.client.email,
|
||||||
|
'username' : self.delegateto.username,
|
||||||
|
'password' : self.delegateto.clearpass,
|
||||||
|
'home' : self.delegateto.home,
|
||||||
|
'shell' : self._get_shell_binary()})
|
||||||
|
template = get_template_string(config.get('sysuser', 'create_subject'))
|
||||||
|
subject = template.substitute({
|
||||||
|
'username' : self.delegateto.username})
|
||||||
|
self.send_mail(subject, text)
|
||||||
|
|
||||||
|
def create_hook(self, session):
|
||||||
|
gecos = config.get('sysuser', 'gecos') % (self.delegateto.username)
|
||||||
cmdline = 'adduser --home "%(home)s" --shell "%(shell)s" --no-create-home --uid %(sysuid)d --ingroup "%(group)s" --disabled-password --gecos "%(gecos)s" %(username)s' % {
|
cmdline = 'adduser --home "%(home)s" --shell "%(shell)s" --no-create-home --uid %(sysuid)d --ingroup "%(group)s" --disabled-password --gecos "%(gecos)s" %(username)s' % {
|
||||||
'home' : self.home,
|
'home' : self.delegateto.home,
|
||||||
'shell' : self.getshellbinary(),
|
'shell' : self._get_shell_binary(),
|
||||||
'sysuid' : self.sysuid,
|
'sysuid' : self.delegateto.sysuid,
|
||||||
'group' : self.config.get('sysuser', 'defaultgroup'),
|
'group' : config.get('sysuser', 'defaultgroup'),
|
||||||
'gecos' : gecos,
|
'gecos' : gecos,
|
||||||
'username' : self.username}
|
'username' : self.delegateto.username}
|
||||||
self.sucommand(cmdline)
|
self.sucommand(cmdline)
|
||||||
cmdline = 'chpasswd --encrypted'
|
cmdline = 'chpasswd --encrypted'
|
||||||
inline = '%(username)s:%(md5pass)s' % {
|
inline = '%(username)s:%(md5pass)s' % {
|
||||||
'username' : self.username,
|
'username' : self.delegateto.username,
|
||||||
'md5pass' : self.md5pass}
|
'md5pass' : self.delegateto.md5pass}
|
||||||
self.sucommand(cmdline, inline)
|
self.sucommand(cmdline, inline)
|
||||||
self.populate_home()
|
self._populate_home()
|
||||||
|
self._mail_sysuser()
|
||||||
|
|
||||||
def delete_hook(self):
|
def delete_hook(self, session):
|
||||||
backupdir = os.path.join(self.config.get('common',
|
if self.delegateto.domains:
|
||||||
'backupdir'),
|
raise CannotDeleteError(
|
||||||
self.config.get('sysuser',
|
self.delegateto,
|
||||||
'homebackupdir'))
|
"it still has the following domains assigned: %s" % (
|
||||||
|
", ".join([domain.name for domain in \
|
||||||
|
self.delegateto.domains])))
|
||||||
|
backupdir = os.path.join(config.get('common', 'backupdir'),
|
||||||
|
config.get('sysuser', 'homebackupdir'))
|
||||||
if not os.path.isdir(backupdir):
|
if not os.path.isdir(backupdir):
|
||||||
cmdline = 'mkdir -p "%(backupdir)s"' % {
|
cmdline = 'mkdir -p "%(backupdir)s"' % {
|
||||||
'backupdir' : backupdir}
|
'backupdir' : backupdir}
|
||||||
|
@ -141,13 +144,11 @@ class Sysuser(BackendEntity):
|
||||||
raise Exception("could not create backup directory")
|
raise Exception("could not create backup directory")
|
||||||
cmdline = 'deluser --remove-home --backup --backup-to "%(backupdir)s" %(username)s' % {
|
cmdline = 'deluser --remove-home --backup --backup-to "%(backupdir)s" %(username)s' % {
|
||||||
'backupdir' : backupdir,
|
'backupdir' : backupdir,
|
||||||
'username' : self.username}
|
'username' : self.delegateto.username}
|
||||||
self.sucommand(cmdline)
|
self.sucommand(cmdline)
|
||||||
|
|
||||||
sysusermapper = mapper(Sysuser, sysuser_table)
|
|
||||||
|
|
||||||
class SysuserHandler(BackendEntityHandler):
|
class SysuserHandler(BackendEntityHandler):
|
||||||
"""BackendEntityHandler for Sysuser entities."""
|
"""BackendEntityHandler for Sysuser entities."""
|
||||||
|
|
||||||
def __init__(self, verbose = False):
|
def __init__(self, verbose = False):
|
||||||
BackendEntityHandler.__init__(self, Sysuser, verbose)
|
BackendEntityHandler.__init__(self, SysuserEntity, Sysuser, verbose)
|
||||||
|
|
|
@ -24,82 +24,49 @@ import sys
|
||||||
import migrate.versioning.api
|
import migrate.versioning.api
|
||||||
from settings import *
|
from settings import *
|
||||||
|
|
||||||
dbversion = migrate.versioning.api.db_version(
|
try:
|
||||||
config.get('database', 'uri'),
|
dbversion = migrate.versioning.api.db_version(
|
||||||
config.get('database', 'repository'))
|
config.get('database', 'uri'),
|
||||||
if dbversion < required_version:
|
config.get('database', 'repository'))
|
||||||
print("""Database version is %d but required version is %d, run
|
if dbversion < required_version:
|
||||||
|
print("""Database version is %d but required version is %d, run
|
||||||
|
|
||||||
migrate upgrade %s %s
|
migrate upgrade %s %s
|
||||||
|
|
||||||
to fix this.""" %
|
to fix this.""" %
|
||||||
(dbversion, required_version, config.get('database', 'uri'),
|
(dbversion, required_version, config.get('database', 'uri'),
|
||||||
config.get('database', 'repository')))
|
config.get('database', 'repository')))
|
||||||
|
sys.exit(1)
|
||||||
|
except exceptions.NoSuchTableError, nste:
|
||||||
|
print nste
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
meta = BoundMetaData(config.get('database', 'uri'))
|
meta = BoundMetaData(config.get('database', 'uri'))
|
||||||
|
#meta.engine.echo = True
|
||||||
client_table = Table(
|
client_table = Table(
|
||||||
'client', meta,
|
'client', meta, schema = dbschema, autoload = True)
|
||||||
Column('clientid', Integer, primary_key=True),
|
|
||||||
Column('title', String(10)),
|
|
||||||
Column('firstname', String(64), nullable=False),
|
|
||||||
Column('lastname', String(64), nullable=False),
|
|
||||||
Column('address1', String(64), nullable=False),
|
|
||||||
Column('address2', String(64)),
|
|
||||||
Column('zip', String(7), nullable=False),
|
|
||||||
Column('city', String(64), nullable=False),
|
|
||||||
Column('country', String(5), nullable=False),
|
|
||||||
Column('phone', String(32), nullable=False),
|
|
||||||
Column('mobile', String(32)),
|
|
||||||
Column('fax', String(32)),
|
|
||||||
Column('email', String(64), unique=True, nullable=False),
|
|
||||||
schema = dbschema
|
|
||||||
)
|
|
||||||
sysuser_table = Table(
|
sysuser_table = Table(
|
||||||
'sysuser', meta,
|
'sysuser', meta, ForeignKeyConstraint(['clientid'], ['client.clientid']),
|
||||||
Column('sysuserid', Integer, primary_key=True),
|
schema = dbschema, autoload = True)
|
||||||
Column('username', String(12), nullable=False, unique=True),
|
|
||||||
Column('usertype', Integer, nullable=False, default=0, index=True),
|
|
||||||
Column('home', String(128)),
|
|
||||||
Column('shell', Boolean, nullable=False, default=False),
|
|
||||||
Column('clearpass', String(64)),
|
|
||||||
Column('md5pass', String(34)),
|
|
||||||
Column('clientid', Integer, ForeignKey("client.clientid"),
|
|
||||||
nullable=False),
|
|
||||||
Column('sysuid', Integer, nullable=False, unique=True),
|
|
||||||
Column('lastchange', DateTime, default=func.now()),
|
|
||||||
schema = dbschema
|
|
||||||
)
|
|
||||||
domain_table = Table(
|
domain_table = Table(
|
||||||
'domain', meta,
|
'domain', meta, ForeignKeyConstraint(['sysuserid'], ['sysuser.sysuserid']),
|
||||||
Column('domainid', Integer, primary_key=True),
|
schema = dbschema, autoload = True)
|
||||||
Column('name', String(255), nullable=False, unique=True),
|
|
||||||
Column('master', String(20)),
|
|
||||||
Column('last_check', Integer),
|
|
||||||
Column('type', String(6), nullable=False),
|
|
||||||
Column('notified_serial', Integer),
|
|
||||||
Column('sysuserid', Integer, ForeignKey("sysuser.sysuserid"),
|
|
||||||
nullable=False),
|
|
||||||
schema = dbschema
|
|
||||||
)
|
|
||||||
record_table = Table(
|
record_table = Table(
|
||||||
'record', meta,
|
'record', meta, ForeignKeyConstraint(['domainid'], ['domain.domainid']),
|
||||||
Column('recordid', Integer, primary_key=True),
|
schema = dbschema, autoload = True)
|
||||||
Column('domainid', Integer, ForeignKey("domain.domainid"),
|
|
||||||
nullable=False),
|
|
||||||
Column('name', String(255)),
|
|
||||||
Column('type', String(6)),
|
|
||||||
Column('content', String(255)),
|
|
||||||
Column('ttl', Integer),
|
|
||||||
Column('prio', Integer),
|
|
||||||
Column('change_date', Integer),
|
|
||||||
schema = dbschema
|
|
||||||
)
|
|
||||||
supermaster_table = Table(
|
supermaster_table = Table(
|
||||||
'supermaster', meta,
|
'supermaster', meta,
|
||||||
Column('ip', String(25), nullable=False),
|
ForeignKeyConstraint(['account'], ['sysuser.sysuserid']),
|
||||||
Column('nameserver', String(255), nullable=False),
|
schema = dbschema, autoload = True)
|
||||||
Column('account', Integer, ForeignKey("sysuser.sysuserid"),
|
mailaccount_table = Table(
|
||||||
nullable=False),
|
'mailaccount', meta,
|
||||||
schema = dbschema
|
ForeignKeyConstraint(['domainid'], ['domain.domainid']),
|
||||||
)
|
schema = dbschema, autoload = True)
|
||||||
|
mailaddress_table = Table(
|
||||||
|
'mailaddress', meta,
|
||||||
|
ForeignKeyConstraint(['domainid'], ['domain.domainid']),
|
||||||
|
schema = dbschema, autoload = True)
|
||||||
|
mailtarget_table = Table(
|
||||||
|
'mailtarget', meta,
|
||||||
|
ForeignKeyConstraint(['mailaddressid'], ['mailaddress.mailaddressid']),
|
||||||
|
schema = dbschema, autoload = True)
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#
|
#
|
||||||
# Version: $Id$
|
# Version: $Id$
|
||||||
|
|
||||||
import getopt, sys
|
import getopt, sys, logging
|
||||||
from gnuviechadmin.exceptions import GnuviechadminError
|
from gnuviechadmin.exceptions import GnuviechadminError
|
||||||
|
|
||||||
class CliCommand:
|
class CliCommand:
|
||||||
|
@ -198,6 +198,8 @@ Common options:
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
"""This initializes the command with the given command line
|
"""This initializes the command with the given command line
|
||||||
arguments and executes it."""
|
arguments and executes it."""
|
||||||
|
self.logger = logging.getLogger("%s.%s" % (
|
||||||
|
self.__class__.__module__, self.__class__.__name__))
|
||||||
self._data = {}
|
self._data = {}
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
if args[0] in self._subcommands():
|
if args[0] in self._subcommands():
|
||||||
|
|
|
@ -59,6 +59,8 @@ class ClientCli(CliCommand.CliCommand):
|
||||||
"the client id", True)])}
|
"the client id", True)])}
|
||||||
|
|
||||||
def _execute(self, subcommand):
|
def _execute(self, subcommand):
|
||||||
|
self.logger.debug("execute %s with data %s", subcommand,
|
||||||
|
str(self._data))
|
||||||
from gnuviechadmin.backend import client
|
from gnuviechadmin.backend import client
|
||||||
from gnuviechadmin import exceptions
|
from gnuviechadmin import exceptions
|
||||||
if subcommand == "create":
|
if subcommand == "create":
|
||||||
|
|
|
@ -43,6 +43,8 @@ class DomainCli(CliCommand.CliCommand):
|
||||||
"the domain id", True)])}
|
"the domain id", True)])}
|
||||||
|
|
||||||
def _execute(self, subcommand):
|
def _execute(self, subcommand):
|
||||||
|
self.logger.debug("execute %s with data %s", subcommand,
|
||||||
|
str(self._data))
|
||||||
from gnuviechadmin.backend.domain import DomainHandler
|
from gnuviechadmin.backend.domain import DomainHandler
|
||||||
from gnuviechadmin import exceptions
|
from gnuviechadmin import exceptions
|
||||||
if subcommand == "create":
|
if subcommand == "create":
|
||||||
|
|
|
@ -37,16 +37,19 @@ class RecordCli(CliCommand.CliCommand):
|
||||||
(["-p", "--prio"], "prio",
|
(["-p", "--prio"], "prio",
|
||||||
"MX record priority", False),
|
"MX record priority", False),
|
||||||
(["--ttl"], "ttl",
|
(["--ttl"], "ttl",
|
||||||
"Time to live", False),
|
"time to live", False),
|
||||||
(["-d", "--domainid"], "domainid",
|
(["-d", "--domainid"], "domainid",
|
||||||
"Domain id", True)]),
|
"domain id", True)]),
|
||||||
'list' : ("lists existing records",
|
'list' : ("lists existing records",
|
||||||
[]),
|
[(["-d", "--domainid"], "domainid",
|
||||||
|
"domain id", False)]),
|
||||||
'delete' : ("delete a record",
|
'delete' : ("delete a record",
|
||||||
[(["-r", "--recordid"], "recordid",
|
[(["-r", "--recordid"], "recordid",
|
||||||
"the record id", True)])}
|
"the record id", True)])}
|
||||||
|
|
||||||
def _execute(self, subcommand):
|
def _execute(self, subcommand):
|
||||||
|
self.logger.debug("execute %s with data %s", subcommand,
|
||||||
|
str(self._data))
|
||||||
from gnuviechadmin.backend.record import RecordHandler
|
from gnuviechadmin.backend.record import RecordHandler
|
||||||
from gnuviechadmin import exceptions
|
from gnuviechadmin import exceptions
|
||||||
if subcommand == "create":
|
if subcommand == "create":
|
||||||
|
@ -60,7 +63,7 @@ class RecordCli(CliCommand.CliCommand):
|
||||||
print cfe
|
print cfe
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
elif subcommand == "list":
|
elif subcommand == "list":
|
||||||
records = RecordHandler(self._verbose).fetchall()
|
records = RecordHandler(self._verbose).fetchall(**self._data)
|
||||||
for record in records:
|
for record in records:
|
||||||
print record
|
print record
|
||||||
elif subcommand == "delete":
|
elif subcommand == "delete":
|
||||||
|
|
|
@ -47,6 +47,8 @@ class SysuserCli(CliCommand.CliCommand):
|
||||||
"the system user id", True)])}
|
"the system user id", True)])}
|
||||||
|
|
||||||
def _execute(self, subcommand):
|
def _execute(self, subcommand):
|
||||||
|
self.logger.debug("execute %s with data %s", subcommand,
|
||||||
|
str(self._data))
|
||||||
from gnuviechadmin.backend import sysuser
|
from gnuviechadmin.backend import sysuser
|
||||||
from gnuviechadmin import exceptions
|
from gnuviechadmin import exceptions
|
||||||
if subcommand == "create":
|
if subcommand == "create":
|
||||||
|
|
|
@ -35,6 +35,13 @@ repository = /etc/gnuviechadmin/dbrepo
|
||||||
[common]
|
[common]
|
||||||
suwrapper = sudo
|
suwrapper = sudo
|
||||||
backupdir = /var/backups/gnuviechadmin
|
backupdir = /var/backups/gnuviechadmin
|
||||||
|
templatedir = /etc/gnuviechadmin/templates
|
||||||
|
mailtemplates = mails
|
||||||
|
log.cfg = /etc/gnuviechadmin/logging.cfg
|
||||||
|
|
||||||
|
[client]
|
||||||
|
create.mail = create_client.txt
|
||||||
|
create_subject = A new client ${firstname} ${lastname} has been created.
|
||||||
|
|
||||||
[sysuser]
|
[sysuser]
|
||||||
nameprefix = usr
|
nameprefix = usr
|
||||||
|
@ -46,4 +53,27 @@ shellno = /usr/bin/scponly
|
||||||
defaultgroup = wwwusers
|
defaultgroup = wwwusers
|
||||||
gecos = Webuser %s
|
gecos = Webuser %s
|
||||||
homebackupdir = homes
|
homebackupdir = homes
|
||||||
hometemplate = /etc/gnuviechadmin/templates/home
|
hometemplate = home
|
||||||
|
create.mail = create_sysuser.txt
|
||||||
|
create_subject = A new system user ${username} has been created.
|
||||||
|
|
||||||
|
[domain]
|
||||||
|
defaultmxprio = 5
|
||||||
|
defaultrefresh = 86400
|
||||||
|
defaultretry = 7200
|
||||||
|
defaultexpire = 1209600
|
||||||
|
defaultminimumttl = 86400
|
||||||
|
defaultttl = 86400
|
||||||
|
htdir = html
|
||||||
|
logpath = /var/log/apache2
|
||||||
|
statspath = /home/stats
|
||||||
|
htdocstemplate = domain
|
||||||
|
conftemplates = domainconf
|
||||||
|
apachetemplate = vhost.conf
|
||||||
|
statshtaccesstemplate = htaccess-stats
|
||||||
|
modlogantemplate = modlogan.conf
|
||||||
|
modlogandir = /var/lib/gnuviechadmin/stats
|
||||||
|
sitesdir = /etc/apache2/sites-available
|
||||||
|
authdir = /etc/apache2/authdata
|
||||||
|
create.mail = create_domain.txt
|
||||||
|
create_subject = A new domain ${domain} has been created.
|
||||||
|
|
|
@ -73,3 +73,16 @@ class ValidationFailedError(GnuviechadminError):
|
||||||
if self.cause:
|
if self.cause:
|
||||||
msg += " The reason is %s." % (str(self.cause))
|
msg += " The reason is %s." % (str(self.cause))
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
class CannotDeleteError(GnuviechadminError):
|
||||||
|
"""This exception should be raised if an entity cannot be deleted
|
||||||
|
because of some unmatched precondition."""
|
||||||
|
def __init__(self, instance, cause = None):
|
||||||
|
self.instance = instance
|
||||||
|
self.cause = cause
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
msg = "Cannot delete %s." % (str(self.instance))
|
||||||
|
if self.cause:
|
||||||
|
msg += " The reason is %s." % (str(self.cause))
|
||||||
|
return msg
|
||||||
|
|
34
gnuviechadmin/logging.cfg
Normal file
34
gnuviechadmin/logging.cfg
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[formatters]
|
||||||
|
keys=simple
|
||||||
|
|
||||||
|
[formatter_simple]
|
||||||
|
format=%(asctime)s %(levelname)s %(name)s: %(message)s
|
||||||
|
datefmt=
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys=handler01,handler02
|
||||||
|
|
||||||
|
[handler_handler01]
|
||||||
|
class=handlers.RotatingFileHandler
|
||||||
|
level=DEBUG
|
||||||
|
formatter=simple
|
||||||
|
args=('gnuviechadmin.log', 'a', 10485760, 5)
|
||||||
|
|
||||||
|
[handler_handler02]
|
||||||
|
class=StreamHandler
|
||||||
|
level=NOTSET
|
||||||
|
formatter=simple
|
||||||
|
args=(sys.stdout,)
|
||||||
|
|
||||||
|
[loggers]
|
||||||
|
keys=root,gnuviechadmin
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level=NOTSET
|
||||||
|
handlers=handler02
|
||||||
|
|
||||||
|
[logger_gnuviechadmin]
|
||||||
|
level=DEBUG
|
||||||
|
handlers=handler01
|
||||||
|
propagate=0
|
||||||
|
qualname=gnuviechadmin
|
|
@ -19,7 +19,7 @@
|
||||||
#
|
#
|
||||||
# Version: $Id$
|
# Version: $Id$
|
||||||
|
|
||||||
import os, popen2
|
import pwd, grp
|
||||||
|
|
||||||
class PasswdUser(object):
|
class PasswdUser(object):
|
||||||
"""This class represents users in the user database."""
|
"""This class represents users in the user database."""
|
||||||
|
@ -45,7 +45,7 @@ class PasswdGroup(object):
|
||||||
def __init__(self, groupname, pw, gid, members):
|
def __init__(self, groupname, pw, gid, members):
|
||||||
self.groupname = groupname
|
self.groupname = groupname
|
||||||
self.gid = int(gid)
|
self.gid = int(gid)
|
||||||
self.members = members.split(",")
|
self.members = members
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%s:%d:%s)" % (self.__class__.__name__,
|
return "%s(%s:%d:%s)" % (self.__class__.__name__,
|
||||||
|
@ -53,42 +53,41 @@ class PasswdGroup(object):
|
||||||
self.gid,
|
self.gid,
|
||||||
",".join(self.members))
|
",".join(self.members))
|
||||||
|
|
||||||
def parsegroups():
|
def parse_groups():
|
||||||
(stdout, stdin) = popen2.popen2("getent group")
|
return [PasswdGroup(*arr) for arr in grp.getgrall()]
|
||||||
return [PasswdGroup(*arr) for arr in [line.strip().split(":") for line in stdout]]
|
|
||||||
|
|
||||||
def parseusers():
|
def parse_users():
|
||||||
(stdout, stdin) = popen2.popen2("getent passwd")
|
return [PasswdUser(*arr) for arr in pwd.getpwall()]
|
||||||
return [PasswdUser(*arr) for arr in [line.strip().split(":") for line in stdout]]
|
|
||||||
|
|
||||||
def finduserbyprefix(prefix):
|
def find_user_by_prefix(prefix):
|
||||||
"""Finds all user entries with the given prefix."""
|
"""Finds all user entries with the given prefix."""
|
||||||
return [user for user in parseusers() if user.username.startswith(prefix)]
|
return [user for user in parse_users() if user.username.startswith(prefix)]
|
||||||
|
|
||||||
def getuserbyid(uid):
|
def get_user_by_id(uid):
|
||||||
"""Gets the user with the given user id."""
|
"""Gets the user with the given user id."""
|
||||||
users = [user for user in parseusers() if user.uid == uid]
|
users = [user for user in parse_users() if user.uid == uid]
|
||||||
if users:
|
if users:
|
||||||
return users[0]
|
return users[0]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getgroupbyid(gid):
|
def get_group_by_id(gid):
|
||||||
"""Gets the group with the given group id."""
|
"""Gets the group with the given group id."""
|
||||||
groups = [group for group in parsegroups() if group.gid == gid]
|
groups = [group for group in parse_groups() if group.gid == gid]
|
||||||
if groups:
|
if groups:
|
||||||
return groups[0]
|
return groups[0]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getmaxuid(boundary = 65536):
|
def get_max_uid(boundary = 65536):
|
||||||
"""Gets the highest uid value."""
|
"""Gets the highest uid value."""
|
||||||
return max([user.uid for user in parseusers() if user.uid <= boundary])
|
return max([user.uid for user in parse_users() if user.uid <= boundary])
|
||||||
|
|
||||||
def getmaxgid(boundary = 65536):
|
def get_max_gid(boundary = 65536):
|
||||||
"""Gets the highest gid value."""
|
"""Gets the highest gid value."""
|
||||||
return max([group.gid for group in parsegroups() if group.gid <= boundary])
|
return max([group.gid for group in parse_groups() \
|
||||||
|
if group.gid <= boundary])
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print "Max UID is %d" % (getmaxuid(40000))
|
print "Max UID is %d" % (get_max_uid(40000))
|
||||||
print "Max GID is %d" % (getmaxgid(40000))
|
print "Max GID is %d" % (get_max_gid(40000))
|
||||||
print "User with max UID is %s" % (getuserbyid(getmaxuid(40000)))
|
print "User with max UID is %s" % (get_user_by_id(get_max_uid(40000)))
|
||||||
print "Group with max GID is %s" % (getgroupbyid(getmaxgid(40000)))
|
print "Group with max GID is %s" % (get_group_by_id(get_max_gid(40000)))
|
||||||
|
|
|
@ -21,16 +21,17 @@
|
||||||
|
|
||||||
import crypt, crack, random
|
import crypt, crack, random
|
||||||
|
|
||||||
|
_pwchars = []
|
||||||
|
for pair in (('0', '9'), ('A', 'Z'), ('a', 'z')):
|
||||||
|
_pwchars.extend(range(ord(pair[0]), ord(pair[1])))
|
||||||
|
for char in "-+/*_@":
|
||||||
|
_pwchars.append(ord(char))
|
||||||
|
|
||||||
def generatepassword(minlength = 8, maxlength = 12):
|
def generatepassword(minlength = 8, maxlength = 12):
|
||||||
"""Generates a random password with a length between the given
|
"""Generates a random password with a length between the given
|
||||||
minlength and maxlength values."""
|
minlength and maxlength values."""
|
||||||
pwchars = []
|
|
||||||
for pair in (('0', '9'), ('A', 'Z'), ('a', 'z')):
|
|
||||||
pwchars.extend(range(ord(pair[0]), ord(pair[1])))
|
|
||||||
for char in "-+/*_@":
|
|
||||||
pwchars.append(ord(char))
|
|
||||||
return "".join([chr(letter) for letter in \
|
return "".join([chr(letter) for letter in \
|
||||||
random.sample(pwchars,
|
random.sample(_pwchars,
|
||||||
random.randint(minlength, maxlength))])
|
random.randint(minlength, maxlength))])
|
||||||
|
|
||||||
def checkpassword(password):
|
def checkpassword(password):
|
||||||
|
@ -45,7 +46,7 @@ def checkpassword(password):
|
||||||
def md5_crypt_password(password):
|
def md5_crypt_password(password):
|
||||||
"""Hashes the given password with MD5 and a random salt value."""
|
"""Hashes the given password with MD5 and a random salt value."""
|
||||||
salt = "".join([chr(letter) for letter in \
|
salt = "".join([chr(letter) for letter in \
|
||||||
random.sample(range(ord('a'), ord('z')), 8)])
|
random.sample(_pwchars, 8)])
|
||||||
return crypt.crypt(password, '$1$' + salt)
|
return crypt.crypt(password, '$1$' + salt)
|
||||||
|
|
||||||
def get_pw_tuple(password = None):
|
def get_pw_tuple(password = None):
|
||||||
|
|
Loading…
Reference in a new issue