| 1 | """ |
|---|
| 2 | SharedCookieAuth: |
|---|
| 3 | a plugin for Trac to share cookies between Tracs |
|---|
| 4 | http://trac.edgewall.org |
|---|
| 5 | """ |
|---|
| 6 | |
|---|
| 7 | import os |
|---|
| 8 | |
|---|
| 9 | from trac.config import ListOption |
|---|
| 10 | from trac.core import * |
|---|
| 11 | from trac.web import auth |
|---|
| 12 | from trac.web.api import IAuthenticator |
|---|
| 13 | from trac.web.main import RequestDispatcher |
|---|
| 14 | from trac.env import open_environment |
|---|
| 15 | from trac.env import IEnvironmentSetupParticipant |
|---|
| 16 | |
|---|
| 17 | class GenericObject(object): |
|---|
| 18 | def __init__(self, **kw): |
|---|
| 19 | for key, item in kw.items(): |
|---|
| 20 | setattr(self, key, item) |
|---|
| 21 | |
|---|
| 22 | ### attributes to take from the request for the monkey-patched methods |
|---|
| 23 | req_attr = [ 'authname', |
|---|
| 24 | 'href', |
|---|
| 25 | 'incookie', |
|---|
| 26 | 'outcookie', |
|---|
| 27 | 'redirect', |
|---|
| 28 | 'remote_addr', |
|---|
| 29 | 'remote_user', ] |
|---|
| 30 | |
|---|
| 31 | def _do_login(self, req): |
|---|
| 32 | kw = dict([ (i, getattr(req, i)) for i in req_attr ]) |
|---|
| 33 | kw['base_path'] = '/' |
|---|
| 34 | fake_req = GenericObject(**kw) |
|---|
| 35 | auth_login_module_do_login(self, fake_req) |
|---|
| 36 | |
|---|
| 37 | def _do_logout(self, req): |
|---|
| 38 | kw = dict([ (i, getattr(req, i)) for i in req_attr ]) |
|---|
| 39 | kw['base_path'] = '/' |
|---|
| 40 | fake_req = GenericObject(**kw) |
|---|
| 41 | auth_login_module_do_logout(self, fake_req) |
|---|
| 42 | |
|---|
| 43 | |
|---|
| 44 | class SharedCookieAuth(Component): |
|---|
| 45 | |
|---|
| 46 | ### class-level data |
|---|
| 47 | implements(IAuthenticator, IEnvironmentSetupParticipant) |
|---|
| 48 | |
|---|
| 49 | ### method for IAuthenticator |
|---|
| 50 | |
|---|
| 51 | def authenticate(self, req): |
|---|
| 52 | |
|---|
| 53 | if req.remote_user: |
|---|
| 54 | return req.remote_user |
|---|
| 55 | |
|---|
| 56 | if 'shared_cookie_auth' in req.environ: |
|---|
| 57 | return req.environ['shared_cookie_auth'] |
|---|
| 58 | else: |
|---|
| 59 | req.environ['shared_cookie_auth'] = None |
|---|
| 60 | if req.incookie.has_key('trac_auth'): |
|---|
| 61 | for project, dispatcher in self.dispatchers().items(): |
|---|
| 62 | agent = dispatcher.authenticate(req) |
|---|
| 63 | if agent != 'anonymous': |
|---|
| 64 | req.authname = agent |
|---|
| 65 | req.environ['shared_cookie_auth'] = agent |
|---|
| 66 | return agent |
|---|
| 67 | |
|---|
| 68 | return None |
|---|
| 69 | |
|---|
| 70 | ### methods for IEnvironmentSetupParticipant |
|---|
| 71 | |
|---|
| 72 | """Extension point interface for components that need to participate in the |
|---|
| 73 | creation and upgrading of Trac environments, for example to create |
|---|
| 74 | additional database tables.""" |
|---|
| 75 | |
|---|
| 76 | def environment_created(self): |
|---|
| 77 | """Called when a new Trac environment is created.""" |
|---|
| 78 | |
|---|
| 79 | def environment_needs_upgrade(self, db): |
|---|
| 80 | """Called when Trac checks whether the environment needs to be upgraded. |
|---|
| 81 | |
|---|
| 82 | Should return `True` if this participant needs an upgrade to be |
|---|
| 83 | performed, `False` otherwise. |
|---|
| 84 | """ |
|---|
| 85 | #self.patch() |
|---|
| 86 | return False |
|---|
| 87 | |
|---|
| 88 | |
|---|
| 89 | def upgrade_environment(self, db): |
|---|
| 90 | """Actually perform an environment upgrade. |
|---|
| 91 | |
|---|
| 92 | Implementations of this method should not commit any database |
|---|
| 93 | transactions. This is done implicitly after all participants have |
|---|
| 94 | performed the upgrades they need without an error being raised. |
|---|
| 95 | """ |
|---|
| 96 | |
|---|
| 97 | ### internal methods |
|---|
| 98 | |
|---|
| 99 | def patch(self): |
|---|
| 100 | if not globals().has_key('auth_login_module_do_login'): |
|---|
| 101 | globals()['auth_login_module_do_login'] = auth.LoginModule._do_login |
|---|
| 102 | auth.LoginModule._do_login = _do_login |
|---|
| 103 | globals()['auth_login_module_do_logout'] = auth.LoginModule._do_logout |
|---|
| 104 | auth.LoginModule._do_logout = _do_logout |
|---|
| 105 | |
|---|
| 106 | |
|---|
| 107 | def dispatchers(self): |
|---|
| 108 | if not hasattr(self, '_dispatchers'): |
|---|
| 109 | |
|---|
| 110 | dispatchers = {} |
|---|
| 111 | base_path, project = os.path.split(self.env.path) |
|---|
| 112 | projects = [ i for i in os.listdir(base_path) |
|---|
| 113 | if i != project ] |
|---|
| 114 | |
|---|
| 115 | for project in projects: |
|---|
| 116 | path = os.path.join(base_path, project) |
|---|
| 117 | try: |
|---|
| 118 | env = open_environment(path) |
|---|
| 119 | rd = RequestDispatcher(env) |
|---|
| 120 | except: |
|---|
| 121 | continue |
|---|
| 122 | dispatchers[project] = rd |
|---|
| 123 | |
|---|
| 124 | self._dispatchers = dispatchers |
|---|
| 125 | return self._dispatchers |
|---|