Ticket #1855: AuthProvider_refactor_r2036.patch
| File AuthProvider_refactor_r2036.patch, 11.5 KB (added by Rede <redetin@…>, 7 years ago) |
|---|
-
trac/web/auth.py
22 22 from __future__ import generators 23 23 24 24 import time 25 import re 25 26 26 27 from trac import util 27 28 from trac.core import * 28 29 from trac.web.chrome import INavigationContributor 30 from trac.web.main import IRequestHandler 29 31 32 class IAuthenticationProvider(Interface): 33 """ 34 Extension point interface for adding authentication provider to the 35 Trac. 36 """ 37 38 def authenticate(self, req): 39 """ 40 Authenticate user. If user is authenticated, this method must return 41 true value. 42 """ 30 43 31 class Authenticator: 44 class Authenticator(Component): 45 46 auth_providers = ExtensionPoint(IAuthenticationProvider) 47 48 def authorize(self, req): 49 for authenticator in self.auth_providers: 50 if authenticator.authenticate(req): 51 break 52 53 class LoginModule(Component): 32 54 """Implements user authentication based on HTTP authentication provided by 33 55 the web-server, combined with cookies for communicating the login 34 56 information across the whole site. … … 40 62 to identify the user in subsequent requests to non-protected resources. 41 63 """ 42 64 43 def __init__(self, db, req, check_ip=True, ignore_case=False): 44 self.db = db 45 self.authname = 'anonymous' 46 self.ignore_case = ignore_case 65 implements(INavigationContributor, IAuthenticationProvider, IRequestHandler) 47 66 48 if req.incookie.has_key('trac_auth'): 49 cookie = req.incookie['trac_auth'].value 50 cursor = db.cursor() 51 if check_ip: 52 cursor.execute("SELECT name FROM auth_cookie " 53 "WHERE cookie=%s AND ipnr=%s", 54 (cookie, req.remote_addr)) 55 else: 56 cursor.execute("SELECT name FROM auth_cookie WHERE cookie=%s", 57 (cookie,)) 58 row = cursor.fetchone() 59 if row: 60 self.authname = row[0] 61 else: 62 # Tell the user to drop any auth_cookie for which no 63 # corresponding entry in our cookie table exists. 64 self.expire_auth_cookie(req) 67 # INavigationContributor methods 68 def get_active_navigation_item(self, req): 69 return 'login' 70 71 def get_navigation_items(self, req): 72 if req.authname and req.authname != 'anonymous': 73 yield 'metanav', 'login', 'logged in as %s' \ 74 % util.escape(req.authname) 75 yield 'metanav', 'logout', '<a href="%s">Logout</a>' \ 76 % util.escape(self.env.href.logout()) 77 else: 78 yield 'metanav', 'login', '<a href="%s">Login</a>' \ 79 % util.escape(self.env.href.login()) 80 81 # IRequestProvider methods 82 def match_request(self, req): 83 match = re.match(r'^/(login|logout)', req.path_info) 84 if match: 85 if match.group(1): 86 req.args['action'] = match.group(1) 87 return 1 65 88 66 def login(self, req): 89 def process_request(self, req): 90 db = self.env.get_db_cnx() 91 92 action = req.args.get('action') or '' 93 if action == 'login': 94 self._login(req, db) 95 else: 96 self._logout(req, db) 97 98 # IAuthenticationProvider methods 99 def authenticate(self, req): 100 return standard_authenticator(self.env, req) 101 102 # Internal methods 103 def _login(self, req, db): 67 104 """Log the remote user in. 68 105 69 106 This function expects to be called when the remote user name is … … 80 117 assert req.remote_user, 'Authentication information not available.' 81 118 82 119 remote_user = req.remote_user 83 if self.ignore_case: 120 ignore_case = self.env.config.get('trac', 'ignore_auth_case') 121 ignore_case = ignore_case.strip().lower() in util.TRUE 122 if ignore_case: 84 123 remote_user = remote_user.lower() 85 124 86 if self.authname == remote_user: 87 # Already logged in with the same user name 88 return 89 assert self.authname == 'anonymous', \ 90 'Already logged in as %s.' % self.authname 125 if req.authname != remote_user: 126 assert req.authname == 'anonymous', \ 127 'Already logged in as %s.' % req.authname 91 128 92 cookie = util.hex_entropy() 93 cursor = self.db.cursor() 94 cursor.execute("INSERT INTO auth_cookie (cookie,name,ipnr,time) " 95 "VALUES (%s, %s, %s, %s)", 96 (cookie, remote_user, req.remote_addr, int(time.time()))) 97 self.db.commit() 98 self.authname = remote_user 99 req.outcookie['trac_auth'] = cookie 100 req.outcookie['trac_auth']['path'] = util.quote_cookie_value(req.cgi_location) 129 req.authname = remote_user 130 131 standard_login(req, db) 101 132 102 def logout(self, req): 133 referer = req.get_header('Referer') 134 if referer and not referer.startswith(req.base_url): 135 # only redirect to referer if the latter is from the 136 # same instance 137 referer = None 138 req.redirect(referer or self.env.href.wiki()) 139 140 def _logout(self, req, db): 103 141 """Log the user out. 104 142 105 143 Simply deletes the corresponding record from the auth_cookie table. 106 144 """ 107 if self.authname == 'anonymous': 108 # Not logged in 109 return 145 if req.authname and req.authname != 'anonymous': 146 standard_logoff(req, db) 147 req.authname = 'anonymous' 148 149 referer = req.get_header('Referer') 150 if referer and not referer.startswith(req.base_url): 151 # only redirect to referer if the latter is from the same 152 # instance 153 referer = None 154 req.redirect(referer or self.env.href.wiki()) 110 155 111 cursor = self.db.cursor() 112 # While deleting this cookie we also take the opportunity to delete 113 # cookies older than 10 days 114 cursor.execute("DELETE FROM auth_cookie WHERE name=%s OR time < %s", 115 (self.authname, int(time.time()) - 86400 * 10)) 116 self.db.commit() 117 self.expire_auth_cookie(req) 156 def standard_authenticator(env, req): 157 """ 158 Does standard authentication check from cookie. 159 160 Optionally checks that IP of requester matches one in db. 161 """ 162 163 db = env.get_db_cnx() 164 authname = 'anonymous' 165 166 check_ip = env.config.get('trac', 'check_auth_ip') 167 check_ip = check_ip.strip().lower() in util.TRUE 168 169 if req.incookie.has_key('trac_auth'): 170 cookie = req.incookie['trac_auth'].value 171 cursor = db.cursor() 172 if check_ip: 173 cursor.execute("SELECT name FROM auth_cookie " 174 "WHERE cookie=%s AND ipnr=%s", 175 (cookie, req.remote_addr)) 176 else: 177 cursor.execute("SELECT name FROM auth_cookie WHERE cookie=%s", 178 (cookie,)) 179 row = cursor.fetchone() 180 if row: 181 authname = row[0] 182 else: 183 # Tell the user to drop any auth_cookie for which no 184 # corresponding entry in our cookie table exists. 185 186 expire_auth_cookie(req) 118 187 119 def expire_auth_cookie(self, req): 120 """Instruct the user agent to drop the auth cookie by setting the 121 "expires" property to a date in the past. 122 """ 123 req.outcookie['trac_auth'] = '' 124 req.outcookie['trac_auth']['path'] = util.quote_cookie_value(req.cgi_location) 125 req.outcookie['trac_auth']['expires'] = -10000 188 req.authname = authname 189 190 return req.authname != 'anonymous' 126 191 192 def standard_login(req, db): 193 """ 194 Does standard login by saving authname, sessionid and time to session table. 195 """ 127 196 128 class LoginModule(Component): 197 cookie = util.hex_entropy() 198 cursor = db.cursor() 199 cursor.execute("INSERT INTO auth_cookie (cookie,name,ipnr,time) " 200 "VALUES (%s, %s, %s, %s)", 201 (cookie, req.authname, req.remote_addr, int(time.time()))) 202 db.commit() 203 204 req.outcookie['trac_auth'] = cookie 205 req.outcookie['trac_auth']['path'] = util.quote_cookie_value(req.cgi_location) 129 206 130 implements(INavigationContributor) 207 def standard_logoff(req, db): 208 """ 209 Does standard logoff by deleting cookie data from session table. 210 211 Also deletes cookies older than 10 days. 212 """ 213 214 cursor = db.cursor() 215 # While deleting this cookie we also take the opportunity to delete 216 # cookies older than 10 days 217 cursor.execute("DELETE FROM auth_cookie WHERE name=%s OR time < %s", 218 (req.authname, int(time.time()) - 86400 * 10)) 219 db.commit() 220 expire_auth_cookie(req) 221 222 def expire_auth_cookie(req): 223 """Instruct the user agent to drop the auth cookie by setting the 224 "expires" property to a date in the past. 225 """ 226 req.outcookie['trac_auth'] = '' 227 req.outcookie['trac_auth']['path'] = util.quote_cookie_value(req.cgi_location) 228 req.outcookie['trac_auth']['expires'] = -10000 131 229 132 # INavigationContributor methods133 134 def get_navigation_items(self, req):135 if req.authname and req.authname != 'anonymous':136 yield 'metanav', 'login', 'logged in as %s' \137 % util.escape(req.authname)138 yield 'metanav', 'logout', '<a href="%s">Logout</a>' \139 % util.escape(self.env.href.logout())140 else:141 yield 'metanav', 'login', '<a href="%s">Login</a>' \142 % util.escape(self.env.href.login()) -
trac/web/main.py
419 419 try: 420 420 try: 421 421 from trac.web.auth import Authenticator 422 check_ip = env.config.get('trac', 'check_auth_ip') 423 check_ip = check_ip.strip().lower() in TRUE 424 ignore_case = env.config.get('trac', 'ignore_auth_case') 425 ignore_case = ignore_case.strip().lower() in TRUE 426 authenticator = Authenticator(db, req, check_ip, ignore_case) 427 if path_info == '/logout': 428 authenticator.logout(req) 429 referer = req.get_header('Referer') 430 if referer and not referer.startswith(req.base_url): 431 # only redirect to referer if the latter is from the same 432 # instance 433 referer = None 434 req.redirect(referer or env.href.wiki()) 435 elif req.remote_user: 436 authenticator.login(req) 437 if path_info == '/login': 438 referer = req.get_header('Referer') 439 if referer and not referer.startswith(req.base_url): 440 # only redirect to referer if the latter is from the 441 # same instance 442 referer = None 443 req.redirect(referer or env.href.wiki()) 444 req.authname = authenticator.authname 422 423 authenticator = Authenticator(env) 424 authenticator.authorize(req) 425 445 426 req.perm = PermissionCache(env, req.authname) 446 427 447 428 newsession = req.args.has_key('newsession')
