##// END OF EJS Templates
Fixed journal action loggin doubled messages.
marcink -
r606:f31f1327 default
parent child Browse files
Show More
@@ -1,225 +1,226
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # middleware to handle mercurial api calls
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on 2010-04-28
22 22
23 23 @author: marcink
24 24 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
25 25 It's implemented with basic auth function
26 26 """
27 27 from itertools import chain
28 28 from mercurial.error import RepoError
29 29 from mercurial.hgweb import hgweb
30 30 from mercurial.hgweb.request import wsgiapplication
31 31 from paste.auth.basic import AuthBasicAuthenticator
32 32 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
33 33 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware, \
34 34 get_user_cached
35 35 from rhodecode.lib.utils import is_mercurial, make_ui, invalidate_cache, \
36 36 check_repo_fast, ui_sections
37 37 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
38 38 from rhodecode.lib.utils import action_logger
39 39 import logging
40 40 import os
41 41 import traceback
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45 class SimpleHg(object):
46 46
47 47 def __init__(self, application, config):
48 48 self.application = application
49 49 self.config = config
50 50 #authenticate this mercurial request using
51 51 self.authenticate = AuthBasicAuthenticator('', authfunc)
52 52
53 53 def __call__(self, environ, start_response):
54 54 if not is_mercurial(environ):
55 55 return self.application(environ, start_response)
56 56
57 57 #===================================================================
58 58 # AUTHENTICATE THIS MERCURIAL REQUEST
59 59 #===================================================================
60 60 username = REMOTE_USER(environ)
61 61 if not username:
62 62 self.authenticate.realm = self.config['rhodecode_realm']
63 63 result = self.authenticate(environ)
64 64 if isinstance(result, str):
65 65 AUTH_TYPE.update(environ, 'basic')
66 66 REMOTE_USER.update(environ, result)
67 67 else:
68 68 return result.wsgi_application(environ, start_response)
69 69
70 70 try:
71 71 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
72 72 if repo_name.endswith('/'):
73 73 repo_name = repo_name.rstrip('/')
74 74 except:
75 75 log.error(traceback.format_exc())
76 76 return HTTPInternalServerError()(environ, start_response)
77 77
78 78 #===================================================================
79 79 # CHECK PERMISSIONS FOR THIS REQUEST
80 80 #===================================================================
81 81 action = self.__get_action(environ)
82 82 if action:
83 83 username = self.__get_environ_user(environ)
84 84 try:
85 85 user = self.__get_user(username)
86 86 except:
87 87 log.error(traceback.format_exc())
88 88 return HTTPInternalServerError()(environ, start_response)
89 89 #check permissions for this repository
90 90
91 91 if action == 'push':
92 92 if not HasPermissionAnyMiddleware('repository.write',
93 93 'repository.admin')\
94 94 (user, repo_name):
95 95 return HTTPForbidden()(environ, start_response)
96 96
97 97 else:
98 98 if not HasPermissionAnyMiddleware('repository.read',
99 99 'repository.write',
100 100 'repository.admin')\
101 101 (user, repo_name):
102 102 return HTTPForbidden()(environ, start_response)
103 103
104 104 #log action
105 if action in ('push', 'pull', 'clone'):
105 106 proxy_key = 'HTTP_X_REAL_IP'
106 107 def_key = 'REMOTE_ADDR'
107 108 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
108 109 self.__log_user_action(user, action, repo_name, ipaddr)
109 110
110 111 #===================================================================
111 112 # MERCURIAL REQUEST HANDLING
112 113 #===================================================================
113 114 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
114 115 self.baseui = make_ui('db')
115 116 self.basepath = self.config['base_path']
116 117 self.repo_path = os.path.join(self.basepath, repo_name)
117 118
118 119 #quick check if that dir exists...
119 120 if check_repo_fast(repo_name, self.basepath):
120 121 return HTTPNotFound()(environ, start_response)
121 122 try:
122 123 app = wsgiapplication(self.__make_app)
123 124 except RepoError, e:
124 125 if str(e).find('not found') != -1:
125 126 return HTTPNotFound()(environ, start_response)
126 127 except Exception:
127 128 log.error(traceback.format_exc())
128 129 return HTTPInternalServerError()(environ, start_response)
129 130
130 131 #invalidate cache on push
131 132 if action == 'push':
132 133 self.__invalidate_cache(repo_name)
133 134 messages = []
134 135 messages.append('thank you for using rhodecode')
135 136
136 137 return self.msg_wrapper(app, environ, start_response, messages)
137 138 else:
138 139 return app(environ, start_response)
139 140
140 141
141 142 def msg_wrapper(self, app, environ, start_response, messages=[]):
142 143 """
143 144 Wrapper for custom messages that come out of mercurial respond messages
144 145 is a list of messages that the user will see at the end of response
145 146 from merurial protocol actions that involves remote answers
146 147 :param app:
147 148 :param environ:
148 149 :param start_response:
149 150 """
150 151 def custom_messages(msg_list):
151 152 for msg in msg_list:
152 153 yield msg + '\n'
153 154 org_response = app(environ, start_response)
154 155 return chain(org_response, custom_messages(messages))
155 156
156 157 def __make_app(self):
157 158 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
158 159 return self.__load_web_settings(hgserve)
159 160
160 161 def __get_environ_user(self, environ):
161 162 return environ.get('REMOTE_USER')
162 163
163 164 def __get_user(self, username):
164 165 return get_user_cached(username)
165 166
166 167 def __get_action(self, environ):
167 168 """
168 Maps mercurial request commands into a pull or push command.
169 This should return generally always something
169 Maps mercurial request commands into a clone,pull or push command.
170 This should always return a valid command string
170 171 :param environ:
171 172 """
172 173 mapping = {'changegroup': 'pull',
173 174 'changegroupsubset': 'pull',
174 175 'stream_out': 'pull',
175 'listkeys': 'pull',
176 #'listkeys': 'pull',
176 177 'unbundle': 'push',
177 178 'pushkey': 'push', }
178 179 for qry in environ['QUERY_STRING'].split('&'):
179 180 if qry.startswith('cmd'):
180 181 cmd = qry.split('=')[-1]
181 182 if mapping.has_key(cmd):
182 183 return mapping[cmd]
183 184 else:
184 185 return cmd
185 186
186 187 def __log_user_action(self, user, action, repo, ipaddr):
187 188 action_logger(user, action, repo, ipaddr)
188 189
189 190 def __invalidate_cache(self, repo_name):
190 191 """we know that some change was made to repositories and we should
191 192 invalidate the cache to see the changes right away but only for
192 193 push requests"""
193 194 invalidate_cache('cached_repo_list')
194 195 invalidate_cache('full_changelog', repo_name)
195 196
196 197
197 198 def __load_web_settings(self, hgserve):
198 199 #set the global ui for hgserve instance passed
199 200 hgserve.repo.ui = self.baseui
200 201
201 202 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
202 203 repoui = make_ui('file', hgrc, False)
203 204
204 205
205 206 if repoui:
206 207 #overwrite our ui instance with the section from hgrc file
207 208 for section in ui_sections:
208 209 for k, v in repoui.configitems(section):
209 210 hgserve.repo.ui.setconfig(section, k, v)
210 211
211 212 return hgserve
212 213
213 214
214 215
215 216
216 217
217 218
218 219
219 220
220 221
221 222
222 223
223 224
224 225
225 226
General Comments 0
You need to be logged in to leave comments. Login now