comparison pylons_app/lib/middleware/simplehg.py @ 204:a8ea3ce3cdc4

Created middleware package. Crated special middleware to handle https requests redirections.
author Marcin Kuzminski <marcin@python-works.com>
date Sun, 23 May 2010 00:54:22 +0200
parents pylons_app/lib/simplehg.py@be6d8aaddbd1
children 58b46f9194c3
comparison
equal deleted inserted replaced
203:be6d8aaddbd1 204:a8ea3ce3cdc4
1 #!/usr/bin/env python
2 # encoding: utf-8
3 #
4 # Copyright (c) 2010 marcink. All rights reserved.
5 #
6 """
7 Created on 2010-04-28
8
9 @author: marcink
10 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
11 It's implemented with basic auth function
12 """
13 from datetime import datetime
14 from mercurial.hgweb import hgweb
15 from mercurial.hgweb.request import wsgiapplication
16 from paste.auth.basic import AuthBasicAuthenticator
17 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
18 from pylons_app.lib.auth import authfunc
19 from pylons_app.lib.utils import is_mercurial, make_ui, invalidate_cache
20 from pylons_app.model import meta
21 from pylons_app.model.db import UserLogs, Users
22 from webob.exc import HTTPNotFound
23 import logging
24 import os
25 log = logging.getLogger(__name__)
26
27 class SimpleHg(object):
28
29 def __init__(self, application, config):
30 self.application = application
31 self.config = config
32 #authenticate this mercurial request using
33 realm = '%s %s' % (config['hg_app_name'], 'mercurial repository')
34 self.authenticate = AuthBasicAuthenticator(realm, authfunc)
35
36 def __call__(self, environ, start_response):
37 if not is_mercurial(environ):
38 return self.application(environ, start_response)
39 else:
40 #===================================================================
41 # AUTHENTICATE THIS MERCURIAL REQUEST
42 #===================================================================
43 username = REMOTE_USER(environ)
44 if not username:
45 result = self.authenticate(environ)
46 if isinstance(result, str):
47 AUTH_TYPE.update(environ, 'basic')
48 REMOTE_USER.update(environ, result)
49 else:
50 return result.wsgi_application(environ, start_response)
51
52 try:
53 repo_name = environ['PATH_INFO'].split('/')[1]
54 except:
55 return HTTPNotFound()(environ, start_response)
56
57 #since we wrap into hgweb, just reset the path
58 environ['PATH_INFO'] = '/'
59 self.baseui = make_ui()
60 self.basepath = self.baseui.configitems('paths')[0][1]\
61 .replace('*', '')
62 self.repo_path = os.path.join(self.basepath, repo_name)
63 try:
64 app = wsgiapplication(self.__make_app)
65 except Exception as e:
66 return HTTPNotFound()(environ, start_response)
67
68 action = self.__get_action(environ)
69 #invalidate cache on push
70 if action == 'push':
71 self.__invalidate_cache(repo_name)
72
73 if action:
74 username = self.__get_environ_user(environ)
75 self.__log_user_action(username, action, repo_name)
76 return app(environ, start_response)
77
78 def __make_app(self):
79 hgserve = hgweb(self.repo_path)
80 return self.load_web_settings(hgserve)
81
82 def __get_environ_user(self, environ):
83 return environ.get('REMOTE_USER')
84
85 def __get_action(self, environ):
86 """
87 Maps mercurial request commands into a pull or push command.
88 @param environ:
89 """
90 mapping = {
91 'changegroup': 'pull',
92 'changegroupsubset': 'pull',
93 'unbundle': 'push',
94 'stream_out': 'pull',
95 }
96 for qry in environ['QUERY_STRING'].split('&'):
97 if qry.startswith('cmd'):
98 cmd = qry.split('=')[-1]
99 if mapping.has_key(cmd):
100 return mapping[cmd]
101
102 def __log_user_action(self, username, action, repo):
103 sa = meta.Session
104 try:
105 user = sa.query(Users)\
106 .filter(Users.username == username).one()
107 user_log = UserLogs()
108 user_log.user_id = user.user_id
109 user_log.action = action
110 user_log.repository = repo.replace('/', '')
111 user_log.action_date = datetime.now()
112 sa.add(user_log)
113 sa.commit()
114 log.info('Adding user %s, action %s on %s',
115 username, action, repo)
116 except Exception as e:
117 sa.rollback()
118 log.error('could not log user action:%s', str(e))
119
120 def __invalidate_cache(self, repo_name):
121 """we know that some change was made to repositories and we should
122 invalidate the cache to see the changes right away but only for
123 push requests"""
124 invalidate_cache('cached_repo_list')
125 invalidate_cache('full_changelog', repo_name)
126
127
128 def load_web_settings(self, hgserve):
129 repoui = make_ui(os.path.join(self.repo_path, '.hg', 'hgrc'), False)
130 #set the global ui for hgserve
131 hgserve.repo.ui = self.baseui
132
133 if repoui:
134 #set the repository based config
135 hgserve.repo.ui = repoui
136
137 return hgserve
138
139