Ticket #442: verify_account_registrations.patch
| File verify_account_registrations.patch, 30.3 kB (added by s0undt3ch, 7 months ago) |
|---|
-
a/acct_mgr/admin.py
old new 16 16 from trac.perm import PermissionSystem 17 17 from trac.util import sorted 18 18 from trac.util.datefmt import format_datetime 19 from trac.web.chrome import ITemplateProvider 19 from trac.web.chrome import ITemplateProvider, add_stylesheet 20 20 from trac.admin import IAdminPanelProvider 21 21 22 22 from acct_mgr.api import AccountManager 23 23 from acct_mgr.web_ui import _create_user 24 from acct_mgr.verify import VerifyAccountModule 24 25 25 26 def _getoptions(cls): 26 27 if isinstance(cls, Component): … … 42 43 yield ('accounts', 'Accounts', 'users', 'Users') 43 44 44 45 def render_admin_panel(self, req, cat, page, path_info): 46 add_stylesheet(req, 'acct_mgr/admin_acct_mgr.css') 45 47 if page == 'config': 46 48 return self._do_config(req) 47 49 elif page == 'users': 48 50 return self._do_users(req) 49 51 50 52 def _do_config(self, req): 53 51 54 if req.method == 'POST': 52 55 selected_class = req.args.get('selected') 53 56 self.config.set('account-manager', 'password_store', selected_class) … … 59 62 self.config.save() 60 63 self.config.set('account-manager', 'force_passwd_change', 61 64 req.args.get('force_passwd_change')) 65 if 'verify_accounts' in req.args: 66 self.config.set('account-manager', 'verify_accounts', 67 req.args.get('verify_accounts')) 62 68 self.config.save() 63 69 64 70 … … 80 86 } for store in self.account_manager.stores 81 87 ] 82 88 sections = sorted(sections, key=lambda i: i['name']) 89 verify_acct_enabled = self.env.is_component_enabled(VerifyAccountModule) 83 90 data = {'sections': sections, 84 'force_passwd_change': self.account_manager.force_passwd_change} 91 'force_passwd_change': self.account_manager.force_passwd_change, 92 'verify_accounts_enabled': verify_acct_enabled, 93 'verify_accounts': self.account_manager.verify_accounts} 85 94 return 'admin_accountsconfig.html', data 86 95 87 96 def _do_users(self, req): … … 89 98 listing_enabled = self.account_manager.supports('get_users') 90 99 create_enabled = self.account_manager.supports('set_password') 91 100 delete_enabled = self.account_manager.supports('delete_user') 101 verify_accounts = self.account_manager.verify_accounts 102 verify_acct_enabled = self.env.is_component_enabled(VerifyAccountModule) 92 103 93 104 data = { 94 105 'listing_enabled': listing_enabled, 95 106 'create_enabled': create_enabled, 96 107 'delete_enabled': delete_enabled, 108 'verify_accounts': verify_accounts, 109 'verify_accounts_enabled': verify_acct_enabled 97 110 } 98 111 99 112 if req.method == 'POST': … … 136 149 if account and last_visit: 137 150 account['last_visit'] = format_datetime(last_visit) 138 151 152 if verify_accounts: 153 cursor.execute("SELECT sid,value FROM session_attribute " 154 "WHERE name=%s AND authenticated=1", 155 ('verified',)) 156 for username, verified in cursor: 157 account = accounts.get(username) 158 if account: 159 account['verified'] = bool(int(verified)) 160 139 161 data['accounts'] = sorted(accounts.itervalues(), 140 162 key=lambda acct: acct['username']) 141 163 … … 147 169 """Return the absolute path of a directory containing additional 148 170 static resources (such as images, style sheets, etc). 149 171 """ 150 return [] 172 from pkg_resources import resource_filename 173 return [('acct_mgr', resource_filename(__name__, 'htdocs'))] 151 174 152 175 def get_templates_dirs(self): 153 176 """Return the absolute path of the directory containing the provided -
a/acct_mgr/api.py
old new 88 88 force_passwd_change = BoolOption('account-manager', 'force_passwd_change', 89 89 True, doc="Forge the user to change " 90 90 "password when it's reset.") 91 verify_accounts = BoolOption('account-manager', 'verify_accounts', True, 92 "Force user to verify their email address") 91 93 92 94 # Public API 93 95 -
/dev/null
old new 1 from trac.db import Table, Column 2 3 name = 'acct_mgr.verify_accounts' 4 version = 1 5 tables = [ 6 Table('acct_mgr_verify_accounts', key=('ts', 'sid', 'hash'))[ 7 Column('ts'), 8 Column('sid'), 9 Column('hash') 10 ] 11 ] 12 13 def current_usernames_to_confirmed_state(data): 14 "Update current sid's to a verified state" 15 if 'session_attribute' in data: 16 colnames, coldata = data['session_attribute'] 17 ndata = [] 18 sids = [] 19 for sid, authenticated, name, value in coldata: 20 ndata.append((sid, authenticated, name, value)) 21 if sid not in sids and authenticated: 22 sids.append(sid) 23 ndata.append((sid, authenticated, 'verified', 1)) 24 data['session_attribute'] = (colnames, ndata) 25 26 migrations = [ 27 (xrange(0, 1), current_usernames_to_confirmed_state) 28 ] -
/dev/null
old new 1 .verified { 2 background-color: #bcecaf; 3 } 4 5 .unverified { 6 background-color: #ffc1c1; 7 } -
/dev/null
old new 1 You, or someone posing as you, has just registered into 2 ${project.name} Trac environment. 3 4 To verify your email address please follow the following link: 5 6 ${hash.link} 7 8 9 10 If the above link got broken because it's too long, go to: 11 12 ${hash.url} 13 14 And the hash value to fill is: 15 16 ${hash.value} 17 18 -- 19 ${project.name} <${project.url}> 20 ${project.descr} 21 -
a/acct_mgr/templates/admin_accountsconfig.html
old new 10 10 </head> 11 11 12 12 <body> 13 <h2>Accounts: Configuration</h2>13 <h2>Accounts: General Configuration</h2> 14 14 15 15 <form id="accountsconfig" class="mod" method="post"> 16 17 <fieldset py:if="verify_accounts_enabled"> 18 <legend>New Accounts</legend> 19 <label for="verify_accounts"> 20 Verify email addresses on new accounts? 21 </label> 22 <input type="radio" name="verify_accounts" value="true" 23 checked="${verify_accounts and 'checked' or None}">Yes</input> 24 <input type="radio" name="verify_accounts" value="false" 25 checked="${not verify_accounts and 'checked' or None}">No</input> 26 </fieldset> 27 28 <fieldset> 29 <legend>Password Reset</legend> 30 <label for="force_passwd_change"> 31 Force users to change passwords after a password reset? 32 </label> 33 <input type="radio" name="force_passwd_change" value="true" 34 checked="${force_passwd_change and 'checked' or None}">Yes</input> 35 <input type="radio" name="force_passwd_change" value="false" 36 checked="${not force_passwd_change and 'checked' or None}">No</input> 37 </fieldset> 38 39 <div class="buttons"> 40 <input type="submit" name="save" value="Save" /> 41 </div> 42 43 <hr/> 44 <h2>Accounts: Stores Configuration</h2> 45 16 46 <fieldset py:for="section in sections"> 17 47 <legend> 18 48 <label> … … 29 59 </label> 30 60 </div> 31 61 </fieldset> 32 <fieldset>33 <legend>Password Reset</legend>34 <label for="force_passwd_change">35 Force users to change passwords after a password reset?36 </label>37 <input type="radio" name="force_passwd_change" value="true"38 checked="${force_passwd_change and 'checked' or None}">Yes</input>39 <input type="radio" name="force_passwd_change" value="false"40 checked="${not force_passwd_change and 'checked' or None}">No</input>41 </fieldset>42 62 <div class="buttons"> 43 63 <input type="submit" name="save" value="Save" /> 44 64 </div> -
a/acct_mgr/templates/admin_users.html
old new 56 56 </tr> 57 57 </thead> 58 58 <tbody> 59 <tr py:for="account in accounts"> 59 <tr py:for="account in accounts" 60 class="${verify_accounts_enabled and ( 61 verify_accounts and ( 62 account.verified and 'verified' or 'unverified') 63 or None) 64 or None}" 65 title="${verify_accounts_enabled and ( 66 verify_accounts and ( 67 account.verified and 68 'Account with confirmed email address' or 69 'Account with un-confirmed email address') 70 or None) 71 or None}"> 60 72 <td py:if="delete_enabled"> 61 73 <input type="checkbox" name="sel" value="${account.username}" /> 62 74 </td> -
a/acct_mgr/templates/register.html
old new 15 15 </head> 16 16 17 17 <body> 18 <py:def function="email_field_output"> 19 <label>Email: 20 <input type="text" name="email" class="textwidget" size="20" 21 value="${defined('email') and email or None}"/> 22 </label> 23 <p py:if="reset_password_enabled">Entering your email address will 24 enable you to reset your password if you ever forget it.</p> 25 </py:def> 18 26 <div id="content" class="register"> 19 27 <h1>Register an account</h1> 20 28 … … 29 37 <div> 30 38 <input type="hidden" name="action" value="create" /> 31 39 <label>Username: 32 <input type="text" name="user" class="textwidget" size="20" /> 40 <input type="text" name="user" class="textwidget" size="20" 41 value="${defined('user') and user or None}"/> 33 42 </label> 34 43 </div> 35 44 <div> 36 45 <label>Password: 37 <input type="password" name="password" class="textwidget" size="20" /> 46 <input type="password" name="password" class="textwidget" 47 size="20" value="${defined('password') and password or None}"/> 38 48 </label> 39 49 </div> 40 50 <div> 41 51 <label>Confirm Password: 42 <input type="password" name="password_confirm" 43 class="textwidget" size="20"/>52 <input type="password" name="password_confirm" class="textwidget" 53 size="20" value="${defined('password_confirm') and password_confirm or None}"/> 44 54 </label> 55 </div> 56 <div py:if="verify_accounts"> 57 ${ email_field_output() } 45 58 </div> 46 59 </fieldset> 47 60 <fieldset> 48 61 <legend>Optional</legend> 49 62 <div> 50 63 <label>Name: 51 <input type="text" name="name" class="textwidget" size="20" /> 64 <input type="text" name="name" class="textwidget" size="20" 65 value="${defined('name') and name or None}"/> 52 66 </label> 53 67 </div> 54 <div> 55 <label>Email: 56 <input type="text" name="email" class="textwidget" size="20" /> 57 </label> 58 <p py:if="reset_password_enabled">Entering your email address will 59 enable you to reset your password if you ever forget it.</p> 68 <div py:if="not verify_accounts"> 69 ${ email_field_output() } 60 70 </div> 61 71 </fieldset> 62 72 <input type="submit" value="Create account" /> -
/dev/null
old new 1 <!DOCTYPE html 2 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 3 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 4 <html xmlns="http://www.w3.org/1999/xhtml" 5 xmlns:py="http://genshi.edgewall.org/" 6 xmlns:xi="http://www.w3.org/2001/XInclude"> 7 <xi:include href="layout.html" /> 8 <head> 9 <title>Verify Account</title> 10 </head> 11 <body> 12 <div class="system-message" py:if="message"> 13 <h2>Error</h2> 14 <p>$message</p> 15 </div> 16 <form action="" method="POST" py:if="not verified"> 17 <fieldset> 18 <legend>Verify Account</legend> 19 <div> 20 <p><b>Please fill in the <em>hash</em> that was sent to you by email</b></p> 21 <label for="hash">Hash:</label> 22 <input type="text" size="55" value="${defined('hash') and hash or None}" 23 name="hash" class="textwidgets"/> 24 </div> 25 <input type="submit" value="Verify account" /> 26 </fieldset> 27 </form> 28 <fieldset py:if="verified"> 29 <legend>Verified Account</legend> 30 <p>Your email address is verified.</p> 31 <p py:if="not req.session.authenticated">You can now 32 <a href="${req.href.login()}">login</a>.</p> 33 </fieldset> 34 </body> 35 </html> -
/dev/null
old new 1 import sha 2 import time 3 import random 4 5 6 from trac.core import * 7 from trac.env import IEnvironmentSetupParticipant 8 from trac.config import BoolOption 9 from trac.web.api import ITemplateStreamFilter 10 from trac.web.main import IRequestHandler, IRequestFilter 11 from trac.notification import NotifyEmail 12 from trac.util.translation import _ 13 14 from genshi.filters.transform import Transformer 15 from genshi.builder import tag 16 17 from api import AccountManager 18 import db_default 19 20 def _build_hash(sid, env, req): 21 import sha, time 22 hash = sha.new(str(random.random())) 23 email = req.session.get('email', "") 24 now = int(time.time()) 25 26 hash.update(sid) 27 hash.update(str(random.random())) 28 hash.update(email) 29 hash.update(str(random.random())) 30 hash.update(str(now)) 31 hash.update(str(random.random())) 32 33 db = env.get_db_cnx() 34 cursor = db.cursor() 35 36 older_than = now - 86400 * 1 # Delete pending older than a day 37 cursor.execute("DELETE FROM acct_mgr_verify_accounts WHERE ts < %s", 38 (older_than,)) 39 env.log.debug("Cleaning %s timed out email verifications", cursor.rowcount) 40 cursor.execute("INSERT INTO acct_mgr_verify_accounts (ts,sid,hash) " 41 "VALUES (%s,%s,%s)", (now, sid, hash.hexdigest())) 42 cursor.execute("UPDATE session_attribute SET value=%s WHERE name=%s", 43 (0, 'verified')) 44 if not cursor.rowcount: 45 cursor.execute("INSERT INTO session_attribute " 46 "(sid,authenticated,name,value) VALUES (%s,%s,%s,%s)", 47 (sid, 1, 'verified', 0)) 48 db.commit() 49 return hash.hexdigest() 50 51 class AccountVerifyNotification(NotifyEmail): 52 template_name = 'account_verify_email.txt' 53 _username = None 54 _hash = None 55 56 def get_recipients(self, resid): 57 return ([resid],[]) 58 59 def get_smtp_address(self, addr): 60 """Overrides `get_smtp_address` in order to prevent CCing users 61 other than the user whose password is being reset. 62 """ 63 if addr == self._username: 64 return NotifyEmail.get_smtp_address(self, addr) 65 else: 66 return None 67 68 def notify(self, username, hash): 69 # save the username for use in `get_smtp_address` 70 self._username = username 71 self._hash = hash 72 self.data.update({ 73 'hash': { 74 'link': self.env.abs_href.verify_account(self._hash), 75 'url': self.env.abs_href.verify_account(), 76 'value': self._hash 77 } 78 }) 79 80 projname = self.config.get('project', 'name') 81 subject = _('[%(project_name)s] Trac Email Verification for user: ' 82 '%(username)s', project_name=projname, username=username) 83 84 NotifyEmail.notify(self, username, subject) 85 86 class VerifyAccountModule(Component): 87 implements(IRequestHandler, IRequestFilter, ITemplateStreamFilter, 88 IEnvironmentSetupParticipant) 89 90 def __init__(self): 91 self.verify_accounts = AccountManager(self.env).verify_accounts 92 93 # IEnvironmentSetupParticipant methods 94 def environment_created(self): 95 if self.verify_accounts: 96 self.found_db_version = 0 97 self.upgrade_environment(self.env.get_db_cnx()) 98 99 def environment_needs_upgrade(self, db): 100 if not self.verify_accounts: 101 return False 102 cursor = db.cursor() 103 cursor.execute("SELECT value FROM system WHERE name=%s", 104 (db_default.name,)) 105 value = cursor.fetchone() 106 if not value: 107 self.found_db_version = 0 108 return True 109 else: 110 self.found_db_version = int(value[0]) 111 self.log.debug(_('AccountManager: Found db version ' 112 '%(db_default_version)s, current is ' 113 '%(system_version)s', 114 db_default_version=self.found_db_version, 115 system_version=db_default.version)) 116 return self.found_db_version < db_default.version 117 118 def upgrade_environment(self, db): 119 # 0.10 compatibility hack (thanks Alec) 120 try: 121 from trac.db import DatabaseManager 122 db_manager, _ = DatabaseManager(self.env)._get_connector() 123 except ImportError: 124 db_manager = db 125 126 from trac.db_default import schema as trac_schema 127 128 session_attribute = [ 129 s for s in trac_schema if s.name == 'session_attribute' 130 ] 131 132 # Insert the default table 133 old_data = {} # {table_name: (col_names, [row, ...]), ...} 134 cursor = db.cursor() 135 if not self.found_db_version: 136 cursor.execute("INSERT INTO system (name, value) VALUES (%s, %s)", 137 (db_default.name, db_default.version)) 138 for tbl in session_attribute: 139 try: 140 cursor.execute('SELECT * FROM %s'%tbl.name) 141 old_data[tbl.name] = ([d[0] for d in cursor.description], 142 cursor.fetchall()) 143 cursor.execute('DROP TABLE %s'%tbl.name) 144 except Exception, e: 145 if 'OperationalError' not in e.__class__.__name__: 146 # If it is an OperationalError, just move on to the 147 # next table 148 raise e 149 else: 150 cursor.execute("UPDATE system SET value=%s WHERE name=%s", 151 (db_default.version, db_default.name)) 152 for tbl in db_default.tables + session_attribute: 153 try: 154 cursor.execute('SELECT * FROM %s'%tbl.name) 155 old_data[tbl.name] = ([d[0] for d in cursor.description], 156 cursor.fetchall()) 157 cursor.execute('DROP TABLE %s'%tbl.name) 158 except Exception, e: 159 if 'OperationalError' not in e.__class__.__name__: 160 # If it is an OperationalError, just move on to the 161 # next table 162 raise e 163 164 165 for vers, migration in db_default.migrations: 166 if self.found_db_version in vers: 167 self.log.info(_('AccountManager: Running migration %(doc)s', 168 doc=migration.__doc__)) 169 migration(old_data) 170 171 for tbl in db_default.tables + session_attribute: 172 for sql in db_manager.to_sql(tbl): 173 cursor.execute(sql) 174 175 # Try to reinsert any old data 176 if tbl.name in old_data: 177 data = old_data[tbl.name] 178 sql = 'INSERT INTO %s (%s) VALUES (%s)' % ( 179 tbl.name, ','.join(data[0]),','.join(['%s'] * len(data[0])) 180 ) 181 for row in data[1]: 182 try: 183 cursor.execute(sql, row) 184 except Exception, e: 185 if 'OperationalError' not in e.__class__.__name__: 186 raise 187 else: 188 self.log.debug(_('AccountManager: Masking exception' 189 ' %(exception)s', exception=e)) 190 191 192 # IRequestHandler methods 193 def match_request(self, req): 194 return (req.path_info.startswith('/verify_account') or 195 req.path_info.startswith('/verified_account')) 196 197 def process_request(self, req): 198 if req.path_info.startswith('/verify_account'): 199 return self._process_verify_account(req) 200 elif req.path_info.startswith('/verified_account'): 201 return self._process_verified_account(req) 202 203 # IRequestFilter methods 204 def pre_process_request(self, req, handler): 205 if not self.verify_accounts or not req.session.authenticated or \ 206 not self.env.is_component_enabled(VerifyAccountModule): 207 # Don't do anything is we're not verifying accounts; 208 # Don't trac anonymous users; 209 # Don't do anything if component is not enabled; 210 return handler 211 if req.path_info.startswith('/prefs'): 212 if not req.method == 'POST': 213 return handler 214 if not hasattr(self, '_old') and ('TRAC_ADMIN' not in req.perm): 215 # Don't track TRAC_ADMIN email changes 216 self._old = req.session._old.copy() 217 return handler 218 219 def post_process_request(self, req, template, data, content_type): 220 if not self.verify_accounts or not req.session.authenticated or \ 221 not self.env.is_component_enabled(VerifyAccountModule): 222 # Don't do anything is we're not verifying accounts; 223 # Don't trac anonymous users; 224 # Don't do anything if component is not enabled; 225 return (template, data, content_type) 226 if req.session.authenticated and ('TRAC_ADMIN' not in req.perm): 227 # Don't track TRAC_ADMIN email changes 228 if req.path_info.startswith('/verify_account'): 229 if req.session.get('verified') == '1': 230 req.redirect(req.href.verified_account()) 231 232 elif req.path_info.startswith('/prefs') and self.verify_accounts: 233 if req.session.get('verified') == '0': 234 req.redirect(req.href.verify_account()) 235 236 if not hasattr(self, '_old'): 237 # No previous POST, prefs were not saved 238 return (template, data, content_type) 239 240 req.session.save() # Just in case 241 242 if self._old['email'] != req.session.get('email'): 243 # Email Changed. Need to re-verify address 244 email = req.session.get('email') 245 notifier = AccountVerifyNotification(self.env) 246 hash = _build_hash(req.session.sid, self.env, req) 247 if email == notifier.email_map.get(req.session.sid): 248 notifier.notify(req.session.sid, hash) 249 # Let user know that he has to verify his new email address 250 del self._old 251 return ('confirm_address_email_sent.html', data, 252 content_type) 253 else: 254 if req.session.get('verified') == '0': 255 req.redirect(req.href.verify_account()) 256 return (template, data, content_type) 257 258 # ITemplateStreamFilter 259 def filter_stream(self, req, method, filename, stream, data): 260 self.log.debug("On ITemplateStreamFilter: %s",req.session.authenticated) 261 if not self.verify_accounts or not req.session.authenticated or \ 262 not self.env.is_component_enabled(VerifyAccountModule): 263 # No point tweaking css if we're not using this module or 264 # if user is not authenticated. Are we actually going to verify 265 # anonymous users email addresses? 266 # Perhaps we should... But that would probably also mean finding a 267 # way to use that info, ie, only notify verified addresses. 268 return stream 269 if filename == 'prefs_general.html': 270 if req.session.get('verified') == '1': 271 return stream | Transformer("td/input[@id='email']").attr( 272 'style', 'border-color: green;').after( 273 tag(tag(" "), tag.span("verified address", 274 style="color: green;"))) 275 else: 276 return stream | Transformer("td/input[@id='email']").attr( 277 'style', 'border-color: red;').after( 278 tag(tag(" "), tag.span("unverified address", 279 style="color: red;"))) 280 return stream 281 282 283 # Internal Methods 284 def _process_verified_account(self, req): 285 data = {'verified': True} 286 return 'verify_account.html', data, None 287 288 def _process_verify_account(self, req): 289 verified = False 290 data = {} 291 def _activate_account(hash): 292 db = self.env.get_db_cnx() 293 cursor = db.cursor() 294 cursor.execute("SELECT sid from acct_mgr_verify_accounts " 295 "WHERE hash=%s", (hash,)) 296 row = cursor.fetchone() 297 if row: 298 if req.session.authenticated: 299 # In case user is authenticated, update session. 300 req.session['verified'] = 1 301 req.session.save() 302 else: 303 # User ain't authenticated, update DB 304 sid = row[0] 305 cursor.execute("UPDATE session_attribute SET value=%s " 306 "WHERE sid=%s AND name=%s",(1, sid, 'verified')) 307 cursor.execute("DELETE FROM acct_mgr_verify_accounts WHERE " 308 "hash=%s", (hash,)) 309 data['verified'] = True 310 db.commit() 311 return True # Verified 312 else: 313 data['message'] = _("Address already verified? Hash not found.") 314 return False # Not verified 315 316 if req.method == 'POST': 317 hash = req.args.get('hash') 318 elif req.method == 'GET': 319 hash = req.path_info[len('/verify_account/'):] 320 321 if hash: 322 verified = _activate_account(hash) 323 data.update({'hash': hash, 324 'verified': verified}) 325 if req.session.get('verified') == '0' and req.session.authenticated: 326 data['message'] = _("Please verify your email address. An email " 327 "message was sent to you just for that") 328 return 'verify_account.html', data, None 329 -
a/acct_mgr/web_ui.py
old new 23 23 from trac.web.chrome import INavigationContributor, ITemplateProvider 24 24 from genshi.builder import tag 25 25 26 from api import AccountManager 26 from acct_mgr.api import AccountManager 27 from acct_mgr.verify import AccountVerifyNotification, _build_hash 28 29 27 30 28 31 def _create_user(req, env, check_permissions=True): 29 32 mgr = AccountManager(env) … … 109 112 110 113 projname = self.config.get('project', 'name') 111 114 subject = '[%s] Trac password reset for user: %s' % (projname, username) 112 113 115 NotifyEmail.notify(self, username, subject) 114 116 115 117 … … 345 347 if req.authname != 'anonymous': 346 348 req.redirect(req.href.prefs('account')) 347 349 action = req.args.get('action') 348 data = {} 350 verify_accounts = AccountManager(self.env).verify_accounts 351 email_sent = False 352 data = {'verify_accounts': verify_accounts} 349 353 if req.method == 'POST' and action == 'create': 350 354 try: 351 _create_user(req, self.env) 355 flabels = { 356 'user': 'Username', 357 'password': 'Password', 358 'password_confirm': 'Confirm Password' 359 } 360 for argname in ('user', 'password', 'password_confirm'): 361 arg = req.args.get(argname) 362 if not arg: 363 data.update(req.args) 364 raise TracError("Missing the required '%s' field" % 365 flabels[argname]) 366 email = req.args.get('email') 367 if verify_accounts and email: 368 _create_user(req, self.env) 369 elif verify_accounts and not email: 370 data.update(req.args) 371 raise TracError("Missing the required 'Email' field") 372 else: 373 _create_user(req, self.env) 374 if verify_accounts: 375 notifier = AccountVerifyNotification(self.env) 376 username = req.args.get('user') 377 hash = _build_hash(username, self.env, req) 378 if email == notifier.email_map.get(username): 379 notifier.notify(username, hash) 380 return 'confirm_address_email_sent.html', data, None 352 381 except TracError, e: 353 382 data['registration_error'] = e.message 354 383 else: … … 451 480 """ 452 481 from pkg_resources import resource_filename 453 482 return [resource_filename(__name__, 'templates')] 454
