Index: acct_mgr/api.py =================================================================== --- acct_mgr/api.py (revision 107) +++ acct_mgr/api.py (working copy) @@ -9,10 +9,6 @@ # # Author: Matthew Good -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():