| 1 | from trac.core import * |
|---|
| 2 | from trac.config import ListOption |
|---|
| 3 | from trac.web.api import IRequestFilter, RequestDone, IAuthenticator |
|---|
| 4 | from trac.web.chrome import INavigationContributor |
|---|
| 5 | |
|---|
| 6 | try: |
|---|
| 7 | from base64 import b64decode |
|---|
| 8 | except ImportError: |
|---|
| 9 | from base64 import decodestring as b64decode |
|---|
| 10 | |
|---|
| 11 | from acct_mgr.api import AccountManager |
|---|
| 12 | |
|---|
| 13 | __all__ = ['HTTPAuthFilter'] |
|---|
| 14 | |
|---|
| 15 | class HTTPAuthFilter(Component): |
|---|
| 16 | """Request filter and handler to provide HTTP authentication.""" |
|---|
| 17 | |
|---|
| 18 | paths = ListOption('httpauth', 'paths', default='/blog', doc='Paths to force HTTP authentication on.') |
|---|
| 19 | |
|---|
| 20 | formats = ListOption('httpauth', 'formats', default='rss', doc='Request formats to force HTTP authentication on') |
|---|
| 21 | |
|---|
| 22 | noredirectpaths = ListOption('httpauth', 'noredirectpaths', default='/login, /reset_password, /register, /chrome', doc='Paths excluded from redirection to login') |
|---|
| 23 | |
|---|
| 24 | implements(IRequestFilter, IAuthenticator) |
|---|
| 25 | |
|---|
| 26 | # IRequestFilter methods |
|---|
| 27 | def pre_process_request(self, req, handler): |
|---|
| 28 | # proceed if user is already authenticated |
|---|
| 29 | if (req.authname and req.authname != 'anonymous'): |
|---|
| 30 | return handler |
|---|
| 31 | # test if HTTP Authentication is applicable |
|---|
| 32 | check = False |
|---|
| 33 | for path in self.paths: |
|---|
| 34 | if req.path_info.startswith(path): |
|---|
| 35 | check = True |
|---|
| 36 | break |
|---|
| 37 | if req.args.get('format') in self.formats: |
|---|
| 38 | check = True |
|---|
| 39 | if check: |
|---|
| 40 | if not self._check_password(req): |
|---|
| 41 | self.log.info('HTTPAuthFilter: No/bad authentication data given, returing 403') |
|---|
| 42 | return self |
|---|
| 43 | else: |
|---|
| 44 | return handle |
|---|
| 45 | else: |
|---|
| 46 | # check if paths are excluded from login, or redirect to login page |
|---|
| 47 | for path in self.noredirectpaths: |
|---|
| 48 | if req.path_info.startswith(path): |
|---|
| 49 | check = True |
|---|
| 50 | break |
|---|
| 51 | if check: |
|---|
| 52 | return handler |
|---|
| 53 | else: |
|---|
| 54 | self.log.debug('Redirecting anonymous request to /login') |
|---|
| 55 | req.redirect(req.href.login() + '?referer=' + req.abs_href(req.path_info)) |
|---|
| 56 | return [] |
|---|
| 57 | |
|---|
| 58 | def post_process_request(self, req, template, content_type): |
|---|
| 59 | return template, content_type |
|---|
| 60 | |
|---|
| 61 | # IRequestHandler methods (sort of) |
|---|
| 62 | def process_request(self, req): |
|---|
| 63 | if req.session: |
|---|
| 64 | req.session.save() # Just in case |
|---|
| 65 | |
|---|
| 66 | req.send_response(401) |
|---|
| 67 | req.send_header('WWW-Authenticate', 'Basic realm="Control Panel"') |
|---|
| 68 | req.send_header('Content-Type', 'text/plain') |
|---|
| 69 | req.send_header('Pragma', 'no-cache') |
|---|
| 70 | req.send_header('Cache-control', 'no-cache') |
|---|
| 71 | req.send_header('Expires', 'Fri, 01 Jan 1999 00:00:00 GMT') |
|---|
| 72 | req.end_headers() |
|---|
| 73 | |
|---|
| 74 | if req.method != 'HEAD': |
|---|
| 75 | req.write('Authentication required') |
|---|
| 76 | raise RequestDone |
|---|
| 77 | |
|---|
| 78 | # IAuthenticator methods |
|---|
| 79 | def authenticate(self, req): |
|---|
| 80 | user = self._check_password(req) |
|---|
| 81 | if user: |
|---|
| 82 | self.log.debug('HTTPAuthFilter: Authentication okay for %s', user) |
|---|
| 83 | return user |
|---|
| 84 | |
|---|
| 85 | # Internal methods |
|---|
| 86 | def _check_password(self, req): |
|---|
| 87 | header = req.get_header('Authorization') |
|---|
| 88 | if header: |
|---|
| 89 | token = header.split()[1] |
|---|
| 90 | user, passwd = b64decode(token).split(':', 1) |
|---|
| 91 | if AccountManager(self.env).check_password(user, passwd): |
|---|
| 92 | return user |
|---|