#!/usr/bin/env python# -*- coding: utf-8 -*-"""Apache Auth Group Manager
A grid of checkboxes represents group memberships for Apache access control.
They are read from the group file, which has to be specified on the command
line, and can be saved back to the very same file.
The file format is as follows::
group1: user1 user2
group2: user1 user3 user4
Optionally, a users file (usually called ``.passwd`` or ``.digest_pw``) can be
specfied to include users in the grid that are not yet members of one of the
groups.
This utility is only meant for adding or removing users to or from groups.
Add users using the standard tools (``htpasswd`` or ``htdigest``). To remove
users, add or remove groups, edit the according file manually.
.. note:: Groups without users assigned won't be saved.
:Copyright: (c) 2008 Jochen Kupperschmidt
:Date: 25-Nov-2008
:License: MIT
"""from__future__importwith_statementfromcollectionsimportdefaultdictfromitertoolsimportchainimportos.pathimportsysimportTkinterastk# Load, handle and store users and groups.defload_groups(filename):"""Load groups and their members from file."""withopen(filename,'rb')asf:forlineinf:group,users=line.split(':',1)yieldgroup,frozenset(users.split())defload_users(filename):"""Load users from file."""withopen(filename,'rb')asf:forlineinf:yieldline.split(':',1)[0]defget_groups_users(groups):"""Return the set of users that are group members."""returnset(chain(*(membersforgroup,membersingroups)))defsave_groups(filename,groups):"""Write groups and their member associations to file."""withopen(filename,'wb')asf:forgroup,membersingroups.iteritems():f.write('%s: %s\n'%(group,' '.join(members)))# Tkinter GUIclassGUI(tk.Tk):"""Graphical frontend."""def__init__(self,users,groups,filename):tk.Tk.__init__(self)self.title('Group Membership Manager')# Build labeled grid of checkbuttons.self.items=[]forcolumn,(group,members)inenumerate(sorted(groups)):tk.Label(self,text=group).grid(row=0,column=column+1)forrow,userinenumerate(sorted(users)):tk.Label(self,text=user).grid(row=row+1,column=0,sticky=tk.W)forcolumn,(group,members)inenumerate(groups):var=tk.BooleanVar()var.set(userinmembers)tk.Checkbutton(self,variable=var) \
.grid(row=row+1,column=column+1)self.items.append((group,user,var))# Add a button to save the current selection.self.filename=filenametk.Button(self,text='Save',command=self.save) \
.grid(row=len(users)+2,column=0,columnspan=len(groups)+1)defsave(self):"""Re-assemble and save groups and memberships."""groups=defaultdict(list)forgroup,user,varinself.items:ifvar.get():groups[group].append(user)save_groups(self.filename,groups)defmain(groups_filename,users_filename=None):groups=list(load_groups(groups_filename))users=get_groups_users(groups)ifusers_filename:users.update(load_users(users_filename))GUI(users,groups,groups_filename).mainloop()if__name__=='__main__':iflen(sys.argv)notin(2,3):print('usage: %s <groups file> [users file]'%os.path.basename(sys.argv[0]))main(*sys.argv[1:])