Methods are added to the AccountChangeListener interface for password
reset and user email verification to decouple trac/notification.py and
accountmanagerplugin. This change will enable a full-featured
announcerplugin module to replacate the accountmanagerplugin
notification behavior.
---
acct_mgr/api.py | 15 ++++++
acct_mgr/notification.py | 79 +++++++++++++++++++++++++++++++++
acct_mgr/web_ui.py | 110 ++++++++++------------------------------------
3 files changed, 117 insertions(+), 87 deletions(-)
diff --git a/acct_mgr/api.py b/acct_mgr/api.py
index d7963b3..caf2140 100644
--- a/acct_mgr/api.py
+++ b/acct_mgr/api.py
@@ -77,6 +77,14 @@ class IAccountChangeListener(Interface):
"""User deleted
"""
+ def user_password_reset(self, user, email, password):
+ """User password reset
+ """
+
+ def user_email_verification_requested(self, user, token):
+ """User verification requested
+ """
+
class AccountManager(Component):
"""The AccountManager component handles all user account management methods
provided by the IPasswordStore interface.
@@ -248,3 +256,10 @@ class AccountManager(Component):
def user_deleted(self, user):
self.log.info('Deleted user: %s' % user)
+ def user_password_reset(self, user, email, password):
+ self.log.info('Password reset user: %s, %s'%(user, email))
+
+ def user_email_verification_requested(self, user, token):
+ self.log.info('Email verification requested user: %s' % user)
+
+
diff --git a/acct_mgr/notification.py b/acct_mgr/notification.py
index e966855..e840c5b 100644
--- a/acct_mgr/notification.py
+++ b/acct_mgr/notification.py
@@ -50,6 +50,17 @@ class AccountChangeListener(Component):
notifier = AccountChangeNotification(self.env)
notifier.notify(username, 'Deleted User')
+ def user_password_reset(self, username, email, password):
+ notifier = PasswordResetNotification(self.env)
+ if email != notifier.email_map.get(username):
+ raise Exception('The email and username do not '
+ 'match a known account.')
+ notifier.notify(username, password)
+
+ def user_email_verification_requested(self, username, token):
+ notifier = EmailVerificationNotification(self.env)
+ notifier.notify(username, token)
+
class AccountChangeNotification(NotifyEmail):
template_name = 'user_changes_email.txt'
@@ -154,6 +165,74 @@ class AccountChangeNotification(NotifyEmail):
self.server.sendmail(msg['From'], recipients, msgtext)
+class SingleUserNotification(NotifyEmail):
+ """Helper class used for account email notifications which should only be
+ sent to one persion, not including the rest of the normally CCed users
+ """
+ _username = None
+
+ def get_recipients(self, resid):
+ return ([resid],[])
+
+ def get_smtp_address(self, addr):
+ """Overrides `get_smtp_address` in order to prevent CCing users
+ other than the user whose password is being reset.
+ """
+ if addr == self._username:
+ return NotifyEmail.get_smtp_address(self, addr)
+ else:
+ return None
+
+ def notify(self, username, subject):
+ # save the username for use in `get_smtp_address`
+ self._username = username
+ old_public_cc = self.config.getbool('notification', 'use_public_cc')
+ # override public cc option so that the user's email is included in
the To: field
+ self.config.set('notification', 'use_public_cc', 'true')
+ try:
+ NotifyEmail.notify(self, username, subject)
+ finally:
+ self.config.set('notification', 'use_public_cc', old_public_cc)
+
+
+class PasswordResetNotification(SingleUserNotification):
+ template_name = 'reset_password_email.txt'
+
+ def notify(self, username, password):
+ self.data.update({
+ 'account': {
+ 'username': username,
+ 'password': password,
+ },
+ 'login': {
+ 'link': self.env.abs_href.login(),
+ }
+ })
+
+ projname = self.config.get('project', 'name')
+ subject = '[%s] Trac password reset for user: %s' % (projname,
username)
+
+ SingleUserNotification.notify(self, username, subject)
+
+
+class EmailVerificationNotification(SingleUserNotification):
+ template_name = 'verify_email.txt'
+
+ def notify(self, username, token):
+ self.data.update({
+ 'account': {
+ 'username': username,
+ 'token': token,
+ },
+ 'verify': {
+ 'link': self.env.abs_href.verify_email(token=token),
+ }
+ })
+
+ projname = self.config.get('project', 'name')
+ subject = '[%s] Trac email verification for user: %s' % (projname,
username)
+
+ SingleUserNotification.notify(self, username, subject)
class AccountChangeNotificationAdminPanel(Component):
diff --git a/acct_mgr/web_ui.py b/acct_mgr/web_ui.py
index 4bd3303..7ebceff 100644
--- a/acct_mgr/web_ui.py
+++ b/acct_mgr/web_ui.py
@@ -16,8 +16,7 @@ import string
from trac import perm, util
from trac.core import *
-from trac.config import IntOption
-from trac.notification import NotificationSystem, NotifyEmail
+from trac.config import IntOption, BoolOption
from trac.prefs import IPreferencePanelProvider
from trac.web import auth
from trac.web.api import IAuthenticator
@@ -96,56 +95,6 @@ def _create_user(req, env, check_permissions=True):
db.commit()
-class SingleUserNotification(NotifyEmail):
- """Helper class used for account email notifications which should only be
- sent to one persion, not including the rest of the normally CCed users
- """
- _username = None
-
- def get_recipients(self, resid):
- return ([resid],[])
-
- def get_smtp_address(self, addr):
- """Overrides `get_smtp_address` in order to prevent CCing users
- other than the user whose password is being reset.
- """
- if addr == self._username:
- return NotifyEmail.get_smtp_address(self, addr)
- else:
- return None
-
- def notify(self, username, subject):
- # save the username for use in `get_smtp_address`
- self._username = username
- old_public_cc = self.config.getbool('notification', 'use_public_cc')
- # override public cc option so that the user's email is included in
the To: field
- self.config.set('notification', 'use_public_cc', 'true')
- try:
- NotifyEmail.notify(self, username, subject)
- finally:
- self.config.set('notification', 'use_public_cc', old_public_cc)
-
-
-class PasswordResetNotification(SingleUserNotification):
- template_name = 'reset_password_email.txt'
-
- def notify(self, username, password):
- self.data.update({
- 'account': {
- 'username': username,
- 'password': password,
- },
- 'login': {
- 'link': self.env.abs_href.login(),
- }
- })
-
- projname = self.config.get('project', 'name')
- subject = '[%s] Trac password reset for user: %s' % (projname,
username)
-
- SingleUserNotification.notify(self, username, subject)
-
-
class AccountModule(Component):
"""Allows users to change their password, reset their password if they've
forgotten it, or delete their account. The settings for the AccountManager
@@ -161,6 +110,10 @@ class AccountModule(Component):
'created when resetting the password for an '
'account.')
+ reset_password = BoolOption('account-manager', 'reset_password',
+ True, 'Set to false if there is no email '
+ 'system setup.')
+
def __init__(self):
self._write_check(log=True)
@@ -219,7 +172,7 @@ class AccountModule(Component):
def reset_password_enabled(self):
return (self.env.is_component_enabled(AccountModule)
- and NotificationSystem(self.env).smtp_enabled
+ and self.reset_password
and self._write_check())
reset_password_enabled = property(reset_password_enabled)
@@ -263,15 +216,12 @@ class AccountModule(Component):
if not email:
return {'error': 'Email is required'}
- notifier = PasswordResetNotification(self.env)
-
- if email != notifier.email_map.get(username):
- return {'error': 'The email and username do not '
- 'match a known account.'}
-
new_password = self._random_password()
- notifier.notify(username, new_password)
mgr = AccountManager(self.env)
+ try:
+ mgr._notify('password_reset', username, email, new_password)
+ except Exception, e:
+ return {'error': ','.join(e.args)}
mgr.set_password(username, new_password)
if mgr.force_passwd_change:
db = self.env.get_db_cnx()
@@ -402,7 +352,7 @@ class RegistrationModule(Component):
req.redirect(req.href.login())
data['reset_password_enabled'] = \
(self.env.is_component_enabled(AccountModule)
- and NotificationSystem(self.env).smtp_enabled)
+ and AccountModule(self.env).reset_password)
return 'register.html', data, None
@@ -500,26 +450,6 @@ class LoginModule(auth.LoginModule):
return [resource_filename(__name__, 'templates')]
-class EmailVerificationNotification(SingleUserNotification):
- template_name = 'verify_email.txt'
-
- def notify(self, username, token):
- self.data.update({
- 'account': {
- 'username': username,
- 'token': token,
- },
- 'verify': {
- 'link': self.env.abs_href.verify_email(token=token),
- }
- })
-
- projname = self.config.get('project', 'name')
- subject = '[%s] Trac email verification for user: %s' % (projname,
username)
-
- SingleUserNotification.notify(self, username, subject)
-
-
class EmailVerificationModule(Component):
implements(IRequestFilter, IRequestHandler)
@@ -551,7 +481,12 @@ class EmailVerificationModule(Component):
if email and email != req.session.get('email_verification_sent_to'):
req.session['email_verification_token'] = self._gen_token()
req.session['email_verification_sent_to'] = email
- self._send_email(req)
+ mgr = AccountManager(self.env)
+ mgr._notify(
+ 'email_verification_requested',
+ req.authname,
+ req.session['email_verification_token']
+ )
chrome.add_notice(req, Markup(tag.span(
'An email has been sent to ', email,
' with a token to ',
@@ -570,7 +505,12 @@ class EmailVerificationModule(Component):
elif req.method != 'POST':
pass
elif 'resend' in req.args:
- self._send_email(req)
+ mgr = AccountManager(self.env)
+ mgr._notify(
+ 'email_verification_requested',
+ req.authname,
+ req.session['email_verification_token']
+ )
chrome.add_notice(req,
'A notification email has been resent to %s.',
req.session.get('email'))
@@ -587,7 +527,3 @@ class EmailVerificationModule(Component):
def _gen_token(self):
return base64.urlsafe_b64encode(urandom(6))
-
- def _send_email(self, req):
- notifier = EmailVerificationNotification(self.env)
- notifier.notify(req.authname, req.session['email_verification_token'])
--
1.6.4.4
_______________________________________________
th-users mailing list
[email protected]
https://lists.trac-hacks.org/mailman/listinfo/th-users