diff --git a/bin/createclient b/bin/createclient
deleted file mode 100755
index 807e40b..0000000
--- a/bin/createclient
+++ /dev/null
@@ -1,115 +0,0 @@
-#!/usr/bin/python
-# -*- 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$
-
-import getopt, sys
-from gnuviechadmin import client, exceptions
-
-def usage():
- print """Usage: %s [-h|--help] [-v|--verbose]
- [-t
|--title=]
- -f |--firstname= -l |--lastname=
- -a |--address= [--address2=]
- -z |--zip= -c |--city= [--country=]
- [-o |--organisation=]
- -e |--email= -p |--phone=
- [-m |--mobile=] [-x |--fax=]
-
-General options:
- -h, --help show this usage message and exit
- -v, --verbose verbose operation
-
-Mandatory client data options:
- -f, --firstname firstname
- -l, --lastname lastname
- -a, --address street address
- -z, --zip zip or postal code
- -c, --city city or location
- -e, --email contact email address
- -p, --phone telephone number
-
-Optional client data options:
- --address2 optional second line of the street address
- -o, --organisation option organisation
- --country country (defaults to de)
- -t, --title optional title
- -m, --mobile optional mobile number
- -x, --fax optional fax number
-""" % (sys.argv[0])
-
-def main():
- try:
- opts, args = getopt.gnu_getopt(sys.argv[1:],
- "hvf:l:a:z:c:e:p:o:t:m:x:",
- ["help", "verbose", "firstname=",
- "lastname=", "address=", "zip=",
- "city=", "email=", "phone=",
- "address2=", "organisation=",
- "country=", "title=", "mobile=",
- "fax="])
- except getopt.GetoptError:
- usage()
- sys.exit(2)
- clientdata = {}
- verbose = False
- for o, a in opts:
- if o in ("-v", "--verbose"):
- verbose = True
- if o in ("-h", "--help"):
- usage()
- sys.exit()
- if o in ("-f", "--firstname"):
- clientdata["firstname"] = a
- if o in ("-l", "--lastname"):
- clientdata["lastname"] = a
- if o in ("-a", "--address"):
- clientdata["address1"] = a
- if o in ("-z", "--zip"):
- clientdata["zip"] = a
- if o in ("-c", "--city"):
- clientdata["city"] = a
- if o == "--country":
- clientdata["country"] = a
- if o in ("-t", "--title"):
- clientdata["title"] = a
- if o in ("-m", "--mobile"):
- clientdata["mobile"] = a
- if o in ("-e", "--email"):
- clientdata["email"] = a
- if o in ("-o", "--organisation"):
- clientdata["organisation"] = a
- if o in ("-x", "--fax"):
- clientdata["fax"] = a
- if o in ("-p", "--phone"):
- clientdata["phone"] = a
- if verbose:
- print "parsed client data is ", clientdata
- try:
- myclient = client.create(**clientdata)
- except exceptions.CreationFailedError, cfe:
- usage()
- print cfe
- sys.exit(2)
- if verbose:
- print myclient
-
-if __name__ == "__main__":
- main()
diff --git a/bin/gva b/bin/gva
index b80a95f..542d727 100755
--- a/bin/gva
+++ b/bin/gva
@@ -24,14 +24,16 @@ import gnuviechadmin.cli.client
import gnuviechadmin.cli.sysuser
import sys
+commands = [gnuviechadmin.cli.client.ClientCli,
+ gnuviechadmin.cli.sysuser.SysuserCli]
+
def usage():
print """%s [commandargs]
where command is one of
-
- client - for creating clients
- sysuser - for creating system users
""" % sys.argv[0]
+ for command in commands:
+ print "%10s - %s" % (command.name, command.description)
def main():
if (sys.argv.__len__() < 2):
@@ -39,12 +41,12 @@ def main():
sys.exit()
command = sys.argv[1]
commargs = sys.argv[2:]
- if command == "client":
- gnuviechadmin.cli.client.ClientCli(commargs)
- elif command == "sysuser":
- gnuviechadmin.cli.sysuser.SysuserCli(commargs)
+ if command in [cmd.name for cmd in commands]:
+ for cmd in commands:
+ if cmd.name == command:
+ cmd(commargs)
else:
- usage()
+ usage()
if __name__ == '__main__':
main()
diff --git a/gnuviechadmin/backend/BackendEntity.py b/gnuviechadmin/backend/BackendEntity.py
new file mode 100644
index 0000000..24fe6af
--- /dev/null
+++ b/gnuviechadmin/backend/BackendEntity.py
@@ -0,0 +1,83 @@
+# -*- 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$
+
+import ConfigParser, os
+from subprocess import *
+from sqlalchemy import *
+from gnuviechadmin.exceptions import *
+
+class BackendEntity(object):
+ """This is the abstract base class for all backend entity classes."""
+
+ def __init__(self, verbose = False):
+ 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):
+ if 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):
+ """Executes a command as root using the configured suwrapper
+ command. If a pipe is specified it is used as stdin of the
+ subprocess."""
+ suwrapper = self.config.get('common', 'suwrapper')
+ toexec = "%s %s" % (suwrapper, cmdline)
+ if pipedata:
+ p = Popen(toexec, shell = True, stdin=PIPE)
+ pipe = p.stdin
+ print >>pipe, pipedata
+ pipe.close()
+ sts = os.waitpid(p.pid, 0)
+ if self.verbose:
+ print "%s|%s: %d" % (pipedata, toexec, sts[1])
+ else:
+ p = Popen(toexec, shell = True)
+ sts = os.waitpid(p.pid, 0)
+ if self.verbose:
+ print "%s: %s" % (toexec, sts[1])
+ return sts[1]
+
+ def validate(self):
+ """Validates whether all mandatory fields of the entity have
+ values."""
+ missingfields = []
+ for key in [col.name for col in \
+ object_mapper(self).local_table.columns \
+ if not col.primary_key and not col.nullable]:
+ if self.__getattribute__(key) is None:
+ missingfields.append(key)
+ if missingfields:
+ raise MissingFieldsError(missingfields)
diff --git a/gnuviechadmin/backend/BackendEntityHandler.py b/gnuviechadmin/backend/BackendEntityHandler.py
new file mode 100644
index 0000000..4238766
--- /dev/null
+++ b/gnuviechadmin/backend/BackendEntityHandler.py
@@ -0,0 +1,69 @@
+# -*- 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 *
+from gnuviechadmin.exceptions import *
+from BackendEntity import *
+
+class BackendEntityHandler(object):
+ def __init__(self, entityclass, verbose = False):
+ self.entityclass = entityclass
+ self.verbose = verbose
+
+ def create(self, **kwargs):
+ try:
+ entity = self.entityclass(self.verbose, **kwargs)
+ sess = create_session()
+ sess.save(entity)
+ sess.flush()
+ try:
+ entity.create_hook()
+ except:
+ sess.delete(entity)
+ sess.flush()
+ raise
+ except Exception, e:
+ raise CreationFailedError(self.entityclass.__name__, e)
+
+ def fetchall(self):
+ """Fetches all entities of the managed entity type."""
+ session = create_session()
+ query = session.query(self.entityclass)
+ allentities = query.select()
+ for entity in allentities:
+ BackendEntity.__init__(entity, self.verbose)
+ return allentities
+
+ def delete(self, pkvalue):
+ """Deletes the entity of the managed entity type that has the
+ specified primary key value."""
+ try:
+ sess = create_session()
+ entity = sess.query(self.entityclass).get(pkvalue)
+ if entity:
+ BackendEntity.__init__(entity, self.verbose)
+ if self.verbose:
+ print "delete %s" % (str(entity))
+ entity.delete_hook()
+ sess.delete(entity)
+ sess.flush()
+ except Exception, e:
+ raise DeleteFailedError(self.entityclass.__name__, e)
diff --git a/bin/listclients b/gnuviechadmin/backend/__init__.py
old mode 100755
new mode 100644
similarity index 83%
rename from bin/listclients
rename to gnuviechadmin/backend/__init__.py
index c9c2998..9c370e3
--- a/bin/listclients
+++ b/gnuviechadmin/backend/__init__.py
@@ -1,4 +1,3 @@
-#!/usr/bin/python
# -*- coding: UTF-8 -*-
#
# Copyright (C) 2007 by Jan Dittberner.
@@ -17,12 +16,10 @@
# 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 gnuviechadmin import client
+"""This is the gnuviechadmin.backend package.
-def main():
- for row in client.fetchall():
- print row
-
-if __name__ == "__main__":
- main()
+The package provides the backend entities and supporting code for
+gnuviechadmin."""
diff --git a/gnuviechadmin/backend/client.py b/gnuviechadmin/backend/client.py
new file mode 100644
index 0000000..2aa07a0
--- /dev/null
+++ b/gnuviechadmin/backend/client.py
@@ -0,0 +1,59 @@
+# -*- 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 *
+from tables import client_table
+from gnuviechadmin.exceptions import *
+
+from BackendEntity import *
+from BackendEntityHandler import *
+
+class Client(BackendEntity):
+ """Entity class for clients."""
+
+ _shortkeys = ('clientid', 'firstname', 'lastname', 'email')
+
+ 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 item in kwargs.items():
+ self.__setattr__(item)
+ self.validate()
+
+ def create_hook(self):
+ pass
+
+ def delete_hook(self):
+ pass
+
+client_mapper = mapper(Client, client_table)
+client_mapper.add_property("sysusers", relation(sysuser.Sysuser))
+
+class ClientHandler(BackendEntityHandler):
+ """BackendEntityHandler for Client entities."""
+
+ def __init__(self, verbose = False):
+ BackendEntityHandler.__init__(self, Client, verbose)
diff --git a/gnuviechadmin/backend/domain.py b/gnuviechadmin/backend/domain.py
new file mode 100644
index 0000000..7a13073
--- /dev/null
+++ b/gnuviechadmin/backend/domain.py
@@ -0,0 +1,50 @@
+# -*- 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: client.py 1101 2007-02-28 21:15:20Z jan $
+
+from sqlalchemy import *
+from tables import domain_table
+from gnuviechadmin.exceptions import *
+
+import record
+from BackendEntity import *
+from BackendEntityHandler import *
+
+class Domain(BackendEntity):
+ """Entity class for DNS domains."""
+
+ _shortkeys = ("domainid", "sysuserid", "name", "type")
+
+ def __init__(self, verbose = False, **kwargs):
+ BackendEntity.__init__(self, verbose)
+ self.domainid = None
+ self.sysuserid = None
+ self.name = None
+ self.type = None
+ self.validate()
+
+domain_mapper = mapper(Domain, domain_table)
+domain_mapper.add_property("records", relation(record.Record))
+
+class DomainHandler(BackendEntityHandler):
+ """BackendEntityHandler for Domain entities."""
+
+ def __init__(self, verbose = False):
+ BackendEntityHandler.__init__(self, Domain, verbose)
diff --git a/gnuviechadmin/backend/record.py b/gnuviechadmin/backend/record.py
new file mode 100644
index 0000000..d85f5e1
--- /dev/null
+++ b/gnuviechadmin/backend/record.py
@@ -0,0 +1,52 @@
+# -*- 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 *
+from tables import record_table
+from gnuviechadmin.exceptions import *
+
+from BackendEntity import *
+from BackendEntityHandler import *
+
+class Record(BackendEntity):
+ """Entity class for DNS domain records."""
+
+ _shortkeys = ("recordid", "domainid", "name", "type", "content")
+
+ def __init__(self, verbose = False, **kwargs):
+ 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):
+ """BackendEntityHandler for Record entities."""
+
+ def __init__(self, verbose = False):
+ BackendEntityHandler.__init__(self, Record, verbose)
diff --git a/gnuviechadmin/backend/sysuser.py b/gnuviechadmin/backend/sysuser.py
new file mode 100644
index 0000000..10bacf0
--- /dev/null
+++ b/gnuviechadmin/backend/sysuser.py
@@ -0,0 +1,153 @@
+# -*- 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 *
+from tables import sysuser_table
+from gnuviechadmin.exceptions import *
+from gnuviechadmin.util import passwordutils, getenttools
+
+from BackendEntity import *
+from BackendEntityHandler import *
+
+class Sysuser(BackendEntity):
+ """Entity class for system users."""
+
+ _shortkeys = ("sysuserid", "clientid", "username", "home", "shell")
+
+ 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 in kwargs.keys():
+ self.__setattr__(key, kwargs[key])
+ if not self.username:
+ self.username = self.getnextsysusername()
+ if not self.usertype:
+ self.usertype = self.getdefaultsysusertype()
+ if not self.home:
+ self.home = self.gethome(self.username)
+ if not self.shell:
+ self.shell = self.getdefaultshell()
+ (self.clearpass, self.md5pass) = \
+ passwordutils.get_pw_tuple(self.clearpass)
+ if not self.sysuid:
+ self.sysuid = self.getnextsysuid()
+ self.validate()
+
+ def getnextsysusername(self):
+ prefix = self.config.get('sysuser', 'nameprefix')
+ usernames = [user.username for user in \
+ getenttools.finduserbyprefix(prefix)]
+ maxid = max([int(username[len(prefix):]) for username in usernames])
+ for num in range(1, maxid + 1):
+ username = "%s%02d" % (prefix, num)
+ if not username in usernames:
+ return username
+
+ def getdefaultsysusertype(self):
+ return 1
+
+ def gethome(self, sysusername):
+ """Gets a valid home directory for the given user name."""
+ return os.path.join(self.config.get('sysuser', 'homedirbase'),
+ sysusername)
+
+ def getdefaultshell(self):
+ return False
+
+ def getshellbinary(self):
+ if self.shell:
+ return self.config.get('sysuser', 'shellyes')
+ return self.config.get('sysuser', 'shellno')
+
+ def getnextsysuid(self):
+ uid = int(self.config.get('sysuser', 'minuid'))
+ muid = getenttools.getmaxuid(int(self.config.get('sysuser',
+ 'maxuid')))
+ if muid >= uid:
+ uid = muid + 1
+ return uid
+
+ def populate_home(self):
+ templatedir = self.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"' % {
+ 'template' : templatedir,
+ 'home' : self.home }
+ self.sucommand(cmdline)
+ cmdline = 'chown -R "%(username)s":"%(group)s" %(home)s' % {
+ 'username' : self.username,
+ 'group' : self.config.get('sysuser', 'defaultgroup'),
+ 'home' : self.home }
+ self.sucommand(cmdline)
+
+ def create_hook(self):
+ gecos = self.config.get('sysuser', 'gecos')
+ gecos = gecos % (self.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' % {
+ 'home' : self.home,
+ 'shell' : self.getshellbinary(),
+ 'sysuid' : self.sysuid,
+ 'group' : self.config.get('sysuser', 'defaultgroup'),
+ 'gecos' : gecos,
+ 'username' : self.username}
+ self.sucommand(cmdline)
+ cmdline = 'chpasswd --encrypted'
+ inline = '%(username)s:%(md5pass)s' % {
+ 'username' : self.username,
+ 'md5pass' : self.md5pass}
+ self.sucommand(cmdline, inline)
+ self.populate_home()
+
+ def delete_hook(self):
+ backupdir = os.path.join(self.config.get('common',
+ 'backupdir'),
+ self.config.get('sysuser',
+ 'homebackupdir'))
+ if not os.path.isdir(backupdir):
+ cmdline = 'mkdir -p "%(backupdir)s"' % {
+ 'backupdir' : backupdir}
+ status = self.sucommand(cmdline)
+ if status != 0:
+ raise Exception("could not create backup directory")
+ cmdline = 'deluser --remove-home --backup --backup-to "%(backupdir)s" %(username)s' % {
+ 'backupdir' : backupdir,
+ 'username' : self.username}
+ self.sucommand(cmdline)
+
+sysusermapper = mapper(Sysuser, sysuser_table)
+
+class SysuserHandler(BackendEntityHandler):
+ """BackendEntityHandler for Sysuser entities."""
+
+ def __init__(self, verbose = False):
+ BackendEntityHandler.__init__(self, Sysuser, verbose)
diff --git a/gnuviechadmin/tables.py b/gnuviechadmin/backend/tables.py
similarity index 66%
rename from gnuviechadmin/tables.py
rename to gnuviechadmin/backend/tables.py
index f254a7d..c02f3d0 100644
--- a/gnuviechadmin/tables.py
+++ b/gnuviechadmin/backend/tables.py
@@ -52,9 +52,42 @@ sysuser_table = Table(
Column('home', String(128)),
Column('shell', Boolean, nullable=False, default=False),
Column('clearpass', String(64)),
- Column('md5pass', String(32)),
+ 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())
)
sysuser_table.create(checkfirst=True)
+
+domain_table = Table(
+ 'domain', meta,
+ Column('domainid', Integer, primary_key=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))
+domain_table.create(checkfirst=True)
+
+record_table = Table(
+ 'record', meta,
+ Column('recordid', Integer, primary_key=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))
+record_table.create(checkfirst=True)
+
+supermaster_table = Table(
+ 'supermaster', meta,
+ Column('ip', String(25), nullable=False),
+ Column('nameserver', String(255), nullable=False),
+ Column('account', Integer, ForeignKey("sysuser.sysuserid"),
+ nullable=False))
+supermaster_table.create(checkfirst=True)
diff --git a/gnuviechadmin/cli/CliCommand.py b/gnuviechadmin/cli/CliCommand.py
index c2f994e..fcc3646 100644
--- a/gnuviechadmin/cli/CliCommand.py
+++ b/gnuviechadmin/cli/CliCommand.py
@@ -20,62 +20,202 @@
# Version: $Id$
import getopt, sys
+from gnuviechadmin.exceptions import GnuviechadminError
class CliCommand:
- """Base class for command line interface."""
- def usage(self):
- """This method should print usage information for the command."""
+ """Base class for command line interface. A specific
+ implementation class must define the fields name, description and
+ _optionmap.
+
+
+ The field name is the name of the subcommand.
+
+ The field description is a short, one line description of the
+ command.
+
+ The field _optionmap is a map which maps the subcommand names to
+ lists of tuples. Each tuple consists of four elements. The first
+ element is a list of command line arguments, short arguments start
+ with dash, long arguments with a double dash. The second element
+ is the name of the field in the data map of the command, it will
+ directly be sent to the underlying entity. The third field is a
+ description for the group of command line options in field
+ one. The fourth field is True for mandatory fields and False
+ otherwise.
+ """
+
+ def _usage(self):
+ """This method shows usage information. The implementation
+ relies on the information in the fields name, description and
+ _optionmap in the implementation classes."""
+ print """GNUViechAdmin command line interface
+
+Subcommand: %(command)s
+
+ %(description)s
+
+Usage:
+
+%(called)s %(command)s -h|--help
+
+ gives this usage information.
+
+Common options:
+
+ %(option)s
+ %(mandatory)s %(optiondesc)s
+""" % { 'called' : sys.argv[0],
+ 'command' : self.name,
+ 'description' : self.description,
+ 'option' : '-v, --verbose',
+ 'optiondesc' : 'verbose operation',
+ 'mandatory' : " "}
+ for commandname in self._optionmap.keys():
+ cmdl = "%(called)s %(command)s %(subcommand)s [-v|--verbose]" % {
+ 'called' : sys.argv[0],
+ 'command' : self.name,
+ 'subcommand' : commandname}
+ desc = """
+ %s
+""" % (self._optionmap[commandname][0])
+ for (options, field, optiondesc, mandatory) in \
+ self._optionmap[commandname][1]:
+ cmd = " "
+ pairs = []
+ for option in options:
+ if field:
+ if option.startswith("--"):
+ pairs.append("%s=<%s>" % (option, field))
+ else:
+ pairs.append("%s <%s>" % (option, field))
+ else:
+ pairs.append(option)
+ if not mandatory:
+ cmd = cmd + "["
+ cmd = cmd + "|".join(pairs)
+ if not mandatory:
+ cmd = cmd + "]"
+ descmap = {
+ 'option' : ", ".join(pairs),
+ 'optiondesc' : optiondesc,
+ 'mandatory' : ' '}
+ if mandatory:
+ descmap['mandatory'] = '*'
+ desc = desc + """ %(option)s
+ %(mandatory)s %(optiondesc)s
+""" % descmap
+ if (len(cmdl) + len(cmd)) > 79:
+ print cmdl
+ cmdl = cmd
+ else:
+ cmdl = cmdl + cmd
+ print cmdl
+ print desc
+ print "Mandatory options are marked with *"
+
+ def _subcommands(self):
+ """Convenience method for retrieving the subcommand names from
+ the _optionmap field."""
+ return self._optionmap.keys()
+
+ def _longopts(self, subcommand):
+ """This method retrieves available long options in a format
+ valid for getopt.gnu_getopt(...) from the _optionmap field."""
+ longopts = []
+ for cur in [(option[0], option[1]) for option in \
+ self._optionmap[subcommand][1]]:
+ for command in cur[0]:
+ if command.startswith("--"):
+ if cur[1]:
+ longopts.append(command[2:] + "=")
+ else:
+ longopts.append(command[2:])
+ return longopts
+
+ def _shortopts(self, subcommand):
+ """This method retrieves available short options in a format
+ valid for getopt.gnu_getopt(...) from the _optionmap field."""
+ shortopts = ""
+ for cur in [(option[0], option[1]) for option in \
+ self._optionmap[subcommand][1]]:
+ for command in cur[0]:
+ if not command.startswith("--"):
+ if cur[1]:
+ shortopts = shortopts + command[1:] + ":"
+ else:
+ shortopts = shortopts + command[1:]
+ return shortopts
+
+ def _checkrequired(self, subcommand):
+ """Checks whether the required fields of the given subcommand
+ are set."""
+ reqcheck = [True, []]
+ for req in [option for option in \
+ self._optionmap[subcommand][1] if option[3]]:
+ if not req[1] in self._data:
+ reqcheck[0] = False
+ reqcheck[1].append(""" %s
+ * %s""" % (", ".join(req[0]), req[2]))
+ return reqcheck
+
+ def _handleoption(self, subcommand, o, a):
+ """Handles a command line option by assigning it to the
+ matching key as defined in the _optionmap property of the
+ implementation class."""
+ optionmap = [(option[0], option[1]) for option in \
+ self._optionmap[subcommand][1]]
+ if optionmap:
+ for (options, datakey) in optionmap:
+ if o in options:
+ self._data[datakey] = a
+
+ def _execute(self, subcommand):
+ """This method is called when the subcommand of the command is
+ executed."""
raise NotImplementedError
- def shortopts(self):
- """This method should return an option string for the short
- options for getopt.gnu_getopt(...)."""
- raise NotImplementedError
-
- def longopts(self):
- """This method should return a list of long options for
- getopt.gnu_getopt(...)."""
- raise NotImplementedError
-
- def handleoption(self, option, argument):
- """This method should handle each option known to the command."""
- raise NotImplementedError
-
- def execute(self):
- """This method is called when the command is executed."""
- raise NotImplementedError
-
- def checkrequired(self):
- """This methode is called after handling command line options
- and should check whether all required values were set."""
- raise NotImplementedError
-
- def __parseopts(self, args):
+ def _parseopts(self, subcommand, args):
"""This method parses the options given on the command line."""
longopts = ["help", "verbose"]
- longopts.extend(self.longopts())
+ longopts.extend(self._longopts(subcommand))
try:
opts, args = getopt.gnu_getopt(
args,
- "hv" + self.shortopts(),
+ "hv" + self._shortopts(subcommand),
longopts)
except getopt.GetoptError:
- self.usage()
+ self._usage()
sys.exit(2)
- self.verbose = False
+ self._verbose = False
for o, a in opts:
if o in ("-v", "--verbose"):
- self.verbose = True
+ self._verbose = True
if o in ("-h", "--help"):
- self.usage()
+ self._usage()
sys.exit()
- self.handleoption(o, a)
+ self._handleoption(subcommand, o, a)
def __init__(self, args):
"""This initializes the command with the given command line
arguments and executes it."""
- self.__parseopts(args)
- if (self.checkrequired()):
- self.execute()
+ self._data = {}
+ if len(args) > 0:
+ if args[0] in self._subcommands():
+ self._parseopts(args[0], args[1:])
+ reqcheck = self._checkrequired(args[0])
+ if reqcheck[0]:
+ try:
+ self._execute(args[0])
+ except GnuviechadminError, e:
+ print e
+ else:
+ self._usage()
+ print """
+the following required arguments are missing:
+"""
+ print "\n".join(reqcheck[1])
+ else:
+ self._usage()
+ print "invalid sub command"
else:
- self.usage()
+ self._usage()
diff --git a/gnuviechadmin/cli/client.py b/gnuviechadmin/cli/client.py
index 646bd68..536e37c 100644
--- a/gnuviechadmin/cli/client.py
+++ b/gnuviechadmin/cli/client.py
@@ -20,91 +20,63 @@
# Version: $Id$
import CliCommand, sys
-from gnuviechadmin import client
class ClientCli(CliCommand.CliCommand):
- """Command line interface command for client creation."""
- def shortopts(self):
- return "f:l:a:z:c:e:p:o:t:m:x:"
+ """Command line interface command for client managament."""
- def longopts(self):
- return ["firstname=", "lastname=", "address=", "zip=",
- "city=", "email=", "phone=", "address2=", "organisation=",
- "country=", "title=", "mobile=", "fax="]
-
- def usage(self):
- print """Usage: %s client [-h|--help] [-v|--verbose]
- [-t |--title=]
- -f |--firstname= -l |--lastname=
- -a |--address= [--address2=]
- -z |--zip= -c |--city= [--country=]
- [-o |--organisation=]
- -e |--email= -p |--phone=
- [-m |--mobile=] [-x |--fax=]
-
-General options:
- -h, --help show this usage message and exit
- -v, --verbose verbose operation
-
-Mandatory client data options:
- -f, --firstname firstname
- -l, --lastname lastname
- -a, --address street address
- -z, --zip zip or postal code
- -c, --city city or location
- -e, --email contact email address
- -p, --phone telephone number
-
-Optional client data options:
- --address2 optional second line of the street address
- -o, --organisation option organisation
- --country country (defaults to de)
- -t, --title optional title
- -m, --mobile optional mobile number
- -x, --fax optional fax number
-""" % sys.argv[0]
-
- def handleoption(self, o, a):
- if o in ("-f", "--firstname"):
- self.data["firstname"] = a
- elif o in ("-l", "--lastname"):
- self.data["lastname"] = a
- elif o in ("-a", "--address"):
- self.data["address1"] = a
- elif o in ("-z", "--zip"):
- self.data["zip"] = a
- elif o in ("-c", "--city"):
- self.data["city"] = a
- elif o == "--country":
- self.data["country"] = a
- elif o in ("-t", "--title"):
- self.data["title"] = a
- elif o in ("-m", "--mobile"):
- self.data["mobile"] = a
- elif o in ("-e", "--email"):
- self.data["email"] = a
- elif o in ("-o", "--organisation"):
- self.data["organisation"] = a
- elif o in ("-x", "--fax"):
- self.data["fax"] = a
- elif o in ("-p", "--phone"):
- self.data["phone"] = a
-
- def checkrequired(self):
- required = ['firstname', 'lastname', 'address1', 'zip', 'city',
- 'phone', 'email']
- if self.verbose:
- print self.data
- for req in required:
- if not req in self.data:
- return False
- return True
-
- def execute(self):
- myclient = client.create(**self.data)
- if self.verbose:
- print myclient
+ name = "client"
+ description = "manage clients"
+ _optionmap = {
+ 'create' : ("creates a new client",
+ [(["-f", "--firstname"], "firstname",
+ "the client's first name", True),
+ (["-l", "--lastname"], "lastname",
+ "the client's last name", True),
+ (["-t", "--title"], "title",
+ "the client's title", False),
+ (["-a", "--address"], "address1",
+ "the address of the client", True),
+ (["--address2"], "address2",
+ "second line of the client's address", False),
+ (["-z", "--zip"], "zip",
+ "the zipcode of the client's address", True),
+ (["-c", "--city"], "city",
+ "the city of the client's address", True),
+ (["--country"], "country",
+ "the client's country", False),
+ (["-e", "--email"], "email",
+ "the client's email address", True),
+ (["-p", "--phone"], "phone",
+ "the client's phone number", True),
+ (["-m", "--mobile"], "mobile",
+ "the client's mobile phone number", False),
+ (["-x", "--fax"], "fax",
+ "the client's fax number", False)]),
+ 'list' : ("lists existing clients",
+ []),
+ 'delete' : ("deletes the specified client if it has no dependent data",
+ [(["-c", "--clientid"], "clientid",
+ "the client id", True)])}
+
+ def _execute(self, subcommand):
+ from gnuviechadmin.backend import client
+ from gnuviechadmin import exceptions
+ if subcommand == "create":
+ try:
+ myclient = client.ClientHandler(self._verbose).create(
+ **self._data)
+ if self._verbose:
+ print myclient
+ except exceptions.CreationFailedError, cfe:
+ self._usage()
+ print cfe
+ sys.exit(2)
+ elif subcommand == "list":
+ clients = client.ClientHandler(self._verbose).fetchall()
+ for client in clients:
+ print client
+ elif subcommand == "delete":
+ client.ClientHandler(self._verbose).delete(self._data["clientid"])
def __init__(self, argv):
- self.data = {}
CliCommand.CliCommand.__init__(self, argv)
diff --git a/gnuviechadmin/cli/sysuser.py b/gnuviechadmin/cli/sysuser.py
index 2ae2839..27ac177 100644
--- a/gnuviechadmin/cli/sysuser.py
+++ b/gnuviechadmin/cli/sysuser.py
@@ -1 +1,71 @@
-pass
+# -*- 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$
+
+import CliCommand, sys
+
+class SysuserCli(CliCommand.CliCommand):
+ """Command line interface command for system user managament."""
+
+ name = "sysuser"
+ description = "manage system users"
+ _optionmap = {
+ "create" : ("create a new system user with the given options.",
+ [(["-n", "--username"], "username",
+ "the system user name", False),
+ (["-t", "--usertype"], "usertype",
+ "the numeric user type", False),
+ (["-h", "--home"], "home",
+ "the home directory", False),
+ (["-s", "--shell"], "shell",
+ "true if the user should get shell access", False),
+ (["-p", "--password"], "clearpass",
+ "the password for the user", False),
+ (["-c", "--clientid"], "clientid",
+ "the client id", True)]),
+ "list" : ("list existing system users.",
+ []),
+ "delete" : ("delete a system user.",
+ [(["-s", "--sysuserid"], "sysuserid",
+ "the system user id", True)])}
+
+ def _execute(self, subcommand):
+ from gnuviechadmin.backend import sysuser
+ from gnuviechadmin import exceptions
+ if subcommand == "create":
+ try:
+ mysysuser = sysuser.SysuserHandler(self._verbose).create(
+ **self._data)
+ if self._verbose:
+ print mysysuser
+ except exceptions.CreationFailedError, cfe:
+ self._usage()
+ print cfe
+ sys.exit(2)
+ elif subcommand == "list":
+ sysusers = sysuser.SysuserHandler(self._verbose).fetchall()
+ for su in sysusers:
+ print su
+ elif subcommand == "delete":
+ sysuser.SysuserHandler(self._verbose).delete(
+ self._data["sysuserid"])
+
+ def __init__(self, argv):
+ CliCommand.CliCommand.__init__(self, argv)
diff --git a/gnuviechadmin/client.py b/gnuviechadmin/client.py
deleted file mode 100644
index 3cf1852..0000000
--- a/gnuviechadmin/client.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*- 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 *
-from gnuviechadmin.tables import *
-from gnuviechadmin import sysuser
-from gnuviechadmin.exceptions import *
-
-class Client(object):
- mandatory = ('firstname', 'lastname', 'address1', 'zip', 'city',
- 'country', 'phone', 'email')
-
- """This class provides a client representation"""
- def __init__(self, **kwargs):
- self.clientid = None
- self.country = 'de'
- self.title = None
- self.address2 = None
- self.mobile = None
- self.fax = None
- self.organisation = None
- for key in kwargs.keys():
- self.__setattr__(key, kwargs[key])
- self.validate()
-
- def validate(self):
- missingfields = []
- for key in self.mandatory:
- if self.__getattribute__(key) is None:
- missingfields.append(key)
- if missingfields:
- raise MissingFieldsError(missingfields)
-
- def __repr__(self):
- return "%(clientid)d,%(firstname)s,%(lastname)s,%(email)s" % (
- {'clientid' : self.clientid, 'firstname' : self.firstname,
- 'lastname' : self.lastname, 'email' : self.email})
-
-clientmapper = mapper(
- Client, client_table,
- properties = {'sysusers': relation(sysuser.Sysuser)})
-
-def create(**kwargs):
- try:
- myclient = Client(**kwargs)
- sess = create_session()
- sess.save(myclient)
- sess.flush()
- except MissingFieldsError, mfe:
- raise CreationFailedError(Client.__name__, mfe)
- except exceptions.SQLError, sqle:
- raise CreationFailedError(Client.__name__, sqle)
- return myclient
-
-def fetchall():
- session = create_session()
- query = session.query(Client)
- return query.select()
diff --git a/gnuviechadmin/defaults.cfg b/gnuviechadmin/defaults.cfg
index fe1f85f..b9f487e 100644
--- a/gnuviechadmin/defaults.cfg
+++ b/gnuviechadmin/defaults.cfg
@@ -32,3 +32,19 @@
# very usable for a real installation.
#
uri = sqlite:///:memory:
+
+[common]
+suwrapper = sudo
+backupdir = /var/backups/gnuviechadmin
+
+[sysuser]
+nameprefix = usr
+homedirbase = /home/www
+minuid = 10000
+maxuid = 39999
+shellyes = /bin/bash
+shellno = /usr/bin/scponly
+defaultgroup = wwwusers
+gecos = Webuser %s
+homebackupdir = homes
+hometemplate = /etc/gnuviechadmin/templates/home
diff --git a/gnuviechadmin/exceptions.py b/gnuviechadmin/exceptions.py
index 34e2002..a8dc0b4 100644
--- a/gnuviechadmin/exceptions.py
+++ b/gnuviechadmin/exceptions.py
@@ -20,18 +20,24 @@
# Version: $Id$
"""This file defines the gnuviechadmin specific exception types."""
-class MissingFieldsError(Exception):
+
+class GnuviechadminError(Exception):
+ """This is the base class for domain specific exceptions of
+ Gnuviechadmin."""
+ pass
+
+class MissingFieldsError(GnuviechadminError):
"""This exception should be raised when a required field of a data
-class is missing."""
+ class is missing."""
def __init__(self, missingfields):
self.missing = missingfields
def __str__(self):
return "the fields %s are missing." % (repr(self.missing))
-class CreationFailedError(Exception):
+class CreationFailedError(GnuviechadminError):
"""This exception should be raised if a business object could not
-be created."""
+ be created."""
def __init__(self, classname, cause = None):
self.classname = classname
self.cause = cause
@@ -41,3 +47,16 @@ be created."""
if self.cause:
msg += " The reason is %s." % (str(self.cause))
return msg
+
+class DeleteFailedError(GnuviechadminError):
+ """This exception should be raise if a business object coild not
+ be deleted."""
+ def __init__(self, classname, cause = None):
+ self.classname = classname
+ self.cause = cause
+
+ def __str__(self):
+ msg = "Deleting an instance of class %s failed." % (self.classname)
+ if self.cause:
+ msg += " The reason is %s." % (str(self.cause))
+ return msg
diff --git a/gnuviechadmin/sysuser.py b/gnuviechadmin/util/__init__.py
similarity index 65%
rename from gnuviechadmin/sysuser.py
rename to gnuviechadmin/util/__init__.py
index 4714217..3fcfaa4 100644
--- a/gnuviechadmin/sysuser.py
+++ b/gnuviechadmin/util/__init__.py
@@ -19,16 +19,6 @@
#
# Version: $Id$
-from sqlalchemy import *
-from gnuviechadmin.tables import *
-from gnuviechadmin.exceptions import *
+"""This is the gnuviechadmin.util package.
-class Sysuser(object):
- def __repr__(self):
- return "%(sysuserid)d,%(username)s,%(clientid)d,%(sysuid)d" % ({
- 'sysuserid': self.sysuserid,
- 'username': self.username,
- 'clientid': self.clientid,
- 'sysuid': self.sysuid})
-
-sysusermapper = mapper(Sysuser, sysuser_table)
+The package provides utility modules for various functions."""
diff --git a/gnuviechadmin/util/getenttools.py b/gnuviechadmin/util/getenttools.py
new file mode 100644
index 0000000..be3ed1f
--- /dev/null
+++ b/gnuviechadmin/util/getenttools.py
@@ -0,0 +1,94 @@
+# -*- 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$
+
+import os, popen2
+
+class PasswdUser(object):
+ """This class represents users in the user database."""
+ def __init__(self, username, pw, uid, gid, gecos, home, shell):
+ self.username = username
+ self.uid = int(uid)
+ self.gid = int(gid)
+ self.gecos = gecos
+ self.home = home
+ self.shell = shell
+
+ def __repr__(self):
+ return "%s(%s:%d:%d:%s:%s:%s)" % (self.__class__.__name__,
+ self.username,
+ self.uid,
+ self.gid,
+ self.gecos,
+ self.home,
+ self.shell)
+
+class PasswdGroup(object):
+ """This class represents lines in the groups database."""
+ def __init__(self, groupname, pw, gid, members):
+ self.groupname = groupname
+ self.gid = int(gid)
+ self.members = members.split(",")
+
+ def __repr__(self):
+ return "%s(%s:%d:%s)" % (self.__class__.__name__,
+ self.groupname,
+ self.gid,
+ ",".join(self.members))
+
+def parsegroups():
+ (stdout, stdin) = popen2.popen2("getent group")
+ return [PasswdGroup(*arr) for arr in [line.strip().split(":") for line in stdout]]
+
+def parseusers():
+ (stdout, stdin) = popen2.popen2("getent passwd")
+ return [PasswdUser(*arr) for arr in [line.strip().split(":") for line in stdout]]
+
+def finduserbyprefix(prefix):
+ """Finds all user entries with the given prefix."""
+ return [user for user in parseusers() if user.username.startswith(prefix)]
+
+def getuserbyid(uid):
+ """Gets the user with the given user id."""
+ users = [user for user in parseusers() if user.uid == uid]
+ if users:
+ return users[0]
+ return None
+
+def getgroupbyid(gid):
+ """Gets the group with the given group id."""
+ groups = [group for group in parsegroups() if group.gid == gid]
+ if groups:
+ return groups[0]
+ return None
+
+def getmaxuid(boundary = 65536):
+ """Gets the highest uid value."""
+ return max([user.uid for user in parseusers() if user.uid <= boundary])
+
+def getmaxgid(boundary = 65536):
+ """Gets the highest gid value."""
+ return max([group.gid for group in parsegroups() if group.gid <= boundary])
+
+if __name__ == "__main__":
+ print "Max UID is %d" % (getmaxuid(40000))
+ print "Max GID is %d" % (getmaxgid(40000))
+ print "User with max UID is %s" % (getuserbyid(getmaxuid(40000)))
+ print "Group with max GID is %s" % (getgroupbyid(getmaxgid(40000)))
diff --git a/gnuviechadmin/util/passwordutils.py b/gnuviechadmin/util/passwordutils.py
new file mode 100644
index 0000000..111e2c6
--- /dev/null
+++ b/gnuviechadmin/util/passwordutils.py
@@ -0,0 +1,57 @@
+# -*- 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$
+
+import crypt, crack, random
+
+def generatepassword(minlength = 8, maxlength = 12):
+ """Generates a random password with a length between the given
+ 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 \
+ random.sample(pwchars,
+ random.randint(minlength, maxlength))])
+
+def checkpassword(password):
+ """Checks the password with cracklib. The password is returned if
+ it is good enough. Otherwise None is returned."""
+ try:
+ return crack.VeryFascistCheck(password)
+ except ValueError, ve:
+ print "Weak password:", ve
+ return None
+
+def md5_crypt_password(password):
+ """Hashes the given password with MD5 and a random salt value."""
+ salt = "".join([chr(letter) for letter in \
+ random.sample(range(ord('a'), ord('z')), 8)])
+ return crypt.crypt(password, '$1$' + salt)
+
+def get_pw_tuple(password = None):
+ """Gets a valid tuple consisting of a password and a md5 hash of the
+ password. If a password is given it is checked and if it is too weak
+ replaced by a generated one."""
+ while password == None or checkpassword(password) == None:
+ password = generatepassword()
+ return (password, md5_crypt_password(password))