Index: acct_mgr/api.py===================================================================--- acct_mgr/api.py (revision 107)+++ acct_mgr/api.py (working copy)@@ -9,10 +9,6 @@ # # Author: Matthew Good <trac@matt-good.net>-from __future__ import generators--import inspect- from trac.core import * class IPasswordStore(Interface):@@ -37,12 +33,30 @@ def set_password(self, user, password): """Sets the password for the user. This should create the user account if it doesn't already exist.+ Returns True if a new account was created, False if an existing account+ was updated. """ def delete_user(self, user): """Deletes the user account. """+class IAccountChangeListener(Interface):+ """An interface for receiving account change events.+ """++ def user_added(self, user, password):+ """New user+ """++ def user_password_changed(self, user, password):+ """Password change+ """++ def user_deleted(self, user):+ """User delete requested.+ """+ class AccountManager(Component): """The AccountManager component handles all user account management methods provided by the IPasswordStore interface.@@ -53,32 +67,33 @@ """ stores = ExtensionPoint(IPasswordStore)+ change_listeners = ExtensionPoint(IAccountChangeListener)- def _dispatch(self, func):- return getattr(self._get_store(), func)+ def get_users(self):+ return self.password_store.get_users()- def _get_store(self):+ def has_user(self, user):+ return self.password_store.has_user(user)++ def set_password(self, user, password):+ if self.password_store.set_password(user, password):+ for l in self.change_listeners:+ l.user_created(user, password)+ else:+ for l in self.change_listeners:+ l.user_password_changed(user, password)++ def delete_user(self, user):+ self.password_store.delete_user(user)+ for l in self.change_listeners:+ l.user_deleted(user, password)++ def password_store(self): fmt = self.config.get('account-manager', 'password_format') for store in self.stores: if store.config_key() == fmt: return store raise TracError('No password store found. Please configure ' '"account-manager.password_format" in trac.ini.')+ password_store = property(password_store)--class DispatchProperty(object):- def __init__(self, name, fget):- self.name = name- self.fget = fget-- def __get__(self, obj, objtype=None):- if obj is None:- return self- return self.fget(obj, self.name)--# Add the IPasswordStore methods to the AccountManager to dispatch to the-# active implementation-for func, v in inspect.getmembers(IPasswordStore, inspect.ismethod):- setattr(AccountManager, func, DispatchProperty(func, AccountManager._dispatch))--Index: acct_mgr/htfile.py===================================================================--- acct_mgr/htfile.py (revision 107)+++ acct_mgr/htfile.py (working copy)@@ -46,7 +46,8 @@ return self._get_users(self._get_filename()) def set_password(self, user, password):- self._update_file(self.prefix(user), self.userline(user, password))+ return self._update_file(self.prefix(user),+ self.userline(user, password)) def delete_user(self, user): self._update_file(self.prefix(user), None)@@ -55,19 +56,23 @@ return self.config.get('account-manager', 'password_file') def _update_file(self, prefix, userline):+ if not userline:+ raise Exception filename = self._get_filename() written = False if os.path.exists(filename): for line in fileinput.input(filename, inplace=True): if not line.startswith(prefix): print line,- elif not written and userline:+ elif not written: print userline written = True- if not written and userline:- f = open(filename, 'a')- print >>f, userline- f.close()+ if written:+ return False # we updated an existing account+ f = open(filename, 'a')+ print >>f, userline+ f.close()+ return True # we appended a new user account def salt():