Compare commits
57 commits
before-orm
...
master
Author | SHA1 | Date | |
---|---|---|---|
Jan Dittberner | 658ffea8ee | ||
Jan Dittberner | ccaa207d36 | ||
Jan Dittberner | d4ba46a329 | ||
Jan Dittberner | 33696436a0 | ||
Jan Dittberner | 06e1e11a61 | ||
Jan Dittberner | 1435a88a5a | ||
Jan Dittberner | 0ac89c5f7b | ||
Jan Dittberner | 45b2865e8e | ||
Jan Dittberner | b8139e91f2 | ||
Jan Dittberner | 076621a0be | ||
Jan Dittberner | 222b35b033 | ||
Jan Dittberner | 483c1f9038 | ||
Jan Dittberner | 92c1e21f32 | ||
Jan Dittberner | 6883909d73 | ||
Jan Dittberner | 3683df2011 | ||
Jan Dittberner | daf9517a83 | ||
Jan Dittberner | 4ee62d5b2e | ||
Jan Dittberner | 030a733fbd | ||
Jan Dittberner | b183465d5e | ||
Jan Dittberner | 065996e0df | ||
Jan Dittberner | 09180938f1 | ||
Jan Dittberner | 7c4d25da43 | ||
Jan Dittberner | 6d33a0e147 | ||
Jan Dittberner | 1d288681b7 | ||
Jan Dittberner | 5e143677d8 | ||
Jan Dittberner | 44b5f81c45 | ||
Jan Dittberner | 27489ef06f | ||
Jan Dittberner | 7152902ff3 | ||
Jan Dittberner | cccc6b8fe3 | ||
Jan Dittberner | 4ae866c559 | ||
Jan Dittberner | c049fd9bc9 | ||
Jan Dittberner | 0838856b76 | ||
Jan Dittberner | 639b58df6e | ||
Jan Dittberner | fbe448ca62 | ||
Jan Dittberner | c6baab12e6 | ||
Jan Dittberner | dea15a6c4f | ||
Jan Dittberner | 5c1a97e82d | ||
Jan Dittberner | f24de13a6f | ||
Jan Dittberner | aaa23c9c5f | ||
Jan Dittberner | d5ace903bf | ||
Jan Dittberner | d46d04567d | ||
Jan Dittberner | 732bb17fc5 | ||
Jan Dittberner | b8c1b30e11 | ||
Jan Dittberner | fdea3217c8 | ||
Jan Dittberner | 3f4457bdca | ||
Jan Dittberner | 0d12afc71e | ||
Jan Dittberner | 926acaddfa | ||
Jan Dittberner | ee36146629 | ||
Jan Dittberner | 3f099c72ff | ||
Jan Dittberner | bd306389f0 | ||
Jan Dittberner | 6b1c80899d | ||
Jan Dittberner | 1846c9bcd8 | ||
Jan Dittberner | 58125cdf29 | ||
Jan Dittberner | 5b7a1d990d | ||
Jan Dittberner | a0778661c6 | ||
Jan Dittberner | adbf8e440d | ||
Jan Dittberner | c84fc9a90c |
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
*~
|
||||
*.pyc
|
||||
*.egg/
|
||||
*.log
|
||||
gva.db
|
340
LICENSE
340
LICENSE
|
@ -1,340 +0,0 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
10
Makefile
10
Makefile
|
@ -1,10 +0,0 @@
|
|||
subdirs=php
|
||||
|
||||
all:
|
||||
|
||||
dumpdb:
|
||||
pg_dump -U gnuviech -h localhost -s -d -O gnuviechadmin > database.sql
|
||||
|
||||
clean:
|
||||
for i in $(subdirs); do make -C $$i clean; done
|
||||
-rm -f *~
|
33
README
33
README
|
@ -1,33 +0,0 @@
|
|||
gnuviechadmin aims to be a server administration tool originally
|
||||
developed for managing gnuviech.info.
|
||||
|
||||
gnuviechadmin is targeted for Debian GNU/Linux 3.0r2 (woody) but may
|
||||
be ported to later Debian GNU/Linux releases in the future.
|
||||
|
||||
application design thoughts
|
||||
===========================
|
||||
|
||||
I think a 2-layered architecture will be best.
|
||||
|
||||
One layer (which needs to be run as root) does all the background work
|
||||
(like creating users, zone files ...). This layer will be implemented
|
||||
in python and will invoke several Debian system utilities.
|
||||
|
||||
The second layer will in the first place be a PHP based Web frontend
|
||||
where the administrator can manage the servers most common
|
||||
tasks. Later there will be support for client and reseller frontends
|
||||
for tasks that the administrator allows them.
|
||||
|
||||
The second layer may also be a GUI application if the need arrises.
|
||||
|
||||
Both layers will be coupled through a sql database. I will use
|
||||
PostgreSQL, but as the need arrises I'll also try to support other
|
||||
databases.
|
||||
|
||||
|
||||
+----------+ +---------+
|
||||
| frontend | | backend |
|
||||
+-----+----+ +---+-----+
|
||||
| +----------+ |
|
||||
+--------->| database |<----------+
|
||||
+----------+
|
43
TODO
43
TODO
|
@ -1,43 +0,0 @@
|
|||
reseller management (NOT YET)
|
||||
|
||||
client management
|
||||
- add client
|
||||
- delete client
|
||||
- create bills
|
||||
- account traffic
|
||||
|
||||
dns management
|
||||
- add zones
|
||||
- whois search
|
||||
- register domains
|
||||
- add records
|
||||
- change records
|
||||
- delete records
|
||||
- delete zones
|
||||
|
||||
apache administration
|
||||
- associate domain with vhost entry
|
||||
- associate domain with path in vhost
|
||||
- special httpd.conf entries for host
|
||||
- logfile analysis
|
||||
- statistics per vhost
|
||||
|
||||
shell access (ssh)
|
||||
- allow input of rsa/dsa public keys in web frontend
|
||||
- set valid shell
|
||||
|
||||
imap/pop3 management
|
||||
- create pop3/imap mailbox for client with generated passwords
|
||||
- associate email address from client's domain to pop3/imap mailboxes
|
||||
|
||||
quota management
|
||||
- assign quotas to pop3/imap, diskspace for website and databases (if possible)
|
||||
|
||||
postgresql management
|
||||
- create databases for client
|
||||
|
||||
mysql management
|
||||
- create databases for client
|
||||
|
||||
statistics
|
||||
- create per user and per domain configuration for modlogan
|
|
@ -1,6 +0,0 @@
|
|||
USERPREFIX="usr"
|
||||
EXIMCONFDIR="etc/exim4/"
|
||||
VIRTUALDOMDIR=EXIMCONFDIR+"virtual/"
|
||||
POPHOMEDIR="/home/mail/"
|
||||
WEBHOMEDIR="/home/www/"
|
||||
DNSZONESDIR="/var/named-root/etc/"
|
|
@ -1,80 +0,0 @@
|
|||
#! /usr/bin/env python
|
||||
|
||||
import os, string
|
||||
|
||||
execfile('gvadm.preferences')
|
||||
|
||||
# if [ -n $USERPREFIX ]; then
|
||||
# USERPREFIX="usr"
|
||||
# fi
|
||||
#
|
||||
# if [ $1 == "" ]; then
|
||||
# echo "give <UserNumber>p<pop3Account> as parameter"
|
||||
# exit
|
||||
# fi
|
||||
#
|
||||
# NEWUSER="$USERPREFIX$1"
|
||||
# NEWHOME="/home/mail/$NEWUSER"
|
||||
# NEWPASS=$(apg -n 1 -a 1 -CL -m 8 -x 12)
|
||||
# echo $NEWHOME
|
||||
#
|
||||
# adduser --home "$NEWHOME" --shell /bin/true --no-create-home
|
||||
# --firstuid 20000 --ingroup poponly --disabled-password
|
||||
# --disabled-login --gecos "Popuser $NEWUSER" $NEWUSER
|
||||
#
|
||||
# mkdir -p "$NEWHOME"
|
||||
# chown -Rc $NEWUSER.poponly "$NEWHOME"
|
||||
# echo "${NEWUSER}:${NEWPASS}" | chpasswd
|
||||
#
|
||||
# echo "Herzlich willkommen auf dem GNU-Viech" |\
|
||||
# mail -s "Willkommen auf dem GNU-Viech" ${NEWUSER}
|
||||
#
|
||||
# echo added new pop3 user $NEWUSER with password $NEWPASS
|
||||
|
||||
def createpopaccount(domainname, sysuser):
|
||||
"""Creates a pop3/imap account for given domain and system user"""
|
||||
"addpopuser sysusrp<num>"
|
||||
|
||||
def readaliasfile(domainname):
|
||||
"""reads the aliasfile for the given domain and returns a dictionary
|
||||
object with the aliases
|
||||
"""
|
||||
aliases={}
|
||||
if (os.access(VIRTUALDOMDIR, os.R_OK)):
|
||||
try:
|
||||
aliasfile=open(VIRTUALDOMDIR+domainname, 'r')
|
||||
for line in aliasfile.readlines():
|
||||
keyvals = string.split(line,":",1)
|
||||
aliases[keyvals[0]]=keyvals[1].strip()
|
||||
aliasfile.close()
|
||||
except IOError:
|
||||
print "couldn't read the aliasfile for "+domainname+"."
|
||||
else:
|
||||
print "couldn't read from "+VIRTUALDOMDIR+"."
|
||||
return aliases
|
||||
|
||||
def writealiasfile(domainname, aliases):
|
||||
"""writes the aliasfile for the given domain with the aliases defined
|
||||
in the dictionary object aliases
|
||||
"""
|
||||
if (os.access(VIRTUALDOMDIR, os.W_OK)):
|
||||
try:
|
||||
aliasfile=open(VIRTUALDOMDIR+domainname, 'w')
|
||||
for key in aliases.keys():
|
||||
aliasfile.write(key+":"+aliases[key]+"\n")
|
||||
aliasfile.close()
|
||||
except IOError:
|
||||
print "writing to aliasfile failed."
|
||||
else:
|
||||
print "no write access to directory "+VIRTUALDOMDIR+"."
|
||||
|
||||
def setmailalias(domainname, alias, target):
|
||||
"""sets a mail alias for given domain which directs the MTA to the
|
||||
given target
|
||||
"""
|
||||
aliases=readaliasfile(domainname)
|
||||
aliases[alias]=target
|
||||
writealiasfile(domainname, aliases)
|
||||
|
||||
createpopaccount("test.de", "usr03")
|
||||
setmailalias("test.de", "doedel", "horst@dittberner.info")
|
|
@ -1,15 +0,0 @@
|
|||
#
|
||||
# Domain manager class for gnuviech-admin tool backend
|
||||
# (c) 2006 Jan Dittberner <jan@dittberner.info>
|
||||
# $Id$
|
||||
#
|
||||
from SessionManager import Session
|
||||
|
||||
class DomainManager:
|
||||
def __init__(self, dbconn):
|
||||
self._dbconn = dbconn
|
||||
|
||||
def listDomains(self, session):
|
||||
if isinstance(session, Session):
|
||||
return 'a,b,c'
|
||||
return ''
|
|
@ -1,62 +0,0 @@
|
|||
#
|
||||
# Service facade for gnuviech-admin tool backend
|
||||
# (c) 2006 Jan Dittberner <jan@dittberner.info>
|
||||
# $Id$
|
||||
#
|
||||
from SessionManager import *
|
||||
from DomainManager import *
|
||||
import Settings
|
||||
import psycopg2
|
||||
|
||||
class ServiceFacade:
|
||||
"""
|
||||
This class implements the facade to the services provided by the
|
||||
gnuviech admin backend.
|
||||
"""
|
||||
def __init__(self):
|
||||
connstr = 'host=%(dbhost)s user=%(dbuser)s ' + \
|
||||
'password=%(dbpassword)s dbname=%(dbname)s'
|
||||
dbconn = psycopg2.connect(connstr % Settings.DBSETTINGS)
|
||||
self.sessionManager = SessionManager(dbconn)
|
||||
self.domainManager = DomainManager(dbconn)
|
||||
|
||||
def _dispatch(self, method, params):
|
||||
try:
|
||||
func = getattr(self, method)
|
||||
except AttributeError:
|
||||
raise Exception('method "%s" is not supported' % method)
|
||||
else:
|
||||
if method != 'login' and method != 'logout':
|
||||
sessionid = params[0]
|
||||
try:
|
||||
session = self.sessionManager.getSession(sessionid)
|
||||
except InvalidSessionError:
|
||||
return ""
|
||||
nparams = [session]
|
||||
for item in params[1:]:
|
||||
nparams.append(item)
|
||||
params = nparams
|
||||
self.sessionManager.updateSession(sessionid)
|
||||
return func(*params)
|
||||
|
||||
def login(self, login, password):
|
||||
"""
|
||||
Logs in the user specified by the given login and password.
|
||||
The method creates a session and returns the session id which
|
||||
has to be sent back by subsequent requests. If the login is
|
||||
invalid the returned id is 0
|
||||
"""
|
||||
try:
|
||||
return self.sessionManager.newSession(login, password)
|
||||
except InvalidLoginError, ile:
|
||||
return 0
|
||||
|
||||
def logout(self, sessionid):
|
||||
self.sessionManager.deleteSession(sessionid)
|
||||
return 0
|
||||
|
||||
def listdomains(self, session):
|
||||
"""
|
||||
Lists the domains the given session may see.
|
||||
"""
|
||||
return self.domainManager.listDomains(session)
|
|
@ -1,95 +0,0 @@
|
|||
"""
|
||||
Session manager class for gnuviech-admin tool backend
|
||||
(c) 2006 Jan Dittberner <jan@dittberner.info>
|
||||
$Id$
|
||||
"""
|
||||
import Settings
|
||||
import os, sha, time, logging, psycopg2
|
||||
from threading import Timer
|
||||
|
||||
SESSIONTIMEOUT=120 # 2 minutes
|
||||
|
||||
class InvalidLoginError(Exception):
|
||||
"""
|
||||
Exception class for invalid logins.
|
||||
"""
|
||||
pass
|
||||
|
||||
class InvalidSessionError(Exception):
|
||||
"""
|
||||
Exception class for invalid sessions.
|
||||
"""
|
||||
pass
|
||||
|
||||
class Session:
|
||||
def __init__(self, id, login):
|
||||
self.id = id
|
||||
self.login = login
|
||||
self._timeoutTimer = None
|
||||
|
||||
def settimeoutTimer(self, timeoutTimer):
|
||||
self._timeoutTimer = timeoutTimer
|
||||
self._timeoutTimer.start()
|
||||
|
||||
def gettimeoutTimer(self):
|
||||
return self._timeoutTimer
|
||||
|
||||
class SessionManager:
|
||||
"""
|
||||
The Sessionmanager provides methods for login and session handling.
|
||||
"""
|
||||
def __init__(self, dbconn):
|
||||
self._sessions = {}
|
||||
self._dbconn = dbconn
|
||||
self._hashobj = sha.new(str(time.time()))
|
||||
self.logger = logging.getLogger('SessionManager')
|
||||
|
||||
def listSessions(self):
|
||||
return self._sessions.keys()
|
||||
|
||||
def newSession(self, login, password):
|
||||
cr = self._dbconn.cursor()
|
||||
cr.execute('SELECT * FROM sysuser WHERE name=%(login)s AND md5pass=md5(%(password)s)' %
|
||||
{'login': psycopg2.QuotedString(login),
|
||||
'password' : psycopg2.QuotedString(password)})
|
||||
self._dbconn.commit()
|
||||
result = cr.fetchall()
|
||||
if cr.rowcount == 1:
|
||||
self._hashobj.update("%s,%s" % (time.time(), login))
|
||||
sessionid = self._hashobj.hexdigest()
|
||||
self._sessions[sessionid] = Session(sessionid, login)
|
||||
self.updateSession(sessionid)
|
||||
self.logger.info('New session with id %s created for %s' %
|
||||
(sessionid, login))
|
||||
return sessionid
|
||||
self.logger.info('Login for %s failed' % login)
|
||||
raise InvalidLoginError
|
||||
|
||||
def updateSession(self, sessionid):
|
||||
self.logger.debug("update session %s" % sessionid)
|
||||
try:
|
||||
session = self.getSession(sessionid)
|
||||
except InvalidSessionError, ev:
|
||||
pass
|
||||
else:
|
||||
if session.gettimeoutTimer() is not None:
|
||||
session.gettimeoutTimer().cancel()
|
||||
session.settimeoutTimer(Timer(SESSIONTIMEOUT, self.deleteSession,
|
||||
args=[sessionid]))
|
||||
|
||||
def getSession(self, sessionid):
|
||||
if self._sessions.has_key(sessionid):
|
||||
return self._sessions[sessionid]
|
||||
raise InvalidSessionError()
|
||||
|
||||
def deleteSession(self, sessionid):
|
||||
self.logger.debug("delete session %s" % sessionid)
|
||||
try:
|
||||
session = self.getSession(sessionid)
|
||||
except InvalidSessionError:
|
||||
print "invalid session"
|
||||
else:
|
||||
if session.gettimeoutTimer() is not None:
|
||||
session.gettimeoutTimer().cancel()
|
||||
del(self._sessions[sessionid])
|
||||
self.logger.debug("%d sessions remaining" % len(self.listSessions()))
|
|
@ -1,25 +0,0 @@
|
|||
#
|
||||
# Settings for gnuviech-admin tool backend
|
||||
# (c) 2006 Jan Dittberner <jan@dittberner.info>
|
||||
# $Id:Settings.py 847 2006-02-21 21:21:30Z jan $
|
||||
#
|
||||
ALLPREFIX = '/home/jan/gnvadmtest'
|
||||
|
||||
DBSETTINGS = { 'dbhost' : 'localhost',
|
||||
'dbuser' : 'gnuviech',
|
||||
'dbpassword' : 'SIKKnsyXsV5yU',
|
||||
'dbname' : 'gnuviechadmin' }
|
||||
# exim:CotOgigmeIk5
|
||||
# courier:jevhi3Wriav
|
||||
# gnuviech:SIKKnsyXsV5yU
|
||||
|
||||
RPCSERVERADDRESS = ('localhost', 8080)
|
||||
|
||||
MAIL_SENDER = 'root@gnuviech.info'
|
||||
MAIL_RECEIVER = 'root@gnuviech.info'
|
||||
POPGROUP = 'poponly'
|
||||
POPHOME = ALLPREFIX + '/home/mail/'
|
||||
WEBHOME = ALLPREFIX + '/home/www/'
|
||||
USERPREFIX = 'usr'
|
||||
|
||||
GNUVIECHADMINDIR = ALLPREFIX + '/var/lib/gnuviechadmin'
|
|
@ -1,9 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# (c) 2006 Jan Dittberner <jan@dittberner.info>
|
||||
# $Id$
|
||||
#
|
||||
"""
|
||||
This package contains classes for the gnuviech administration tool backend.
|
||||
"""
|
||||
from ServiceFacade import *
|
|
@ -1,55 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# (c) 2006 Jan Dittberner <jan@dittberner.info>
|
||||
# $Id$
|
||||
#
|
||||
import psycopg
|
||||
import getopt
|
||||
import sys
|
||||
from gvadm import PasswordTools, DomainTools
|
||||
|
||||
def usage():
|
||||
print """Usage information:
|
||||
=====================
|
||||
%(process)s -h|--help
|
||||
- prints this help text
|
||||
|
||||
%(process)s --domain=<domain> [--password=<password>]
|
||||
- adds a new pop user for the given domain
|
||||
- if the optional password is ommitted a generated one is used
|
||||
- the password is checked using cracklib
|
||||
- if the password is too weak a generated one is used
|
||||
""" % {'process': sys.argv[0]}
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
options = getopt.getopt(sys.argv[1:], "h", ['help', 'password=', 'domain='])
|
||||
except getopt.GetoptError:
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
if (not options[0] or
|
||||
dict(options[0]).has_key('-h') or
|
||||
dict(options[0]).has_key('--help') or
|
||||
not dict(options[0]).has_key('--domain') or
|
||||
not dict(options[0])['--domain'].strip()):
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
# specify the domain
|
||||
domain = None
|
||||
try:
|
||||
domain = DomainTools.Domain(dict(options[0])['--domain'])
|
||||
except DomainTools.InvalidDomain, iv:
|
||||
print iv
|
||||
sys.exit(1)
|
||||
|
||||
# specify the password
|
||||
password = None
|
||||
|
||||
if dict(options[0]).has_key('--password'):
|
||||
password = PasswordTools.check_password(dict(options[0])['--password'])
|
||||
if (password == None):
|
||||
password = PasswordTools.generate_password()
|
||||
|
||||
domain.makePopUser(password)
|
|
@ -1,59 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import psycopg, getopt, sys
|
||||
|
||||
from gvadm import PasswordTools, DomainTools
|
||||
|
||||
def usage():
|
||||
print """Usage information:
|
||||
=====================
|
||||
%(process)s -h|--help
|
||||
- prints this help text
|
||||
|
||||
%(process)s --domain=<domain> --user=<user> [--password=<password>]
|
||||
- updates the password of a pop user for the given domain
|
||||
- if the optional password is ommitted a generated one is used
|
||||
- the password is checked using cracklib
|
||||
- if the password is too weak a generated one is used
|
||||
""" % {'process': sys.argv[0]}
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
options = getopt.getopt(sys.argv[1:], "h", ['help', 'password=',
|
||||
'domain=', 'user='])
|
||||
except getopt.GetoptError:
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
if (not options[0] or
|
||||
dict(options[0]).has_key('-h') or
|
||||
dict(options[0]).has_key('--help') or
|
||||
not dict(options[0]).has_key('--domain') or
|
||||
not dict(options[0])['--domain'].strip() or
|
||||
not dict(options[0]).has_key('--user') or
|
||||
not dict(options[0])['--user'].strip()):
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
# specify the domain
|
||||
domain = None
|
||||
try:
|
||||
domain = DomainTools.Domain(dict(options[0])['--domain'])
|
||||
except DomainTools.InvalidDomain, iv:
|
||||
print iv
|
||||
sys.exit(1)
|
||||
|
||||
username = dict(options[0])['--user']
|
||||
if not domain.hasPopUser(username):
|
||||
print "Domain doesn't have pop user", username
|
||||
sys.exit(1)
|
||||
|
||||
# specify the password
|
||||
password = None
|
||||
|
||||
if dict(options[0]).has_key('--password'):
|
||||
password = PasswordTools.check_password(dict(options[0])['--password'])
|
||||
if (password == None):
|
||||
password = PasswordTools.generate_password()
|
||||
|
||||
domain.updatePopPassword(username, password)
|
|
@ -1,177 +0,0 @@
|
|||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
SET client_encoding = 'UNICODE';
|
||||
SET check_function_bodies = false;
|
||||
|
||||
SET SESSION AUTHORIZATION 'postgres';
|
||||
|
||||
BEGIN;
|
||||
|
||||
--
|
||||
-- Name: public; Type: ACL; Schema: -; Owner: postgres
|
||||
--
|
||||
|
||||
REVOKE ALL ON SCHEMA public FROM PUBLIC;
|
||||
GRANT ALL ON SCHEMA public TO PUBLIC;
|
||||
|
||||
SET SESSION AUTHORIZATION 'gnuviech';
|
||||
|
||||
SET search_path = public, pg_catalog;
|
||||
|
||||
--
|
||||
-- Name: country; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
CREATE TABLE country (
|
||||
countryid serial PRIMARY KEY,
|
||||
name character varying(40) NOT NULL
|
||||
);
|
||||
|
||||
REVOKE ALL ON TABLE country FROM PUBLIC;
|
||||
|
||||
--
|
||||
-- Name: reseller; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE reseller (
|
||||
resellerid serial PRIMARY KEY,
|
||||
name character varying(40) NOT NULL
|
||||
);
|
||||
|
||||
REVOKE ALL ON TABLE reseller FROM PUBLIC;
|
||||
|
||||
--
|
||||
-- Name: client; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE client (
|
||||
clientid serial PRIMARY KEY,
|
||||
firstname character varying(40) NOT NULL,
|
||||
lastname character varying(40) NOT NULL,
|
||||
address1 character varying(40) NOT NULL,
|
||||
address2 character varying(40),
|
||||
countryid integer NOT NULL REFERENCES country(countryid),
|
||||
town character varying(50) NOT NULL,
|
||||
zipcode character(5) NOT NULL,
|
||||
state character varying(40),
|
||||
active boolean DEFAULT false NOT NULL,
|
||||
phone character varying(20),
|
||||
mobile character varying(20),
|
||||
reseller_id integer REFERENCES reseller(resellerid),
|
||||
organisation character varying(200)
|
||||
);
|
||||
|
||||
REVOKE ALL ON TABLE client FROM PUBLIC;
|
||||
|
||||
--
|
||||
-- Name: email; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE email (
|
||||
emailid serial PRIMARY KEY,
|
||||
email character varying(128) NOT NULL,
|
||||
clientid integer NOT NULL REFERENCES client(clientid),
|
||||
verified boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
REVOKE ALL ON TABLE email FROM PUBLIC;
|
||||
|
||||
--
|
||||
-- Name: sysuser; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE sysuser (
|
||||
sysuserid serial PRIMARY KEY,
|
||||
name character varying(12) NOT NULL,
|
||||
"type" integer DEFAULT 0 NOT NULL,
|
||||
home character varying(128),
|
||||
shell boolean,
|
||||
"password" character varying(64),
|
||||
clientid integer NOT NULL REFERENCES client(clientid),
|
||||
toupdate boolean DEFAULT false NOT NULL,
|
||||
md5pass character varying(32),
|
||||
sysuid integer UNIQUE
|
||||
);
|
||||
|
||||
REVOKE ALL ON TABLE sysuser FROM PUBLIC;
|
||||
GRANT SELECT ON TABLE sysuser TO GROUP services;
|
||||
|
||||
--
|
||||
-- Name: domain; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE "domain" (
|
||||
domainid serial PRIMARY KEY,
|
||||
domainname character varying(64) NOT NULL,
|
||||
clientid integer NOT NULL REFERENCES client(clientid),
|
||||
status integer DEFAULT 0 NOT NULL,
|
||||
sysuserid integer NOT NULL REFERENCES sysuser(sysuserid)
|
||||
);
|
||||
|
||||
REVOKE ALL ON TABLE "domain" FROM PUBLIC;
|
||||
GRANT SELECT ON TABLE "domain" TO GROUP services;
|
||||
|
||||
--
|
||||
-- Name: zone; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE "zone" (
|
||||
zoneid serial PRIMARY KEY,
|
||||
"domain" integer NOT NULL REFERENCES domain(domainid),
|
||||
"type" character varying(5) NOT NULL,
|
||||
ttl integer NOT NULL,
|
||||
mxprio integer,
|
||||
resource character varying(256),
|
||||
toupdate boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
REVOKE ALL ON TABLE "zone" FROM PUBLIC;
|
||||
|
||||
--
|
||||
-- Name: mailpasswd; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE mailpasswd (
|
||||
id character varying(128) NOT NULL,
|
||||
crypt character varying(128) DEFAULT '' NOT NULL,
|
||||
clear character varying(128) DEFAULT '' NOT NULL,
|
||||
name character varying(128) DEFAULT '' NOT NULL,
|
||||
uid integer DEFAULT 65534 NOT NULL,
|
||||
gid integer DEFAULT 65534 NOT NULL,
|
||||
home character varying(255) DEFAULT '' NOT NULL,
|
||||
maildir character varying(255) DEFAULT '' NOT NULL,
|
||||
defaultdelivery character varying(255) DEFAULT '' NOT NULL,
|
||||
quota character varying(255) DEFAULT '' NOT NULL,
|
||||
spamcheck boolean DEFAULT false NOT NULL,
|
||||
sajunkscore integer
|
||||
);
|
||||
|
||||
REVOKE ALL ON TABLE mailpasswd FROM PUBLIC;
|
||||
GRANT ALL ON TABLE mailpasswd TO GROUP services;
|
||||
|
||||
--
|
||||
-- Name: mailalias; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE mailalias (
|
||||
email character varying(255) NOT NULL,
|
||||
"domain" character varying(255) NOT NULL,
|
||||
target character varying(255) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
REVOKE ALL ON TABLE mailalias FROM PUBLIC;
|
||||
GRANT ALL ON TABLE mailalias TO GROUP services;
|
||||
|
||||
--
|
||||
-- Name: country; Type: TABLE DATA; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
INSERT INTO country (countryid, name) VALUES (1, 'Deutschland');
|
||||
INSERT INTO country (countryid, name) VALUES (2, 'Schweiz');
|
||||
INSERT INTO country (countryid, name) VALUES (3, 'Österreich');
|
||||
|
||||
SET SESSION AUTHORIZATION 'postgres';
|
||||
|
||||
COMMIT;
|
|
@ -1,128 +0,0 @@
|
|||
"""GNUViech administration tool helper classes.
|
||||
|
||||
(c) 2004 Jan Dittberner <jan@gnuviech.info>
|
||||
"""
|
||||
|
||||
import os
|
||||
from log4py import Logger, FileAppender, LOGLEVEL_DEBUG
|
||||
|
||||
class GNVPrefs:
|
||||
"""This class has static variables for the settings of the GNUViech
|
||||
administration tool. These settings may be customized in the file
|
||||
gvadm.preferences."""
|
||||
# define standard values
|
||||
PWDMINLENGTH = 6
|
||||
PWDMAXLENGTH = 12
|
||||
PWDCHARS = range(ord('a'), ord('z'))
|
||||
PWDCHARS.extend(range(ord('A'), ord('Z')))
|
||||
PWDCHARS.extend(range(ord('0'), ord('9')))
|
||||
PWDCHARS.extend((ord('@'), ord('#'), ord(','), ord('.'), ord('*'),
|
||||
ord('+'), ord('-'), ord('='), ord('!'), ord('$'),
|
||||
ord('"')))
|
||||
USERPREFIX = "usr"
|
||||
BASEPREFIX = ""
|
||||
GVADMDIR = BASEPREFIX+"/etc/gvadm/"
|
||||
EXIMCONFDIR = BASEPREFIX+"/etc/exim/"
|
||||
VIRTUALDOMDIR = EXIMCONFDIR+"virtual/"
|
||||
HOMEDIR = BASEPREFIX+"/home"
|
||||
POPHOMEDIR = HOMEDIR+"/mail/"
|
||||
WEBHOMEDIR = HOMEDIR+"/www/"
|
||||
WEBLOGDIR = WEBHOMEDIR+"logs/"
|
||||
WEBSTATSDIR = WEBHOMEDIR+"stats/"
|
||||
LOGDIR = BASEPREFIX+"/var/log"
|
||||
LOGFILE = LOGDIR+"/gnvadm.log"
|
||||
USERTYPES = {
|
||||
"web" : {
|
||||
"minuid" : 10000,
|
||||
"maxuid" : 10999,
|
||||
"group" : "wwwusers",
|
||||
"fullname" : "Webuser %s",
|
||||
"home" : WEBHOMEDIR + "%s",
|
||||
"shell" : "/bin/true",
|
||||
"nohome" : 1,
|
||||
"disabledpass" : 1
|
||||
},
|
||||
"pop3" : {
|
||||
"minuid" : 20000,
|
||||
"maxuid" : 29999,
|
||||
"group" : "poponly",
|
||||
"fullname" : "Popuser %s",
|
||||
"home" : POPHOMEDIR + "%s",
|
||||
"shell" : "/bin/true",
|
||||
"nohome" : 1,
|
||||
"disabledpass" : 1
|
||||
}
|
||||
}
|
||||
# load custom settings
|
||||
execfile("gvadm.preferences")
|
||||
|
||||
def __init__(self):
|
||||
self.logger = self.getLogger(self)
|
||||
self.setupDirs()
|
||||
|
||||
def __repr__(self):
|
||||
items = dir(self)
|
||||
items.sort()
|
||||
return "gnuviech.GNVPrefs\n\t" + "\n\t".join(["%s = %s" %
|
||||
(item, getattr(self, item)) for item in items if getattr(self, item).__class__ in (str, int, list, dict)]) + "\n"
|
||||
|
||||
def setupDirs(self):
|
||||
"""Setup the directories and files required for proper operation of the
|
||||
GNUViech administration tool."""
|
||||
for directory in (self.BASEPREFIX,
|
||||
self.BASEPREFIX+"/etc",
|
||||
self.BASEPREFIX+"/var",
|
||||
self.GVADMDIR,
|
||||
self.EXIMCONFDIR,
|
||||
self.VIRTUALDOMDIR,
|
||||
self.HOMEDIR,
|
||||
self.POPHOMEDIR,
|
||||
self.WEBHOMEDIR,
|
||||
self.WEBLOGDIR,
|
||||
self.WEBSTATSDIR,
|
||||
self.LOGDIR):
|
||||
if (not os.access(directory, os.R_OK & os.X_OK)):
|
||||
print "making %s." % directory
|
||||
os.mkdir(directory)
|
||||
for required in (self.BASEPREFIX+"/etc/passwd",
|
||||
self.BASEPREFIX+"/etc/shadow",
|
||||
self.EXIMCONFDIR+"eximpasswords"):
|
||||
if (not os.access(required, os.R_OK)):
|
||||
print "creating %s." % required
|
||||
file = open(required, "w")
|
||||
file.close()
|
||||
|
||||
def getLogger(self, instance):
|
||||
logger = Logger().get_instance(instance)
|
||||
logger.remove_all_targets()
|
||||
logger.add_target(FileAppender(self.LOGFILE))
|
||||
logger.set_loglevel(LOGLEVEL_DEBUG)
|
||||
return logger
|
||||
|
||||
def getNextSysId(self, type):
|
||||
nextid = self.USERTYPES[type]["minuid"]
|
||||
file = open(self.BASEPREFIX+"/etc/passwd", "r")
|
||||
for line in file.readlines():
|
||||
pwdline = tools.splitPasswdLine(line)
|
||||
self.logger.debug(str(pwdline))
|
||||
uid = int(pwdline["uid"])
|
||||
if (uid in
|
||||
range(int(self.USERTYPES[type]["minuid"]),
|
||||
int(self.USERTYPES[type]["maxuid"]))
|
||||
and nextid <= uid): nextid = uid+1
|
||||
return nextid
|
||||
|
||||
def getGroupId(self, type): pass
|
||||
|
||||
def getFullName(self, type, username):
|
||||
return self.USERTYPES[type]["fullname"] % username
|
||||
|
||||
def getHomeDir(self, type, username):
|
||||
return self.USERTYPES[type]["home"] % username
|
||||
|
||||
def getShell(self, type):
|
||||
return self.USERTYPES[type]["shell"]
|
||||
|
||||
class NoAdmDirError(Exception):
|
||||
"""This exception is raised if the admin directory does'nt exist."""
|
||||
pass
|
|
@ -1,152 +0,0 @@
|
|||
"""Package for GNUViech Admin main types and functions
|
||||
|
||||
(c) Copyright 2004 Jan Dittberner, IT-Consulting & Solutions
|
||||
Germany
|
||||
"""
|
||||
|
||||
import os, pwd
|
||||
import gnuviech
|
||||
from gnuviech import sysuser
|
||||
|
||||
class DomainNotExistentError(Exception): pass
|
||||
|
||||
class DomainFileNotExistentError(Exception): pass
|
||||
|
||||
class GNVDomain:
|
||||
"""Represents a domain in the GNUViech admin tool"""
|
||||
|
||||
def __init__(self, domain, prefs):
|
||||
"""Initializes the domain object"""
|
||||
self.logger = prefs.getLogger(self)
|
||||
self.prefs = prefs
|
||||
self.name = domain
|
||||
self.webaccount = None
|
||||
self.zone = None
|
||||
self.statsusers = {}
|
||||
self.mailaliases = {}
|
||||
self.pop3accounts = {}
|
||||
try:
|
||||
self.__findUser()
|
||||
except gnuviech.NoAdmDirError:
|
||||
prefs.setupDirs()
|
||||
self.__init__(domain)
|
||||
except DomainFileNotExistentError:
|
||||
self.__createDomainFile()
|
||||
self.__init__(domain, prefs)
|
||||
except DomainNotExistentError:
|
||||
self.__createUser()
|
||||
self.createWebUser()
|
||||
|
||||
def __repr__(self):
|
||||
retval = "Domain "+self.name
|
||||
if not self.username is None:
|
||||
retval += ", User "+self.username
|
||||
else:
|
||||
retval += ", new domain"
|
||||
return retval
|
||||
|
||||
def __createDomainFile(self):
|
||||
"""Create the domain user id map file."""
|
||||
file = open(gnuviech.GNVPrefs.GVADMDIR+"domains", "w")
|
||||
file.close()
|
||||
|
||||
def __createUser(self):
|
||||
"""Create a user for the domain."""
|
||||
file = open(self.prefs.GVADMDIR+"domains", "r")
|
||||
id = 0
|
||||
for line in file.readlines():
|
||||
(key, value) = line.split(":")
|
||||
if (int(value) > id): id = int(value)
|
||||
file.close()
|
||||
id += 1
|
||||
file = open(self.prefs.GVADMDIR+"domains", "a")
|
||||
file.write("%s:%d\n" % (self.name, id))
|
||||
file.close()
|
||||
self.__findUser()
|
||||
|
||||
def __findUser(self):
|
||||
"""Finds the user for the domain."""
|
||||
self.username = None
|
||||
if (os.access(self.prefs.GVADMDIR, os.R_OK)):
|
||||
try:
|
||||
domainsfile = open(self.prefs.GVADMDIR+"domains", "r")
|
||||
for line in domainsfile.readlines():
|
||||
(key, value) = line.split(":")
|
||||
if (key == self.name):
|
||||
self.username = "%s%02d" % (
|
||||
self.prefs.USERPREFIX,
|
||||
int(value))
|
||||
domainsfile.close()
|
||||
if self.username is None:
|
||||
raise DomainNotExistentError
|
||||
except IOError:
|
||||
raise DomainFileNotExistentError
|
||||
else:
|
||||
raise gnuviech.NoAdmDirError
|
||||
|
||||
def getMaxPop3Id(self):
|
||||
maxid = 0
|
||||
try:
|
||||
passwdfile = open(gnuviech.GNVPrefs.BASEPREFIX+"/etc/passwd", "r")
|
||||
for line in passwdfile.readlines():
|
||||
(login, passwd, uid, gid, name, dir, shell) = line.split(":")
|
||||
if login.startswith(self.username + "p"):
|
||||
id = int(login[len(self.username):])
|
||||
print id
|
||||
if (id > maxid): maxid = id
|
||||
except IOError:
|
||||
pass
|
||||
return maxid
|
||||
|
||||
def getNextUser(self, usertype):
|
||||
"""Gets the next user for the given type."""
|
||||
if (usertype == "web"):
|
||||
return self.username
|
||||
if (usertype == "pop3"):
|
||||
return "%sp%d" % (self.username, self.getMaxPop3Id()+1)
|
||||
|
||||
def addPOP3Account(self, account):
|
||||
self.pop3accounts[account.localpart] = account
|
||||
|
||||
def addMailAlias(self, alias):
|
||||
self.mailaliases[alias.localpart] = alias
|
||||
|
||||
def createWebUser(self):
|
||||
try:
|
||||
self.webaccount = sysuser.SystemUser(self.prefs, self.username)
|
||||
except sysuser.UserNotInPasswdError:
|
||||
self.webaccount = sysuser.createUser(self.prefs, self.username,
|
||||
"web")
|
||||
self.logger.debug(str(self.webaccount))
|
||||
|
||||
# #!/bin/sh
|
||||
# . /usr/local/etc/preferences
|
||||
# if [ -n $USERPREFIX ]; then
|
||||
# USERPREFIX="usr"
|
||||
# fi
|
||||
# if [ $1 == "" ]; then
|
||||
# echo "usage: $0 <usernum>"
|
||||
# exit
|
||||
# fi
|
||||
|
||||
# NEWUSER="$USERPREFIX$1"
|
||||
# NEWHOME="/home/www/$NEWUSER"
|
||||
# LOGDIR="/home/www/logfiles/$NEWUSER"
|
||||
|
||||
# adduser --home "$NEWHOME" --shell /bin/true --no-create-home --firstuid 10000 --ingroup wwwusers --disabled-password --gecos "Webuser $NEWUSER" $NEWUSER
|
||||
|
||||
# echo "${NEWUSER}:${NEWPASS}" | chpasswd
|
||||
# mkdir -p "$NEWHOME/"{html,cgi-bin}
|
||||
# mkdir -p "$LOGDIR"
|
||||
# chown -Rc www-data.www-data "$LOGDIR"
|
||||
# chmod 0750 "$LOGDIR"
|
||||
# chown -Rc $NEWUSER.wwwusers "$NEWHOME"
|
||||
# mkdir -p "$NEWHOME/html/stats"
|
||||
# chown modlogan.wwwusers "$NEWHOME/html/stats"
|
||||
# htpasswd -bc "/home/www/${NEWUSER}stats" "${NEWUSER}" "${NEWPASS}"
|
||||
|
||||
# echo added new web user $NEWUSER with password $NEWPASS
|
||||
|
||||
if __name__ == "__main__":
|
||||
dom = GNVDomain("dittberner.info")
|
||||
print dom
|
|
@ -1,62 +0,0 @@
|
|||
from gnuviech import tools
|
||||
|
||||
class UserNotInPasswdError(Exception): pass
|
||||
|
||||
class NoPasswordInShadowError(Exception): pass
|
||||
|
||||
class SystemUser:
|
||||
def __init__(self, prefs, username):
|
||||
self.prefs = prefs
|
||||
self.logger = prefs.getLogger(self)
|
||||
self.getUser(username)
|
||||
self.logger.debug(str(self))
|
||||
|
||||
def getUser(self, username):
|
||||
pwdfile = open(self.prefs.BASEPREFIX+"/etc/passwd", "r")
|
||||
for line in pwdfile.readlines():
|
||||
pwdline = tools.splitPasswdLine(line)
|
||||
self.logger.debug("PWDLINE: %s" % pwdline)
|
||||
if pwdline["loginname"] == username:
|
||||
self.username = pwdline["loginname"]
|
||||
self.password = self.getPassword()
|
||||
self.uid = pwdline["uid"]
|
||||
self.gid = pwdline["gid"]
|
||||
self.fullname = pwdline["fullname"]
|
||||
self.homedir = pwdline["homedir"]
|
||||
self.shell = pwdline["shell"]
|
||||
return
|
||||
pwdfile.close()
|
||||
raise UserNotInPasswdError
|
||||
|
||||
def getPassword(self):
|
||||
shadowfile = open(self.prefs.BASEPREFIX+"/etc/shadow", "r")
|
||||
for line in shadowfile.readlines():
|
||||
shadowline = tools.splitShadowLine(line)
|
||||
self.logger.debug("SHADOWLINE: %s" % shadowline)
|
||||
if shadowline["loginname"] == self.username:
|
||||
shadowfile.close()
|
||||
return shadowline["passwordhash"]
|
||||
shadowfile.close()
|
||||
raise NoPasswordInShadowError
|
||||
|
||||
def createUser(prefs, username, type):
|
||||
line = ":".join((username, "x",
|
||||
str(prefs.getNextSysId(type)),
|
||||
str(prefs.getGroupId(type)),
|
||||
prefs.getFullName(type, username),
|
||||
prefs.getHomeDir(type, username),
|
||||
prefs.getShell(type)))
|
||||
passwdfile = open(prefs.BASEPREFIX+"/etc/passwd", "a")
|
||||
passwdfile.write("%s\n" % line)
|
||||
passwdfile.close()
|
||||
createShadowItem(prefs, username, type, tools.generatePassword())
|
||||
return SystemUser(prefs, username)
|
||||
|
||||
def createShadowItem(prefs, username, type, password):
|
||||
line = ":".join((username,
|
||||
tools.hashPassword(password, "md5"),
|
||||
str(tools.daysSince1970()),
|
||||
"0", "99999", "7", "", "", ""))
|
||||
shadowfile = open(prefs.BASEPREFIX+"/etc/shadow", "a")
|
||||
shadowfile.write("%s\n" % line)
|
||||
shadowfile.close()
|
|
@ -1,71 +0,0 @@
|
|||
"""Tool functions for GNUViech administration tool
|
||||
|
||||
(c) 2004 Jan Dittberner <jan@gnuviech.info>
|
||||
"""
|
||||
|
||||
import random, re
|
||||
from gnuviech import GNVPrefs
|
||||
try:
|
||||
from crypt import crypt
|
||||
except ImportError, ie:
|
||||
import sys
|
||||
sys.__stderr__.write("Unsupported platform without crypt: " + sys.platform)
|
||||
sys.exit()
|
||||
from time import time
|
||||
|
||||
def generatePassword():
|
||||
"""Generates a password from the chars in GNVPrefs.PWDCHARS with
|
||||
a length between GNVPrefs.PWDMINLENGTH and GNVPrefs.PWDMAXLENGTH."""
|
||||
return "".join([chr(char) for char in
|
||||
random.sample(GNVPrefs.PWDCHARS,
|
||||
random.randint(GNVPrefs.PWDMINLENGTH,
|
||||
GNVPrefs.PWDMAXLENGTH))])
|
||||
|
||||
def generateSalt():
|
||||
saltchars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
salt = []
|
||||
for i in range(8):
|
||||
salt.append(saltchars[random.randint(0, len(saltchars) - 1)])
|
||||
return "".join(salt)
|
||||
|
||||
def checkEmail(email):
|
||||
"""Returns a match object if the given email address is syntactically
|
||||
correct otherwise it returns None"""
|
||||
# regex for email check
|
||||
p = re.compile(r'^([a-zA-Z0-9_\-.]+)@([a-zA-Z0-9\-]+(\.|[a-zA-Z0-9\-]+)*\.[a-z]{2,5})$')
|
||||
return p.search(email)
|
||||
|
||||
def splitPasswdLine(line):
|
||||
loginname, password, uid, gid, fullname, directory, shell = line.split(":")
|
||||
return {
|
||||
"loginname" : loginname,
|
||||
"password" : password,
|
||||
"uid" : uid,
|
||||
"gid" : gid,
|
||||
"fullname" : fullname,
|
||||
"homedir" : directory,
|
||||
"shell" : shell
|
||||
}
|
||||
|
||||
def splitShadowLine(line):
|
||||
(loginname, passwordhash, lastchange, maychange, mustchange, warnexpire,
|
||||
disabled, disabledsince, reserved) = line.split(":")
|
||||
return {
|
||||
"loginname" : loginname,
|
||||
"passwordhash" : passwordhash,
|
||||
"lastchange" : lastchange,
|
||||
"maychange" : maychange,
|
||||
"mustchange" : mustchange,
|
||||
"warnexpire" : warnexpire,
|
||||
"disabled" : disabled,
|
||||
"disabledsince" : disabledsince,
|
||||
"reserved" : reserved
|
||||
}
|
||||
|
||||
def hashPassword(password, method="md5"):
|
||||
if (method == "md5"):
|
||||
return crypt(password, "$1$%s" % generateSalt())
|
||||
return crypt(password, generateSalt())
|
||||
|
||||
def daysSince1970():
|
||||
return int(time()/(3600*24))
|
|
@ -1,12 +0,0 @@
|
|||
USERPREFIX = "usr"
|
||||
BASEPREFIX = "../tmp"
|
||||
GVADMDIR = BASEPREFIX+"/etc/gvadm/"
|
||||
EXIMCONFDIR = BASEPREFIX+"/etc/exim4/"
|
||||
VIRTUALDOMDIR = EXIMCONFDIR+"virtual/"
|
||||
HOMEDIR = BASEPREFIX+"/home"
|
||||
POPHOMEDIR = HOMEDIR+"/mail/"
|
||||
WEBHOMEDIR = HOMEDIR+"/www/"
|
||||
WEBLOGDIR = WEBHOMEDIR+"logs/"
|
||||
WEBSTATSDIR = WEBHOMEDIR+"stats/"
|
||||
LOGDIR = BASEPREFIX+"/var/log"
|
||||
LOGFILE = LOGDIR+"/gnvadm.log"
|
|
@ -1,175 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import psycopg, pwd, grp, smtplib, os
|
||||
from email.MIMEText import MIMEText
|
||||
import Settings, PasswordTools
|
||||
|
||||
class InvalidDomain(Exception):
|
||||
"""This exception is raised if an invalid domain is used."""
|
||||
def __init__(self, domain):
|
||||
self.domain = domain
|
||||
|
||||
def __str__(self):
|
||||
return repr("Invalid domain %s" % (self.domain))
|
||||
|
||||
class NoSysuserForDomain(Exception):
|
||||
"""This exception is raised if no system user is associated with a domain."""
|
||||
def __init__(self, domain):
|
||||
self.domain = domain
|
||||
|
||||
def __str__(self):
|
||||
return repr("No system user for domain %s" % (self.domain))
|
||||
|
||||
class InvalidPopUser(Exception):
|
||||
"""This exception is raised if an invalid POP3/IMAP user has been specified."""
|
||||
def __init__(self, domain, username):
|
||||
self.domain = domain
|
||||
self.username = username
|
||||
|
||||
def __str__(self):
|
||||
return "Invalid POP3/IMAP user %s in domain %s." % (self.username, self.domain)
|
||||
|
||||
class Domain:
|
||||
"""A Domain representation object with service methods."""
|
||||
def __init__(self, domain):
|
||||
self.cnx = psycopg.connect("user=%(dbuser)s password=%(dbpassword)s dbname=%(dbname)s" % Settings.dbsettings)
|
||||
self.domain = domain
|
||||
self.validate_domain()
|
||||
|
||||
def validate_domain(self):
|
||||
"""This function validates whether the given domain is allowed.
|
||||
That means that the domain needs to be registered in the database.
|
||||
|
||||
If the domain is invalid InvalidDomain is raised."""
|
||||
cr = self.cnx.cursor()
|
||||
|
||||
cr.execute("SELECT * FROM domain WHERE domainname=%(name)s" %
|
||||
{'name': psycopg.QuotedString(self.domain)})
|
||||
self.cnx.commit()
|
||||
|
||||
result = cr.fetchall()
|
||||
if (not result):
|
||||
raise InvalidDomain(self)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.domain)
|
||||
|
||||
def getSysuser(self):
|
||||
"""Gets the system user id of the domain."""
|
||||
cr = self.cnx.cursor()
|
||||
|
||||
cr.execute("""SELECT sysuser.name FROM domain, sysuser
|
||||
WHERE domain.domainname=%(name)s
|
||||
AND domain.sysuserid=sysuser.sysuserid""" %
|
||||
{'name': psycopg.QuotedString(self.domain)})
|
||||
self.cnx.commit()
|
||||
|
||||
result = cr.fetchall()
|
||||
if (not result):
|
||||
raise NoSysuserForDomain(self)
|
||||
# return row 0, field 0
|
||||
return result[0][0]
|
||||
|
||||
def getNextPopUser(self):
|
||||
"""Gets the user id of the next available POP3/IMAP-user for the domain."""
|
||||
cr = self.cnx.cursor()
|
||||
|
||||
sysuser = self.getSysuser()
|
||||
|
||||
cr.execute("""SELECT max(id) FROM mailpasswd WHERE
|
||||
id LIKE %(username)s""" %
|
||||
{'username': psycopg.QuotedString(sysuser+'%')})
|
||||
self.cnx.commit()
|
||||
|
||||
result = cr.fetchall()
|
||||
if (not result):
|
||||
return sysuser + "p1"
|
||||
|
||||
maxpopuser = result[0][0]
|
||||
if (not maxpopuser):
|
||||
return sysuser + "p1"
|
||||
|
||||
num = int(maxpopuser[len(sysuser)+1:])+1
|
||||
return "%sp%d" % (sysuser, num)
|
||||
|
||||
def makePopUser(self, password):
|
||||
"""Creates a new POP3/IMAP-user for the domain using the given password."""
|
||||
cr = self.cnx.cursor()
|
||||
|
||||
sysuser = self.getSysuser()
|
||||
popaccount = self.getNextPopUser()
|
||||
crypted = PasswordTools.md5_crypt_password(password)
|
||||
uid = pwd.getpwnam(sysuser)[2]
|
||||
gid = grp.getgrnam(Settings.popgroup)[2]
|
||||
homedir = Settings.pophome + popaccount
|
||||
|
||||
os.mkdir(homedir, 0755)
|
||||
os.system("maildirmake \"%s/Maildir\"" % (homedir))
|
||||
os.system("chown -R %s.%s %s" % ( sysuser, Settings.popgroup, homedir ))
|
||||
|
||||
cr = self.cnx.cursor()
|
||||
cr.execute("""INSERT INTO mailpasswd (id, crypt, clear, uid, gid, home)
|
||||
VALUES (%(id)s, %(crypt)s, %(clear)s, %(uid)d, %(gid)d, %(home)s)""" % {
|
||||
'id': psycopg.QuotedString(popaccount),
|
||||
'crypt': psycopg.QuotedString(crypted),
|
||||
'clear': psycopg.QuotedString(password),
|
||||
'uid': uid, 'gid': gid,
|
||||
'home': psycopg.QuotedString(homedir)})
|
||||
self.cnx.commit()
|
||||
|
||||
text = """A new POP3/IMAP account has been created
|
||||
Domain: %(domain)s
|
||||
User: %(user)s
|
||||
Password: %(password)s""" % {'domain': self.domain,
|
||||
'user': popaccount,
|
||||
'password': password}
|
||||
themail = MIMEText(text)
|
||||
themail['Subject'] = "A new POP3/IMAP account has been created"
|
||||
themail['From'] = Settings.mailsender
|
||||
themail['To'] = Settings.mailreceiver
|
||||
|
||||
s = smtplib.SMTP()
|
||||
s.connect()
|
||||
s.sendmail(Settings.mailsender, [Settings.mailreceiver], themail.as_string())
|
||||
s.close()
|
||||
|
||||
def listPopUsers(self):
|
||||
sysuser = self.getSysuser()
|
||||
|
||||
cr = self.cnx.cursor()
|
||||
cr.execute("SELECT id FROM mailpasswd WHERE id LIKE %(user)s" % {
|
||||
'user': psycopg.QuotedString(sysuser + '%')})
|
||||
self.cnx.commit()
|
||||
|
||||
result = cr.fetchall()
|
||||
return [line[0] for line in result]
|
||||
|
||||
def hasPopUser(self, username):
|
||||
"""Checks whether the specified POP3/IMAP user exists in the domain."""
|
||||
return ([user for user in self.listPopUsers() if (user == username)])
|
||||
|
||||
def updatePopPassword(self, username, password=PasswordTools.generate_password()):
|
||||
"""Updates the password of the given POP3/IMAP user."""
|
||||
if self.hasPopUser(username):
|
||||
crypted = PasswordTools.md5_crypt_password(password)
|
||||
|
||||
cr = self.cnx.cursor()
|
||||
cr.execute("UPDATE mailpasswd SET clear=%(clear)s, crypt=%(crypt)s WHERE id=%(user)s" % {
|
||||
'clear': psycopg.QuotedString(password),
|
||||
'crypt': psycopg.QuotedString(crypted),
|
||||
'user': psycopg.QuotedString(username)})
|
||||
self.cnx.commit()
|
||||
print("updated password of user %s to %s" % (username, password))
|
||||
else:
|
||||
raise InvalidPopUser(self, username)
|
||||
|
||||
if __name__ == '__main__':
|
||||
domain = Domain('centrum-warenhaus-dresden.de')
|
||||
# list pop users
|
||||
print ", ".join(domain.listPopUsers())
|
||||
# check for not existing user
|
||||
#try:
|
||||
# domain.updatePopPassword('usr03p2', 'test')
|
||||
#except InvalidPopUser, ipu:
|
||||
# print ipu
|
||||
#domain.updatePopPassword('usr05p2')
|
|
@ -1,25 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import crypt, crack, popen2, random
|
||||
|
||||
def generate_password():
|
||||
(o, i, e) = popen2.popen3("apg -n 1 -m 8 -x 12 -a 0")
|
||||
|
||||
return "".join(o.readlines()).strip()
|
||||
|
||||
def check_password(password):
|
||||
try:
|
||||
return crack.VeryFascistCheck(password)
|
||||
except ValueError, ve:
|
||||
print "Weak password:", ve
|
||||
return None
|
||||
|
||||
def md5_crypt_password(password):
|
||||
salt = "".join([chr(letter) for letter in random.sample(range(ord('a'), ord('z')), 8)])
|
||||
|
||||
return crypt.crypt(password, '$1$' + salt)
|
||||
|
||||
if __name__ == '__main__':
|
||||
print check_password("test")
|
||||
print generate_password()
|
||||
print md5_crypt_password("test")
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""This package contains classes for the gnuviech administration tool
|
||||
backend."""
|
||||
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
#! /usr/bin/env python
|
||||
|
||||
import os, string
|
||||
import gnuviech
|
||||
#from GNVAdm import GNVDomain
|
||||
from gnuviech import GNVPrefs, tools
|
||||
from gnuviech.gnvdomain import GNVDomain
|
||||
|
||||
# if [ -n $USERPREFIX ]; then
|
||||
# USERPREFIX="usr"
|
||||
# fi
|
||||
|
||||
# if [ $1 == "" ]; then
|
||||
# echo "give <UserNumber>p<pop3Account> as parameter"
|
||||
# exit
|
||||
# fi
|
||||
|
||||
# NEWUSER="$USERPREFIX$1"
|
||||
# NEWHOME="/home/mail/$NEWUSER"
|
||||
# NEWPASS=$(apg -n 1 -a 1 -CL -m 8 -x 12)
|
||||
# echo $NEWHOME
|
||||
|
||||
# adduser --home "$NEWHOME" --shell /bin/true --no-create-home --firstuid 20000 --ingroup poponly --disabled-password --disabled-login --gecos "Popuser $NEWUSER" $NEWUSER
|
||||
|
||||
# mkdir -p "$NEWHOME"
|
||||
# chown -Rc $NEWUSER.poponly "$NEWHOME"
|
||||
# echo "${NEWUSER}:${NEWPASS}" | chpasswd
|
||||
|
||||
# echo "Herzlich willkommen auf dem GNU-Viech" | mail -s "Willkommen auf dem GNU-Viech" ${NEWUSER}
|
||||
# echo added new pop3 user $NEWUSER with password $NEWPASS
|
||||
|
||||
class MailAliasExists(Exception): pass
|
||||
|
||||
class POP3AccountExists(Exception): pass
|
||||
|
||||
class MailAccount:
|
||||
def __init__(self, domain, localpart):
|
||||
"Initialize a MailAccount instance for a given domain"
|
||||
if (not os.access(gnuviech.GNVPrefs.VIRTUALDOMDIR, os.R_OK & os.X_OK)):
|
||||
self.setupDirs()
|
||||
self.domain = domain
|
||||
self.localpart = localpart
|
||||
self.prefs = domain.prefs
|
||||
self.logger = domain.prefs.getLogger(self)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s@%s" % (self.localpart, self.domain.name)
|
||||
|
||||
class MailAlias(MailAccount):
|
||||
"""This represents a mail alias"""
|
||||
|
||||
def __init__(self, domain, localpart, target):
|
||||
"Initialize the POPAccount class for a given domain"
|
||||
if localpart in domain.mailaliases.keys():
|
||||
raise MailAliasExists
|
||||
MailAccount.__init__(self, domain, localpart)
|
||||
self.setTarget(target)
|
||||
|
||||
def setTarget(self, target):
|
||||
self.target = target
|
||||
self.logger.debug("setting target for alias %s to %s." %
|
||||
(str(self), self.target))
|
||||
# self.aliases = {}
|
||||
# self.readAll()
|
||||
|
||||
# def readAll():
|
||||
# """reads the aliasfile for the given domain"""
|
||||
# self.aliases = {}
|
||||
# if (os.access(gnuviech.GNVPrefs.VIRTUALDOMDIR, os.R_OK)):
|
||||
# try:
|
||||
# aliasfile = open(gnuviech.GNVPrefs.VIRTUALDOMDIR+self.domain.name , 'r')
|
||||
# for line in aliasfile.readlines():
|
||||
# keyvals = string.split(line,":",1)
|
||||
# self.aliases[keyvals[0]] = keyvals[1].strip()
|
||||
# aliasfile.close()
|
||||
# except IOError:
|
||||
# self.logger.error("couldn't read the aliasfile for "+self.domain.name+".")
|
||||
# else:
|
||||
# self.logger.error("couldn't read from "+gnuviech.GNVPrefs.VIRTUALDOMDIR+".")
|
||||
|
||||
# def writeAll(self):
|
||||
# """writes the aliasfile for the given domain with the aliases defined
|
||||
# in the dictionary object aliases"""
|
||||
# if (os.access(gnuviech.GNVPrefs.VIRTUALDOMDIR, os.W_OK)):
|
||||
# try:
|
||||
# aliasfile = open(gnuviech.GNVPrefs.VIRTUALDOMDIR+self.domain.name, 'w')
|
||||
# keys = self.aliases.keys();
|
||||
# keys.sort();
|
||||
# for key in keys:
|
||||
# aliasfile.write("%s:%s" % (key, self.aliases[key]) + "\n")
|
||||
# aliasfile.close()
|
||||
# except IOError:
|
||||
# self.logger.error("writing to aliasfile failed.")
|
||||
# else:
|
||||
# self.logger.error("no write access to directory "+gnuviech.GNVPrefs.VIRTUALDOMDIR+".")
|
||||
|
||||
# def setAlias(self, alias, target):
|
||||
# """sets a mail alias for given domain which directs the MTA to the
|
||||
# given target
|
||||
# """
|
||||
# self.readAll()
|
||||
# self.aliases[alias]=target
|
||||
# self.writeAll()
|
||||
|
||||
class POP3Account(MailAccount):
|
||||
"""This represents a pop 3 account"""
|
||||
|
||||
def __init__(self, domain, localpart):
|
||||
"""Creates a new pop3 mail account"""
|
||||
if localpart in domain.pop3accounts.keys():
|
||||
raise POP3AccountExists
|
||||
MailAccount.__init__(self, domain, localpart)
|
||||
self.logger.debug("adding address %s@%s." % (self.localpart,
|
||||
self.domain.name))
|
||||
self.setPassword(tools.generatePassword())
|
||||
self.setSysUser(domain.getNextUser("pop3"))
|
||||
self.domain.addMailAlias(MailAlias(self.domain,
|
||||
self.localpart, self.sysuser))
|
||||
|
||||
def setPassword(self, newpassword):
|
||||
self.password = newpassword
|
||||
self.logger.debug("set password for %s to %s." %
|
||||
(str(self), self.password))
|
||||
|
||||
def setSysUser(self, username):
|
||||
self.sysuser = username
|
||||
self.logger.debug("set system user for %s to %s" %
|
||||
(str(self), self.sysuser))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
prefs = GNVPrefs()
|
||||
domain = GNVDomain("test.local", prefs)
|
||||
domain.addPOP3Account(POP3Account(domain, "test"))
|
||||
domain.addMailAlias(MailAlias(domain, "klaus", "klaus@test.de"))
|
|
@ -1,50 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import unittest
|
||||
import psycopg
|
||||
|
||||
class TestDBConnection(unittest.TestCase):
|
||||
def setUp(self):
|
||||
"""
|
||||
set up database connection used in tests
|
||||
"""
|
||||
self.cnx = psycopg.connect("host=localhost user=gnuviech password=SIKKnsyXsV5yU dbname=gnuviechadmin")
|
||||
self.cr = self.cnx.cursor()
|
||||
|
||||
def testSelectMailAliases(self):
|
||||
"""
|
||||
select all mail aliases
|
||||
"""
|
||||
self.cr.execute('SELECT * FROM mailalias')
|
||||
self.cnx.commit()
|
||||
|
||||
print self.cr.description
|
||||
|
||||
result = self.cr.fetchall()
|
||||
for line in result:
|
||||
print line
|
||||
|
||||
def testSelectMailPasswd(self):
|
||||
"""
|
||||
select all mail passwords
|
||||
"""
|
||||
self.cr.execute('SELECT * FROM mailpasswd')
|
||||
self.cnx.commit()
|
||||
|
||||
print self.cr.description
|
||||
|
||||
result = cr.fetchall()
|
||||
for line in result:
|
||||
print line
|
||||
|
||||
def testSelectDomains(self):
|
||||
"""
|
||||
select all domains
|
||||
"""
|
||||
self.cr.execute('SELECT DISTINCT domain FROM mailalias')
|
||||
self.cnx.commit()
|
||||
|
||||
print self.cr.description
|
||||
|
||||
result = cr.fetchall()
|
||||
for line in result:
|
||||
print line
|
|
@ -1,8 +0,0 @@
|
|||
#
|
||||
# Business methoden
|
||||
#
|
||||
def login(login, password):
|
||||
if (('jan' == login) and ('jan' == password)):
|
||||
return 'XXX'
|
||||
else:
|
||||
return None
|
|
@ -1,39 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# (c) 2006 Jan Dittberner <jan@dittberner.info>
|
||||
# $Id$
|
||||
#
|
||||
|
||||
from ZSI.ServiceContainer import ServiceContainer
|
||||
from ZSI.dispatch import SOAPRequestHandler
|
||||
from gnuviechadmin_services_server import *
|
||||
|
||||
class mySOAPRequestHandler(SOAPRequestHandler):
|
||||
'''
|
||||
Own SOAP request handler implementation.
|
||||
'''
|
||||
def do_GET(self):
|
||||
'''
|
||||
Process the HTTP GET method, delivers service's WSDL.
|
||||
'''
|
||||
self.send_xml(service._wsdl)
|
||||
|
||||
def AsServer(iporhost='', port=80, services=(),
|
||||
RequestHandlerClass=SOAPRequestHandler):
|
||||
"""
|
||||
iporhost -- IP address or hostname to bind to
|
||||
port -- TCP port
|
||||
services -- list of service instances
|
||||
"""
|
||||
address = (iporhost, port)
|
||||
sc = ServiceContainer(address, RequestHandlerClass=RequestHandlerClass)
|
||||
for service in services:
|
||||
path = service.getPost()
|
||||
sc.setNode(service, path)
|
||||
sc.serve_forever()
|
||||
|
||||
service = gnuviechadmin()
|
||||
|
||||
if __name__ == '__main__':
|
||||
AsServer(iporhost='localhost', port=8080, services=[service],
|
||||
RequestHandlerClass=mySOAPRequestHandler)
|
|
@ -1,74 +0,0 @@
|
|||
from gnuviechadminCommon.gnuviechadmin_services import *
|
||||
from ZSI.ServiceContainer import ServiceSOAPBinding
|
||||
import BusinessMethods
|
||||
|
||||
class gnuviechadmin(ServiceSOAPBinding):
|
||||
soapAction = {
|
||||
'https://ssl.gnuviech.info/gnuviechadmin/Authenticate': 'soap_Authenticate',
|
||||
}
|
||||
_wsdl = """<?xml version=\"1.0\" ?>
|
||||
<!--
|
||||
Webservice description for gnuviechadmin
|
||||
(c) 2006 Jan Dittberner <jan@dittberner.info>
|
||||
Version: $Id$
|
||||
--><wsdl:definitions name=\"gnuviechadmin\" targetNamespace=\"https://ssl.gnuviech.info/gnuviechadmin/\" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" xmlns:tns=\"https://ssl.gnuviech.info/gnuviechadmin/\" xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
|
||||
<wsdl:types>
|
||||
<xsd:schema targetNamespace=\"https://ssl.gnuviech.info/gnuviechadmin/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
|
||||
<xsd:element name=\"AuthCode\" type=\"xsd:string\"/>
|
||||
<xsd:element name=\"LoginInfo\">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence maxOccurs=\"1\" minOccurs=\"1\">
|
||||
<xsd:element name=\"login\" type=\"xsd:string\"/>
|
||||
<xsd:element name=\"password\" type=\"xsd:string\"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
</wsdl:types>
|
||||
<wsdl:message name=\"AuthResponse\">
|
||||
<wsdl:part element=\"tns:AuthCode\" name=\"AuthResponse\"/>
|
||||
</wsdl:message>
|
||||
<wsdl:message name=\"AuthRequest\">
|
||||
<wsdl:part element=\"tns:LoginInfo\" name=\"AuthRequest\"/>
|
||||
</wsdl:message>
|
||||
<wsdl:portType name=\"gnuviechadmin\">
|
||||
<wsdl:operation name=\"Authenticate\">
|
||||
<wsdl:input message=\"tns:AuthRequest\"/>
|
||||
<wsdl:output message=\"tns:AuthResponse\"/>
|
||||
</wsdl:operation>
|
||||
</wsdl:portType>
|
||||
<wsdl:binding name=\"gnuviechadminSOAP\" type=\"tns:gnuviechadmin\">
|
||||
<soap:binding style=\"document\" transport=\"http://schemas.xmlsoap.org/soap/http\"/>
|
||||
<wsdl:operation name=\"Authenticate\">
|
||||
<soap:operation soapAction=\"https://ssl.gnuviech.info/gnuviechadmin/Authenticate\"/>
|
||||
<wsdl:input>
|
||||
<soap:body parts=\" AuthRequest\" use=\"literal\"/>
|
||||
</wsdl:input>
|
||||
<wsdl:output>
|
||||
<soap:body parts=\" AuthResponse\" use=\"literal\"/>
|
||||
</wsdl:output>
|
||||
</wsdl:operation>
|
||||
</wsdl:binding>
|
||||
<wsdl:service name=\"gnuviechadmin\">
|
||||
<wsdl:port binding=\"tns:gnuviechadminSOAP\" name=\"gnuviechadminSOAP\">
|
||||
<soap:address location=\"https://ssl.gnuviech.info/gnuviechadmin\"/>
|
||||
</wsdl:port>
|
||||
</wsdl:service>
|
||||
</wsdl:definitions>"""
|
||||
|
||||
def __init__(self, post='/gnuviechadmin', **kw):
|
||||
ServiceSOAPBinding.__init__(self, post)
|
||||
|
||||
|
||||
def soap_Authenticate(self, ps):
|
||||
# input vals in request object
|
||||
args = ps.Parse( AuthRequestWrapper )
|
||||
|
||||
# assign return values to response object
|
||||
class SimpleTypeWrapper(str): typecode = AuthResponseWrapper()
|
||||
|
||||
# WARNING specify value eg. SimpleTypeWrapper(1)
|
||||
response = SimpleTypeWrapper(BusinessMethods.login(login=args._login, password=args._password))
|
||||
|
||||
# Return the response
|
||||
return response
|
|
@ -1,43 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import gnuviech, sys
|
||||
import gnuviech.tools
|
||||
from gnuviech.gnvdomain import GNVDomain
|
||||
|
||||
class Test:
|
||||
def __init__(self, prefs):
|
||||
self.logger = prefs.getLogger(self)
|
||||
self.prefs = prefs
|
||||
|
||||
def doTest(self):
|
||||
self.logger.debug(str(prefs))
|
||||
minlen = 0
|
||||
avglen = 0
|
||||
maxlen = 0
|
||||
pwds = 20
|
||||
for i in range(pwds):
|
||||
pwd = gnuviech.tools.generatePassword()
|
||||
self.logger.debug("%02d: %s (%d)" % (i, pwd, len(pwd)))
|
||||
if (minlen == 0) or (len(pwd) < minlen): minlen = len(pwd)
|
||||
if (len(pwd) > maxlen): maxlen = len(pwd)
|
||||
avglen += len(pwd)
|
||||
avglen = avglen/pwds
|
||||
self.logger.debug("""average password length: %d
|
||||
minimum password length: %d
|
||||
maximum password length: %d""" % (avglen, minlen, maxlen))
|
||||
|
||||
for address in ('jan@dittberner.info', 'jan', 'jan@gnuelf#test.de',
|
||||
'd.arnstadt@gmx.net'):
|
||||
if gnuviech.tools.checkEmail(address):
|
||||
self.logger.debug("%s is a valid email address." % address)
|
||||
else:
|
||||
self.logger.debug("%s is an invalid email address." % address)
|
||||
|
||||
domain = GNVDomain("dittberner.info", self.prefs)
|
||||
self.logger.debug("Domain %s." % domain)
|
||||
domain = GNVDomain("jesusgemeindesohland.de", self.prefs)
|
||||
self.logger.debug("Domain %s." % domain)
|
||||
|
||||
if __name__ == "__main__":
|
||||
prefs = gnuviech.GNVPrefs()
|
||||
Test(prefs).doTest()
|
|
@ -1,30 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
"""
|
||||
XML-RPC server for gnuviech-admin tool
|
||||
(c) 2006 Jan Dittberner
|
||||
$Id$
|
||||
"""
|
||||
from DocXMLRPCServer import DocXMLRPCServer
|
||||
from GnuviechAdmin import ServiceFacade, Settings
|
||||
import logging, threading
|
||||
|
||||
logger = logging.getLogger()
|
||||
hdlr = logging.FileHandler('xmlrpcserver.log')
|
||||
f = logging.Formatter('%(asctime)s %(levelname)s %(module)s: %(message)s')
|
||||
hdlr.setFormatter(f)
|
||||
logger.addHandler(hdlr)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
server = DocXMLRPCServer(Settings.RPCSERVERADDRESS)
|
||||
server.register_introspection_functions()
|
||||
server.register_instance(ServiceFacade())
|
||||
|
||||
try:
|
||||
server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
server.server_close()
|
||||
|
||||
for thread in [t for t in threading.enumerate() if t.isAlive() \
|
||||
and isinstance(t, threading._Timer)]:
|
||||
logger.debug("Stopping timer thread %s" % thread.getName())
|
||||
thread.cancel()
|
28
bin/gva
Executable file
28
bin/gva
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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 sys import argv
|
||||
from gnuviechadmin.config import CONFIG
|
||||
from gnuviechadmin.cli import CommandLineInterface
|
||||
|
||||
CommandLineInterface(CONFIG, argv).run()
|
70
bin/gvaserver
Executable file
70
bin/gvaserver
Executable file
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2008, 2009 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 sys, os, logging.config, socket
|
||||
from gnuviechadmin.util.passwordutils import md5_crypt_password
|
||||
from SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
|
||||
logcfgs = ('gnuviechadmin/logging.cfg', '/etc/gnuviechadmin/logging.cfg',
|
||||
os.path.expanduser('~/.gva-logging.cfg'))
|
||||
for cfg in [x for x in logcfgs if os.path.exists(x)]:
|
||||
logging.config.fileConfig(cfg)
|
||||
|
||||
|
||||
def usage():
|
||||
print """%s <host> <port>
|
||||
|
||||
where host and port specify the connection parameters. Host is the
|
||||
hostname or ip address to listen on.
|
||||
""" % sys.argv[0]
|
||||
|
||||
|
||||
class GnuviechAdminXMLRPCServer(SimpleXMLRPCServer):
|
||||
def __init__(self, server_address, **kwargs):
|
||||
try:
|
||||
SimpleXMLRPCServer.__init__(self, server_address, **kwargs)
|
||||
except socket.gaierror, e:
|
||||
self.address_family = socket.AF_INET6
|
||||
SimpleXMLRPCServer.__init__(self, server_address, **kwargs)
|
||||
|
||||
|
||||
def main():
|
||||
if (sys.argv.__len__() < 3):
|
||||
usage()
|
||||
sys.exit()
|
||||
server = GnuviechAdminXMLRPCServer((sys.argv[1], int(sys.argv[2])),
|
||||
allow_none=True)
|
||||
server.register_introspection_functions()
|
||||
|
||||
from gnuviechadmin.xmlrpc import XMLRPCFacade
|
||||
from gnuviechadmin.xmlrpc.users import \
|
||||
ClientUserProvider, MailuserUserProvider, SysuserUserProvider
|
||||
server.register_instance(XMLRPCFacade('data',
|
||||
[ClientUserProvider,
|
||||
MailuserUserProvider,
|
||||
SysuserUserProvider],
|
||||
md5_crypt_password))
|
||||
server.serve_forever()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,6 +0,0 @@
|
|||
<?php
|
||||
$config["db_host"] = "localhost";
|
||||
$config["db_name"] = "gnuviechadmin";
|
||||
$config["db_user"] = "gnuviech";
|
||||
$config["db_pass"] = "test";
|
||||
?>
|
4
data/dbrepo/README
Normal file
4
data/dbrepo/README
Normal file
|
@ -0,0 +1,4 @@
|
|||
This is a database migration repository.
|
||||
|
||||
More information at
|
||||
http://trac.erosson.com/migrate
|
4
data/dbrepo/manage.py
Normal file
4
data/dbrepo/manage.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/python
|
||||
from migrate.versioning.shell import main
|
||||
|
||||
main(repository='data/dbrepo')
|
20
data/dbrepo/migrate.cfg
Normal file
20
data/dbrepo/migrate.cfg
Normal file
|
@ -0,0 +1,20 @@
|
|||
[db_settings]
|
||||
# Used to identify which repository this database is versioned under.
|
||||
# You can use the name of your project.
|
||||
repository_id=Gnuviechadmin Schema Repository
|
||||
|
||||
# The name of the database table used to track the schema version.
|
||||
# This name shouldn't already be used by your project.
|
||||
# If this is changed once a database is under version control, you'll need to
|
||||
# change the table name in each database too.
|
||||
version_table=migrate_version
|
||||
|
||||
# When committing a change script, Migrate will attempt to generate the
|
||||
# sql for all supported databases; normally, if one of them fails - probably
|
||||
# because you don't have that database installed - it is ignored and the
|
||||
# commit continues, perhaps ending successfully.
|
||||
# Databases in this list MUST compile successfully during a commit, or the
|
||||
# entire commit will fail. List the databases your application will actually
|
||||
# be using to ensure your updates to that database work properly.
|
||||
# This must be a list; example: ['postgres','sqlite']
|
||||
required_dbs=['postgres', 'sqlite']
|
1
data/dbrepo/versions/001_postgres_downgrade.sql
Normal file
1
data/dbrepo/versions/001_postgres_downgrade.sql
Normal file
|
@ -0,0 +1 @@
|
|||
DROP SCHEMA gva;
|
1
data/dbrepo/versions/001_postgres_upgrade.sql
Normal file
1
data/dbrepo/versions/001_postgres_upgrade.sql
Normal file
|
@ -0,0 +1 @@
|
|||
CREATE SCHEMA gva;
|
85
data/dbrepo/versions/002.py
Normal file
85
data/dbrepo/versions/002.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
from sqlalchemy import *
|
||||
from migrate import *
|
||||
from gnuviechadmin.config import CONFIG
|
||||
|
||||
dbschema = None
|
||||
if 'database.schema' in CONFIG:
|
||||
dbschema = CONFIG['database.schema']
|
||||
|
||||
meta = MetaData(migrate_engine)
|
||||
client = Table(
|
||||
'client', meta,
|
||||
Column('clientid', Integer, primary_key=True),
|
||||
Column('title', Unicode(10)),
|
||||
Column('firstname', Unicode(64), nullable=False),
|
||||
Column('lastname', Unicode(64), nullable=False),
|
||||
Column('address1', Unicode(64), nullable=False),
|
||||
Column('address2', Unicode(64)),
|
||||
Column('zip', String(7), nullable=False),
|
||||
Column('city', Unicode(64), nullable=False),
|
||||
Column('country', String(5), nullable=False),
|
||||
Column('phone', String(32), nullable=False),
|
||||
Column('mobile', String(32)),
|
||||
Column('fax', String(32)),
|
||||
Column('email', String(64), unique=True, nullable=False),
|
||||
schema = dbschema)
|
||||
sysuser = Table(
|
||||
'sysuser', meta,
|
||||
Column('sysuserid', Integer, primary_key=True),
|
||||
Column('username', String(12), nullable=False, unique=True),
|
||||
Column('usertype', Integer, nullable=False, default=0, index=True),
|
||||
Column('home', String(128)),
|
||||
Column('shell', Boolean, nullable=False, default=False),
|
||||
Column('clearpass', String(64)),
|
||||
Column('md5pass', String(34)),
|
||||
Column('clientid', Integer, ForeignKey(client.c.clientid),
|
||||
nullable=False),
|
||||
Column('sysuid', Integer, nullable=False, unique=True),
|
||||
Column('lastchange', DateTime, default=func.now()),
|
||||
schema = dbschema)
|
||||
domain = 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.c.sysuserid),
|
||||
nullable=False),
|
||||
schema = dbschema)
|
||||
record = Table(
|
||||
'record', meta,
|
||||
Column('recordid', Integer, primary_key=True),
|
||||
Column('domainid', Integer, ForeignKey(domain.c.domainid),
|
||||
nullable=False),
|
||||
Column('name', String(255)),
|
||||
Column('type', String(6)),
|
||||
Column('content', String(255)),
|
||||
Column('ttl', Integer),
|
||||
Column('prio', Integer),
|
||||
Column('change_date', Integer),
|
||||
schema = dbschema)
|
||||
supermaster = Table(
|
||||
'supermaster', meta,
|
||||
Column('ip', String(25), nullable=False),
|
||||
Column('nameserver', String(255), nullable=False),
|
||||
Column('account', Integer, ForeignKey(sysuser.c.sysuserid),
|
||||
nullable=False),
|
||||
schema = dbschema)
|
||||
|
||||
|
||||
def upgrade():
|
||||
client.create()
|
||||
sysuser.create()
|
||||
domain.create()
|
||||
record.create()
|
||||
supermaster.create()
|
||||
|
||||
|
||||
def downgrade():
|
||||
supermaster.drop()
|
||||
record.drop()
|
||||
domain.drop()
|
||||
sysuser.drop()
|
||||
client.drop()
|
52
data/dbrepo/versions/003.py
Normal file
52
data/dbrepo/versions/003.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
from sqlalchemy import *
|
||||
from migrate import *
|
||||
from gnuviechadmin.config import CONFIG
|
||||
|
||||
dbschema = None
|
||||
if 'database.schema' in CONFIG:
|
||||
dbschema = CONFIG['database.schema']
|
||||
|
||||
meta = MetaData(migrate_engine)
|
||||
domain = Table('domain', meta, schema = dbschema, autoload = True)
|
||||
mailaccount = Table(
|
||||
'mailaccount', meta,
|
||||
Column('mailaccountid', Integer, primary_key = True),
|
||||
Column('domainid', Integer, ForeignKey(domain.c.domainid),
|
||||
nullable = False),
|
||||
Column('mailaccount', String(12), nullable = False, unique = True),
|
||||
Column('clearpass', String(64)),
|
||||
Column('cryptpass', String(34)),
|
||||
Column('uid', Integer, nullable = False),
|
||||
Column('gid', Integer, nullable = False),
|
||||
Column('home', String(128), nullable = False),
|
||||
Column('spamcheck', Boolean, nullable = False, default = False),
|
||||
Column('sajunkscore', Integer),
|
||||
schema = dbschema)
|
||||
mailaddress = Table(
|
||||
'mailaddress', meta,
|
||||
Column('mailaddressid', Integer, primary_key = True),
|
||||
Column('domainid', Integer, ForeignKey(domain.c.domainid),
|
||||
nullable = False),
|
||||
Column('email', String(255), nullable = False),
|
||||
UniqueConstraint('email', 'domainid'),
|
||||
schema = dbschema)
|
||||
mailtarget = Table(
|
||||
'mailtarget', meta,
|
||||
Column('mailtargetid', Integer, primary_key = True),
|
||||
Column('mailaddressid', Integer, ForeignKey(mailaddress.c.mailaddressid),
|
||||
nullable = False),
|
||||
Column('target', String(128), nullable = False),
|
||||
UniqueConstraint('target', 'mailaddressid'),
|
||||
schema = dbschema)
|
||||
|
||||
|
||||
def upgrade():
|
||||
mailaccount.create()
|
||||
mailaddress.create()
|
||||
mailtarget.create()
|
||||
|
||||
|
||||
def downgrade():
|
||||
mailtarget.drop()
|
||||
mailaddress.drop()
|
||||
mailaccount.drop()
|
29
data/dbrepo/versions/004_Add_organization_table.py
Normal file
29
data/dbrepo/versions/004_Add_organization_table.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from sqlalchemy import MetaData, Table, Column, Integer, Unicode, ForeignKey
|
||||
from migrate import migrate_engine
|
||||
from migrate.changeset.schema import create_column
|
||||
from gnuviechadmin.config import CONFIG
|
||||
|
||||
dbschema = None
|
||||
if 'database.schema' in CONFIG:
|
||||
dbschema = CONFIG['database.schema']
|
||||
|
||||
meta = MetaData(migrate_engine)
|
||||
client = Table('client', meta, schema = dbschema, autoload = True)
|
||||
organization = Table(
|
||||
'organization', meta,
|
||||
Column('organizationid', Integer, primary_key = True),
|
||||
Column('name', Unicode(200), nullable = False, unique = True),
|
||||
schema = dbschema,
|
||||
useexisting = True)
|
||||
|
||||
def upgrade():
|
||||
organization.create()
|
||||
col = Column('organizationid', Integer,
|
||||
ForeignKey(organization.c.organizationid),
|
||||
nullable = True)
|
||||
create_column(col, client)
|
||||
|
||||
|
||||
def downgrade():
|
||||
client.c.organizationid.drop()
|
||||
organization.drop()
|
6
data/templates/domain/index.html
Normal file
6
data/templates/domain/index.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<head><title>Hier entsteht ein neuer Internetauftritt</title></head>
|
||||
<body>
|
||||
<p>Hier entsteht der Internetauftritt für www.${domain}.</p>
|
||||
</body>
|
||||
</html>
|
4
data/templates/domainconf/htaccess-stats
Normal file
4
data/templates/domainconf/htaccess-stats
Normal file
|
@ -0,0 +1,4 @@
|
|||
AuthType Basic
|
||||
AuthName "Statistics for ${domain}"
|
||||
AuthUserFile "${userfile}"
|
||||
require valid-user
|
42
data/templates/domainconf/modlogan.conf
Normal file
42
data/templates/domainconf/modlogan.conf
Normal file
|
@ -0,0 +1,42 @@
|
|||
[global]
|
||||
includepath = /etc/modlogan
|
||||
include = modlogan.def.conf,global
|
||||
|
||||
loadplugin = input_clf
|
||||
loadplugin = processor_web
|
||||
loadplugin = output_modlogan
|
||||
|
||||
statedir = ${statsdir}
|
||||
|
||||
incremental = 1
|
||||
enable_resolver = 1
|
||||
|
||||
read_ahead_limit = 100
|
||||
|
||||
[processor_web]
|
||||
# to group known bot attacks like Nimda and CodeRed
|
||||
include = group.url.conf,group_exploits
|
||||
# to use the german file extension descriptions uncomment the following
|
||||
#include = group.extension.conf,groupext_german
|
||||
# include the default config
|
||||
include = modlogan.def.conf,processor_web
|
||||
|
||||
# to only have pages listed, not files
|
||||
#hideurl="\.(?i:gif|png|jpe?g|css|js|class|mid|swf|mp3|mpg)$$"
|
||||
# to not show people they're reading the stats more often than people their page...
|
||||
hideurl="^/stats"
|
||||
|
||||
debug_searchengines = 1
|
||||
hidereferrer = "^http://([^.]+\.)*${domainesc}/"
|
||||
|
||||
## configure the output generator
|
||||
[output_modlogan]
|
||||
include = modlogan.def.conf,output_modlogan
|
||||
hostname = ${domain}
|
||||
|
||||
outputdir = ${statsdir}
|
||||
|
||||
## configure the parser
|
||||
[input_clf]
|
||||
include = modlogan.def.conf,input_clf
|
||||
inputfile = -
|
10
data/templates/domainconf/vhost.conf
Normal file
10
data/templates/domainconf/vhost.conf
Normal file
|
@ -0,0 +1,10 @@
|
|||
<VirtualHost ${ipaddr}:80>
|
||||
ServerName www.${domain}
|
||||
ServerAlias ${domain}
|
||||
|
||||
Alias /stats ${statsdir}
|
||||
DocumentRoot ${docroot}
|
||||
|
||||
ErrorLog ${logdir}/error.log
|
||||
CustomLog ${logdir}/access.log combined
|
||||
</VirtualHost>
|
0
data/templates/home/README
Normal file
0
data/templates/home/README
Normal file
8
data/templates/mails/create_client.txt
Normal file
8
data/templates/mails/create_client.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
A new client with the following data has been created:
|
||||
|
||||
Firstname : ${firstname}
|
||||
Lastname : ${lastname}
|
||||
Email : ${email}
|
||||
Address : ${address1}
|
||||
: ${zipcode} ${city}
|
||||
Phone : ${phone}
|
14
data/templates/mails/create_domain.txt
Normal file
14
data/templates/mails/create_domain.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
A new domain with the following data has been created:
|
||||
|
||||
System user : ${sysuser}
|
||||
Domain name : ${domain}
|
||||
Document root : ${docroot}
|
||||
Statistics password : ${statspass}
|
||||
|
||||
To enable the domain in apache use
|
||||
|
||||
sudo a2ensite ${domain}
|
||||
|
||||
You can access statistics for the domain at
|
||||
|
||||
http://www.${domain}/stats/
|
8
data/templates/mails/create_sysuser.txt
Normal file
8
data/templates/mails/create_sysuser.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
A new system user has been created
|
||||
|
||||
UID : ${uid}
|
||||
Client : ${firstname} ${lastname} <${email}>
|
||||
Username : ${username}
|
||||
Password : ${password}
|
||||
Home : ${home}
|
||||
Shell : ${shell}
|
278
database.sql
278
database.sql
|
@ -1,278 +0,0 @@
|
|||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
SET search_path = public, pg_catalog;
|
||||
|
||||
--
|
||||
-- TOC entry 2 (OID 17132)
|
||||
-- Name: country; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE country (
|
||||
id serial NOT NULL,
|
||||
name character varying(40) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 3 (OID 17137)
|
||||
-- Name: client; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE client (
|
||||
id serial NOT NULL,
|
||||
firstname character varying(40) NOT NULL,
|
||||
lastname character varying(40) NOT NULL,
|
||||
address1 character varying(40) NOT NULL,
|
||||
address2 character varying(40),
|
||||
country integer NOT NULL,
|
||||
town character varying(50) NOT NULL,
|
||||
zipcode character(5) NOT NULL,
|
||||
state character varying(40),
|
||||
active boolean DEFAULT false NOT NULL,
|
||||
phone character varying(20),
|
||||
mobile character varying(20),
|
||||
reseller_id integer
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 4 (OID 17143)
|
||||
-- Name: reseller; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE reseller (
|
||||
id serial NOT NULL,
|
||||
name character varying(40) NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 5 (OID 17148)
|
||||
-- Name: email; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE email (
|
||||
id serial NOT NULL,
|
||||
email character varying(128) NOT NULL,
|
||||
client integer,
|
||||
verified boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 6 (OID 17154)
|
||||
-- Name: sysuser; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE sysuser (
|
||||
id serial NOT NULL,
|
||||
name character varying(12) NOT NULL,
|
||||
"type" integer DEFAULT 0 NOT NULL,
|
||||
home character varying(128),
|
||||
shell boolean,
|
||||
"password" character varying(64),
|
||||
client integer NOT NULL,
|
||||
toupdate boolean DEFAULT false NOT NULL,
|
||||
md5pass character varying(32),
|
||||
sysuid integer
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 7 (OID 17161)
|
||||
-- Name: domain; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE "domain" (
|
||||
id serial NOT NULL,
|
||||
domainname character varying(64) NOT NULL,
|
||||
client integer NOT NULL,
|
||||
status integer DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 8 (OID 17167)
|
||||
-- Name: zone; Type: TABLE; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE TABLE "zone" (
|
||||
id serial NOT NULL,
|
||||
"domain" integer NOT NULL,
|
||||
"type" character varying(5) NOT NULL,
|
||||
ttl integer NOT NULL,
|
||||
mxprio integer,
|
||||
resource character varying(256),
|
||||
toupdate boolean DEFAULT false NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 12 (OID 17171)
|
||||
-- Name: reseller_name_idx; Type: INDEX; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE UNIQUE INDEX reseller_name_idx ON reseller USING btree (name);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 9 (OID 17172)
|
||||
-- Name: country_name_idx; Type: INDEX; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE UNIQUE INDEX country_name_idx ON country USING btree (name);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 19 (OID 17173)
|
||||
-- Name: sysuser_type_idx; Type: INDEX; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE INDEX sysuser_type_idx ON sysuser USING btree ("type");
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 18 (OID 17231)
|
||||
-- Name: sysuser_sysuid_idx; Type: INDEX; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
CREATE UNIQUE INDEX sysuser_sysuid_idx ON sysuser USING btree (sysuid);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 10 (OID 17174)
|
||||
-- Name: country_pkey; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY country
|
||||
ADD CONSTRAINT country_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 11 (OID 17176)
|
||||
-- Name: client_pkey; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY client
|
||||
ADD CONSTRAINT client_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 22 (OID 17178)
|
||||
-- Name: $1; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY client
|
||||
ADD CONSTRAINT "$1" FOREIGN KEY (country) REFERENCES country(id) ON UPDATE NO ACTION ON DELETE NO ACTION;
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 13 (OID 17182)
|
||||
-- Name: reseller_pkey; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY reseller
|
||||
ADD CONSTRAINT reseller_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 23 (OID 17184)
|
||||
-- Name: $2; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY client
|
||||
ADD CONSTRAINT "$2" FOREIGN KEY (reseller_id) REFERENCES reseller(id) ON UPDATE NO ACTION ON DELETE NO ACTION;
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 15 (OID 17188)
|
||||
-- Name: email_pkey; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY email
|
||||
ADD CONSTRAINT email_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 14 (OID 17190)
|
||||
-- Name: email_email_key; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY email
|
||||
ADD CONSTRAINT email_email_key UNIQUE (email);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 24 (OID 17192)
|
||||
-- Name: $1; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY email
|
||||
ADD CONSTRAINT "$1" FOREIGN KEY (client) REFERENCES client(id) ON UPDATE NO ACTION ON DELETE NO ACTION;
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 17 (OID 17196)
|
||||
-- Name: sysuser_pkey; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY sysuser
|
||||
ADD CONSTRAINT sysuser_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 16 (OID 17198)
|
||||
-- Name: sysuser_name_key; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY sysuser
|
||||
ADD CONSTRAINT sysuser_name_key UNIQUE (name);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 25 (OID 17200)
|
||||
-- Name: $1; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY sysuser
|
||||
ADD CONSTRAINT "$1" FOREIGN KEY (client) REFERENCES client(id) ON UPDATE NO ACTION ON DELETE NO ACTION;
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 20 (OID 17204)
|
||||
-- Name: domain_pkey; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY "domain"
|
||||
ADD CONSTRAINT domain_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 26 (OID 17206)
|
||||
-- Name: $1; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY "domain"
|
||||
ADD CONSTRAINT "$1" FOREIGN KEY (client) REFERENCES client(id) ON UPDATE NO ACTION ON DELETE NO ACTION;
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 21 (OID 17210)
|
||||
-- Name: zone_pkey; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY "zone"
|
||||
ADD CONSTRAINT zone_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- TOC entry 27 (OID 17212)
|
||||
-- Name: $1; Type: CONSTRAINT; Schema: public; Owner: gnuviech
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY "zone"
|
||||
ADD CONSTRAINT "$1" FOREIGN KEY ("domain") REFERENCES "domain"(id) ON UPDATE NO ACTION ON DELETE NO ACTION;
|
||||
|
||||
|
62
development.ini
Normal file
62
development.ini
Normal file
|
@ -0,0 +1,62 @@
|
|||
[DEFAULT]
|
||||
mailfrom = gva@gnuviech.info
|
||||
mailto = jan@dittberner.info
|
||||
|
||||
suwrapper = sudo
|
||||
backupdir = /var/backups/gnuviechadmin
|
||||
|
||||
[app:main]
|
||||
use = egg:gnuviechadmin#cli
|
||||
|
||||
# The database connection string in a format usable for
|
||||
# sqlalchemy. The default is an sqlite in memory database which is not
|
||||
# very usable for a real installation.
|
||||
#
|
||||
sqlalchemy.uri = postgres://localhost/gvatest
|
||||
sqlalchemy.echo = false
|
||||
|
||||
database.repository = %(here)s/data/dbrepo
|
||||
migrate.required_version = 4
|
||||
templatedir = %(here)s/data/templates
|
||||
mailtemplates = %(templatedir)s/mails
|
||||
|
||||
client.defaultcountry = de
|
||||
client.create.mail = create_client.txt
|
||||
client.create_subject = A new client ${firstname} ${lastname} has been created.
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root, gnuviechadmin, sqlalchemy
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = INFO
|
||||
handlers = console
|
||||
|
||||
[logger_gnuviechadmin]
|
||||
level = DEBUG
|
||||
handlers =
|
||||
qualname = gnuviechadmin
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
# "level = INFO" logs SQL queries.
|
||||
# "level = DEBUG" logs SQL queries and results.
|
||||
# "level = WARN" logs neither. (Recommended for production systems.)
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
BIN
doc/er_gnuviechadmin.png
Normal file
BIN
doc/er_gnuviechadmin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
9
doc/gnupg/7dc3f0c0-rev.asc
Normal file
9
doc/gnupg/7dc3f0c0-rev.asc
Normal file
|
@ -0,0 +1,9 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1.4.6 (GNU/Linux)
|
||||
Comment: A revocation certificate should follow
|
||||
|
||||
iF8EIBECAB8FAkWY+1cYHQNSZXZvY2F0aW9uIGNlcnRpZmljYXRlAAoJEOfoxRR9
|
||||
w/DAzhMAniSnjpqj7kqOl5j3AHaFbtFp101nAJ4/gJQ2ZIj5dWg/iDkVZb7qu1Bw
|
||||
kA==
|
||||
=1DuH
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
9
doc/gnuviech-migration.txt
Normal file
9
doc/gnuviech-migration.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
Ansehen:
|
||||
|
||||
/etc/exim4/conf.d/main/02_exim4-config_options
|
||||
/etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs
|
||||
/etc/exim4/exim4.conf.template
|
||||
|
||||
Ausprobieren:
|
||||
|
||||
awstats
|
47
doc/gnuviechadmin.mm
Normal file
47
doc/gnuviechadmin.mm
Normal file
|
@ -0,0 +1,47 @@
|
|||
<map version="0.7.1">
|
||||
<node TEXT="gnuviechadmin">
|
||||
<node TEXT="Kundenverwaltung" POSITION="right">
|
||||
<node TEXT="Kunde erfassen"/>
|
||||
<node TEXT="Adressdaten ändern"/>
|
||||
<node TEXT="Webuser anlegen">
|
||||
<node TEXT="Mail mit Login"/>
|
||||
<node TEXT="home mit sshchroot-Umgebung"/>
|
||||
<node TEXT="quota setzen"/>
|
||||
</node>
|
||||
</node>
|
||||
<node TEXT="Domain anlegen" POSITION="right">
|
||||
<node TEXT="Kundenzuordnung"/>
|
||||
<node TEXT="DNS-Zone anlegen">
|
||||
<node TEXT="aus Template"/>
|
||||
</node>
|
||||
<node TEXT="Apache-Konfiguration anlegen">
|
||||
<node TEXT="Frameweiterleitung"/>
|
||||
<node TEXT="Defaultseite"/>
|
||||
<node TEXT="Webuser zuordnen"/>
|
||||
</node>
|
||||
</node>
|
||||
<node TEXT="Domainverwaltung" POSITION="right">
|
||||
<node TEXT="Mailaccounts verwalten">
|
||||
<node TEXT="Mailaccounts anlegen">
|
||||
<node TEXT="Mail mit Accountdaten"/>
|
||||
</node>
|
||||
<node TEXT="Mailpasswörter ändern">
|
||||
<node TEXT="Mail mit Accountdaten"/>
|
||||
</node>
|
||||
<node TEXT="Mailadressen anlegen">
|
||||
<node TEXT="auf Account ablegen"/>
|
||||
<node TEXT="an Adresse(n) weiterleiten"/>
|
||||
<node TEXT="Mail mit Weiterleitungsdaten"/>
|
||||
</node>
|
||||
</node>
|
||||
<node TEXT="Apache-Einstellungen ändern">
|
||||
<node TEXT="Spezialeinstellungen"/>
|
||||
<node TEXT="Subdomains mit Extra-Documentroot"/>
|
||||
</node>
|
||||
<node TEXT="SCP-Passwort ändern"/>
|
||||
<node TEXT="mehrere Domains auf gleichen SCP-Nutzer"/>
|
||||
<node TEXT="Selfservice-Funktionen"/>
|
||||
</node>
|
||||
<node TEXT="Rechnungsmodul" POSITION="left"/>
|
||||
</node>
|
||||
</map>
|
|
@ -1,29 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# XML-RPC-Client
|
||||
# (c) 2006 Jan Dittberner
|
||||
# version: $Id$
|
||||
#
|
||||
from xmlrpclib import ServerProxy
|
||||
import logging, sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
server = ServerProxy('http://localhost:8080')
|
||||
logger = logging.getLogger()
|
||||
logging.basicConfig()
|
||||
|
||||
sessionid = None
|
||||
try:
|
||||
sessionid = server.login('jan', 'heyyou97')
|
||||
if sessionid:
|
||||
print "Session %s" % sessionid
|
||||
print server.listdomains(sessionid)
|
||||
server.logout(sessionid)
|
||||
else:
|
||||
print "login failed"
|
||||
except Exception, v:
|
||||
logger.exception(v)
|
||||
if sessionid is not None:
|
||||
try:
|
||||
server.logout(sessionid)
|
||||
except Exception, vn:
|
||||
logger.exception(vn)
|
|
@ -1,42 +0,0 @@
|
|||
from mod_python import apache, Session
|
||||
from genshi.template import TemplateLoader, TemplateNotFound
|
||||
from genshi import ParseError
|
||||
|
||||
def findtemplate(uri):
|
||||
templates = {"/" : "index.xml"}
|
||||
if uri in templates:
|
||||
return templates[uri]
|
||||
return None
|
||||
|
||||
def handler(req):
|
||||
session = Session.Session(req)
|
||||
try:
|
||||
session['hits'] += 1
|
||||
except:
|
||||
session['hits'] = 1
|
||||
|
||||
session.save()
|
||||
|
||||
template = findtemplate(req.uri)
|
||||
if template:
|
||||
|
||||
loader = TemplateLoader([req.document_root()])
|
||||
try:
|
||||
req.content_type = "text/html; charset=UTF-8"
|
||||
tmpl = loader.load(template)
|
||||
stream = tmpl.generate(title='Hello World: Reloaded',
|
||||
hits=session['hits'])
|
||||
pagebuffer = stream.render('xhtml')
|
||||
except TemplateNotFound, tnf:
|
||||
req.content_type = "text/plain; charset=UTF-8"
|
||||
pagebuffer = str(tnf)
|
||||
except ParseError, pe:
|
||||
req.content_type = "text/plain; charset=UTF-8"
|
||||
pagebuffer = str(pe)
|
||||
|
||||
#pagebuffer = "Hits: %d\n" % session['hits']
|
||||
#pagebuffer += "Yippieh: I found %s -> %s!" % (req.uri, template)
|
||||
|
||||
req.write(pagebuffer)
|
||||
return (apache.OK)
|
||||
return (apache.HTTP_NOT_FOUND)
|
|
@ -1,9 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>$title</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>$title</h1>
|
||||
<p>Hits: $hits</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,88 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
from soapclient import soapclient
|
||||
|
||||
class GnuviechAdminGnome:
|
||||
"""GnuviechAdmin Gnome Interface"""
|
||||
_version = '0.1'
|
||||
|
||||
def delete_event(self, widget, event, data = None):
|
||||
return False
|
||||
|
||||
def duplicate_text(self, widget, data = None):
|
||||
text = self.textfield.get_text()
|
||||
self.label.set_text(self.soapclient.echo(text))
|
||||
|
||||
def destroy_event(self, widget, data = None):
|
||||
gtk.main_quit()
|
||||
|
||||
def show_aboutbox(self, widget, data = None):
|
||||
aboutbox = gtk.AboutDialog()
|
||||
aboutbox.set_authors(['Jan Dittberner <jan@dittberner.info>'])
|
||||
aboutbox.set_name("GnuviechAdmin\nGNOME frontend")
|
||||
aboutbox.set_copyright('(c) 2006 Jan Dittberner')
|
||||
aboutbox.set_license('This program is licensed under the terms of the GNU General Public License (GPL) version 2 or later.')
|
||||
aboutbox.set_version(self._version)
|
||||
aboutbox.show()
|
||||
|
||||
def _create_menubar(self):
|
||||
menubar = gtk.MenuBar()
|
||||
menu = gtk.Menu()
|
||||
menuitem = gtk.MenuItem(label = '_Exit')
|
||||
menuitem.connect_object("activate", gtk.Widget.destroy, self.window)
|
||||
|
||||
menu.append(menuitem)
|
||||
|
||||
rootmenu = gtk.MenuItem('_File')
|
||||
rootmenu.set_submenu(menu)
|
||||
|
||||
menubar.append(rootmenu)
|
||||
|
||||
menu = gtk.Menu()
|
||||
menuitem = gtk.MenuItem(label = '_About')
|
||||
menuitem.connect("activate", self.show_aboutbox)
|
||||
|
||||
menu.append(menuitem)
|
||||
|
||||
rootmenu = gtk.MenuItem('_Help')
|
||||
rootmenu.set_right_justified(True)
|
||||
rootmenu.set_submenu(menu)
|
||||
|
||||
menubar.append(rootmenu)
|
||||
return menubar
|
||||
|
||||
def __init__( self ):
|
||||
self.soapclient = soapclient.SOAPClient()
|
||||
|
||||
self.window = gtk.Window()
|
||||
self.window.connect("delete_event", self.delete_event)
|
||||
self.window.connect("destroy", self.destroy_event)
|
||||
|
||||
button = gtk.Button("Hello world")
|
||||
#button.connect_object("clicked", gtk.Widget.destroy, self.window)
|
||||
|
||||
self.textfield = gtk.Entry()
|
||||
button.connect("clicked", self.duplicate_text)
|
||||
|
||||
self.label = gtk.Label()
|
||||
menubar = self._create_menubar()
|
||||
|
||||
vbox = gtk.VBox()
|
||||
vbox.add(menubar)
|
||||
vbox.add(button)
|
||||
vbox.add(self.textfield)
|
||||
vbox.add(self.label)
|
||||
|
||||
self.window.add(vbox)
|
||||
|
||||
self.window.show_all()
|
||||
|
||||
def main(self):
|
||||
gtk.main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
g = GnuviechAdminGnome()
|
||||
g.main()
|
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
"""
|
||||
This package contains the SOAP client for the gnuviech admin tool
|
||||
"""
|
|
@ -1,23 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
import sys
|
||||
import socket
|
||||
from ZSI import ServiceProxy
|
||||
|
||||
class SOAPClient:
|
||||
def __init__(self):
|
||||
url = 'http://localhost:8080/gnuviechadmin'
|
||||
self.service = ServiceProxy(url, use_wsdl=True,
|
||||
tracefile=sys.stdout)
|
||||
print 'service is ', self.service
|
||||
print self.service.__dict__
|
||||
|
||||
def authenticate(self, login, password):
|
||||
try:
|
||||
response = self.service.Authenticate(login=login, password=password)
|
||||
print response
|
||||
except socket.error:
|
||||
return "couldn't connect"
|
||||
|
||||
if __name__ == "__main__":
|
||||
client = SOAPClient()
|
||||
print client.authenticate("jan", "jan")
|
13
gnuviechadmin.egg-info/PKG-INFO
Normal file
13
gnuviechadmin.egg-info/PKG-INFO
Normal file
|
@ -0,0 +1,13 @@
|
|||
Metadata-Version: 1.0
|
||||
Name: gnuviechadmin
|
||||
Version: 0.1.dev-20090802
|
||||
Summary: gnuviechadmin server administration suite
|
||||
Home-page: http://www.gnuviech-server.de/projects/gnuviechadmin
|
||||
Author: Jan Dittberner
|
||||
Author-email: jan@dittberner.info
|
||||
License: GPL
|
||||
Description: this is a suite of tools for administering a server
|
||||
it contains tools for maintaining e.g. clients, domains, users, mail
|
||||
accounts
|
||||
Keywords: administration backend frontend
|
||||
Platform: UNKNOWN
|
41
gnuviechadmin.egg-info/SOURCES.txt
Normal file
41
gnuviechadmin.egg-info/SOURCES.txt
Normal file
|
@ -0,0 +1,41 @@
|
|||
setup.cfg
|
||||
setup.py
|
||||
bin/gva
|
||||
bin/gvaserver
|
||||
gnuviechadmin/__init__.py
|
||||
gnuviechadmin/config.py
|
||||
gnuviechadmin/exceptions.py
|
||||
gnuviechadmin.egg-info/PKG-INFO
|
||||
gnuviechadmin.egg-info/SOURCES.txt
|
||||
gnuviechadmin.egg-info/dependency_links.txt
|
||||
gnuviechadmin.egg-info/entry_points.txt
|
||||
gnuviechadmin.egg-info/requires.txt
|
||||
gnuviechadmin.egg-info/top_level.txt
|
||||
gnuviechadmin/backend/BackendEntity.py
|
||||
gnuviechadmin/backend/BackendEntityHandler.py
|
||||
gnuviechadmin/backend/BackendTo.py
|
||||
gnuviechadmin/backend/__init__.py
|
||||
gnuviechadmin/backend/client.py
|
||||
gnuviechadmin/backend/domain.py
|
||||
gnuviechadmin/backend/record.py
|
||||
gnuviechadmin/backend/settings.py
|
||||
gnuviechadmin/backend/sysuser.py
|
||||
gnuviechadmin/backend/tables.py
|
||||
gnuviechadmin/cli/CliCommand.py
|
||||
gnuviechadmin/cli/__init__.py
|
||||
gnuviechadmin/cli/client.py
|
||||
gnuviechadmin/cli/domain.py
|
||||
gnuviechadmin/cli/record.py
|
||||
gnuviechadmin/cli/sysuser.py
|
||||
gnuviechadmin/tests/__init__.py
|
||||
gnuviechadmin/tests/functional/__init__.py
|
||||
gnuviechadmin/tests/functional/test_util_passwordutils.py
|
||||
gnuviechadmin/tests/functional/test_xmlrpc_users.py
|
||||
gnuviechadmin/util/__init__.py
|
||||
gnuviechadmin/util/getenttools.py
|
||||
gnuviechadmin/util/gpgmail.py
|
||||
gnuviechadmin/util/passwordutils.py
|
||||
gnuviechadmin/util/stmtcreator.py
|
||||
gnuviechadmin/xmlrpc/XMLRPCFacade.py
|
||||
gnuviechadmin/xmlrpc/__init__.py
|
||||
gnuviechadmin/xmlrpc/users.py
|
1
gnuviechadmin.egg-info/dependency_links.txt
Normal file
1
gnuviechadmin.egg-info/dependency_links.txt
Normal file
|
@ -0,0 +1 @@
|
|||
|
3
gnuviechadmin.egg-info/entry_points.txt
Normal file
3
gnuviechadmin.egg-info/entry_points.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
[paste.app_factory]
|
||||
cli = gnuviechadmin.cli.client
|
6
gnuviechadmin.egg-info/requires.txt
Normal file
6
gnuviechadmin.egg-info/requires.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
SQLAlchemy>=0.5
|
||||
sqlalchemy-migrate>=0.5
|
||||
AuthKit>=0.4
|
||||
PasteDeploy>=1.3.3
|
||||
python-gnutls>=1.1
|
||||
argparse
|
1
gnuviechadmin.egg-info/top_level.txt
Normal file
1
gnuviechadmin.egg-info/top_level.txt
Normal file
|
@ -0,0 +1 @@
|
|||
gnuviechadmin
|
26
gnuviechadmin/__init__.py
Normal file
26
gnuviechadmin/__init__.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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$
|
||||
|
||||
"""This is the Gnuviech Admin main package.
|
||||
|
||||
Gnuviech Admin is a server administration toolkit. The project page is
|
||||
at http://www.gnuviech-server.de/projects/gnuviechadmin/.
|
||||
"""
|
114
gnuviechadmin/backend/BackendEntity.py
Normal file
114
gnuviechadmin/backend/BackendEntity.py
Normal file
|
@ -0,0 +1,114 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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$
|
||||
"""This module defines the BackendEntity base class."""
|
||||
|
||||
import os
|
||||
import logging
|
||||
import tempfile
|
||||
|
||||
from gnuviechadmin.exceptions import MissingFieldsError
|
||||
from gnuviechadmin.util import gpgmail
|
||||
from subprocess import Popen, PIPE
|
||||
from sqlalchemy.orm import object_mapper
|
||||
|
||||
|
||||
class BackendEntity(object):
|
||||
"""This is the abstract base class for all backend entity classes."""
|
||||
|
||||
def __init__(self, config, delegateto, verbose = False):
|
||||
self.logger = logging.getLogger("%s.%s" % (
|
||||
self.__class__.__module__, self.__class__.__name__))
|
||||
self.config = config
|
||||
self.delegateto = delegateto
|
||||
self.verbose = verbose
|
||||
|
||||
def __repr__(self):
|
||||
return self.delegateto.__repr__(verbose = self.verbose)
|
||||
|
||||
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."""
|
||||
self.logger.debug("sucommand called: %s (pipedata=%s)", cmdline,
|
||||
str(pipedata))
|
||||
suwrapper = self.config['suwrapper']
|
||||
toexec = "%s %s" % (suwrapper, cmdline)
|
||||
if pipedata:
|
||||
pipeproc = Popen(toexec, shell = True, stdin=PIPE)
|
||||
pipe = pipeproc.stdin
|
||||
print >> pipe, pipedata
|
||||
pipe.close()
|
||||
sts = os.waitpid(pipeproc.pid, 0)
|
||||
if self.verbose:
|
||||
print "%s|%s: %d" % (pipedata, toexec, sts[1])
|
||||
self.logger.info("%s|%s: %d", pipedata, toexec, sts[1])
|
||||
else:
|
||||
pipeproc = Popen(toexec, shell = True)
|
||||
sts = os.waitpid(pipeproc.pid, 0)
|
||||
if self.verbose:
|
||||
print "%s: %s" % (toexec, sts[1])
|
||||
self.logger.info("%s: %s", toexec, sts[1])
|
||||
return sts[1]
|
||||
|
||||
def supipe(self, cmdlines):
|
||||
"""Executes multiple commands as root and pipes the output of
|
||||
the commands to the input of the next commands."""
|
||||
self.logger.debug("supipe called: %s", " | ".join(cmdlines))
|
||||
suwrapper = self.config['suwrapper']
|
||||
predecessor = None
|
||||
for cmdline in cmdlines:
|
||||
toexec = "%s %s" % (suwrapper, cmdline)
|
||||
if predecessor is None:
|
||||
pipeproc = Popen(toexec, shell = True, stdout = PIPE)
|
||||
else:
|
||||
pipeproc = Popen(toexec, shell = True,
|
||||
stdin = predecessor.stdout, stdout = PIPE)
|
||||
predecessor = pipeproc
|
||||
output = predecessor.communicate()[0]
|
||||
return predecessor.wait()
|
||||
|
||||
def send_mail(self, subject, text):
|
||||
"""This method sends a mail with the given text and subject
|
||||
and signs it usign GnuPG. If a public key of the recipient is
|
||||
available the mail is encrypted."""
|
||||
gpgmail.send_mail(self.config, subject, text)
|
||||
|
||||
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.delegateto).local_table.columns \
|
||||
if not col.primary_key and not col.nullable]:
|
||||
if self.delegateto.__getattribute__(key) is None:
|
||||
missingfields.append(key)
|
||||
if missingfields:
|
||||
raise MissingFieldsError(missingfields)
|
||||
|
||||
def write_to_file(self, filename, template):
|
||||
"""Write the data from template to the specified file."""
|
||||
tmp = tempfile.NamedTemporaryFile()
|
||||
tmp.write(template.encode('utf_8'))
|
||||
tmp.flush()
|
||||
cmd = 'cp "%s" "%s"' % (tmp.name, filename)
|
||||
self.sucommand(cmd)
|
||||
tmp.close()
|
94
gnuviechadmin/backend/BackendEntityHandler.py
Normal file
94
gnuviechadmin/backend/BackendEntityHandler.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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$
|
||||
"""This module defines the BackendEntityHandler class."""
|
||||
|
||||
import logging
|
||||
from sqlalchemy.orm import create_session
|
||||
from gnuviechadmin.backend.tables import dbsetup
|
||||
|
||||
|
||||
class BackendEntityHandler(object):
|
||||
"""This class is a handler for BackendEntity instances."""
|
||||
|
||||
def __init__(self, entityclass, toclass, config, verbose = False):
|
||||
"""Initialize the handler with a specific entity class,
|
||||
transfer object class and verbosity flag."""
|
||||
self.logger = logging.getLogger("%s.%s" % (
|
||||
self.__class__.__module__, self.__class__.__name__))
|
||||
dbsetup(config)
|
||||
|
||||
self.entityclass = entityclass
|
||||
self.toclass = toclass
|
||||
self.config = config
|
||||
self.verbose = verbose
|
||||
|
||||
def create(self, **kwargs):
|
||||
"""Create a new entity of the managed type with the fields set
|
||||
to the values in kwargs."""
|
||||
self.logger.debug("create with params %s", str(kwargs))
|
||||
delegate = self.toclass(self.config, **kwargs)
|
||||
entity = self.entityclass(self.config, delegate, self.verbose)
|
||||
sess = create_session()
|
||||
try:
|
||||
sess.begin()
|
||||
sess.add(delegate)
|
||||
sess.flush()
|
||||
sess.refresh(delegate)
|
||||
entity.create_hook(sess)
|
||||
sess.commit()
|
||||
except:
|
||||
sess.rollback()
|
||||
self.logger.exception("Exception in create.")
|
||||
raise
|
||||
return entity
|
||||
|
||||
def fetchall(self, **kwargs):
|
||||
"""Fetches all entities of the managed entity type."""
|
||||
self.logger.debug("fetchall with params %s", str(kwargs))
|
||||
session = create_session()
|
||||
query = session.query(self.toclass)
|
||||
if kwargs:
|
||||
allentities = query.filter_by(**kwargs).all()
|
||||
else:
|
||||
allentities = query.all()
|
||||
return [self.entityclass(self.config, entity, self.verbose) \
|
||||
for entity in allentities]
|
||||
|
||||
def delete(self, pkvalue):
|
||||
"""Deletes the entity of the managed entity type that has the
|
||||
specified primary key value."""
|
||||
self.logger.debug("delete with primary key %s", str(pkvalue))
|
||||
sess = create_session()
|
||||
try:
|
||||
sess.begin()
|
||||
tobj = sess.query(self.toclass).get(pkvalue)
|
||||
if tobj:
|
||||
entity = self.entityclass(self.config, tobj, self.verbose)
|
||||
self.logger.info("delete %s", str(entity))
|
||||
if self.verbose:
|
||||
print "delete %s" % (str(entity))
|
||||
entity.delete_hook(sess)
|
||||
sess.delete(tobj)
|
||||
sess.commit()
|
||||
except Exception:
|
||||
sess.rollback()
|
||||
self.logger.exception("Exception in delete.")
|
||||
raise
|
70
gnuviechadmin/backend/BackendTo.py
Normal file
70
gnuviechadmin/backend/BackendTo.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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.orm import object_mapper
|
||||
from sqlalchemy import Unicode
|
||||
|
||||
|
||||
class BackendTo(object):
|
||||
"""Backend transfer object class."""
|
||||
|
||||
def __init__(self, config, **kwargs):
|
||||
cols = object_mapper(self).local_table.columns
|
||||
for (key, value) in kwargs.items():
|
||||
if key in cols.keys() and value is not None:
|
||||
if isinstance(cols[key].type, Unicode):
|
||||
self.__setattr__(key, value.decode('utf8'))
|
||||
else:
|
||||
self.__setattr__(key, value)
|
||||
|
||||
def __repr__(self, **kwargs):
|
||||
if 'verbose' in kwargs and kwargs['verbose']:
|
||||
cols = object_mapper(self).local_table.columns.keys()
|
||||
format = "%(class)s:"
|
||||
format = format + ", ".join([col + "=%(" + col + ")s" for col in \
|
||||
cols])
|
||||
data = {'class': self.__class__.__name__}
|
||||
else:
|
||||
cols = self._shortkeys
|
||||
format = ",".join("%(" + col + ")s" for col in cols)
|
||||
data = {}
|
||||
data.update(dict([(col, self.__getattribute__(col)) for col in cols]))
|
||||
return format % data
|
||||
|
||||
|
||||
class Client(BackendTo):
|
||||
"""Transfer object class for clients."""
|
||||
_shortkeys = ('clientid', 'firstname', 'lastname', 'email')
|
||||
|
||||
|
||||
class Sysuser(BackendTo):
|
||||
"""Transfer object class for system users."""
|
||||
_shortkeys = ("sysuserid", "clientid", "username", "home", "shell")
|
||||
|
||||
|
||||
class Domain(BackendTo):
|
||||
"""Transfer object class for DNS domains."""
|
||||
_shortkeys = ("domainid", "sysuserid", "name", "type")
|
||||
|
||||
|
||||
class Record(BackendTo):
|
||||
"""Transfer object class for DNS domain records."""
|
||||
_shortkeys = ("recordid", "domainid", "name", "type", "content")
|
25
gnuviechadmin/backend/__init__.py
Normal file
25
gnuviechadmin/backend/__init__.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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$
|
||||
|
||||
"""This is the gnuviechadmin.backend package.
|
||||
|
||||
The package provides the backend entities and supporting code for
|
||||
gnuviechadmin."""
|
82
gnuviechadmin/backend/client.py
Normal file
82
gnuviechadmin/backend/client.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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$
|
||||
"""This module defines the ClientEntity class."""
|
||||
|
||||
from gnuviechadmin.backend.settings import get_template, get_template_string
|
||||
from gnuviechadmin.exceptions import CannotDeleteError
|
||||
from gnuviechadmin.backend.BackendTo import Client
|
||||
from gnuviechadmin.backend.BackendEntity import BackendEntity
|
||||
from gnuviechadmin.backend.BackendEntityHandler import BackendEntityHandler
|
||||
|
||||
|
||||
class ClientEntity(BackendEntity):
|
||||
"""Entity class for clients."""
|
||||
|
||||
def __init__(self, config, delegate, verbose = False, **kwargs):
|
||||
"""Initializes the client entity instance."""
|
||||
BackendEntity.__init__(self, config, delegate, verbose)
|
||||
for (key, value) in kwargs.items():
|
||||
self.__setattr__(key, value)
|
||||
if not self.delegateto.country:
|
||||
self.delegateto.country = self._get_default_country()
|
||||
self.validate()
|
||||
|
||||
def _client_mail(self):
|
||||
"""Mails a summary about the creation of the client."""
|
||||
text = get_template(self.config['mailtemplates'],
|
||||
self.config['client.create.mail']).substitute({
|
||||
'firstname': self.delegateto.firstname,
|
||||
'lastname': self.delegateto.lastname,
|
||||
'email': self.delegateto.email,
|
||||
'address1': self.delegateto.address1,
|
||||
'zipcode': self.delegateto.zip,
|
||||
'city': self.delegateto.city,
|
||||
'phone': self.delegateto.phone})
|
||||
subject = get_template_string(
|
||||
self.config['client.create_subject']).substitute({
|
||||
'firstname': self.delegateto.firstname,
|
||||
'lastname': self.delegateto.lastname})
|
||||
self.send_mail(subject, text)
|
||||
|
||||
def create_hook(self, session):
|
||||
"""Actions to perform when a client is created."""
|
||||
self._client_mail()
|
||||
|
||||
def delete_hook(self, session):
|
||||
"""Actions to perform when a client is deleted."""
|
||||
if self.delegateto.sysusers:
|
||||
raise CannotDeleteError(
|
||||
self.delegateto,
|
||||
"it still has the following system users assigned: %s" % (
|
||||
", ".join([sysuser.username for sysuser in \
|
||||
self.delegateto.sysusers])))
|
||||
|
||||
def _get_default_country(self):
|
||||
"""Gets the default country."""
|
||||
return self.config['client.defaultcountry']
|
||||
|
||||
|
||||
class ClientHandler(BackendEntityHandler):
|
||||
"""BackendEntityHandler for Client entities."""
|
||||
|
||||
def __init__(self, config, verbose = False):
|
||||
BackendEntityHandler.__init__(self, ClientEntity, Client, config,
|
||||
verbose)
|
371
gnuviechadmin/backend/domain.py
Normal file
371
gnuviechadmin/backend/domain.py
Normal file
|
@ -0,0 +1,371 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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$
|
||||
"""This module defines the code for handling domains."""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
|
||||
from gnuviechadmin.exceptions import ValidationFailedError
|
||||
from gnuviechadmin.backend.settings import config, get_template, \
|
||||
get_template_dir, get_template_string
|
||||
from gnuviechadmin.backend.BackendTo import Record, Domain
|
||||
from gnuviechadmin.backend.BackendEntity import BackendEntity
|
||||
from gnuviechadmin.backend.BackendEntityHandler import BackendEntityHandler
|
||||
|
||||
|
||||
class DomainEntity(BackendEntity):
|
||||
"""Entity class for DNS domains."""
|
||||
|
||||
# the valid domain types
|
||||
_valid_domain_types = ("MASTER", "SLAVE")
|
||||
|
||||
def __init__(self, delegate, verbose = False, **kwargs):
|
||||
"""Initializes the DomainEntity instance.
|
||||
|
||||
`delegate` is the corresponding database object.
|
||||
If `verbose` is `True` verbose logging is turned on.
|
||||
"""
|
||||
BackendEntity.__init__(self, delegate, verbose)
|
||||
self.ns1 = None
|
||||
self.ns2 = None
|
||||
self.mxrr = None
|
||||
self.ipaddr = None
|
||||
for (key, value) in kwargs.items():
|
||||
self.__setattr__(key, value)
|
||||
if not self.delegateto.type:
|
||||
self.delegateto.type = self.getdefaultdomaintype()
|
||||
if not self.ns1:
|
||||
self.ns1 = config.get('domain', 'defaultns1')
|
||||
if not self.ns2:
|
||||
self.ns2 = config.get('domain', 'defaultns2')
|
||||
if not self.mxrr:
|
||||
self.mxrr = config.get('domain', 'defaultmx')
|
||||
if not self.ipaddr:
|
||||
self.ipaddr = config.get('domain', 'defaultip')
|
||||
self.delegateto.type = self.delegateto.type.upper()
|
||||
self.validate()
|
||||
|
||||
def getdefaultdomaintype(self):
|
||||
"""Returns the default domain type."""
|
||||
return self._valid_domain_types[0]
|
||||
|
||||
def validate(self):
|
||||
"""Validates the consistency if the entity instance and
|
||||
dependent entities."""
|
||||
BackendEntity.validate(self)
|
||||
if not self.delegateto.type in self._valid_domain_types:
|
||||
raise ValidationFailedError(
|
||||
self, "invalid domain type %s" % (self.delegateto.type))
|
||||
if self.delegateto.type == 'SLAVE' and not self.delegateto.master:
|
||||
raise ValidationFailedError(
|
||||
self, "you have to specify a master for slave domains.")
|
||||
if self.delegateto.type == 'MASTER':
|
||||
if not self.ns1 or not self.ns2:
|
||||
raise ValidationFailedError(
|
||||
self, "two nameservers must be specified.")
|
||||
if not self.mxrr:
|
||||
raise ValidationFailedError(
|
||||
self, "a primary mx host must be specified.")
|
||||
|
||||
def _getnewserial(self, oldserial = None):
|
||||
"""Gets a new zone serial number for the DNS domain entity."""
|
||||
current = datetime.datetime.now()
|
||||
datepart = "%04d%02d%02d" % \
|
||||
(current.year, current.month, current.day)
|
||||
retval = None
|
||||
if oldserial:
|
||||
if str(oldserial)[:len(datepart)] == datepart:
|
||||
retval = oldserial + 1
|
||||
if not retval:
|
||||
retval = int("%s01" % (datepart))
|
||||
return retval
|
||||
|
||||
def _getnewsoa(self):
|
||||
"""Gets a new SOA record for the DNS domain entity."""
|
||||
return '%s %s %d %d %d %d %d' % \
|
||||
(self.ns1,
|
||||
config.get('domain', 'defaulthostmaster'),
|
||||
self._getnewserial(),
|
||||
config.getint('domain', 'defaultrefresh'),
|
||||
config.getint('domain', 'defaultretry'),
|
||||
config.getint('domain', 'defaultexpire'),
|
||||
config.getint('domain', 'defaultminimumttl'))
|
||||
|
||||
def update_serial(self, session):
|
||||
"""Updates the serial of the domain."""
|
||||
query = session.query(Record)
|
||||
soarecord = query.get_by(Record.c.type == 'SOA',
|
||||
Record.c.domainid == self.delegateto.domainid)
|
||||
parts = soarecord.content.split(" ")
|
||||
parts[2] = str(self._getnewserial(int(parts[2])))
|
||||
soarecord.content = " ".join(parts)
|
||||
session.save(soarecord)
|
||||
session.flush()
|
||||
|
||||
def _get_vhost_dir(self):
|
||||
"""Gets the directory name for the Apache VirtualHost of the
|
||||
domain."""
|
||||
return os.path.join(self.delegateto.sysuser.home,
|
||||
self.delegateto.name,
|
||||
config.get('domain', 'htdir'))
|
||||
|
||||
def _get_log_dir(self):
|
||||
"""Gets the Apache log file directory for the domain."""
|
||||
return os.path.join(config.get('domain', 'logpath'),
|
||||
self.delegateto.name)
|
||||
|
||||
def _get_stats_dir(self):
|
||||
"""Gets the statistics dir for the domain."""
|
||||
return os.path.join(config.get('domain', 'statspath'),
|
||||
self.delegateto.name)
|
||||
|
||||
def _create_vhost_dir(self):
|
||||
"""Creates the Apache VirtualHost directory for the domain."""
|
||||
vhostdir = self._get_vhost_dir()
|
||||
self.logger.debug("creating virtual host dir %s" % (vhostdir))
|
||||
cmd = 'mkdir -p "%s"' % (vhostdir)
|
||||
self.sucommand(cmd)
|
||||
for tpl in [tpl for tpl in os.listdir(
|
||||
get_template_dir(config.get('domain', 'htdocstemplate'))) \
|
||||
if not tpl.startswith('.')]:
|
||||
self.logger.debug("processing template %s" % (tpl))
|
||||
template = get_template(config.get('domain', 'htdocstemplate'),
|
||||
tpl)
|
||||
template = template.substitute({
|
||||
'domain': self.delegateto.name})
|
||||
self.write_to_file(os.path.join(vhostdir, tpl), template)
|
||||
cmd = 'chown -R %(username)s:%(group)s "%(dir)s"' % {
|
||||
'username': self.delegateto.sysuser.username,
|
||||
'group': config.get('sysuser', 'defaultgroup'),
|
||||
'dir': vhostdir}
|
||||
self.sucommand(cmd)
|
||||
|
||||
def _create_log_dir(self):
|
||||
"""Creates the Apache log file directory for the domain."""
|
||||
cmd = 'mkdir -p "%s"' % (self._get_log_dir())
|
||||
self.sucommand(cmd)
|
||||
|
||||
def _get_auth_userfile(self):
|
||||
"""Gets the file name of the password file for statistic
|
||||
logins for the domain."""
|
||||
authdir = config.get('domain', 'authdir')
|
||||
if not os.path.isdir(authdir):
|
||||
cmd = 'mkdir -p "%s"' % (authdir)
|
||||
self.sucommand(cmd)
|
||||
return os.path.join(authdir, '%s.passwd' % (self.delegateto.name))
|
||||
|
||||
def _create_stats_dir(self):
|
||||
"""Creates the statistics directory for the domain and sets
|
||||
Apache .htaccess password protection."""
|
||||
statsdir = self._get_stats_dir()
|
||||
authfile = self._get_auth_userfile()
|
||||
cmd = 'htpasswd -m -c -b "%s" "%s" "%s"' % (
|
||||
authfile,
|
||||
self.delegateto.sysuser.username,
|
||||
self.delegateto.sysuser.clearpass)
|
||||
self.sucommand(cmd)
|
||||
cmd = 'mkdir -p "%s"' % (statsdir)
|
||||
self.sucommand(cmd)
|
||||
template = get_template(config.get('domain', 'conftemplates'),
|
||||
config.get('domain', 'statshtaccesstemplate'))
|
||||
template = template.substitute({
|
||||
'domain': self.delegateto.name,
|
||||
'userfile': authfile})
|
||||
self.write_to_file(os.path.join(self._get_stats_dir(),
|
||||
'.htaccess'), template)
|
||||
|
||||
def _create_stats_conf(self):
|
||||
"""Creates the modlogan statistics configuration for the
|
||||
domain."""
|
||||
modlogandir = os.path.join(config.get('domain',
|
||||
'modlogandir'),
|
||||
self.delegateto.sysuser.username)
|
||||
cmd = 'mkdir -p "%s"' % (modlogandir)
|
||||
self.sucommand(cmd)
|
||||
template = get_template(config.get('domain', 'conftemplates'),
|
||||
config.get('domain', 'modlogantemplate'))
|
||||
template = template.substitute({
|
||||
'statsdir': self._get_stats_dir(),
|
||||
'logdir': self._get_log_dir(),
|
||||
'domain': self.delegateto.name,
|
||||
'domainesc': self.delegateto.name.replace('.', '\.')})
|
||||
self.write_to_file(os.path.join(modlogandir,
|
||||
self.delegateto.name + '.conf'),
|
||||
template)
|
||||
|
||||
def _create_apache_conf(self):
|
||||
"""Creates the Apache configuration file for the domain."""
|
||||
template = get_template(config.get('domain', 'conftemplates'),
|
||||
config.get('domain', 'apachetemplate'))
|
||||
template = template.substitute({
|
||||
'ipaddr': self.ipaddr,
|
||||
'statsdir': self._get_stats_dir(),
|
||||
'logdir': self._get_log_dir(),
|
||||
'domain': self.delegateto.name,
|
||||
'docroot': self._get_vhost_dir()})
|
||||
self.write_to_file(os.path.join(config.get('domain', 'sitesdir'),
|
||||
self.delegateto.name), template)
|
||||
|
||||
def _mail_domain(self):
|
||||
"""Mail a summary of the domain data."""
|
||||
template = get_template(config.get('common', 'mailtemplates'),
|
||||
config.get('domain', 'create.mail'))
|
||||
text = template.substitute({
|
||||
'sysuser': self.delegateto.sysuser.username,
|
||||
'domain': self.delegateto.name,
|
||||
'docroot': self._get_vhost_dir(),
|
||||
'statspass': self.delegateto.sysuser.clearpass})
|
||||
template = get_template_string(config.get('domain', 'create_subject'))
|
||||
subject = template.substitute({
|
||||
'domain': self.delegateto.name})
|
||||
self.send_mail(subject, text)
|
||||
|
||||
def create_hook(self, session):
|
||||
"""Hook for the creation of the domain.
|
||||
|
||||
This method is called by
|
||||
`gnuviechadmin.backend.BackendEntityHandler.create()`.
|
||||
"""
|
||||
self.delegateto.records.append(Record(
|
||||
name = self.delegateto.name, type = 'SOA',
|
||||
content = self._getnewsoa(),
|
||||
ttl = config.getint('domain', 'defaultttl')))
|
||||
self.delegateto.records.append(Record(
|
||||
name = self.delegateto.name, type = 'NS', content = self.ns1,
|
||||
ttl = config.getint('domain', 'defaultttl')))
|
||||
self.delegateto.records.append(Record(
|
||||
name = self.delegateto.name, type = 'NS', content = self.ns2,
|
||||
ttl = config.getint('domain', 'defaultttl')))
|
||||
self.delegateto.records.append(Record(
|
||||
name = self.delegateto.name, type = 'MX', content = self.mxrr,
|
||||
ttl = config.getint('domain', 'defaultttl'),
|
||||
prio = config.getint('domain', 'defaultmxprio')))
|
||||
self.delegateto.records.append(Record(
|
||||
name = self.delegateto.name, type = 'A', content = self.ipaddr,
|
||||
ttl = config.getint('domain', 'defaultttl')))
|
||||
self.delegateto.records.append(Record(
|
||||
name = "www.%s" % (self.delegateto.name), type = 'A',
|
||||
content = self.ipaddr,
|
||||
ttl = config.getint('domain', 'defaultttl')))
|
||||
session.save_or_update(self.delegateto)
|
||||
session.flush()
|
||||
self._create_vhost_dir()
|
||||
self._create_log_dir()
|
||||
self._create_stats_dir()
|
||||
self._create_stats_conf()
|
||||
self._create_apache_conf()
|
||||
self._mail_domain()
|
||||
|
||||
def _delete_apache_conf(self):
|
||||
"""Deletes the Apache configuration file for the domain."""
|
||||
cmd = 'a2dissite %s' % (self.delegateto.name)
|
||||
self.sucommand(cmd)
|
||||
cmd = 'rm "%s"' % (os.path.join(config.get('domain', 'sitesdir'),
|
||||
self.delegateto.name))
|
||||
self.sucommand(cmd)
|
||||
|
||||
def _delete_stats_conf(self):
|
||||
"""Deletes the modlogan stastics configuration for the
|
||||
domain."""
|
||||
cmd = 'rm "%s"' % (os.path.join(config.get('domain', 'modlogandir'),
|
||||
self.delegateto.sysuser.username,
|
||||
self.delegateto.name + '.conf'))
|
||||
self.sucommand(cmd)
|
||||
|
||||
def _archive_stats_dir(self):
|
||||
"""Archives the statistics directory for the domain."""
|
||||
archive = os.path.join(self.delegateto.sysuser.home,
|
||||
'%(domain)s-stats.tar.gz' % {
|
||||
'domain': self.delegateto.name})
|
||||
cmd = 'tar czf "%(archive)s" --directory="%(statsbase)s" ' + \
|
||||
'"%(statsdir)s"' % {
|
||||
'archive': archive,
|
||||
'statsbase': config.get('domain', 'statspath'),
|
||||
'statsdir': self.delegateto.name}
|
||||
self.sucommand(cmd)
|
||||
cmd = 'rm -r "%(statsdir)s"' % {
|
||||
'statsdir': self._get_stats_dir()}
|
||||
self.sucommand(cmd)
|
||||
cmd = 'chown "%(username)s:%(group)s" "%(archive)s"' % {
|
||||
'username': self.delegateto.sysuser.username,
|
||||
'group': config.get('sysuser', 'defaultgroup'),
|
||||
'archive': archive}
|
||||
self.sucommand(cmd)
|
||||
|
||||
def _archive_log_dir(self):
|
||||
"""Archives the Apache log file directory for the domain."""
|
||||
archive = os.path.join(self.delegateto.sysuser.home,
|
||||
'%(domain)s-logs.tar.gz' % {
|
||||
'domain': self.delegateto.name})
|
||||
cmd = 'tar czf "%(archive)s" --directory="%(logbase)s" ' + \
|
||||
'"%(logdir)s"' % {
|
||||
'archive': archive,
|
||||
'logbase': config.get('domain', 'logpath'),
|
||||
'logdir': self.delegateto.name}
|
||||
self.sucommand(cmd)
|
||||
cmd = 'rm -r "%(logdir)s"' % {
|
||||
'logdir': self._get_log_dir()}
|
||||
self.sucommand(cmd)
|
||||
cmd = 'chown "%(username)s:%(group)s" "%(archive)s"' % {
|
||||
'username': self.delegateto.sysuser.username,
|
||||
'group': config.get('sysuser', 'defaultgroup'),
|
||||
'archive': archive}
|
||||
self.sucommand(cmd)
|
||||
|
||||
def _archive_vhost_dir(self):
|
||||
"""Archives the Apache VirtualHost directory for the domain."""
|
||||
archive = os.path.join(self.delegateto.sysuser.home,
|
||||
'%(domain)s-vhost.tar.gz' % {
|
||||
'domain': self.delegateto.name})
|
||||
cmd = 'tar czf "%(archive)s" --directory="%(vhostbase)s" ' + \
|
||||
'"%(vhostdir)s"' % {
|
||||
'archive': archive,
|
||||
'vhostbase': self.delegateto.sysuser.home,
|
||||
'vhostdir': self.delegateto.name}
|
||||
self.sucommand(cmd)
|
||||
cmd = 'rm -r "%(vhostdir)s"' % {
|
||||
'vhostdir': os.path.join(self.delegateto.sysuser.home,
|
||||
self.delegateto.name)}
|
||||
self.sucommand(cmd)
|
||||
cmd = 'chown "%(username)s:%(group)s" "%(archive)s"' % {
|
||||
'username': self.delegateto.sysuser.username,
|
||||
'group': config.get('sysuser', 'defaultgroup'),
|
||||
'archive': archive}
|
||||
self.sucommand(cmd)
|
||||
|
||||
def delete_hook(self, session):
|
||||
"""Deletes domain related files and directories.
|
||||
|
||||
This method is called by `BackendEntityHandler.delete()`.
|
||||
"""
|
||||
self._delete_apache_conf()
|
||||
self._delete_stats_conf()
|
||||
self._archive_stats_dir()
|
||||
self._archive_log_dir()
|
||||
self._archive_vhost_dir()
|
||||
|
||||
|
||||
class DomainHandler(BackendEntityHandler):
|
||||
"""BackendEntityHandler for Domain entities."""
|
||||
|
||||
def __init__(self, verbose = False):
|
||||
"""Initialize the DomainHandler."""
|
||||
BackendEntityHandler.__init__(self, DomainEntity, Domain, verbose)
|
45
gnuviechadmin/backend/record.py
Normal file
45
gnuviechadmin/backend/record.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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 gnuviechadmin.exceptions import *
|
||||
from BackendTo import Record, Domain
|
||||
from domain import DomainEntity
|
||||
from BackendEntity import *
|
||||
from BackendEntityHandler import *
|
||||
|
||||
|
||||
class RecordEntity(BackendEntity):
|
||||
"""Entity class for DNS domain records."""
|
||||
|
||||
def create_hook(self, session):
|
||||
domain = session.load(Domain, self.delegateto.domainid)
|
||||
DomainEntity(domain).update_serial(session)
|
||||
|
||||
def delete_hook(self, session):
|
||||
domain = session.load(Domain, self.delegateto.domainid)
|
||||
DomainEntity(domain).update_serial(session)
|
||||
|
||||
|
||||
class RecordHandler(BackendEntityHandler):
|
||||
"""BackendEntityHandler for Record entities."""
|
||||
|
||||
def __init__(self, verbose = False):
|
||||
BackendEntityHandler.__init__(self, RecordEntity, Record, verbose)
|
42
gnuviechadmin/backend/settings.py
Normal file
42
gnuviechadmin/backend/settings.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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$
|
||||
|
||||
"""The Gnuviech Admin settings module.
|
||||
|
||||
This module handles all central configuration of Gnuviech Admin. It
|
||||
parses configuration files and provides functions for reading
|
||||
templates."""
|
||||
|
||||
import os
|
||||
from string import Template
|
||||
|
||||
|
||||
def get_template(templatedir, filename):
|
||||
"""Returns the template data from the given template file."""
|
||||
templatefile = file(os.path.join(templatedir,
|
||||
filename))
|
||||
templatedata = templatefile.read()
|
||||
return Template(templatedata.decode('utf_8'))
|
||||
|
||||
|
||||
def get_template_string(templatestring):
|
||||
"""Returns a template object for the given template string."""
|
||||
return Template(templatestring)
|
164
gnuviechadmin/backend/sysuser.py
Normal file
164
gnuviechadmin/backend/sysuser.py
Normal file
|
@ -0,0 +1,164 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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 gnuviechadmin.exceptions import CannotDeleteError
|
||||
from gnuviechadmin.util import passwordutils, getenttools
|
||||
from gnuviechadmin.backend.BackendTo import Sysuser
|
||||
from gnuviechadmin.backend.BackendEntity import BackendEntity
|
||||
from gnuviechadmin.backend.BackendEntityHandler import BackendEntityHandler
|
||||
import os
|
||||
|
||||
|
||||
class SysuserEntity(BackendEntity):
|
||||
"""Entity class for system users."""
|
||||
|
||||
def __init__(self, config, delegate, verbose = False, **kwargs):
|
||||
BackendEntity.__init__(self, config, delegate, verbose)
|
||||
for (key, value) in kwargs.items():
|
||||
self.__setattr__(key, value)
|
||||
if not self.delegateto.username:
|
||||
self.delegateto.username = self._get_next_sysusername()
|
||||
if not self.delegateto.usertype:
|
||||
self.delegateto.usertype = self._get_default_sysusertype()
|
||||
if not self.delegateto.home:
|
||||
self.delegateto.home = self._get_home(self.delegateto.username)
|
||||
if not self.delegateto.shell:
|
||||
self.delegateto.shell = self._get_default_shell()
|
||||
(self.delegateto.clearpass, self.delegateto.md5pass) = \
|
||||
passwordutils.get_pw_tuple(self.delegateto.clearpass)
|
||||
if not self.delegateto.sysuid:
|
||||
self.delegateto.sysuid = self._get_next_sysuid()
|
||||
self.validate()
|
||||
|
||||
def _get_next_sysusername(self):
|
||||
prefix = config.get('sysuser', 'nameprefix')
|
||||
self.logger.debug("looking for next user with prefix %s" % (prefix))
|
||||
usernames = [user.username for user in \
|
||||
getenttools.find_user_by_prefix(prefix)]
|
||||
if usernames:
|
||||
maxid = max([int(username[len(prefix):]) \
|
||||
for username in usernames])
|
||||
maxid += 2
|
||||
for number in range(1, maxid):
|
||||
username = "%s%02d" % (prefix, number)
|
||||
if not username in usernames:
|
||||
return username
|
||||
else:
|
||||
return "%s%02d" % (prefix, 1)
|
||||
|
||||
def _get_default_sysusertype(self):
|
||||
return 1
|
||||
|
||||
def _get_home(self, sysusername):
|
||||
"""Gets a valid home directory for the given user name."""
|
||||
return os.path.join(config.get('sysuser', 'homedirbase'),
|
||||
sysusername)
|
||||
|
||||
def _get_default_shell(self):
|
||||
return False
|
||||
|
||||
def _get_shell_binary(self):
|
||||
if self.delegateto.shell:
|
||||
return config.get('sysuser', 'shellyes')
|
||||
return config.get('sysuser', 'shellno')
|
||||
|
||||
def _get_next_sysuid(self):
|
||||
return getenttools.get_next_uid(int(config.get('sysuser', 'minuid')),
|
||||
int(config.get('sysuser', 'maxuid')))
|
||||
|
||||
def _populate_home(self):
|
||||
templatedir = get_template_dir(config.get('sysuser', 'hometemplate'))
|
||||
olddir = os.getcwd()
|
||||
os.chdir(templatedir)
|
||||
cmd1 = 'find . -depth \! -regex ".*\.svn.*" \! -name "*~" -print0'
|
||||
cmd2 = 'cpio --pass-through --owner=%(username)s.%(group)s --null ' + \
|
||||
'--make-directories %(home)s' % {
|
||||
'username': self.delegateto.username,
|
||||
'group': config.get('sysuser', 'defaultgroup'),
|
||||
'home': self.delegateto.home}
|
||||
self.supipe((cmd1, cmd2))
|
||||
os.chdir(olddir)
|
||||
|
||||
def _mail_sysuser(self):
|
||||
template = get_template(config.get('common', 'mailtemplates'),
|
||||
config.get('sysuser', 'create.mail'))
|
||||
text = template.substitute({
|
||||
'uid': self.delegateto.sysuid,
|
||||
'firstname': self.delegateto.client.firstname,
|
||||
'lastname': self.delegateto.client.lastname,
|
||||
'email': self.delegateto.client.email,
|
||||
'username': self.delegateto.username,
|
||||
'password': self.delegateto.clearpass,
|
||||
'home': self.delegateto.home,
|
||||
'shell': self._get_shell_binary()})
|
||||
template = get_template_string(config.get('sysuser', 'create_subject'))
|
||||
subject = template.substitute({
|
||||
'username': self.delegateto.username})
|
||||
self.send_mail(subject, text)
|
||||
|
||||
def create_hook(self, session):
|
||||
gecos = config.get('sysuser', 'gecos') % (self.delegateto.username)
|
||||
cmdline = 'adduser --home "%(home)s" --shell "%(shell)s" ' + \
|
||||
'--no-create-home --uid %(sysuid)d --ingroup "%(group)s" ' + \
|
||||
'--disabled-password --gecos "%(gecos)s" %(username)s' % {
|
||||
'home': self.delegateto.home,
|
||||
'shell': self._get_shell_binary(),
|
||||
'sysuid': self.delegateto.sysuid,
|
||||
'group': config.get('sysuser', 'defaultgroup'),
|
||||
'gecos': gecos,
|
||||
'username': self.delegateto.username}
|
||||
self.sucommand(cmdline)
|
||||
cmdline = 'chpasswd --encrypted'
|
||||
inline = '%(username)s:%(md5pass)s' % {
|
||||
'username': self.delegateto.username,
|
||||
'md5pass': self.delegateto.md5pass}
|
||||
self.sucommand(cmdline, inline)
|
||||
self._populate_home()
|
||||
self._mail_sysuser()
|
||||
|
||||
def delete_hook(self, session):
|
||||
if self.delegateto.domains:
|
||||
raise CannotDeleteError(
|
||||
self.delegateto,
|
||||
"it still has the following domains assigned: %s" % (
|
||||
", ".join([domain.name for domain in \
|
||||
self.delegateto.domains])))
|
||||
backupdir = os.path.join(config.get('common', 'backupdir'),
|
||||
config.get('sysuser', 'homebackupdir'))
|
||||
if not os.path.isdir(backupdir):
|
||||
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.delegateto.username}
|
||||
self.sucommand(cmdline)
|
||||
|
||||
|
||||
class SysuserHandler(BackendEntityHandler):
|
||||
"""BackendEntityHandler for Sysuser entities."""
|
||||
|
||||
def __init__(self, config, verbose = False):
|
||||
BackendEntityHandler.__init__(self, SysuserEntity, Sysuser,
|
||||
config, verbose)
|
101
gnuviechadmin/backend/tables.py
Normal file
101
gnuviechadmin/backend/tables.py
Normal file
|
@ -0,0 +1,101 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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 MetaData, Table, Column, Unicode
|
||||
from sqlalchemy.orm import mapper, relation
|
||||
from sqlalchemy.exceptions import NoSuchTableError
|
||||
import sys
|
||||
import migrate.versioning.api
|
||||
import logging
|
||||
from gnuviechadmin.backend.BackendTo import Client, Sysuser, Domain, Record
|
||||
|
||||
|
||||
def dbsetup(config):
|
||||
logger = logging.getLogger(__name__)
|
||||
required_version = int(config['migrate.required_version'])
|
||||
try:
|
||||
dbversion = migrate.versioning.api.db_version(
|
||||
config['sqlalchemy.uri'], config['database.repository'])
|
||||
if dbversion < required_version:
|
||||
logger.info("""Database version is %d but required version \
|
||||
is %d. Trying automatic upgrade.""" % (dbversion, required_version))
|
||||
try:
|
||||
migrate.versioning.api.upgrade(
|
||||
config['sqlalchemy.uri'], config['database.repository'],
|
||||
required_version)
|
||||
except e:
|
||||
logger.error("Automatic upgrade failed.", e)
|
||||
raise
|
||||
elif dbversion > required_version:
|
||||
logger.error("""Database version is %d which is higher than \
|
||||
the required version %d. I cannot handle this situation without possible \
|
||||
data loss.""" % (dbversion, required_version))
|
||||
sys.exit(1)
|
||||
except NoSuchTableError, nste:
|
||||
logger.info("""The database is not versioned. \
|
||||
Trying automatic versioning.""")
|
||||
try:
|
||||
migrate.versioning.api.version_control(
|
||||
config['sqlalchemy.uri'], config['database.repository'])
|
||||
migrate.versioning.api.upgrade(
|
||||
config['sqlalchemy.uri'], config['database.repository'],
|
||||
required_version)
|
||||
except:
|
||||
logger.error("Automatic setup failed.")
|
||||
raise
|
||||
|
||||
meta = MetaData(config['sqlalchemy.uri'])
|
||||
meta.bind.engine.echo = config['sqlalchemy.echo']
|
||||
|
||||
dbschema = None
|
||||
if 'database.schema' in config:
|
||||
dbschema = config['database.schema']
|
||||
|
||||
organization_table = Table(
|
||||
'organization', meta,
|
||||
Column('name', Unicode(200)),
|
||||
schema = dbschema, autoload = True)
|
||||
client_table = Table(
|
||||
'client', meta,
|
||||
Column('title', Unicode(10)),
|
||||
Column('firstname', Unicode(64)),
|
||||
Column('lastname', Unicode(64)),
|
||||
Column('address1', Unicode(64)),
|
||||
Column('address2', Unicode(64)),
|
||||
Column('city', Unicode(64)),
|
||||
schema = dbschema, autoload = True)
|
||||
|
||||
(sysuser_table, domain_table, \
|
||||
record_table, supermaster_table, mailaccount_table, \
|
||||
mailaddress_table, mailtarget_table) = \
|
||||
[Table(tabname, meta, schema = dbschema,
|
||||
autoload = True) for tabname in \
|
||||
('sysuser', 'domain', 'record',
|
||||
'supermaster', 'mailaccount', 'mailaddress', 'mailtarget')]
|
||||
|
||||
client_mapper = mapper(Client, client_table, {
|
||||
'sysusers': relation(Sysuser, backref = 'client')})
|
||||
sysuser_mapper = mapper(Sysuser, sysuser_table, {
|
||||
'domains': relation(Domain, backref = 'sysuser')})
|
||||
domain_mapper = mapper(Domain, domain_table, {
|
||||
'records': relation(Record, cascade = 'all',
|
||||
backref = 'domain')})
|
||||
record_mapper = mapper(Record, record_table)
|
64
gnuviechadmin/cli/CliCommand.py
Normal file
64
gnuviechadmin/cli/CliCommand.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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 argparse
|
||||
import sys
|
||||
import logging
|
||||
from gnuviechadmin.exceptions import GnuviechadminError
|
||||
|
||||
|
||||
class CliCommand(object):
|
||||
"""
|
||||
Base class for command line interface.
|
||||
|
||||
A specific implementation class must implement the static
|
||||
setup_arparser method and the _execute method.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def setup_argparser(subparsers):
|
||||
"""
|
||||
This static method is used to initialize the command line
|
||||
argument parser.
|
||||
|
||||
The method has to be overridden by subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def _execute(self):
|
||||
"""
|
||||
This method is called when the subcommand of the command is
|
||||
executed.
|
||||
|
||||
The method has to be overridden by subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def __init__(self, args, config):
|
||||
"""
|
||||
This initializes the command with the given command line
|
||||
arguments and executes it.
|
||||
"""
|
||||
self.config = config
|
||||
self.args = args
|
||||
self.logger = logging.getLogger("%s.%s" % (
|
||||
self.__class__.__module__, self.__class__.__name__))
|
||||
self._execute()
|
55
gnuviechadmin/cli/__init__.py
Normal file
55
gnuviechadmin/cli/__init__.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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$
|
||||
"""This is the gnuviechadmin.cli package.
|
||||
|
||||
This package provides modules for the command line interface of the
|
||||
gnuviechadmin server administration suite."""
|
||||
|
||||
__all__ = ["client", "sysuser", "domain", "record"]
|
||||
|
||||
from logging import getLogger
|
||||
from sys import exit
|
||||
from argparse import ArgumentParser
|
||||
import gettext
|
||||
|
||||
|
||||
class CommandLineInterface(object):
|
||||
def __init__(self, config, args):
|
||||
gettext.install('gnuviechadmin')
|
||||
self.log = getLogger(__name__)
|
||||
self.config = config
|
||||
parser = ArgumentParser(prog=args[0])
|
||||
parser.add_argument(
|
||||
'-v', '--verbose',
|
||||
help = _('enable verbose output'),
|
||||
action = 'store_true')
|
||||
subparsers = parser.add_subparsers(
|
||||
title = _('subcommands'), dest = 'command')
|
||||
from gnuviechadmin.cli.client import ClientCli
|
||||
from gnuviechadmin.cli.sysuser import SysuserCli
|
||||
from gnuviechadmin.cli.domain import DomainCli
|
||||
from gnuviechadmin.cli.record import RecordCli
|
||||
for command in [ClientCli, SysuserCli]:
|
||||
command.setup_argparser(subparsers)
|
||||
self.parsedargs = parser.parse_args(args[1:])
|
||||
|
||||
def run(self):
|
||||
self.parsedargs.commandclass(self.parsedargs, self.config)
|
107
gnuviechadmin/cli/client.py
Normal file
107
gnuviechadmin/cli/client.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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
|
||||
import sys
|
||||
|
||||
|
||||
class ClientCli(CliCommand.CliCommand):
|
||||
"""Command line interface command for client management."""
|
||||
|
||||
@staticmethod
|
||||
def setup_argparser(subparsers):
|
||||
parser = subparsers.add_parser(
|
||||
'client',
|
||||
help = _('manage clients'))
|
||||
parser.set_defaults(commandclass=ClientCli)
|
||||
cmdsub = parser.add_subparsers(
|
||||
title = _('client subcommands'), dest = 'subcommand')
|
||||
cmdparser = cmdsub.add_parser(
|
||||
'create',
|
||||
help = _('creates a new client'))
|
||||
cmdparser.add_argument(
|
||||
'-f', '--firstname', required = True,
|
||||
help = _("the client's first name"))
|
||||
cmdparser.add_argument(
|
||||
'-l', '--lastname', required = True,
|
||||
help = _("the client's last name"))
|
||||
cmdparser.add_argument(
|
||||
'-t', '--title',
|
||||
help = _("the client's title"))
|
||||
cmdparser.add_argument(
|
||||
'-a', '--address', dest = 'address1', required = True,
|
||||
help = _("the address of the client"))
|
||||
cmdparser.add_argument(
|
||||
'--address2',
|
||||
help = _("second line of the client's address"))
|
||||
cmdparser.add_argument(
|
||||
'-z', '--zip', required = True,
|
||||
help = _("the zipcode of the client's address"))
|
||||
cmdparser.add_argument(
|
||||
'-c', '--city', required = True,
|
||||
help = _("the city of the client's address"))
|
||||
cmdparser.add_argument(
|
||||
'--country',
|
||||
help = _("the client's country"))
|
||||
cmdparser.add_argument(
|
||||
'-e', '--email', required = True,
|
||||
help = _("the client's email address"))
|
||||
cmdparser.add_argument(
|
||||
'-p', '--phone', required = True,
|
||||
help = _("the client's phone number"))
|
||||
cmdparser.add_argument(
|
||||
'-m', '--mobile',
|
||||
help = _("the client's mobile phone number"))
|
||||
cmdparser.add_argument(
|
||||
'-x', '--fax',
|
||||
help = _("the client's fax number"))
|
||||
cmdparser = cmdsub.add_parser(
|
||||
'list',
|
||||
help = _('lists existing clients'))
|
||||
cmdparser = cmdsub.add_parser(
|
||||
'delete',
|
||||
help = _(
|
||||
'deletes the specified client if it has no dependent data'))
|
||||
cmdparser.add_argument(
|
||||
'-c', '--clientid', required = True,
|
||||
help = _("the client id"))
|
||||
|
||||
def _execute(self):
|
||||
self.logger.debug("execute %s", self.args)
|
||||
from gnuviechadmin.backend.client import ClientHandler
|
||||
from gnuviechadmin.exceptions import CreationFailedError
|
||||
ch = ClientHandler(self.config, self.args.verbose)
|
||||
if self.args.subcommand == "create":
|
||||
try:
|
||||
myclient = ch.create(**self.args.__dict__)
|
||||
if self.args.verbose:
|
||||
print unicode(myclient).encode('utf8')
|
||||
except CreationFailedError, cfe:
|
||||
print cfe
|
||||
sys.exit(2)
|
||||
elif self.args.subcommand == "list":
|
||||
clients = ch.fetchall()
|
||||
for client in clients:
|
||||
print unicode(client).encode('utf8')
|
||||
elif self.args.subcommand == "delete":
|
||||
ch.delete(self.args.clientid)
|
||||
|
||||
def __init__(self, args, config):
|
||||
CliCommand.CliCommand.__init__(self, args, config)
|
68
gnuviechadmin/cli/domain.py
Normal file
68
gnuviechadmin/cli/domain.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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
|
||||
import sys
|
||||
|
||||
|
||||
class DomainCli(CliCommand.CliCommand):
|
||||
"""Command line interface command for domain management."""
|
||||
|
||||
name = "domain"
|
||||
description = "manage domains"
|
||||
_optionmap = {
|
||||
'create': ("creates a new domain",
|
||||
[(["-n", "--name"], "name",
|
||||
"the domain name", True),
|
||||
(["-t", "--type"], "type",
|
||||
"domain type m for master or s for slave", False),
|
||||
(["-m", "--master"], "master",
|
||||
"master server for slave domains", False),
|
||||
(["-s", "--sysuserid"], "sysuserid",
|
||||
"system user id", True)]),
|
||||
'list': ("lists existing domains", []),
|
||||
'delete': ("delete a domain",
|
||||
[(["-d", "--domainid"], "domainid",
|
||||
"the domain id", True)])}
|
||||
|
||||
def _execute(self, subcommand):
|
||||
self.logger.debug("execute %s with data %s", subcommand,
|
||||
str(self._data))
|
||||
from gnuviechadmin.backend.domain import DomainHandler
|
||||
from gnuviechadmin import exceptions
|
||||
if subcommand == "create":
|
||||
try:
|
||||
mydomain = DomainHandler(self._verbose).create(
|
||||
**self._data)
|
||||
if self._verbose:
|
||||
print mydomain
|
||||
except exceptions.CreationFailedError, cfe:
|
||||
self._usage()
|
||||
print cfe
|
||||
sys.exit(2)
|
||||
elif subcommand == "list":
|
||||
domains = DomainHandler(self._verbose).fetchall()
|
||||
for domain in domains:
|
||||
print domain
|
||||
elif subcommand == "delete":
|
||||
DomainHandler(self._verbose).delete(self._data["domainid"])
|
||||
|
||||
def __init__(self, argv):
|
||||
CliCommand.CliCommand.__init__(self, argv)
|
74
gnuviechadmin/cli/record.py
Normal file
74
gnuviechadmin/cli/record.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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
|
||||
import sys
|
||||
|
||||
|
||||
class RecordCli(CliCommand.CliCommand):
|
||||
"""Command line interface command for DNS record management."""
|
||||
|
||||
name = "record"
|
||||
description = "manage DNS records"
|
||||
_optionmap = {
|
||||
'create': ("creates a new record",
|
||||
[(["-n", "--name"], "name",
|
||||
"the record name", True),
|
||||
(["-t", "--type"], "type",
|
||||
"record type", True),
|
||||
(["-c", "--content"], "content",
|
||||
"record content", True),
|
||||
(["-p", "--prio"], "prio",
|
||||
"MX record priority", False),
|
||||
(["--ttl"], "ttl",
|
||||
"time to live", False),
|
||||
(["-d", "--domainid"], "domainid",
|
||||
"domain id", True)]),
|
||||
'list': ("lists existing records",
|
||||
[(["-d", "--domainid"], "domainid",
|
||||
"domain id", False)]),
|
||||
'delete': ("delete a record",
|
||||
[(["-r", "--recordid"], "recordid",
|
||||
"the record id", True)])}
|
||||
|
||||
def _execute(self, subcommand):
|
||||
self.logger.debug("execute %s with data %s", subcommand,
|
||||
str(self._data))
|
||||
from gnuviechadmin.backend.record import RecordHandler
|
||||
from gnuviechadmin import exceptions
|
||||
if subcommand == "create":
|
||||
try:
|
||||
myrecord = RecordHandler(self._verbose).create(
|
||||
**self._data)
|
||||
if self._verbose:
|
||||
print myrecord
|
||||
except exceptions.CreationFailedError, cfe:
|
||||
self._usage()
|
||||
print cfe
|
||||
sys.exit(2)
|
||||
elif subcommand == "list":
|
||||
records = RecordHandler(self._verbose).fetchall(**self._data)
|
||||
for record in records:
|
||||
print record
|
||||
elif subcommand == "delete":
|
||||
RecordHandler(self._verbose).delete(self._data["recordid"])
|
||||
|
||||
def __init__(self, argv):
|
||||
CliCommand.CliCommand.__init__(self, argv)
|
99
gnuviechadmin/cli/sysuser.py
Normal file
99
gnuviechadmin/cli/sysuser.py
Normal file
|
@ -0,0 +1,99 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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$
|
||||
"""This module provides the command line interface code for system
|
||||
user management."""
|
||||
from gnuviechadmin.cli.CliCommand import CliCommand
|
||||
import sys
|
||||
|
||||
|
||||
class SysuserCli(CliCommand):
|
||||
"""Command line interface command for system user management.
|
||||
|
||||
This class implements `gnuviechadmin.cli.CliCommand.CliCommand`.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def setup_argparser(subparsers):
|
||||
parser = subparsers.add_parser(
|
||||
'sysuser',
|
||||
help = _('manage system users'))
|
||||
parser.set_defaults(commandclass=SysuserCli)
|
||||
cmdsub = parser.add_subparsers(
|
||||
title = _('sysuser subcommands'),
|
||||
dest = 'subcommand')
|
||||
cmdparser = cmdsub.add_parser(
|
||||
'create',
|
||||
help = _('create a new system user with the given options.'))
|
||||
cmdparser.add_argument(
|
||||
'-n', '--username',
|
||||
help = _('the system user name'))
|
||||
cmdparser.add_argument(
|
||||
'-t', '--usertype',
|
||||
help = _('the numeric user type'))
|
||||
cmdparser.add_argument(
|
||||
'--home',
|
||||
help = _('the home directory'))
|
||||
cmdparser.add_argument(
|
||||
'-s', '--shell',
|
||||
help = _('true if the user should get shell access'))
|
||||
cmdparser.add_argument(
|
||||
'-p', '--password', dest = 'clearpass',
|
||||
help = _('the password for the user'))
|
||||
cmdparser.add_argument(
|
||||
'-c', '--clientid', required = True,
|
||||
help = _('the client id'))
|
||||
cmdparser = cmdsub.add_parser(
|
||||
'list',
|
||||
help = _('list existing system users.'))
|
||||
cmdparser = cmdsub.add_parser(
|
||||
'delete',
|
||||
help = _('delete a system user.'))
|
||||
cmdparser.add_argument(
|
||||
'-s', '--sysuserid', required = True,
|
||||
help = _('the system user id'))
|
||||
|
||||
def _execute(self):
|
||||
"""Executes `subcommand`.
|
||||
|
||||
This method implements `CliCommand._execute()`.
|
||||
"""
|
||||
self.logger.debug("execute %s", self.args)
|
||||
from gnuviechadmin.backend import sysuser
|
||||
from gnuviechadmin import exceptions
|
||||
sh = sysuser.SysuserHandler(self.config, self.args.verbose)
|
||||
if self.args.subcommand == "create":
|
||||
try:
|
||||
mysysuser = sh.create(**self.args.__dict__)
|
||||
if self.args.verbose:
|
||||
print mysysuser
|
||||
except exceptions.CreationFailedError, cfe:
|
||||
print cfe
|
||||
sys.exit(2)
|
||||
elif self.args.subcommand == "list":
|
||||
sysusers = sh.fetchall()
|
||||
for su in sysusers:
|
||||
print su
|
||||
elif self.args.subcommand == "delete":
|
||||
sh.delete(self.args.sysuserid)
|
||||
|
||||
def __init__(self, args, config):
|
||||
"""Constructor for the sysuser command."""
|
||||
CliCommand.__init__(self, args, config)
|
40
gnuviechadmin/config.py
Normal file
40
gnuviechadmin/config.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2009 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 3 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$
|
||||
"""
|
||||
Parse the configuration and setup logging.
|
||||
"""
|
||||
|
||||
from sys import argv
|
||||
from os import getcwd
|
||||
from os.path import isfile
|
||||
from paste.deploy import appconfig
|
||||
from logging.config import fileConfig
|
||||
|
||||
if len(argv) > 1 and isfile(argv[1]):
|
||||
CONFIGFILE = argv[1]
|
||||
del argv[1]
|
||||
else:
|
||||
CONFIGFILE = 'development.ini'
|
||||
|
||||
CONFIG = appconfig('config:%s' % CONFIGFILE, relative_to=getcwd())
|
||||
fileConfig(CONFIGFILE, CONFIG)
|
79
gnuviechadmin/defaults.cfg
Normal file
79
gnuviechadmin/defaults.cfg
Normal file
|
@ -0,0 +1,79 @@
|
|||
# Copyright (C) 2007, 2008 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$
|
||||
|
||||
# Distributed default settings for significant GnuviechAdmin config
|
||||
# variables.
|
||||
#
|
||||
# NEVER make site configuration changes to this file. ALWAYS make
|
||||
# them in gva.cfg instead, in the designated area. See the comments
|
||||
# in that file for details.
|
||||
|
||||
[database]
|
||||
# The database connection string in a format usable for
|
||||
# sqlalchemy. The default is an sqlite in memory database which is not
|
||||
# very usable for a real installation.
|
||||
#
|
||||
uri = sqlite:////etc/gnuviechadmin/gva.db
|
||||
repository = /etc/gnuviechadmin/dbrepo
|
||||
|
||||
[common]
|
||||
suwrapper = sudo
|
||||
backupdir = /var/backups/gnuviechadmin
|
||||
templatedir = /etc/gnuviechadmin/templates
|
||||
mailtemplates = mails
|
||||
log.cfg = /etc/gnuviechadmin/logging.cfg
|
||||
|
||||
[client]
|
||||
create.mail = create_client.txt
|
||||
create_subject = A new client ${firstname} ${lastname} has been created.
|
||||
|
||||
[sysuser]
|
||||
nameprefix = usr
|
||||
homedirbase = /home/www
|
||||
minuid = 10000
|
||||
maxuid = 39999
|
||||
shellyes = /bin/bash
|
||||
shellno = /usr/bin/scponly
|
||||
defaultgroup = wwwusers
|
||||
gecos = Webuser %s
|
||||
homebackupdir = homes
|
||||
hometemplate = home
|
||||
create.mail = create_sysuser.txt
|
||||
create_subject = A new system user ${username} has been created.
|
||||
|
||||
[domain]
|
||||
defaultmxprio = 5
|
||||
defaultrefresh = 86400
|
||||
defaultretry = 7200
|
||||
defaultexpire = 1209600
|
||||
defaultminimumttl = 86400
|
||||
defaultttl = 86400
|
||||
htdir = html
|
||||
logpath = /var/log/apache2
|
||||
statspath = /home/stats
|
||||
htdocstemplate = domain
|
||||
conftemplates = domainconf
|
||||
apachetemplate = vhost.conf
|
||||
statshtaccesstemplate = htaccess-stats
|
||||
modlogantemplate = modlogan.conf
|
||||
modlogandir = /var/lib/gnuviechadmin/stats
|
||||
sitesdir = /etc/apache2/sites-available
|
||||
authdir = /etc/apache2/authdata
|
||||
create.mail = create_domain.txt
|
||||
create_subject = A new domain ${domain} has been created.
|
103
gnuviechadmin/exceptions.py
Normal file
103
gnuviechadmin/exceptions.py
Normal file
|
@ -0,0 +1,103 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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$
|
||||
"""This file defines the gnuviechadmin specific exception types."""
|
||||
|
||||
|
||||
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."""
|
||||
|
||||
def __init__(self, missingfields):
|
||||
GnuviechadminError.__init__(self)
|
||||
self.missing = missingfields
|
||||
|
||||
def __str__(self):
|
||||
return "the fields %s are missing." % (repr(self.missing))
|
||||
|
||||
|
||||
class CreationFailedError(GnuviechadminError):
|
||||
"""This exception should be raised if a business object could not
|
||||
be created."""
|
||||
|
||||
def __init__(self, classname, cause = None):
|
||||
GnuviechadminError.__init__(self)
|
||||
self.classname = classname
|
||||
self.cause = cause
|
||||
|
||||
def __str__(self):
|
||||
msg = "Creating an instance of class %s failed." % (self.classname)
|
||||
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):
|
||||
GnuviechadminError.__init__(self)
|
||||
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
|
||||
|
||||
|
||||
class ValidationFailedError(GnuviechadminError):
|
||||
"""This exception should be raised if the validation of a business
|
||||
object failed."""
|
||||
|
||||
def __init__(self, instance, cause = None):
|
||||
GnuviechadminError.__init__(self)
|
||||
self.instance = instance
|
||||
self.cause = cause
|
||||
|
||||
def __str__(self):
|
||||
msg = "Validating %s failed." % (str(self.instance))
|
||||
if self.cause:
|
||||
msg += " The reason is %s." % (str(self.cause))
|
||||
return msg
|
||||
|
||||
|
||||
class CannotDeleteError(GnuviechadminError):
|
||||
"""This exception should be raised if an entity cannot be deleted
|
||||
because of some unmatched precondition."""
|
||||
|
||||
def __init__(self, instance, cause = None):
|
||||
GnuviechadminError.__init__(self)
|
||||
self.instance = instance
|
||||
self.cause = cause
|
||||
|
||||
def __str__(self):
|
||||
msg = "Cannot delete %s." % (str(self.instance))
|
||||
if self.cause:
|
||||
msg += " The reason is %s." % (str(self.cause))
|
||||
return msg
|
80
gnuviechadmin/gva.cfg.tmpl
Normal file
80
gnuviechadmin/gva.cfg.tmpl
Normal file
|
@ -0,0 +1,80 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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$
|
||||
|
||||
[database]
|
||||
# This section contains database related configuration parameters.
|
||||
#
|
||||
# The database connection string in a format usable for
|
||||
# sqlalchemy. For examples see
|
||||
# http://www.sqlalchemy.org/docs/dbengine.myt
|
||||
#
|
||||
uri = sqlite:///database.txt
|
||||
#
|
||||
# The path to the sqlalchemy-migrate database repository
|
||||
#repository = /etc/gnuviechadmin/dbrepo
|
||||
|
||||
[common]
|
||||
# This section contains common configuration parameters.
|
||||
#
|
||||
# Wrapper to execute commands with superuser (root) privileges
|
||||
#suwrapper = sudo
|
||||
#
|
||||
# Directory to store backup data to
|
||||
#backupdir = /var/backups/gnuviechadmin
|
||||
#
|
||||
# Directory where the Gnuviech Admin templates are stored
|
||||
#templatedir = /etc/gnuviechadmin/templates
|
||||
#
|
||||
# Logging configuration file
|
||||
#log.cfg = /etc/gnuviechadmin/logging.cfg
|
||||
#
|
||||
# Default country code to use for new clients when country is not specified
|
||||
#defaultcountry = de
|
||||
#
|
||||
# Mail sender address for mails sent out by Gnuviech Admin. This
|
||||
# address is used to find the GnuPG signing key too.
|
||||
#mailfrom = gva@example.org
|
||||
#
|
||||
# Mail recipient address for mails sent out by Gnuviech Admin. This
|
||||
# address is used to find the GnuPG encryption key too.
|
||||
#mailto = admin@example.org
|
||||
|
||||
[domain]
|
||||
# This section contains various configuration parameters for domain
|
||||
# creation.
|
||||
#
|
||||
# Default primary nameserver name
|
||||
#defaultns1 = ns1.example.org
|
||||
#
|
||||
# Default secondary nameserver name
|
||||
#defaultns2 = ns2.example.org
|
||||
#
|
||||
# Default mail exchanger hostname
|
||||
#defaultmx = mail.example.org
|
||||
#
|
||||
# Default ip address
|
||||
#defaultip = 127.0.0.1
|
||||
#
|
||||
# Default hostmaster address for SOA records (the first dot is equal
|
||||
# to an at sign in a normal email address. So if your hostmaster has
|
||||
# the address hostmaster@example.org the value for defaulthostmaster
|
||||
# ist hostmaster.example.org)
|
||||
#defaulthostmaster = hostmaster.example.org
|
34
gnuviechadmin/logging.cfg
Normal file
34
gnuviechadmin/logging.cfg
Normal file
|
@ -0,0 +1,34 @@
|
|||
[formatters]
|
||||
keys=simple
|
||||
|
||||
[formatter_simple]
|
||||
format=%(asctime)s %(levelname)s %(name)s: %(message)s
|
||||
datefmt=
|
||||
|
||||
[handlers]
|
||||
keys=handler01,handler02
|
||||
|
||||
[handler_handler01]
|
||||
class=handlers.RotatingFileHandler
|
||||
level=DEBUG
|
||||
formatter=simple
|
||||
args=('gnuviechadmin.log', 'a', 10485760, 5)
|
||||
|
||||
[handler_handler02]
|
||||
class=StreamHandler
|
||||
level=NOTSET
|
||||
formatter=simple
|
||||
args=(sys.stdout,)
|
||||
|
||||
[loggers]
|
||||
keys=root,gnuviechadmin
|
||||
|
||||
[logger_root]
|
||||
level=NOTSET
|
||||
handlers=handler02
|
||||
|
||||
[logger_gnuviechadmin]
|
||||
level=DEBUG
|
||||
handlers=handler01
|
||||
propagate=0
|
||||
qualname=gnuviechadmin
|
44
gnuviechadmin/tests/__init__.py
Normal file
44
gnuviechadmin/tests/__init__.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2008 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$
|
||||
"""Gnuviechadmin application test package
|
||||
|
||||
When the test runner finds and executes test within this directory,
|
||||
this file will be loaded to setup the test environment.
|
||||
|
||||
It registers the root directory of the project in sys.path and
|
||||
pkg_resources, in case the project hasn't been installed with
|
||||
setuptools.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import logging.config
|
||||
from unittest import TestCase
|
||||
|
||||
import pkg_resources
|
||||
|
||||
here_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
conf_dir = os.path.dirname(os.path.dirname(here_dir))
|
||||
|
||||
sys.path.insert(0, conf_dir)
|
||||
pkg_resources.working_set.add_entry(conf_dir)
|
||||
|
||||
test_file = os.path.join(conf_dir, 'test.ini')
|
||||
logging.config.fileConfig(test_file)
|
23
gnuviechadmin/tests/functional/__init__.py
Normal file
23
gnuviechadmin/tests/functional/__init__.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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$
|
||||
"""This package provides functional unit tests for gnuviechadmin.
|
||||
|
||||
"""
|
72
gnuviechadmin/tests/functional/test_util_passwordutils.py
Normal file
72
gnuviechadmin/tests/functional/test_util_passwordutils.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2008 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$
|
||||
"""Unit tests for gnuviechadmin.util.passwordutils.
|
||||
|
||||
"""
|
||||
from gnuviechadmin.util import passwordutils
|
||||
from unittest import TestCase
|
||||
import crypt
|
||||
|
||||
|
||||
class TestPasswordUtils(TestCase):
|
||||
weakpw = "test"
|
||||
strongpw = "Str0ng#l33tP4assword"
|
||||
|
||||
def test_generatepassword(self):
|
||||
password = passwordutils.generatepassword()
|
||||
self.assert_(password is not None)
|
||||
|
||||
def test_checkpassword(self):
|
||||
weakcheck = passwordutils.checkpassword(self.weakpw)
|
||||
self.assertEqual(weakcheck, None,
|
||||
"checking a weak password should return ''None''." + \
|
||||
"%s was returned." % weakcheck)
|
||||
strongcheck = passwordutils.checkpassword(self.strongpw)
|
||||
self.assertEqual(self.strongpw, strongcheck,
|
||||
'checking a strong password should return the ' + \
|
||||
' the password. %s was returned.' % strongcheck)
|
||||
|
||||
def test_md5_crypt_password(self):
|
||||
encrypted = passwordutils.md5_crypt_password(self.strongpw)
|
||||
self.assertNotEqual(self.strongpw, encrypted)
|
||||
self.assertEqual(encrypted, crypt.crypt(self.strongpw, encrypted))
|
||||
|
||||
def test_get_pw_tuple(self):
|
||||
|
||||
def check_tuple(pwtuple):
|
||||
self.assertEqual(len(pwtuple), 2)
|
||||
self.assertNotEqual(pwtuple[0], None)
|
||||
self.assert_(len(pwtuple[0]) >= 8)
|
||||
self.assertEqual(pwtuple[1], crypt.crypt(pwtuple[0], pwtuple[1]))
|
||||
check_tuple(passwordutils.get_pw_tuple())
|
||||
weakpwtuple = passwordutils.get_pw_tuple(self.weakpw)
|
||||
check_tuple(weakpwtuple)
|
||||
self.assertNotEqual(self.weakpw, weakpwtuple[0])
|
||||
strongpwtuple = passwordutils.get_pw_tuple(self.strongpw)
|
||||
check_tuple(strongpwtuple)
|
||||
self.assertEqual(self.strongpw, strongpwtuple[0])
|
||||
|
||||
def test_validate_password(self):
|
||||
hashed = passwordutils.md5_crypt_password(self.strongpw)
|
||||
self.assertEqual(passwordutils.validate_password(hashed,
|
||||
self.strongpw), True)
|
||||
self.assertEqual(passwordutils.validate_password(self.weakpw,
|
||||
self.strongpw), False)
|
106
gnuviechadmin/tests/functional/test_xmlrpc_users.py
Normal file
106
gnuviechadmin/tests/functional/test_xmlrpc_users.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2008 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$
|
||||
"""Unit tests for gnuviechadmin.xmlrpc.users.
|
||||
|
||||
"""
|
||||
from gnuviechadmin.xmlrpc.users import ClientUserProvider, \
|
||||
MailuserUserProvider, SysuserUserProvider, GVAUsers
|
||||
from authkit.users import AuthKitNoSuchUserError
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
class TestClientUserProvider(TestCase):
|
||||
subject = None
|
||||
|
||||
def setUp(self):
|
||||
self.subject = ClientUserProvider(None)
|
||||
|
||||
def test_user(self):
|
||||
user = self.subject.user('dummy')
|
||||
self.assertEquals(user['roles'], ['client'])
|
||||
try:
|
||||
user = self.subject.user('nodummy')
|
||||
self.fail('should have raised AuthKitNoSuchUserError.')
|
||||
except AuthKitNoSuchUserError, e:
|
||||
pass
|
||||
|
||||
def test_list_roles(self):
|
||||
self.assertEquals(self.subject.list_roles(), ['client'])
|
||||
|
||||
|
||||
class TestMailuserUserProvider(TestCase):
|
||||
subject = None
|
||||
|
||||
def setUp(self):
|
||||
self.subject = MailuserUserProvider(None)
|
||||
|
||||
def test_user(self):
|
||||
try:
|
||||
user = self.subject.user('dummy')
|
||||
self.fail('should have raised AuthKitNoSuchUserError.')
|
||||
except AuthKitNoSuchUserError, e:
|
||||
pass
|
||||
|
||||
def test_list_roles(self):
|
||||
self.assertEquals(self.subject.list_roles(), ['mailuser'])
|
||||
|
||||
class TestSysuserUserProvider(TestCase):
|
||||
subject = None
|
||||
|
||||
def setUp(self):
|
||||
self.subject = SysuserUserProvider(None)
|
||||
|
||||
def test_user(self):
|
||||
try:
|
||||
user = self.subject.user('dummy')
|
||||
self.fail('should have raised AuthKitNoSuchUserError.')
|
||||
except AuthKitNoSuchUserError, e:
|
||||
pass
|
||||
|
||||
def test_list_roles(self):
|
||||
self.assertEquals(self.subject.list_roles(), ['sysuser'])
|
||||
|
||||
|
||||
class TestGVAUsers(TestCase):
|
||||
subject = None
|
||||
|
||||
def setUp(self):
|
||||
self.subject = GVAUsers(None, [ClientUserProvider,
|
||||
MailuserUserProvider,
|
||||
SysuserUserProvider])
|
||||
|
||||
def test_list_roles(self):
|
||||
roles = self.subject.list_roles()
|
||||
self.assertEquals(roles, ['client', 'mailuser', 'sysuser'])
|
||||
|
||||
def test_role_exists(self):
|
||||
self.assertFalse(self.subject.role_exists('dummy'))
|
||||
for role in ('client', 'mailuser', 'sysuser'):
|
||||
self.assertTrue(self.subject.role_exists(role))
|
||||
|
||||
def test_user(self):
|
||||
try:
|
||||
self.subject.user('nouser')
|
||||
self.fail('should have raised AuthKitNoSuchUserError.')
|
||||
except AuthKitNoSuchUserError, e:
|
||||
pass
|
||||
user = self.subject.user('dummy')
|
||||
print user
|
29
gnuviechadmin/util/__init__.py
Normal file
29
gnuviechadmin/util/__init__.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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$
|
||||
|
||||
"""This is the gnuviechadmin.util package.
|
||||
|
||||
The package provides utility modules for various functions.
|
||||
|
||||
* getenttools to handle user and group information
|
||||
* gpgmail for sending gpg signed and encrypted mails
|
||||
* passwordutils for generating and checking passwords
|
||||
* stmtcreator to create email account SQL statements"""
|
134
gnuviechadmin/util/getenttools.py
Normal file
134
gnuviechadmin/util/getenttools.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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$
|
||||
|
||||
"""Tools for handling user and group information."""
|
||||
|
||||
import pwd
|
||||
import grp
|
||||
|
||||
|
||||
class PasswdUser(object):
|
||||
"""This class represents users in the user database."""
|
||||
|
||||
def __init__(self, username, passw, uid, gid, gecos, home, shell):
|
||||
"""Create a new PasswdUser."""
|
||||
self.username = username
|
||||
self.uid = int(uid)
|
||||
self.gid = int(gid)
|
||||
self.gecos = gecos
|
||||
self.home = home
|
||||
self.shell = shell
|
||||
|
||||
def __repr__(self):
|
||||
"""Returns a user string representation."""
|
||||
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, passwd, gid, members):
|
||||
"""Create a new PasswdGroup."""
|
||||
self.groupname = groupname
|
||||
self.gid = int(gid)
|
||||
self.members = members
|
||||
|
||||
def __repr__(self):
|
||||
"""Returns a group string representation."""
|
||||
return "%s(%s:%d:%s)" % (self.__class__.__name__,
|
||||
self.groupname,
|
||||
self.gid,
|
||||
",".join(self.members))
|
||||
|
||||
|
||||
def parse_groups():
|
||||
"""Parses all available groups to PasswdGroup instances."""
|
||||
return [PasswdGroup(*arr) for arr in grp.getgrall()]
|
||||
|
||||
|
||||
def parse_users():
|
||||
"""Parses all available users to PasswdUser instances."""
|
||||
return [PasswdUser(*arr) for arr in pwd.getpwall()]
|
||||
|
||||
|
||||
def find_user_by_prefix(prefix):
|
||||
"""Finds all user entries with the given prefix."""
|
||||
return [user for user in parse_users() if user.username.startswith(prefix)]
|
||||
|
||||
|
||||
def get_user_by_id(uid):
|
||||
"""Gets the user with the given user id."""
|
||||
users = [user for user in parse_users() if user.uid == uid]
|
||||
if users:
|
||||
return users[0]
|
||||
return None
|
||||
|
||||
|
||||
def get_group_by_id(gid):
|
||||
"""Gets the group with the given group id."""
|
||||
groups = [group for group in parse_groups() if group.gid == gid]
|
||||
if groups:
|
||||
return groups[0]
|
||||
return None
|
||||
|
||||
|
||||
def get_next_uid(lowerboundary = 10000, upperboundary = 65536):
|
||||
"""Gets the first available user id in the given range.
|
||||
|
||||
The returned uid is a value between lowerboundary and upper
|
||||
boundary. An exception is raised if no uid can be found.
|
||||
|
||||
Keyword arguments:
|
||||
lowerboundary -- lower boundary for uid range
|
||||
upperboundary -- upper boundary for uid range
|
||||
|
||||
"""
|
||||
for uid in range(lowerboundary, upperboundary):
|
||||
try:
|
||||
pwd.getpwuid(uid)
|
||||
except KeyError:
|
||||
return uid
|
||||
raise Exception("no free uid found in range %d to %d",
|
||||
lowerboundary, upperboundary)
|
||||
|
||||
|
||||
def get_max_uid(boundary = 65536):
|
||||
"""Gets the highest uid value."""
|
||||
return max([user.uid for user in parse_users() if user.uid <= boundary])
|
||||
|
||||
|
||||
def get_max_gid(boundary = 65536):
|
||||
"""Gets the highest gid value."""
|
||||
return max([group.gid for group in parse_groups() \
|
||||
if group.gid <= boundary])
|
||||
|
||||
if __name__ == "__main__":
|
||||
print "Max UID is %d" % (get_max_uid(40000))
|
||||
print "Max GID is %d" % (get_max_gid(40000))
|
||||
print "User with max UID is %s" % (get_user_by_id(get_max_uid(40000)))
|
||||
print "Group with max GID is %s" % (get_group_by_id(get_max_gid(40000)))
|
||||
print "First free UID is %s" % (get_next_uid(10000, 40000))
|
89
gnuviechadmin/util/gpgmail.py
Normal file
89
gnuviechadmin/util/gpgmail.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008, 2009 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 3 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$
|
||||
|
||||
"""GPG mail handling.
|
||||
|
||||
This module provides functionallity for sending signed and encrypted
|
||||
email using GnuPG.
|
||||
"""
|
||||
|
||||
import smtplib
|
||||
from email.MIMEText import MIMEText
|
||||
from pyme import core
|
||||
from pyme.constants.sig import mode
|
||||
|
||||
|
||||
def send_mail(config, subject, text):
|
||||
"""Send a signed and possibly encrypted mail.
|
||||
|
||||
This method sends a mail with the given text and subject and signs
|
||||
it using GnuPG. If a public key of the recipient is available the
|
||||
mail is encrypted. The sender and recipient addresses are taken
|
||||
from the configuration (section: common, properties: mailfrom,
|
||||
mailto)
|
||||
|
||||
Arguments:
|
||||
subject -- mail subject
|
||||
text -- mail text
|
||||
"""
|
||||
if not text.__class__.__name__ == 'str':
|
||||
text = text.encode('ascii', 'replace')
|
||||
plain = core.Data(text)
|
||||
cipher = core.Data()
|
||||
c = core.Context()
|
||||
c.set_armor(1)
|
||||
signer = config['mailfrom']
|
||||
rcpt = config['mailto']
|
||||
c.signers_clear()
|
||||
for sigkey in [x for x in c.op_keylist_all(signer, 1)]:
|
||||
if sigkey.can_sign:
|
||||
c.signers_add(sigkey)
|
||||
if not c.signers_enum(0):
|
||||
raise Exception("No secret keys for signing available for %s." % (
|
||||
signer))
|
||||
keylist = []
|
||||
for key in c.op_keylist_all(rcpt, 0):
|
||||
valid = 0
|
||||
for subkey in key.subkeys:
|
||||
keyid = subkey.keyid
|
||||
if keyid == None:
|
||||
break
|
||||
can_encrypt = subkey.can_encrypt
|
||||
valid += can_encrypt
|
||||
if valid:
|
||||
keylist.append(key)
|
||||
if keylist:
|
||||
c.op_encrypt_sign(keylist, 1, plain, cipher)
|
||||
else:
|
||||
c.op_sign(plain, cipher, mode.CLEAR)
|
||||
cipher.seek(0, 0)
|
||||
|
||||
msg = MIMEText(cipher.read())
|
||||
if keylist:
|
||||
msg.set_param("x-action", "pgp-encrypted")
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = signer
|
||||
msg['To'] = rcpt
|
||||
|
||||
s = smtplib.SMTP()
|
||||
s.connect()
|
||||
s.sendmail(signer, [rcpt], msg.as_string())
|
||||
s.close()
|
98
gnuviechadmin/util/passwordutils.py
Normal file
98
gnuviechadmin/util/passwordutils.py
Normal file
|
@ -0,0 +1,98 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2007, 2008 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$
|
||||
|
||||
"""This module provides some functions for password handling."""
|
||||
import crypt
|
||||
import crack
|
||||
import random
|
||||
import logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_PWCHARS = []
|
||||
for _pair in (('0', '9'), ('A', 'Z'), ('a', 'z')):
|
||||
_PWCHARS.extend(range(ord(_pair[0]), ord(_pair[1])))
|
||||
_SALTCHARS = [_char for _char in _PWCHARS]
|
||||
for _char in "-+/*_@":
|
||||
_PWCHARS.append(ord(_char))
|
||||
|
||||
|
||||
def generatepassword(minlength = 8, maxlength = 12):
|
||||
"""Generates a new random password with a given length.
|
||||
|
||||
The generated password has a length between minlength and maxlength.
|
||||
|
||||
Keyword arguments:
|
||||
`minlength` -- minimum length of the generated password
|
||||
`maxlength` -- the maximum length of the generated password
|
||||
"""
|
||||
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.
|
||||
|
||||
Arguments:
|
||||
`password` -- the password to check
|
||||
"""
|
||||
try:
|
||||
return crack.VeryFascistCheck(password)
|
||||
except ValueError, ve:
|
||||
LOG.info("Weak password: %s", ve)
|
||||
return None
|
||||
|
||||
|
||||
def md5_crypt_password(password):
|
||||
"""Hashes the given password with MD5 and a random salt value.
|
||||
|
||||
A password hashed with MD5 and a random salt value is returned.
|
||||
|
||||
Arguments:
|
||||
`password` -- the password to hash
|
||||
"""
|
||||
salt = "".join([chr(letter) for letter in \
|
||||
random.sample(_SALTCHARS, 8)])
|
||||
return crypt.crypt(password, '$1$' + salt)
|
||||
|
||||
|
||||
def get_pw_tuple(password = None):
|
||||
"""Gets a valid (password, hashvalue) tuple.
|
||||
|
||||
The tuple consists of a password and a md5 hash of the same
|
||||
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))
|
||||
|
||||
|
||||
def validate_password(hashvalue, password):
|
||||
"""Validates whether the given clear text `password` matches the
|
||||
given `hashvalue`.
|
||||
"""
|
||||
return hashvalue == crypt.crypt(password, hashvalue)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue