##// END OF EJS Templates
possible fix for #486 undefined variable username...
marcink -
r2500:94ef0b60 beta
parent child Browse files
Show More
@@ -1,304 +1,303 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.middleware.simplegit
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 SimpleGit middleware for handling git protocol request (push/clone etc.)
7 7 It's implemented with basic auth function
8 8
9 9 :created_on: Apr 28, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import re
29 29 import logging
30 30 import traceback
31 31
32 32 from dulwich import server as dulserver
33 33
34 34
35 35 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
36 36
37 37 def handle(self):
38 38 write = lambda x: self.proto.write_sideband(1, x)
39 39
40 40 graph_walker = dulserver.ProtocolGraphWalker(self,
41 41 self.repo.object_store,
42 42 self.repo.get_peeled)
43 43 objects_iter = self.repo.fetch_objects(
44 44 graph_walker.determine_wants, graph_walker, self.progress,
45 45 get_tagged=self.get_tagged)
46 46
47 47 # Did the process short-circuit (e.g. in a stateless RPC call)? Note
48 48 # that the client still expects a 0-object pack in most cases.
49 49 if objects_iter is None:
50 50 return
51 51
52 52 self.progress("counting objects: %d, done.\n" % len(objects_iter))
53 53 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
54 54 objects_iter)
55 55 messages = []
56 56 messages.append('thank you for using rhodecode')
57 57
58 58 for msg in messages:
59 59 self.progress(msg + "\n")
60 60 # we are done
61 61 self.proto.write("0000")
62 62
63 63
64 64 dulserver.DEFAULT_HANDLERS = {
65 65 #git-ls-remote, git-clone, git-fetch and git-pull
66 66 'git-upload-pack': SimpleGitUploadPackHandler,
67 67 #git-push
68 68 'git-receive-pack': dulserver.ReceivePackHandler,
69 69 }
70 70
71 71 # not used for now until dulwich get's fixed
72 72 #from dulwich.repo import Repo
73 73 #from dulwich.web import make_wsgi_chain
74 74
75 75 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
76 76
77 77 from rhodecode.lib.utils2 import safe_str
78 78 from rhodecode.lib.base import BaseVCSController
79 79 from rhodecode.lib.auth import get_container_username
80 80 from rhodecode.lib.utils import is_valid_repo, make_ui
81 81 from rhodecode.model.db import User, RhodeCodeUi
82 82
83 83 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
84 84
85 85 log = logging.getLogger(__name__)
86 86
87 87
88 88 GIT_PROTO_PAT = re.compile(r'^/(.+)/(info/refs|git-upload-pack|git-receive-pack)')
89 89
90 90
91 91 def is_git(environ):
92 92 path_info = environ['PATH_INFO']
93 93 isgit_path = GIT_PROTO_PAT.match(path_info)
94 94 log.debug('pathinfo: %s detected as GIT %s' % (
95 95 path_info, isgit_path != None)
96 96 )
97 97 return isgit_path
98 98
99 99
100 100 class SimpleGit(BaseVCSController):
101 101
102 102 def _handle_request(self, environ, start_response):
103 103
104 104 if not is_git(environ):
105 105 return self.application(environ, start_response)
106 106
107 107 ipaddr = self._get_ip_addr(environ)
108 108 username = None
109 109 self._git_first_op = False
110 110 # skip passing error to error controller
111 111 environ['pylons.status_code_redirect'] = True
112 112
113 113 #======================================================================
114 114 # EXTRACT REPOSITORY NAME FROM ENV
115 115 #======================================================================
116 116 try:
117 117 repo_name = self.__get_repository(environ)
118 118 log.debug('Extracted repo name is %s' % repo_name)
119 119 except:
120 120 return HTTPInternalServerError()(environ, start_response)
121 121
122 122 # quick check if that dir exists...
123 123 if is_valid_repo(repo_name, self.basepath) is False:
124 124 return HTTPNotFound()(environ, start_response)
125 125
126 126 #======================================================================
127 127 # GET ACTION PULL or PUSH
128 128 #======================================================================
129 129 action = self.__get_action(environ)
130 130
131 131 #======================================================================
132 132 # CHECK ANONYMOUS PERMISSION
133 133 #======================================================================
134 134 if action in ['pull', 'push']:
135 135 anonymous_user = self.__get_user('default')
136 136 username = anonymous_user.username
137 137 anonymous_perm = self._check_permission(action, anonymous_user,
138 138 repo_name)
139 139
140 140 if anonymous_perm is not True or anonymous_user.active is False:
141 141 if anonymous_perm is not True:
142 142 log.debug('Not enough credentials to access this '
143 143 'repository as anonymous user')
144 144 if anonymous_user.active is False:
145 145 log.debug('Anonymous access is disabled, running '
146 146 'authentication')
147 147 #==============================================================
148 148 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
149 149 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
150 150 #==============================================================
151 151
152 152 # Attempting to retrieve username from the container
153 153 username = get_container_username(environ, self.config)
154 154
155 155 # If not authenticated by the container, running basic auth
156 156 if not username:
157 157 self.authenticate.realm = \
158 158 safe_str(self.config['rhodecode_realm'])
159 159 result = self.authenticate(environ)
160 160 if isinstance(result, str):
161 161 AUTH_TYPE.update(environ, 'basic')
162 162 REMOTE_USER.update(environ, result)
163 163 username = result
164 164 else:
165 165 return result.wsgi_application(environ, start_response)
166 166
167 167 #==============================================================
168 168 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
169 169 #==============================================================
170 if action in ['pull', 'push']:
171 try:
172 user = self.__get_user(username)
173 if user is None or not user.active:
174 return HTTPForbidden()(environ, start_response)
175 username = user.username
176 except:
177 log.error(traceback.format_exc())
178 return HTTPInternalServerError()(environ,
179 start_response)
170 try:
171 user = self.__get_user(username)
172 if user is None or not user.active:
173 return HTTPForbidden()(environ, start_response)
174 username = user.username
175 except:
176 log.error(traceback.format_exc())
177 return HTTPInternalServerError()(environ, start_response)
180 178
181 #check permissions for this repository
182 perm = self._check_permission(action, user, repo_name)
183 if perm is not True:
184 return HTTPForbidden()(environ, start_response)
179 #check permissions for this repository
180 perm = self._check_permission(action, user, repo_name)
181 if perm is not True:
182 return HTTPForbidden()(environ, start_response)
183
185 184 extras = {
186 185 'ip': ipaddr,
187 186 'username': username,
188 187 'action': action,
189 188 'repository': repo_name,
190 189 'scm': 'git',
191 190 }
192 191
193 192 #===================================================================
194 193 # GIT REQUEST HANDLING
195 194 #===================================================================
196 195 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
197 196 log.debug('Repository path is %s' % repo_path)
198 197
199 198 baseui = make_ui('db')
200 199 self.__inject_extras(repo_path, baseui, extras)
201 200
202 201 try:
203 202 # invalidate cache on push
204 203 if action == 'push':
205 204 self._invalidate_cache(repo_name)
206 205 self._handle_githooks(repo_name, action, baseui, environ)
207 206
208 207 log.info('%s action on GIT repo "%s"' % (action, repo_name))
209 208 app = self.__make_app(repo_name, repo_path, username)
210 209 return app(environ, start_response)
211 210 except Exception:
212 211 log.error(traceback.format_exc())
213 212 return HTTPInternalServerError()(environ, start_response)
214 213
215 214 def __make_app(self, repo_name, repo_path, username):
216 215 """
217 216 Make an wsgi application using dulserver
218 217
219 218 :param repo_name: name of the repository
220 219 :param repo_path: full path to the repository
221 220 """
222 221
223 222 from rhodecode.lib.middleware.pygrack import make_wsgi_app
224 223 app = make_wsgi_app(
225 224 repo_root=safe_str(self.basepath),
226 225 repo_name=repo_name,
227 226 username=username,
228 227 )
229 228 return app
230 229
231 230 def __get_repository(self, environ):
232 231 """
233 232 Get's repository name out of PATH_INFO header
234 233
235 234 :param environ: environ where PATH_INFO is stored
236 235 """
237 236 try:
238 237 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
239 238 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
240 239 except:
241 240 log.error(traceback.format_exc())
242 241 raise
243 242
244 243 return repo_name
245 244
246 245 def __get_user(self, username):
247 246 return User.get_by_username(username)
248 247
249 248 def __get_action(self, environ):
250 249 """
251 250 Maps git request commands into a pull or push command.
252 251
253 252 :param environ:
254 253 """
255 254 service = environ['QUERY_STRING'].split('=')
256 255
257 256 if len(service) > 1:
258 257 service_cmd = service[1]
259 258 mapping = {
260 259 'git-receive-pack': 'push',
261 260 'git-upload-pack': 'pull',
262 261 }
263 262 op = mapping[service_cmd]
264 263 self._git_stored_op = op
265 264 return op
266 265 else:
267 266 # try to fallback to stored variable as we don't know if the last
268 267 # operation is pull/push
269 268 op = getattr(self, '_git_stored_op', 'pull')
270 269 return op
271 270
272 271 def _handle_githooks(self, repo_name, action, baseui, environ):
273 272 """
274 273 Handles pull action, push is handled by post-receive hook
275 274 """
276 275 from rhodecode.lib.hooks import log_pull_action
277 276 service = environ['QUERY_STRING'].split('=')
278 277 if len(service) < 2:
279 278 return
280 279
281 280 from rhodecode.model.db import Repository
282 281 _repo = Repository.get_by_repo_name(repo_name)
283 282 _repo = _repo.scm_instance
284 283 _repo._repo.ui = baseui
285 284
286 285 _hooks = dict(baseui.configitems('hooks')) or {}
287 286 if action == 'pull' and _hooks.get(RhodeCodeUi.HOOK_PULL):
288 287 log_pull_action(ui=baseui, repo=_repo._repo)
289 288
290 289 def __inject_extras(self, repo_path, baseui, extras={}):
291 290 """
292 291 Injects some extra params into baseui instance
293 292
294 293 :param baseui: baseui instance
295 294 :param extras: dict with extra params to put into baseui
296 295 """
297 296
298 297 # make our hgweb quiet so it doesn't print output
299 298 baseui.setconfig('ui', 'quiet', 'true')
300 299
301 300 #inject some additional parameters that will be available in ui
302 301 #for hooks
303 302 for k, v in extras.items():
304 303 baseui.setconfig('rhodecode_extras', k, v)
@@ -1,257 +1,255 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.middleware.simplehg
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 SimpleHG middleware for handling mercurial protocol request
7 7 (push/clone etc.). It's implemented with basic auth function
8 8
9 9 :created_on: Apr 28, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import logging
29 29 import traceback
30 30 import urllib
31 31
32 32 from mercurial.error import RepoError
33 33 from mercurial.hgweb import hgweb_mod
34 34
35 35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
36 36
37 37 from rhodecode.lib.utils2 import safe_str
38 38 from rhodecode.lib.base import BaseVCSController
39 39 from rhodecode.lib.auth import get_container_username
40 40 from rhodecode.lib.utils import make_ui, is_valid_repo, ui_sections
41 41 from rhodecode.model.db import User
42 42
43 43 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 def is_mercurial(environ):
49 49 """
50 50 Returns True if request's target is mercurial server - header
51 51 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
52 52 """
53 53 http_accept = environ.get('HTTP_ACCEPT')
54 54 path_info = environ['PATH_INFO']
55 55 if http_accept and http_accept.startswith('application/mercurial'):
56 56 ishg_path = True
57 57 else:
58 58 ishg_path = False
59 59
60 60 log.debug('pathinfo: %s detected as HG %s' % (
61 61 path_info, ishg_path)
62 62 )
63 63 return ishg_path
64 64
65 65
66 66 class SimpleHg(BaseVCSController):
67 67
68 68 def _handle_request(self, environ, start_response):
69 69 if not is_mercurial(environ):
70 70 return self.application(environ, start_response)
71 71
72 72 ipaddr = self._get_ip_addr(environ)
73
73 username = None
74 74 # skip passing error to error controller
75 75 environ['pylons.status_code_redirect'] = True
76 76
77 77 #======================================================================
78 78 # EXTRACT REPOSITORY NAME FROM ENV
79 79 #======================================================================
80 80 try:
81 81 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
82 82 log.debug('Extracted repo name is %s' % repo_name)
83 83 except:
84 84 return HTTPInternalServerError()(environ, start_response)
85 85
86 86 # quick check if that dir exists...
87 87 if is_valid_repo(repo_name, self.basepath) is False:
88 88 return HTTPNotFound()(environ, start_response)
89 89
90 90 #======================================================================
91 91 # GET ACTION PULL or PUSH
92 92 #======================================================================
93 93 action = self.__get_action(environ)
94 94
95 95 #======================================================================
96 96 # CHECK ANONYMOUS PERMISSION
97 97 #======================================================================
98 98 if action in ['pull', 'push']:
99 99 anonymous_user = self.__get_user('default')
100 100 username = anonymous_user.username
101 101 anonymous_perm = self._check_permission(action, anonymous_user,
102 102 repo_name)
103 103
104 104 if anonymous_perm is not True or anonymous_user.active is False:
105 105 if anonymous_perm is not True:
106 106 log.debug('Not enough credentials to access this '
107 107 'repository as anonymous user')
108 108 if anonymous_user.active is False:
109 109 log.debug('Anonymous access is disabled, running '
110 110 'authentication')
111 111 #==============================================================
112 112 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
113 113 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
114 114 #==============================================================
115 115
116 116 # Attempting to retrieve username from the container
117 117 username = get_container_username(environ, self.config)
118 118
119 119 # If not authenticated by the container, running basic auth
120 120 if not username:
121 121 self.authenticate.realm = \
122 122 safe_str(self.config['rhodecode_realm'])
123 123 result = self.authenticate(environ)
124 124 if isinstance(result, str):
125 125 AUTH_TYPE.update(environ, 'basic')
126 126 REMOTE_USER.update(environ, result)
127 127 username = result
128 128 else:
129 129 return result.wsgi_application(environ, start_response)
130 130
131 131 #==============================================================
132 132 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
133 133 #==============================================================
134 if action in ['pull', 'push']:
135 try:
136 user = self.__get_user(username)
137 if user is None or not user.active:
138 return HTTPForbidden()(environ, start_response)
139 username = user.username
140 except:
141 log.error(traceback.format_exc())
142 return HTTPInternalServerError()(environ,
143 start_response)
134 try:
135 user = self.__get_user(username)
136 if user is None or not user.active:
137 return HTTPForbidden()(environ, start_response)
138 username = user.username
139 except:
140 log.error(traceback.format_exc())
141 return HTTPInternalServerError()(environ, start_response)
144 142
145 #check permissions for this repository
146 perm = self._check_permission(action, user, repo_name)
147 if perm is not True:
148 return HTTPForbidden()(environ, start_response)
143 #check permissions for this repository
144 perm = self._check_permission(action, user, repo_name)
145 if perm is not True:
146 return HTTPForbidden()(environ, start_response)
149 147
150 148 # extras are injected into mercurial UI object and later available
151 149 # in hg hooks executed by rhodecode
152 150 extras = {
153 151 'ip': ipaddr,
154 152 'username': username,
155 153 'action': action,
156 154 'repository': repo_name,
157 155 'scm': 'hg',
158 156 }
159 157
160 158 #======================================================================
161 159 # MERCURIAL REQUEST HANDLING
162 160 #======================================================================
163 161 repo_path = os.path.join(safe_str(self.basepath), safe_str(repo_name))
164 162 log.debug('Repository path is %s' % repo_path)
165 163
166 164 baseui = make_ui('db')
167 165 self.__inject_extras(repo_path, baseui, extras)
168 166
169 167 try:
170 168 # invalidate cache on push
171 169 if action == 'push':
172 170 self._invalidate_cache(repo_name)
173 171 log.info('%s action on HG repo "%s"' % (action, repo_name))
174 172 app = self.__make_app(repo_path, baseui, extras)
175 173 return app(environ, start_response)
176 174 except RepoError, e:
177 175 if str(e).find('not found') != -1:
178 176 return HTTPNotFound()(environ, start_response)
179 177 except Exception:
180 178 log.error(traceback.format_exc())
181 179 return HTTPInternalServerError()(environ, start_response)
182 180
183 181 def __make_app(self, repo_name, baseui, extras):
184 182 """
185 183 Make an wsgi application using hgweb, and inject generated baseui
186 184 instance, additionally inject some extras into ui object
187 185 """
188 186 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
189 187
190 188 def __get_repository(self, environ):
191 189 """
192 190 Get's repository name out of PATH_INFO header
193 191
194 192 :param environ: environ where PATH_INFO is stored
195 193 """
196 194 try:
197 195 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
198 196 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
199 197 if repo_name.endswith('/'):
200 198 repo_name = repo_name.rstrip('/')
201 199 except:
202 200 log.error(traceback.format_exc())
203 201 raise
204 202
205 203 return repo_name
206 204
207 205 def __get_user(self, username):
208 206 return User.get_by_username(username)
209 207
210 208 def __get_action(self, environ):
211 209 """
212 210 Maps mercurial request commands into a clone,pull or push command.
213 211 This should always return a valid command string
214 212
215 213 :param environ:
216 214 """
217 215 mapping = {'changegroup': 'pull',
218 216 'changegroupsubset': 'pull',
219 217 'stream_out': 'pull',
220 218 'listkeys': 'pull',
221 219 'unbundle': 'push',
222 220 'pushkey': 'push', }
223 221 for qry in environ['QUERY_STRING'].split('&'):
224 222 if qry.startswith('cmd'):
225 223 cmd = qry.split('=')[-1]
226 224 if cmd in mapping:
227 225 return mapping[cmd]
228 226 else:
229 227 return 'pull'
230 228
231 229 def __inject_extras(self, repo_path, baseui, extras={}):
232 230 """
233 231 Injects some extra params into baseui instance
234 232
235 233 also overwrites global settings with those takes from local hgrc file
236 234
237 235 :param baseui: baseui instance
238 236 :param extras: dict with extra params to put into baseui
239 237 """
240 238
241 239 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
242 240
243 241 # make our hgweb quiet so it doesn't print output
244 242 baseui.setconfig('ui', 'quiet', 'true')
245 243
246 244 #inject some additional parameters that will be available in ui
247 245 #for hooks
248 246 for k, v in extras.items():
249 247 baseui.setconfig('rhodecode_extras', k, v)
250 248
251 249 repoui = make_ui('file', hgrc, False)
252 250
253 251 if repoui:
254 252 #overwrite our ui instance with the section from hgrc file
255 253 for section in ui_sections:
256 254 for k, v in repoui.configitems(section):
257 255 baseui.setconfig(section, k, v)
General Comments 0
You need to be logged in to leave comments. Login now