- restructured
- implementation of client and sysuser cli - backend for client, sysuser, domain and record - unified cli binary gva git-svn-id: file:///home/www/usr01/svn/gnuviechadmin/gnuviech.info/gnuviechadmin/trunk@226 a67ec6bc-e5d5-0310-a910-815c51eb3124
This commit is contained in:
		
							parent
							
								
									ee36146629
								
							
						
					
					
						commit
						926acaddfa
					
				
					 19 changed files with 1010 additions and 345 deletions
				
			
		
							
								
								
									
										115
									
								
								bin/createclient
									
										
									
									
									
								
							
							
						
						
									
										115
									
								
								bin/createclient
									
										
									
									
									
								
							|  | @ -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>|--title=<title>] | ||||
|  -f <firstname>|--firstname=<firstname> -l <lastname>|--lastname=<lastname> | ||||
|  -a <address1>|--address=<address1> [--address2=<address2>] | ||||
|  -z <zip>|--zip=<zip> -c <city>|--city=<city> [--country=<isocode>] | ||||
|  [-o <organisation>|--organisation=<organisation>] | ||||
|  -e <email>|--email=<email> -p <phone>|--phone=<phone> | ||||
|  [-m <mobile>|--mobile=<mobile>] [-x <fax>|--fax=<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() | ||||
							
								
								
									
										18
									
								
								bin/gva
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								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 <command> [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() | ||||
|  |  | |||
							
								
								
									
										83
									
								
								gnuviechadmin/backend/BackendEntity.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								gnuviechadmin/backend/BackendEntity.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
							
								
								
									
										69
									
								
								gnuviechadmin/backend/BackendEntityHandler.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								gnuviechadmin/backend/BackendEntityHandler.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
							
								
								
									
										13
									
								
								bin/listclients → gnuviechadmin/backend/__init__.py
									
										
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										13
									
								
								bin/listclients → gnuviechadmin/backend/__init__.py
									
										
									
									
									
										
										
										Executable file → Normal file
									
								
							|  | @ -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.""" | ||||
							
								
								
									
										59
									
								
								gnuviechadmin/backend/client.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								gnuviechadmin/backend/client.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
							
								
								
									
										50
									
								
								gnuviechadmin/backend/domain.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								gnuviechadmin/backend/domain.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
							
								
								
									
										52
									
								
								gnuviechadmin/backend/record.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								gnuviechadmin/backend/record.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
							
								
								
									
										153
									
								
								gnuviechadmin/backend/sysuser.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								gnuviechadmin/backend/sysuser.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -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) | ||||
|  | @ -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) | ||||
|  | @ -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() | ||||
|  |  | |||
|  | @ -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>|--title=<title>] | ||||
|  -f <firstname>|--firstname=<firstname> -l <lastname>|--lastname=<lastname> | ||||
|  -a <address1>|--address=<address1> [--address2=<address2>] | ||||
|  -z <zip>|--zip=<zip> -c <city>|--city=<city> [--country=<isocode>] | ||||
|  [-o <organisation>|--organisation=<organisation>] | ||||
|  -e <email>|--email=<email> -p <phone>|--phone=<phone> | ||||
|  [-m <mobile>|--mobile=<mobile>] [-x <fax>|--fax=<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) | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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() | ||||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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.""" | ||||
							
								
								
									
										94
									
								
								gnuviechadmin/util/getenttools.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								gnuviechadmin/util/getenttools.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -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))) | ||||
							
								
								
									
										57
									
								
								gnuviechadmin/util/passwordutils.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								gnuviechadmin/util/passwordutils.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -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)) | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue