Ticket #131: remember_me.4.patch
| File remember_me.4.patch, 10.5 kB (added by scratcher, 3 months ago) |
|---|
-
a/acct_mgr/admin.py
old new 59 59 self.config.save() 60 60 self.config.set('account-manager', 'force_passwd_change', 61 61 req.args.get('force_passwd_change')) 62 self.config.set('account-manager', 'persistent_sessions', 63 req.args.get('persistent_sessions')) 62 64 self.config.save() 63 65 64 66 … … 81 83 ] 82 84 sections = sorted(sections, key=lambda i: i['name']) 83 85 data = {'sections': sections, 84 'force_passwd_change': self.account_manager.force_passwd_change} 86 'force_passwd_change': self.account_manager.force_passwd_change, 87 'persistent_sessions': self.account_manager.persistent_sessions} 85 88 return 'admin_accountsconfig.html', data 86 89 87 90 def _do_users(self, req): -
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 persistent_sessions = BoolOption('account-manager', 'persistent_sessions', 92 False, doc="Allow the user to be " 93 "remembered across sessions without " 94 "needing to re-authenticate. This is, " 95 "user checks a \"Remember Me\" checkbox " 96 "and, next time he visits the site, he'll " 97 "be remembered") 91 98 92 99 # Public API 93 100 … … 107 114 return self.password_store.check_password(user, password) 108 115 109 116 def delete_user(self, user): 110 db = self.env.get_db_cnx() 111 cursor = db.cursor() 112 # Delete session attributes 113 cursor.execute("DELETE FROM session_attribute where sid=%s", (user,)) 114 # Delete session 115 cursor.execute("DELETE FROM session where sid=%s", (user,)) 116 # Delete any custom permissions set for the user 117 cursor.execute("DELETE FROM permission where username=%s", (user,)) 117 db = self.env.get_db_cnx() 118 cursor = db.cursor() 119 # Delete session attributes 120 cursor.execute("DELETE FROM session_attribute where sid=%s", (user,)) 121 # Delete session 122 cursor.execute("DELETE FROM session where sid=%s", (user,)) 123 # Delete any custom permissions set for the user 124 cursor.execute("DELETE FROM permission where username=%s", (user,)) 118 125 db.commit() 119 126 db.close() 120 # Delete from password store 127 # Delete from password store 121 128 self.log.debug('deleted user') 122 129 if self.password_store.delete_user(user): 123 130 self._notify('deleted', user) -
a/acct_mgr/templates/admin_accountsconfig.html
old new 39 39 <input type="radio" name="force_passwd_change" value="false" 40 40 checked="${not force_passwd_change and 'checked' or None}">No</input> 41 41 </fieldset> 42 43 <fieldset> 44 <legend>Persistent Sessions</legend> 45 <label for="persistent_sessions"> 46 Allow the user to be remembered across sessions without needing to 47 re-authenticate?<br/> 48 This is, user checks a "Remember Me" <tt>checkbox</tt> and, next time 49 he visits the site,<br/> 50 he'll be remembered and automatically authenticated. 51 </label> 52 <input type="radio" name="persistent_sessions" value="true" 53 checked="${persistent_sessions and 'checked' or None}">Yes</input> 54 <input type="radio" name="persistent_sessions" value="false" 55 checked="${not persistent_sessions and 'checked' or None}">No</input> 56 </fieldset> 57 42 58 <div class="buttons"> 43 59 <input type="submit" name="save" value="Save" /> 44 60 </div> -
a/acct_mgr/templates/login.html
old new 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 py:if="persistent_sessions"> 38 <input type="checkbox" id="rememberme" name="rememberme" value="1" /> 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"> -
a/acct_mgr/web_ui.py
old new 13 13 import os 14 14 import random 15 15 import string 16 import time 16 17 17 18 from trac import perm, util 18 19 from trac.core import * … … 411 412 412 413 class LoginModule(auth.LoginModule): 413 414 414 implements(ITemplateProvider )415 implements(ITemplateProvider, IRequestFilter) 415 416 416 417 def authenticate(self, req): 417 418 if req.method == 'POST' and req.path_info.startswith('/login'): … … 425 426 if req.path_info.startswith('/login') and req.authname == 'anonymous': 426 427 data = { 427 428 'referer': self._referer(req), 428 'reset_password_enabled': AccountModule(self.env).reset_password_enabled 429 'reset_password_enabled': \ 430 AccountModule(self.env).reset_password_enabled, 431 'persistent_sessions': \ 432 AccountManager(self.env).persistent_sessions 429 433 } 430 434 if req.method == 'POST': 431 435 data['login_error'] = 'Invalid username or password' 432 436 return 'login.html', data, None 433 437 return auth.LoginModule.process_request(self, req) 434 438 439 # IRequestFilter methods 440 def pre_process_request(self, req, handler): 441 if 'trac_auth_session' in req.incookie: 442 # Let's check for a matching IP, proxy'ed or not 443 remote_addr = req.get_header("X-Forwarded-For") or req.remote_addr 444 if not req.incookie['trac_auth_session'].value == remote_addr: 445 self.log.debug("Cookie IP does not match Remote address IP: " 446 "'%s'!='%s'; Killing Session", 447 req.incookie['trac_auth_session'].value, 448 remote_addr) 449 self._do_logout(req) 450 # Repeat request after logout in order for the user not to 451 # think he's logged in 452 req.redirect(req.path_info) 453 454 455 # Let's now check with the one we have on auth_cookie db table 456 db = self.env.get_db_cnx() 457 cursor = db.cursor() 458 cursor.execute("SELECT ipnr from auth_cookie WHERE cookie=%s", 459 (req.incookie['trac_auth'].value,)) 460 row = cursor.fetchone() 461 if not req.incookie['trac_auth_session'].value == row[0]: 462 self.log.debug("Cookie IP does not match DB IP: '%s'!='%s'; " 463 "Killing Session", 464 req.incookie['trac_auth_session'].value, 465 row[0]) 466 self._do_logout(req) 467 # Repeat request after logout in order for the user not to 468 # think he's logged in 469 req.redirect(req.path_info) 470 # XXX: Would there be a way to know if a user authenticated using a 471 # cookie and not the login form, and, if he requested /prefs force him 472 # to re-authenticate? 473 return handler 474 475 def post_process_request(self, req, template, data, content_type): 476 return (template, data, content_type) 477 478 def _get_name_for_cookie(self, req, cookie): 479 name = auth.LoginModule._get_name_for_cookie(self, req, cookie) 480 if not AccountManager(self.env).persistent_sessions: 481 # Persistent sessions not enabled 482 return name 483 484 self.env.log.debug('Updating auth cookie %s for user %s' % 485 (cookie.value, name)) 486 db = self.env.get_db_cnx() 487 cursor = db.cursor() 488 cursor.execute('UPDATE auth_cookie SET time=%s WHERE cookie=%s', 489 (int(time.time()), cookie.value)) 490 db.commit() 491 req.outcookie['trac_auth'] = cookie.value 492 req.outcookie['trac_auth']['path'] = self.env.href() 493 494 if 'trac_auth_session' in req.incookie: 495 # Let's check for a matching IP, proxy'ed or not 496 remote_addr = req.get_header("X-Forwarded-For") or req.remote_addr 497 if req.incookie['trac_auth_session'].value == remote_addr: 498 req.outcookie['trac_auth']['expires'] = 86400 * 30 499 return name 500 435 501 def _do_login(self, req): 436 502 if not req.remote_user: 437 503 req.redirect(self.env.abs_href()) 438 return auth.LoginModule._do_login(self, req) 504 res = auth.LoginModule._do_login(self, req) 505 if req.args.get('rememberme', '0') == '1': 506 req.outcookie['trac_auth']['expires'] = 86400 * 30 507 req.outcookie['trac_auth_session'] = req.remote_addr 508 req.outcookie['trac_auth_session']['expires'] = 86400 * 30 509 return res 510 511 def _do_logout(self, req): 512 """Log the user out. 513 514 Simply deletes the corresponding record from the auth_cookie table. 515 """ 516 if req.authname == 'anonymous': 517 # Not logged in 518 return 519 520 # While deleting this cookie we also take the opportunity to delete 521 # cookies older than 30 days 522 db = self.env.get_db_cnx() 523 cursor = db.cursor() 524 cursor.execute("DELETE FROM auth_cookie WHERE name=%s OR time < %s", 525 (req.authname, int(time.time()) - 86400 * 30)) 526 db.commit() 527 self._expire_cookie(req) 528 529 # Expire the persistent session cookie 530 req.outcookie['trac_auth_session'] = '' 531 #req.outcookie['trac_auth_session']['path'] = self.env.href() 532 req.outcookie['trac_auth_session']['expires'] = -10000 439 533 440 534 def _remote_user(self, req): 441 535 user = req.args.get('user')
