[Mailman-Developers] Creation/deletion of lists through-the-web

Barry A. Warsaw barry@digicool.com
Wed, 9 May 2001 01:45:36 -0400


Last night I got inspired to add back the through-the-web creation and
deletion of mailing lists.  I've now got this working for Postfix and
can work with other MTAs with a little help from y'all.  I will soon
be checking all this code into the 2.1 codebase, so watch
mailman-checkins shortly.  (And hey, it only took 24 hours from
inception to completion! :).

Apologies in advance for the length of this message.

First, there is a new pseudo-role[1] called `list creator'.  There is
a separate, global list creator password, similar to but different
than the site admin password.  This is because list creation is a
site-wide operation, but I don't want to have to give out the site
admin password to someone who may only have list creation duties.
Needless to say, the site admin can create lists through the web too.
You set the list creator's password by using mmsitepass with the -c
option. 

Let's say Mailman is installed at http://yoursite.com/mailman, then
the list creation page is at http://yoursite.com/mailman/create which
is linked to from the admin overview page (but not the listinfo
overview page).

The create page requires you to enter the name of your list, the email
address of the initial list owner, the initial list password (with
confirmation), and the list creator's password.  There is then a
"Create" button to submit the form.

Assuming everything checks out, the new list is created, and the
results page gives you three links to follow from there (go to the
list's info page, the list's admin page, or create another list).

Deleting a list is done from the list's admin page.  There is now
another link under "Other Administrative Activities" that says "Delete
this list (requires confirmation)".  Click on that and you're brought
to a page for confirmation of the list deletion operation.  You have a
radio button to choose whether the archives should be deleted or not
(the default is not), and you're prompted for the list admin
password[2].  There's a link to cancel the deletion and return to the
list's admin page, and a button to actually do the list deletion.

For bonus, the site administrator can inhibit the ability for
individual list owners to delete their lists through the web by
setting OWNERS_CAN_DELETE_THEIR_OWN_LISTS = 0 in mm_cfg.py.  Do this,
and the link is not shown in the "Other Administrative Activities"
list, and the rmlist cgi will error out every time.  The current
default for OWNERS_CAN_DELETE_THEIR_OWN_LISTS is 0, but that may
change to 1 before the final release.  The disadvantage is that with
this variable set to 0, list deletion must be done via the command
line script.

Now, how to integrate this with MTAs?  One reason why ttw list
creation and deletion hasn't been (re-)added to Mailman until now is
that you typically have to do some manual and difficult crud like edit
an /etc/aliases file and run `newaliases' (as root!).  I've figured a
way around this with Postfix, and of course Exim can be configured to
automatically recognize new mailing lists, so I figured it was time to
do it.  I'm hoping that Sendmail, Qmail, and other MTA users amongst
yourselves will contribute the code to glue this together for other
mailers.

Here's how I solved it for Postfix; this is outlined in a new
README.POSTFIX file.

Postfix has a configuration variable called `alias_maps' which tells
it where to look for local delivery address associations.  It has
another variable called `alias_databases' which tells it which files
to rebuild when invoked as newaliases.  For each of these you can
specify the type of map database the file is kept as.  One of these
choices is `hash', which is really a BSD dbhash file.  Python has a
dbhash module which nicely handles reading and writing dbhash files
(and it should be enabled by default in Python 2.x, if your system has
Berkeley DB installed, as most Linux distros ought to).

It's moderately well-published what Postfix expects as entries in the
dbhash (and it's easy to figure out by dumping a newaliases generated
.db file) so, when creating a new list, Mailman can add the necessary
keys and values to make Postfix happy.  Let's say Mailman is installed
as /home/mailman and writes its new list alias entries to the dbhash
file at /home/mailman/data/aliases.db.  By adding
"hash:/home/mailman/data/aliases" to your Postfix's alias_maps
variable, Postfix will automatically deliver to your new list.
Deleting is as simple as removing the keys from the dbhash.

Note that you do /not/ want to add this file to alias_databases since
newaliases won't be touching it.

So this works great, but there's a catch!  Postfix will invoke the
filter programs under the uid of the owner of the aliases.db file,
unless that's root, in which case it'll invoke it as the user defined
in the default_privs variable (by default `nobody').  And of course
the gid has to match what you specify in --with-mail-gid or Mailman's
mail wrapper will complain.

The trick is to touch the alias.db file as root, set the group owner
to mailman, and make sure the file is user and group writable.  Now,
when Mailman adds new keys to aliases.db, the user ownership will
remain as root, so Postfix invokes it correctly as nobody.  But
because aliases.db is group-owned by mailman, the cgi processes can
write to the file.  And no setuid-root script in sight.  I've tested
this and it works like a charm.

(Of course, all this is described in cookbook form without the gory
details in README.POSTFIX.  Also, I like this approach much better
than the luser_relay hack I posted about a while ago.)

There's one last bit of glue, and here's where you come in (I'm
speaking to the one of you who is still reading this. :).  There's a
new variable in Defaults.py.in called MTA which must point to a module
in the Mailman/MTA directory.  This implements the MTA-specific
operations needed when creating or deleting a list.  The API is that
this module should provide two functions: create() and remove() both
of which take a MailList object.  They should do whatever is necessary
to inform the MTA that it's alias database has changed.  For Postfix
it's really not a lot of code[3].

For Exim, I predict "MTA = None" in mm_cfg.py will Do The Right Thing,
since nothing special has to be done.  I've no idea at this time what
Sendmail, Qmail, or any other MTA will require, and I'm hoping those
of you who use those mailers will be able to donate a module.

Phew, that's it.  I need sleep.

I'm very interested in getting some feedback, especially for those
hearty souls who can check out the current codebase, and give it a
whirl.  I'm no doubt forgetting some important details, but it sure
has been fun. :)

Enjoy,
-Barry


[1] I call it a pseudo-role because eventually this will be a role
that can be given to specific users (once there's a Real User
Database).

[2] Because list deletion is not handled by the admin.py cgi script,
it isn't protected by the normal admin login screen.  I could (and
may) add this extra authentication step, but I'll probably still keep
the list password requirement on the deletion page as a strong
confirmation of the list owner's intention.

[3] There's one caveat: because I don't want to have to include a
setuid-root script, the Postfix.py module doesn't call `postfix
reload'.  This command has to be done as root.  The tradeoff is that
Postfix will take about a minute to recognize its alias database has
changed.  To me that's an acceptable limitation.