Trac is being migrated to new services! Issues can be found in our new
YouTrack instance and WIKI pages can be found on our
website.
LocalTracChanges: acct_mgr_plugin_ssl_remember_me_rev_4111.patch
File acct_mgr_plugin_ssl_remember_me_rev_4111.patch, 9.1 KB (added by datallah, 15 years ago) |
AccountManager? rev. 4111 patch to implement SSL login and admin screens and persistent sessions
|
-
|
|
|
13 | 13 | import os |
14 | 14 | import random |
15 | 15 | import string |
| 16 | import time |
| 17 | import re |
| 18 | import urlparse |
16 | 19 | |
17 | 20 | from trac import perm, util |
18 | 21 | from trac.core import * |
19 | 22 | from trac.config import IntOption |
20 | 23 | from trac.notification import NotificationSystem, NotifyEmail |
21 | 24 | from trac.prefs import IPreferencePanelProvider |
| 25 | from trac.util.text import unicode_urlencode |
22 | 26 | from trac.web import auth |
23 | 27 | from trac.web.api import IAuthenticator |
24 | 28 | from trac.web.main import IRequestHandler, IRequestFilter |
… |
… |
|
53 | 57 | if password != req.args.get('password_confirm'): |
54 | 58 | raise TracError('The passwords must match.') |
55 | 59 | |
| 60 | if not req.args.get('email'): |
| 61 | raise TracError('You must enter a valid Email address.') |
| 62 | |
56 | 63 | mgr.set_password(user, password) |
57 | 64 | |
58 | 65 | db = env.get_db_cnx() |
… |
… |
|
157 | 164 | 'store does not support writing.') |
158 | 165 | return writable |
159 | 166 | |
| 167 | |
| 168 | def _redirect_to_scheme(self, req, scheme): |
| 169 | self.log.debug("Forwarding to '%s' for '%s%s'" % (scheme, req.base_path, req.path_info)) |
| 170 | referer_param = req.args.get('referer') |
| 171 | if referer_param: |
| 172 | referer_param = "referer=%s" % referer_param |
| 173 | req.redirect(urlparse.urlunparse((scheme, req.server_name, req.base_path + req.path_info, None, referer_param, None))) |
| 174 | |
| 175 | def _needs_ssl(self, path_info): |
| 176 | return re.match('/prefs(?:/([^/]+))?', path_info) \ |
| 177 | or path_info == '/reset_password' \ |
| 178 | or path_info == '/register' \ |
| 179 | or path_info == '/login' \ |
| 180 | or path_info == '/admin/accounts/users' |
| 181 | |
| 182 | def _needs_session_cookie(self, path_info): |
| 183 | return re.match('/admin(?:/([^/]+))?', path_info) \ |
| 184 | or path_info == '/prefs/account' |
| 185 | |
160 | 186 | #IPreferencePanelProvider methods |
161 | 187 | def get_preference_panels(self, req): |
162 | 188 | if not self._write_check(): |
… |
… |
|
179 | 205 | |
180 | 206 | # IRequestFilter methods |
181 | 207 | def pre_process_request(self, req, handler): |
| 208 | |
| 209 | ssl_path = self._needs_ssl(req.path_info) |
| 210 | # Catch the user leaving an ssl path /prefs and redirect to non-secure |
| 211 | if req.scheme.lower() == 'https' and not ssl_path: |
| 212 | self._redirect_to_scheme(req, 'http') |
| 213 | # Catch the user entering /prefs or and redirect to secure |
| 214 | elif ssl_path: |
| 215 | if req.scheme.lower() != 'https': |
| 216 | self._redirect_to_scheme(req, 'https') |
| 217 | |
| 218 | # If we're accessing a page that you should be logged in the current session for, deal with it |
| 219 | if self._needs_session_cookie(req.path_info) \ |
| 220 | and not req.incookie.has_key('trac_auth_session'): |
| 221 | self.log.debug("Accessing %s requires a session login, forwarding to login screen." % req.path_info) |
| 222 | referal_url = urlparse.urlunparse((ssl_path and 'https' or 'http', req.server_name, req.base_path + req.path_info, None, None, None)) |
| 223 | req.redirect(self.env.href.login() + '?%s' % unicode_urlencode({'referer' : referal_url, 'force_session' : '1'})) |
| 224 | |
182 | 225 | return handler |
183 | 226 | |
184 | 227 | def post_process_request(self, req, template, data, content_type): |
… |
… |
|
377 | 420 | except TracError, e: |
378 | 421 | data['registration_error'] = e.message |
379 | 422 | else: |
380 | | req.redirect(req.href.login()) |
| 423 | redirect_url = None |
| 424 | referer = req.args.get('referer') |
| 425 | if referer: |
| 426 | redirect_url = "%s?referer=%s" % (req.href.login(), referer) |
| 427 | req.redirect(redirect_url or req.href.login()) |
381 | 428 | data['reset_password_enabled'] = \ |
382 | 429 | (self.env.is_component_enabled(AccountModule) |
383 | 430 | and NotificationSystem(self.env).smtp_enabled) |
… |
… |
|
422 | 469 | match_request = if_enabled(auth.LoginModule.match_request) |
423 | 470 | |
424 | 471 | def process_request(self, req): |
425 | | if req.path_info.startswith('/login') and req.authname == 'anonymous': |
| 472 | if req.path_info.startswith('/login') and (req.authname == 'anonymous' or req.args.get('force_session')): |
426 | 473 | data = { |
427 | 474 | 'referer': self._referer(req), |
428 | 475 | 'reset_password_enabled': AccountModule(self.env).reset_password_enabled |
… |
… |
|
432 | 479 | return 'login.html', data, None |
433 | 480 | return auth.LoginModule.process_request(self, req) |
434 | 481 | |
| 482 | #def _get_name_for_cookie(self, req, cookie): |
| 483 | # name = auth.LoginModule._get_name_for_cookie(self, req, cookie) |
| 484 | # if name and not req.incookie.has_key('trac_auth_session'): |
| 485 | # self.env.log.debug('Updating auth cookie %s for user %s' % |
| 486 | # (cookie.value, name)) |
| 487 | # db = self.env.get_db_cnx() |
| 488 | # cursor = db.cursor() |
| 489 | # cursor.execute('UPDATE auth_cookie SET time=%s WHERE cookie=%s', |
| 490 | # (int(time.time()), cookie.value)) |
| 491 | # req.outcookie['trac_auth'] = cookie.value |
| 492 | # req.outcookie['trac_auth']['path'] = self.env.href() |
| 493 | # req.outcookie['trac_auth']['expires'] = 86400 * 30 |
| 494 | # req.outcookie['trac_auth_session'] = '1' |
| 495 | # req.outcookie['trac_auth_session']['path'] = self.env.href() |
| 496 | # return name |
| 497 | |
435 | 498 | def _do_login(self, req): |
436 | 499 | if not req.remote_user: |
437 | 500 | req.redirect(self.env.abs_href()) |
438 | | return auth.LoginModule._do_login(self, req) |
| 501 | res = auth.LoginModule._do_login(self, req) |
| 502 | if req.args.get('rememberme', '0') == '1': |
| 503 | req.outcookie['trac_auth']['expires'] = 86400 * 30 |
| 504 | req.outcookie['trac_auth_session'] = '1' |
| 505 | req.outcookie['trac_auth_session']['path'] = self.env.href() |
| 506 | return res |
439 | 507 | |
| 508 | def _do_logout(self, req): |
| 509 | """Log the user out. |
| 510 | |
| 511 | Simply deletes the corresponding record from the auth_cookie table. |
| 512 | """ |
| 513 | if req.authname == 'anonymous': |
| 514 | # Not logged in |
| 515 | return |
| 516 | |
| 517 | # While deleting this cookie we also take the opportunity to delete |
| 518 | # cookies older than 30 days |
| 519 | db = self.env.get_db_cnx() |
| 520 | cursor = db.cursor() |
| 521 | cursor.execute("DELETE FROM auth_cookie WHERE name=%s OR time < %s", |
| 522 | (req.authname, int(time.time()) - 86400 * 30)) |
| 523 | db.commit() |
| 524 | self._expire_cookie(req) |
| 525 | |
440 | 526 | def _remote_user(self, req): |
441 | 527 | user = req.args.get('user') |
442 | 528 | password = req.args.get('password') |
… |
… |
|
449 | 535 | def _redirect_back(self, req): |
450 | 536 | """Redirect the user back to the URL she came from.""" |
451 | 537 | referer = self._referer(req) |
452 | | if referer and not referer.startswith(req.base_url): |
453 | | # don't redirect to external sites |
454 | | referer = None |
| 538 | if referer: |
| 539 | u = urlparse.urlparse(referer) |
| 540 | r = urlparse.urlparse(req.base_url) |
| 541 | if u[1] and u[1] != r[1]: |
| 542 | # don't redirect to external sites |
| 543 | referer = None |
455 | 544 | req.redirect(referer or self.env.abs_href()) |
456 | 545 | |
457 | 546 | def _referer(self, req): |
… |
… |
|
576 | 665 | def _send_email(self, req): |
577 | 666 | notifier = EmailVerificationNotification(self.env) |
578 | 667 | notifier.notify(req.authname, req.session['email_verification_token']) |
| 668 | |
-
|
|
|
24 | 24 | </div> |
25 | 25 | |
26 | 26 | <form method="post" id="acctmgr_registerform" action=""> |
| 27 | <input type="hidden" name="referer" value="${req.args.get('referer')}" /> |
27 | 28 | <fieldset> |
28 | 29 | <legend>Required</legend> |
29 | 30 | <div> |
… |
… |
|
43 | 44 | class="textwidget" size="20" /> |
44 | 45 | </label> |
45 | 46 | </div> |
46 | | </fieldset> |
47 | | <fieldset> |
48 | | <legend>Optional</legend> |
49 | 47 | <div> |
50 | 48 | <label>Name: |
51 | 49 | <input type="text" name="name" class="textwidget" size="20" /> |
-
|
|
|
28 | 28 | <input type="hidden" name="referer" value="${referer}" /> |
29 | 29 | <div> |
30 | 30 | <label for="user">Username:</label> |
31 | | <input type="text" id="user" name="user" class="textwidget" size="20" /> |
| 31 | <input type="text" id="user" name="user" class="textwidget" size="20" value="${req.authname != 'anonymous' and req.authname or None}"/> |
32 | 32 | </div> |
33 | 33 | <div> |
34 | 34 | <label for="password">Password:</label> |
35 | 35 | <input type="password" id="password" name="password" class="textwidget" size="20" /> |
36 | 36 | </div> |
| 37 | <div> |
| 38 | <input type="checkbox" id="rememberme" name="rememberme" value="1" checked="${req.authname != 'anonymous' and req.authname or None}"/> |
| 39 | <label for="rememberme">Remember me</label> |
| 40 | </div> |
37 | 41 | <input type="submit" value="Login" /> |
38 | 42 | |
39 | 43 | <p py:if="reset_password_enabled"> |
Download in other formats:
All information, including names and email addresses, entered onto this website or sent to mailing lists affiliated with this website will be public. Do not post confidential information, especially passwords!