Changeset 346
- Timestamp:
- 06/29/08 10:57:38 (5 years ago)
- Location:
- trunk/trac-hacks/accountmanagerplugin/acct_mgr
- Files:
-
- 8 added
- 11 edited
-
htfile.py (modified) (1 diff)
-
pwhash.py (modified) (2 diffs)
-
templates/login.html (modified) (1 diff)
-
templates/prefs_account.html (modified) (2 diffs)
-
templates/register.html (modified) (1 diff)
-
templates/reset_password.html (modified) (1 diff)
-
templates/verify_email.html (added)
-
templates/verify_email.txt (added)
-
tests/__init__.py (modified) (2 diffs)
-
tests/db.py (modified) (1 diff)
-
tests/functional (added)
-
tests/functional/__init__.py (added)
-
tests/functional/smtpd.py (added)
-
tests/functional/testcases.py (added)
-
tests/functional/testenv.py (added)
-
tests/functional/tester.py (added)
-
tests/htfile.py (modified) (1 diff)
-
util.py (modified) (1 diff)
-
web_ui.py (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/trac-hacks/accountmanagerplugin/acct_mgr/htfile.py
r56 r346 84 84 print userline 85 85 matched = True 86 el se:86 elif line.endswith('\n'): 87 87 print line, 88 else: # make sure the last line has a newline 89 print line 88 90 except EnvironmentError, e: 89 91 if e.errno == errno.ENOENT: -
trunk/trac-hacks/accountmanagerplugin/acct_mgr/pwhash.py
r56 r346 17 17 18 18 from md5crypt import md5crypt 19 from acct_mgr.util import urandom 19 20 20 21 class IPasswordHashMethod(Interface): … … 64 65 crypt = None 65 66 66 # os.urandom was added in Python 2.467 # try to fall back on reading from /dev/urandom on older Python versions68 try:69 from os import urandom70 except ImportError:71 from random import randrange72 def urandom(n):73 return ''.join([chr(randrange(256)) for _ in xrange(n)])74 75 67 def salt(): 76 68 s = '' -
trunk/trac-hacks/accountmanagerplugin/acct_mgr/templates/login.html
r266 r346 25 25 </div> 26 26 27 <form method="post" action="">27 <form method="post" id="acctmgr_loginform" action=""> 28 28 <input type="hidden" name="referer" value="${referer}" /> 29 29 <div> -
trunk/trac-hacks/accountmanagerplugin/acct_mgr/templates/prefs_account.html
r266 r346 20 20 </div> 21 21 22 <form method="post" action="" 22 <form method="post" action="" id="acctmgr_delete_account" 23 23 onsubmit="return confirm('Are you sure you want to delete your account?');"> 24 24 <div class="field"> … … 44 44 <h2>Error</h2> 45 45 <p>$account.error</p> 46 </div>47 <div class="system-message" py:if="account.force_change_passwd">48 <h2>Immediate action required</h2>49 <p>You are required to change password because of a recent50 password change request.</p>51 <p><strong>Please change your password now.</strong></p>52 46 </div> 53 47 <p py:if="account.message">$account.message</p> -
trunk/trac-hacks/accountmanagerplugin/acct_mgr/templates/register.html
r266 r346 24 24 </div> 25 25 26 <form method="post" action="">26 <form method="post" id="acctmgr_registerform" action=""> 27 27 <fieldset> 28 28 <legend>Required</legend> -
trunk/trac-hacks/accountmanagerplugin/acct_mgr/templates/reset_password.html
r266 r346 41 41 <p py:if="reset.message">$reset.message</p> 42 42 43 <form method="post" action="">43 <form method="post" id="acctmgr_passwd_reset" action=""> 44 44 <div> 45 45 <label>Username: -
trunk/trac-hacks/accountmanagerplugin/acct_mgr/tests/__init__.py
r56 r346 12 12 import doctest 13 13 import unittest 14 try: 15 import twill, subprocess 16 INCLUDE_FUNCTIONAL_TESTS = True 17 except ImportError: 18 INCLUDE_FUNCTIONAL_TESTS = False 14 19 15 20 def suite(): … … 18 23 suite.addTest(htfile.suite()) 19 24 suite.addTest(db.suite()) 25 if INCLUDE_FUNCTIONAL_TESTS: 26 from acct_mgr.tests.functional import suite as functional_suite 27 suite.addTest(functional_suite()) 20 28 return suite 21 29 22 30 if __name__ == '__main__': 31 import sys 32 if '--skip-functional-tests' in sys.argv: 33 sys.argv.remove('--skip-functional-tests') 34 INCLUDE_FUNCTIONAL_TESTS = False 23 35 unittest.main(defaultTest='suite') -
trunk/trac-hacks/accountmanagerplugin/acct_mgr/tests/db.py
r56 r346 22 22 def setUp(self): 23 23 #self.basedir = os.path.realpath(tempfile.mkdtemp()) 24 self.env = EnvironmentStub( )24 self.env = EnvironmentStub(enable=['trac.*', 'acct_mgr.*']) 25 25 self.env.config.set('account-manager', 'password_store', 26 26 'SessionStore') -
trunk/trac-hacks/accountmanagerplugin/acct_mgr/tests/htfile.py
r56 r346 86 86 'user:$apr1$xW/09...$fb150dT95SoL1HwXtHS/I0') 87 87 88 def test_add_with_no_trailing_newline(self): 89 filename = self._create_file('test_add_with_no_trailing_newline', 90 content='user:$apr1$' 91 'xW/09...$fb150dT95SoL1HwXtHS/I0') 92 self.env.config.set('account-manager', 'password_file', filename) 93 self.assertTrue(self.store.check_password('user', 'password')) 94 self.store.set_password('user2', 'password2') 95 self.assertTrue(self.store.check_password('user', 'password')) 96 self.assertTrue(self.store.check_password('user2', 'password2')) 97 88 98 89 99 def suite(): -
trunk/trac-hacks/accountmanagerplugin/acct_mgr/util.py
r56 r346 23 23 return path 24 24 return os.path.normpath(os.path.join(instance.env.path, path)) 25 26 27 # os.urandom was added in Python 2.4 28 # try to fall back on pseudo-random values if it's not available 29 try: 30 from os import urandom 31 except ImportError: 32 from random import randrange 33 def urandom(n): 34 return ''.join([chr(randrange(256)) for _ in xrange(n)]) 35 36 -
trunk/trac-hacks/accountmanagerplugin/acct_mgr/web_ui.py
r266 r346 10 10 # Author: Matthew Good <trac@matt-good.net> 11 11 12 import base64 13 import os 12 14 import random 13 15 import string … … 21 23 from trac.web.api import IAuthenticator 22 24 from trac.web.main import IRequestHandler, IRequestFilter 25 from trac.web import chrome 23 26 from trac.web.chrome import INavigationContributor, ITemplateProvider 24 27 from genshi.builder import tag 25 28 26 29 from api import AccountManager 30 from acct_mgr.util import urandom 27 31 28 32 def _create_user(req, env, check_permissions=True): … … 79 83 80 84 81 class PasswordResetNotification(NotifyEmail): 82 template_name = 'reset_password_email.txt' 85 class SingleUserNofification(NotifyEmail): 86 """Helper class used for account email notifications which should only be 87 sent to one persion, not including the rest of the normally CCed users 88 """ 83 89 _username = None 84 90 … … 95 101 return None 96 102 97 def notify(self, username, password):103 def notify(self, username, subject): 98 104 # save the username for use in `get_smtp_address` 99 105 self._username = username 106 old_public_cc = self.config.getbool('notification', 'use_public_cc') 107 # override public cc option so that the user's email is included in the To: field 108 self.config.set('notification', 'use_public_cc', 'true') 109 try: 110 NotifyEmail.notify(self, username, subject) 111 finally: 112 self.config.set('notification', 'use_public_cc', old_public_cc) 113 114 115 class PasswordResetNotification(SingleUserNofification): 116 template_name = 'reset_password_email.txt' 117 118 def notify(self, username, password): 100 119 self.data.update({ 101 120 'account': { … … 111 130 subject = '[%s] Trac password reset for user: %s' % (projname, username) 112 131 113 NotifyEmail.notify(self, username, subject)132 SingleUserNofification.notify(self, username, subject) 114 133 115 134 … … 195 214 data = {'delete_enabled': delete_enabled} 196 215 force_change_password = req.session.get('force_change_passwd', False) 197 if force_change_password:198 data['force_change_passwd'] = True199 216 if req.method == 'POST': 200 217 if action == 'save': 201 218 data.update(self._do_change_password(req)) 202 219 if force_change_password: 203 data['force_change_passwd'] = False204 220 del(req.session['force_change_passwd']) 205 221 req.session.save() 222 chrome.add_notice(req, MessageWrapper(tag( 223 "Thank you for taking the time to update your password." 224 ))) 225 force_change_password = False 206 226 elif action == 'delete' and delete_enabled: 207 227 data.update(self._do_delete(req)) 208 228 else: 209 229 data.update({'error': 'Invalid action'}) 230 if force_change_password: 231 chrome.add_warning(req, MessageWrapper(tag( 232 "You are required to change password because of a recent " 233 "password change request. ", 234 tag.b("Please change your password now.")))) 210 235 return data 211 236 … … 453 478 return [resource_filename(__name__, 'templates')] 454 479 480 481 class MessageWrapper(object): 482 """Wrapper for add_warning and add_notice to work around the requirement 483 for a % operator.""" 484 def __init__(self, body): 485 self.body = body 486 487 def __mod__(self, rhs): 488 return self.body 489 490 491 class EmailVerificationNotification(SingleUserNofification): 492 template_name = 'verify_email.txt' 493 494 def notify(self, username, token): 495 self.data.update({ 496 'account': { 497 'username': username, 498 'token': token, 499 }, 500 'verify': { 501 'link': self.env.abs_href.verify_email(token=token), 502 } 503 }) 504 505 projname = self.config.get('project', 'name') 506 subject = '[%s] Trac email verification for user: %s' % (projname, username) 507 508 SingleUserNofification.notify(self, username, subject) 509 510 511 class EmailVerificationModule(Component): 512 implements(IRequestFilter, IRequestHandler) 513 514 # IRequestFilter methods 515 516 def pre_process_request(self, req, handler): 517 if not req.session.authenticated: 518 # Anonymous users should register and perms should be tweaked so 519 # that anonymous users can't edit wiki pages and change or create 520 # tickets. As such, this email verifying code won't be used on them 521 return handler 522 if handler is not self and 'email_verification_token' in req.session: 523 chrome.add_warning(req, MessageWrapper(tag.span( 524 'Your permissions have been limited until you ', 525 tag.a(href=req.href.verify_email())( 526 'verify your email address')))) 527 req.perm = perm.PermissionCache(self.env, 'anonymous') 528 return handler 529 530 def post_process_request(self, req, template, data, content_type): 531 if not req.session.authenticated: 532 # Anonymous users should register and perms should be tweaked so 533 # that anonymous users can't edit wiki pages and change or create 534 # tickets. As such, this email verifying code won't be used on them 535 return template, data, content_type 536 if req.session.get('email') != req.session.get('email_verification_sent_to'): 537 req.session['email_verification_token'] = self._gen_token() 538 req.session['email_verification_sent_to'] = req.session.get('email') 539 self._send_email(req) 540 chrome.add_notice(req, MessageWrapper(tag.span( 541 'An email has been sent to ', req.session['email'], 542 ' with a token to ', 543 tag.a(href=req.href.verify_email())( 544 'verify your new email address')))) 545 return template, data, content_type 546 547 # IRequestHandler methods 548 549 def match_request(self, req): 550 return req.path_info == '/verify_email' 551 552 def process_request(self, req): 553 if 'email_verification_token' not in req.session: 554 chrome.add_notice(req, 'Your email is already verified') 555 elif req.method != 'POST': 556 pass 557 elif 'resend' in req.args: 558 self._send_email(req) 559 chrome.add_notice(req, 560 'A notification email has been resent to %s.', 561 req.session.get('email')) 562 elif 'verify' in req.args: 563 if req.args['token'] == req.session['email_verification_token']: 564 del req.session['email_verification_token'] 565 chrome.add_notice(req, 'Thank you for verifying your email address') 566 else: 567 chrome.add_warning(req, 'Invalid verification token') 568 data = {} 569 if 'token' in req.args: 570 data['token'] = req.args['token'] 571 return 'verify_email.html', data, None 572 573 def _gen_token(self): 574 return base64.urlsafe_b64encode(urandom(6)) 575 576 def _send_email(self, req): 577 notifier = EmailVerificationNotification(self.env) 578 notifier.notify(req.authname, req.session['email_verification_token'])
Note: See TracChangeset
for help on using the changeset viewer.
