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