##// END OF EJS Templates
Removed deprecated usage of UserModel() in simplehg and simplegit
marcink -
r1497:71738535 beta
parent child Browse files
Show More
@@ -1,289 +1,289
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) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2010 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
30
31 from dulwich import server as dulserver
31 from dulwich import server as dulserver
32
32
33
33
34 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
34 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
35
35
36 def handle(self):
36 def handle(self):
37 write = lambda x: self.proto.write_sideband(1, x)
37 write = lambda x: self.proto.write_sideband(1, x)
38
38
39 graph_walker = dulserver.ProtocolGraphWalker(self,
39 graph_walker = dulserver.ProtocolGraphWalker(self,
40 self.repo.object_store,
40 self.repo.object_store,
41 self.repo.get_peeled)
41 self.repo.get_peeled)
42 objects_iter = self.repo.fetch_objects(
42 objects_iter = self.repo.fetch_objects(
43 graph_walker.determine_wants, graph_walker, self.progress,
43 graph_walker.determine_wants, graph_walker, self.progress,
44 get_tagged=self.get_tagged)
44 get_tagged=self.get_tagged)
45
45
46 # Do they want any objects?
46 # Do they want any objects?
47 if objects_iter is None or len(objects_iter) == 0:
47 if objects_iter is None or len(objects_iter) == 0:
48 return
48 return
49
49
50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
51 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
51 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
52 objects_iter, len(objects_iter))
52 objects_iter, len(objects_iter))
53 messages = []
53 messages = []
54 messages.append('thank you for using rhodecode')
54 messages.append('thank you for using rhodecode')
55
55
56 for msg in messages:
56 for msg in messages:
57 self.progress(msg + "\n")
57 self.progress(msg + "\n")
58 # we are done
58 # we are done
59 self.proto.write("0000")
59 self.proto.write("0000")
60
60
61 dulserver.DEFAULT_HANDLERS = {
61 dulserver.DEFAULT_HANDLERS = {
62 'git-upload-pack': SimpleGitUploadPackHandler,
62 'git-upload-pack': SimpleGitUploadPackHandler,
63 'git-receive-pack': dulserver.ReceivePackHandler,
63 'git-receive-pack': dulserver.ReceivePackHandler,
64 }
64 }
65
65
66 from dulwich.repo import Repo
66 from dulwich.repo import Repo
67 from dulwich.web import HTTPGitApplication
67 from dulwich.web import HTTPGitApplication
68
68
69 from paste.auth.basic import AuthBasicAuthenticator
69 from paste.auth.basic import AuthBasicAuthenticator
70 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
70 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
71
71
72 from rhodecode.lib import safe_str
72 from rhodecode.lib import safe_str
73 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
73 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
74 from rhodecode.lib.utils import invalidate_cache, check_repo_fast
74 from rhodecode.lib.utils import invalidate_cache, check_repo_fast
75 from rhodecode.model.user import UserModel
75 from rhodecode.model.db import User
76
76
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
78
78
79 log = logging.getLogger(__name__)
79 log = logging.getLogger(__name__)
80
80
81
81
82 def is_git(environ):
82 def is_git(environ):
83 """Returns True if request's target is git server.
83 """Returns True if request's target is git server.
84 ``HTTP_USER_AGENT`` would then have git client version given.
84 ``HTTP_USER_AGENT`` would then have git client version given.
85
85
86 :param environ:
86 :param environ:
87 """
87 """
88 http_user_agent = environ.get('HTTP_USER_AGENT')
88 http_user_agent = environ.get('HTTP_USER_AGENT')
89 if http_user_agent and http_user_agent.startswith('git'):
89 if http_user_agent and http_user_agent.startswith('git'):
90 return True
90 return True
91 return False
91 return False
92
92
93
93
94 class SimpleGit(object):
94 class SimpleGit(object):
95
95
96 def __init__(self, application, config):
96 def __init__(self, application, config):
97 self.application = application
97 self.application = application
98 self.config = config
98 self.config = config
99 # base path of repo locations
99 # base path of repo locations
100 self.basepath = self.config['base_path']
100 self.basepath = self.config['base_path']
101 #authenticate this mercurial request using authfunc
101 #authenticate this mercurial request using authfunc
102 self.authenticate = AuthBasicAuthenticator('', authfunc)
102 self.authenticate = AuthBasicAuthenticator('', authfunc)
103
103
104 def __call__(self, environ, start_response):
104 def __call__(self, environ, start_response):
105 if not is_git(environ):
105 if not is_git(environ):
106 return self.application(environ, start_response)
106 return self.application(environ, start_response)
107
107
108 proxy_key = 'HTTP_X_REAL_IP'
108 proxy_key = 'HTTP_X_REAL_IP'
109 def_key = 'REMOTE_ADDR'
109 def_key = 'REMOTE_ADDR'
110 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
110 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
111 username = None
111 username = None
112 # skip passing error to error controller
112 # skip passing error to error controller
113 environ['pylons.status_code_redirect'] = True
113 environ['pylons.status_code_redirect'] = True
114
114
115 #======================================================================
115 #======================================================================
116 # EXTRACT REPOSITORY NAME FROM ENV
116 # EXTRACT REPOSITORY NAME FROM ENV
117 #======================================================================
117 #======================================================================
118 try:
118 try:
119 repo_name = self.__get_repository(environ)
119 repo_name = self.__get_repository(environ)
120 log.debug('Extracted repo name is %s' % repo_name)
120 log.debug('Extracted repo name is %s' % repo_name)
121 except:
121 except:
122 return HTTPInternalServerError()(environ, start_response)
122 return HTTPInternalServerError()(environ, start_response)
123
123
124 #======================================================================
124 #======================================================================
125 # GET ACTION PULL or PUSH
125 # GET ACTION PULL or PUSH
126 #======================================================================
126 #======================================================================
127 action = self.__get_action(environ)
127 action = self.__get_action(environ)
128
128
129 #======================================================================
129 #======================================================================
130 # CHECK ANONYMOUS PERMISSION
130 # CHECK ANONYMOUS PERMISSION
131 #======================================================================
131 #======================================================================
132 if action in ['pull', 'push']:
132 if action in ['pull', 'push']:
133 anonymous_user = self.__get_user('default')
133 anonymous_user = self.__get_user('default')
134 username = anonymous_user.username
134 username = anonymous_user.username
135 anonymous_perm = self.__check_permission(action,
135 anonymous_perm = self.__check_permission(action,
136 anonymous_user,
136 anonymous_user,
137 repo_name)
137 repo_name)
138
138
139 if anonymous_perm is not True or anonymous_user.active is False:
139 if anonymous_perm is not True or anonymous_user.active is False:
140 if anonymous_perm is not True:
140 if anonymous_perm is not True:
141 log.debug('Not enough credentials to access this '
141 log.debug('Not enough credentials to access this '
142 'repository as anonymous user')
142 'repository as anonymous user')
143 if anonymous_user.active is False:
143 if anonymous_user.active is False:
144 log.debug('Anonymous access is disabled, running '
144 log.debug('Anonymous access is disabled, running '
145 'authentication')
145 'authentication')
146 #==============================================================
146 #==============================================================
147 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
147 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
148 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
148 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
149 #==============================================================
149 #==============================================================
150
150
151 if not REMOTE_USER(environ):
151 if not REMOTE_USER(environ):
152 self.authenticate.realm = \
152 self.authenticate.realm = \
153 safe_str(self.config['rhodecode_realm'])
153 safe_str(self.config['rhodecode_realm'])
154 result = self.authenticate(environ)
154 result = self.authenticate(environ)
155 if isinstance(result, str):
155 if isinstance(result, str):
156 AUTH_TYPE.update(environ, 'basic')
156 AUTH_TYPE.update(environ, 'basic')
157 REMOTE_USER.update(environ, result)
157 REMOTE_USER.update(environ, result)
158 else:
158 else:
159 return result.wsgi_application(environ, start_response)
159 return result.wsgi_application(environ, start_response)
160
160
161 #==============================================================
161 #==============================================================
162 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
162 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
163 # BASIC AUTH
163 # BASIC AUTH
164 #==============================================================
164 #==============================================================
165
165
166 if action in ['pull', 'push']:
166 if action in ['pull', 'push']:
167 username = REMOTE_USER(environ)
167 username = REMOTE_USER(environ)
168 try:
168 try:
169 user = self.__get_user(username)
169 user = self.__get_user(username)
170 username = user.username
170 username = user.username
171 except:
171 except:
172 log.error(traceback.format_exc())
172 log.error(traceback.format_exc())
173 return HTTPInternalServerError()(environ,
173 return HTTPInternalServerError()(environ,
174 start_response)
174 start_response)
175
175
176 #check permissions for this repository
176 #check permissions for this repository
177 perm = self.__check_permission(action, user,
177 perm = self.__check_permission(action, user,
178 repo_name)
178 repo_name)
179 if perm is not True:
179 if perm is not True:
180 return HTTPForbidden()(environ, start_response)
180 return HTTPForbidden()(environ, start_response)
181
181
182 extras = {'ip': ipaddr,
182 extras = {'ip': ipaddr,
183 'username': username,
183 'username': username,
184 'action': action,
184 'action': action,
185 'repository': repo_name}
185 'repository': repo_name}
186
186
187 #===================================================================
187 #===================================================================
188 # GIT REQUEST HANDLING
188 # GIT REQUEST HANDLING
189 #===================================================================
189 #===================================================================
190
190
191 repo_path = safe_str(os.path.join(self.basepath, repo_name))
191 repo_path = safe_str(os.path.join(self.basepath, repo_name))
192 log.debug('Repository path is %s' % repo_path)
192 log.debug('Repository path is %s' % repo_path)
193
193
194 # quick check if that dir exists...
194 # quick check if that dir exists...
195 if check_repo_fast(repo_name, self.basepath):
195 if check_repo_fast(repo_name, self.basepath):
196 return HTTPNotFound()(environ, start_response)
196 return HTTPNotFound()(environ, start_response)
197
197
198 try:
198 try:
199 #invalidate cache on push
199 #invalidate cache on push
200 if action == 'push':
200 if action == 'push':
201 self.__invalidate_cache(repo_name)
201 self.__invalidate_cache(repo_name)
202
202
203 app = self.__make_app(repo_name, repo_path)
203 app = self.__make_app(repo_name, repo_path)
204 return app(environ, start_response)
204 return app(environ, start_response)
205 except Exception:
205 except Exception:
206 log.error(traceback.format_exc())
206 log.error(traceback.format_exc())
207 return HTTPInternalServerError()(environ, start_response)
207 return HTTPInternalServerError()(environ, start_response)
208
208
209 def __make_app(self, repo_name, repo_path):
209 def __make_app(self, repo_name, repo_path):
210 """
210 """
211 Make an wsgi application using dulserver
211 Make an wsgi application using dulserver
212
212
213 :param repo_name: name of the repository
213 :param repo_name: name of the repository
214 :param repo_path: full path to the repository
214 :param repo_path: full path to the repository
215 """
215 """
216
216
217 _d = {'/' + repo_name: Repo(repo_path)}
217 _d = {'/' + repo_name: Repo(repo_path)}
218 backend = dulserver.DictBackend(_d)
218 backend = dulserver.DictBackend(_d)
219 gitserve = HTTPGitApplication(backend)
219 gitserve = HTTPGitApplication(backend)
220
220
221 return gitserve
221 return gitserve
222
222
223 def __check_permission(self, action, user, repo_name):
223 def __check_permission(self, action, user, repo_name):
224 """
224 """
225 Checks permissions using action (push/pull) user and repository
225 Checks permissions using action (push/pull) user and repository
226 name
226 name
227
227
228 :param action: push or pull action
228 :param action: push or pull action
229 :param user: user instance
229 :param user: user instance
230 :param repo_name: repository name
230 :param repo_name: repository name
231 """
231 """
232 if action == 'push':
232 if action == 'push':
233 if not HasPermissionAnyMiddleware('repository.write',
233 if not HasPermissionAnyMiddleware('repository.write',
234 'repository.admin')(user,
234 'repository.admin')(user,
235 repo_name):
235 repo_name):
236 return False
236 return False
237
237
238 else:
238 else:
239 #any other action need at least read permission
239 #any other action need at least read permission
240 if not HasPermissionAnyMiddleware('repository.read',
240 if not HasPermissionAnyMiddleware('repository.read',
241 'repository.write',
241 'repository.write',
242 'repository.admin')(user,
242 'repository.admin')(user,
243 repo_name):
243 repo_name):
244 return False
244 return False
245
245
246 return True
246 return True
247
247
248 def __get_repository(self, environ):
248 def __get_repository(self, environ):
249 """
249 """
250 Get's repository name out of PATH_INFO header
250 Get's repository name out of PATH_INFO header
251
251
252 :param environ: environ where PATH_INFO is stored
252 :param environ: environ where PATH_INFO is stored
253 """
253 """
254 try:
254 try:
255 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
255 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
256 if repo_name.endswith('/'):
256 if repo_name.endswith('/'):
257 repo_name = repo_name.rstrip('/')
257 repo_name = repo_name.rstrip('/')
258 except:
258 except:
259 log.error(traceback.format_exc())
259 log.error(traceback.format_exc())
260 raise
260 raise
261 repo_name = repo_name.split('/')[0]
261 repo_name = repo_name.split('/')[0]
262 return repo_name
262 return repo_name
263
263
264 def __get_user(self, username):
264 def __get_user(self, username):
265 return UserModel().get_by_username(username, cache=True)
265 return User.by_username(username)
266
266
267 def __get_action(self, environ):
267 def __get_action(self, environ):
268 """Maps git request commands into a pull or push command.
268 """Maps git request commands into a pull or push command.
269
269
270 :param environ:
270 :param environ:
271 """
271 """
272 service = environ['QUERY_STRING'].split('=')
272 service = environ['QUERY_STRING'].split('=')
273 if len(service) > 1:
273 if len(service) > 1:
274 service_cmd = service[1]
274 service_cmd = service[1]
275 mapping = {'git-receive-pack': 'push',
275 mapping = {'git-receive-pack': 'push',
276 'git-upload-pack': 'pull',
276 'git-upload-pack': 'pull',
277 }
277 }
278
278
279 return mapping.get(service_cmd,
279 return mapping.get(service_cmd,
280 service_cmd if service_cmd else 'other')
280 service_cmd if service_cmd else 'other')
281 else:
281 else:
282 return 'other'
282 return 'other'
283
283
284 def __invalidate_cache(self, repo_name):
284 def __invalidate_cache(self, repo_name):
285 """we know that some change was made to repositories and we should
285 """we know that some change was made to repositories and we should
286 invalidate the cache to see the changes right away but only for
286 invalidate the cache to see the changes right away but only for
287 push requests"""
287 push requests"""
288 invalidate_cache('get_repo_cached_%s' % repo_name)
288 invalidate_cache('get_repo_cached_%s' % repo_name)
289
289
@@ -1,287 +1,287
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) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2010 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
30
31 from mercurial.error import RepoError
31 from mercurial.error import RepoError
32 from mercurial.hgweb import hgweb_mod
32 from mercurial.hgweb import hgweb_mod
33
33
34 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.auth.basic import AuthBasicAuthenticator
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
36
36
37 from rhodecode.lib import safe_str
37 from rhodecode.lib import safe_str
38 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
38 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
39 from rhodecode.lib.utils import make_ui, invalidate_cache, \
39 from rhodecode.lib.utils import make_ui, invalidate_cache, \
40 check_repo_fast, ui_sections
40 check_repo_fast, ui_sections
41 from rhodecode.model.user import UserModel
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 """Returns True if request's target is mercurial server - header
49 """Returns True if request's target is mercurial server - header
50 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
50 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
51 """
51 """
52 http_accept = environ.get('HTTP_ACCEPT')
52 http_accept = environ.get('HTTP_ACCEPT')
53 if http_accept and http_accept.startswith('application/mercurial'):
53 if http_accept and http_accept.startswith('application/mercurial'):
54 return True
54 return True
55 return False
55 return False
56
56
57
57
58 class SimpleHg(object):
58 class SimpleHg(object):
59
59
60 def __init__(self, application, config):
60 def __init__(self, application, config):
61 self.application = application
61 self.application = application
62 self.config = config
62 self.config = config
63 # base path of repo locations
63 # base path of repo locations
64 self.basepath = self.config['base_path']
64 self.basepath = self.config['base_path']
65 #authenticate this mercurial request using authfunc
65 #authenticate this mercurial request using authfunc
66 self.authenticate = AuthBasicAuthenticator('', authfunc)
66 self.authenticate = AuthBasicAuthenticator('', authfunc)
67 self.ipaddr = '0.0.0.0'
67 self.ipaddr = '0.0.0.0'
68
68
69 def __call__(self, environ, start_response):
69 def __call__(self, environ, start_response):
70 if not is_mercurial(environ):
70 if not is_mercurial(environ):
71 return self.application(environ, start_response)
71 return self.application(environ, start_response)
72
72
73 proxy_key = 'HTTP_X_REAL_IP'
73 proxy_key = 'HTTP_X_REAL_IP'
74 def_key = 'REMOTE_ADDR'
74 def_key = 'REMOTE_ADDR'
75 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
75 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
76
76
77 # skip passing error to error controller
77 # skip passing error to error controller
78 environ['pylons.status_code_redirect'] = True
78 environ['pylons.status_code_redirect'] = True
79
79
80 #======================================================================
80 #======================================================================
81 # EXTRACT REPOSITORY NAME FROM ENV
81 # EXTRACT REPOSITORY NAME FROM ENV
82 #======================================================================
82 #======================================================================
83 try:
83 try:
84 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
84 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
85 log.debug('Extracted repo name is %s' % repo_name)
85 log.debug('Extracted repo name is %s' % repo_name)
86 except:
86 except:
87 return HTTPInternalServerError()(environ, start_response)
87 return HTTPInternalServerError()(environ, start_response)
88
88
89 #======================================================================
89 #======================================================================
90 # GET ACTION PULL or PUSH
90 # GET ACTION PULL or PUSH
91 #======================================================================
91 #======================================================================
92 action = self.__get_action(environ)
92 action = self.__get_action(environ)
93
93
94 #======================================================================
94 #======================================================================
95 # CHECK ANONYMOUS PERMISSION
95 # CHECK ANONYMOUS PERMISSION
96 #======================================================================
96 #======================================================================
97 if action in ['pull', 'push']:
97 if action in ['pull', 'push']:
98 anonymous_user = self.__get_user('default')
98 anonymous_user = self.__get_user('default')
99 username = anonymous_user.username
99 username = anonymous_user.username
100 anonymous_perm = self.__check_permission(action,
100 anonymous_perm = self.__check_permission(action,
101 anonymous_user,
101 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 if not REMOTE_USER(environ):
116 if not REMOTE_USER(environ):
117 self.authenticate.realm = \
117 self.authenticate.realm = \
118 safe_str(self.config['rhodecode_realm'])
118 safe_str(self.config['rhodecode_realm'])
119 result = self.authenticate(environ)
119 result = self.authenticate(environ)
120 if isinstance(result, str):
120 if isinstance(result, str):
121 AUTH_TYPE.update(environ, 'basic')
121 AUTH_TYPE.update(environ, 'basic')
122 REMOTE_USER.update(environ, result)
122 REMOTE_USER.update(environ, result)
123 else:
123 else:
124 return result.wsgi_application(environ, start_response)
124 return result.wsgi_application(environ, start_response)
125
125
126 #==============================================================
126 #==============================================================
127 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
127 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
128 # BASIC AUTH
128 # BASIC AUTH
129 #==============================================================
129 #==============================================================
130
130
131 if action in ['pull', 'push']:
131 if action in ['pull', 'push']:
132 username = REMOTE_USER(environ)
132 username = REMOTE_USER(environ)
133 try:
133 try:
134 user = self.__get_user(username)
134 user = self.__get_user(username)
135 username = user.username
135 username = user.username
136 except:
136 except:
137 log.error(traceback.format_exc())
137 log.error(traceback.format_exc())
138 return HTTPInternalServerError()(environ,
138 return HTTPInternalServerError()(environ,
139 start_response)
139 start_response)
140
140
141 #check permissions for this repository
141 #check permissions for this repository
142 perm = self.__check_permission(action, user,
142 perm = self.__check_permission(action, user,
143 repo_name)
143 repo_name)
144 if perm is not True:
144 if perm is not True:
145 return HTTPForbidden()(environ, start_response)
145 return HTTPForbidden()(environ, start_response)
146
146
147 extras = {'ip': ipaddr,
147 extras = {'ip': ipaddr,
148 'username': username,
148 'username': username,
149 'action': action,
149 'action': action,
150 'repository': repo_name}
150 'repository': repo_name}
151
151
152 #======================================================================
152 #======================================================================
153 # MERCURIAL REQUEST HANDLING
153 # MERCURIAL REQUEST HANDLING
154 #======================================================================
154 #======================================================================
155
155
156 repo_path = safe_str(os.path.join(self.basepath, repo_name))
156 repo_path = safe_str(os.path.join(self.basepath, repo_name))
157 log.debug('Repository path is %s' % repo_path)
157 log.debug('Repository path is %s' % repo_path)
158
158
159 baseui = make_ui('db')
159 baseui = make_ui('db')
160 self.__inject_extras(repo_path, baseui, extras)
160 self.__inject_extras(repo_path, baseui, extras)
161
161
162
162
163 # quick check if that dir exists...
163 # quick check if that dir exists...
164 if check_repo_fast(repo_name, self.basepath):
164 if check_repo_fast(repo_name, self.basepath):
165 return HTTPNotFound()(environ, start_response)
165 return HTTPNotFound()(environ, start_response)
166
166
167 try:
167 try:
168 #invalidate cache on push
168 #invalidate cache on push
169 if action == 'push':
169 if action == 'push':
170 self.__invalidate_cache(repo_name)
170 self.__invalidate_cache(repo_name)
171
171
172 app = self.__make_app(repo_path, baseui, extras)
172 app = self.__make_app(repo_path, baseui, extras)
173 return app(environ, start_response)
173 return app(environ, start_response)
174 except RepoError, e:
174 except RepoError, e:
175 if str(e).find('not found') != -1:
175 if str(e).find('not found') != -1:
176 return HTTPNotFound()(environ, start_response)
176 return HTTPNotFound()(environ, start_response)
177 except Exception:
177 except Exception:
178 log.error(traceback.format_exc())
178 log.error(traceback.format_exc())
179 return HTTPInternalServerError()(environ, start_response)
179 return HTTPInternalServerError()(environ, start_response)
180
180
181 def __make_app(self, repo_name, baseui, extras):
181 def __make_app(self, repo_name, baseui, extras):
182 """
182 """
183 Make an wsgi application using hgweb, and inject generated baseui
183 Make an wsgi application using hgweb, and inject generated baseui
184 instance, additionally inject some extras into ui object
184 instance, additionally inject some extras into ui object
185 """
185 """
186 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
186 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
187
187
188
188
189 def __check_permission(self, action, user, repo_name):
189 def __check_permission(self, action, user, repo_name):
190 """
190 """
191 Checks permissions using action (push/pull) user and repository
191 Checks permissions using action (push/pull) user and repository
192 name
192 name
193
193
194 :param action: push or pull action
194 :param action: push or pull action
195 :param user: user instance
195 :param user: user instance
196 :param repo_name: repository name
196 :param repo_name: repository name
197 """
197 """
198 if action == 'push':
198 if action == 'push':
199 if not HasPermissionAnyMiddleware('repository.write',
199 if not HasPermissionAnyMiddleware('repository.write',
200 'repository.admin')(user,
200 'repository.admin')(user,
201 repo_name):
201 repo_name):
202 return False
202 return False
203
203
204 else:
204 else:
205 #any other action need at least read permission
205 #any other action need at least read permission
206 if not HasPermissionAnyMiddleware('repository.read',
206 if not HasPermissionAnyMiddleware('repository.read',
207 'repository.write',
207 'repository.write',
208 'repository.admin')(user,
208 'repository.admin')(user,
209 repo_name):
209 repo_name):
210 return False
210 return False
211
211
212 return True
212 return True
213
213
214 def __get_repository(self, environ):
214 def __get_repository(self, environ):
215 """
215 """
216 Get's repository name out of PATH_INFO header
216 Get's repository name out of PATH_INFO header
217
217
218 :param environ: environ where PATH_INFO is stored
218 :param environ: environ where PATH_INFO is stored
219 """
219 """
220 try:
220 try:
221 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
221 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
222 if repo_name.endswith('/'):
222 if repo_name.endswith('/'):
223 repo_name = repo_name.rstrip('/')
223 repo_name = repo_name.rstrip('/')
224 except:
224 except:
225 log.error(traceback.format_exc())
225 log.error(traceback.format_exc())
226 raise
226 raise
227
227
228 return repo_name
228 return repo_name
229
229
230 def __get_user(self, username):
230 def __get_user(self, username):
231 return UserModel().get_by_username(username, cache=True)
231 return User.by_username(username)
232
232
233 def __get_action(self, environ):
233 def __get_action(self, environ):
234 """
234 """
235 Maps mercurial request commands into a clone,pull or push command.
235 Maps mercurial request commands into a clone,pull or push command.
236 This should always return a valid command string
236 This should always return a valid command string
237
237
238 :param environ:
238 :param environ:
239 """
239 """
240 mapping = {'changegroup': 'pull',
240 mapping = {'changegroup': 'pull',
241 'changegroupsubset': 'pull',
241 'changegroupsubset': 'pull',
242 'stream_out': 'pull',
242 'stream_out': 'pull',
243 'listkeys': 'pull',
243 'listkeys': 'pull',
244 'unbundle': 'push',
244 'unbundle': 'push',
245 'pushkey': 'push', }
245 'pushkey': 'push', }
246 for qry in environ['QUERY_STRING'].split('&'):
246 for qry in environ['QUERY_STRING'].split('&'):
247 if qry.startswith('cmd'):
247 if qry.startswith('cmd'):
248 cmd = qry.split('=')[-1]
248 cmd = qry.split('=')[-1]
249 if cmd in mapping:
249 if cmd in mapping:
250 return mapping[cmd]
250 return mapping[cmd]
251 else:
251 else:
252 return 'pull'
252 return 'pull'
253
253
254 def __invalidate_cache(self, repo_name):
254 def __invalidate_cache(self, repo_name):
255 """we know that some change was made to repositories and we should
255 """we know that some change was made to repositories and we should
256 invalidate the cache to see the changes right away but only for
256 invalidate the cache to see the changes right away but only for
257 push requests"""
257 push requests"""
258 invalidate_cache('get_repo_cached_%s' % repo_name)
258 invalidate_cache('get_repo_cached_%s' % repo_name)
259
259
260 def __inject_extras(self,repo_path, baseui, extras={}):
260 def __inject_extras(self,repo_path, baseui, extras={}):
261 """
261 """
262 Injects some extra params into baseui instance
262 Injects some extra params into baseui instance
263
263
264 also overwrites global settings with those takes from local hgrc file
264 also overwrites global settings with those takes from local hgrc file
265
265
266 :param baseui: baseui instance
266 :param baseui: baseui instance
267 :param extras: dict with extra params to put into baseui
267 :param extras: dict with extra params to put into baseui
268 """
268 """
269
269
270 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
270 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
271
271
272 # make our hgweb quiet so it doesn't print output
272 # make our hgweb quiet so it doesn't print output
273 baseui.setconfig('ui', 'quiet', 'true')
273 baseui.setconfig('ui', 'quiet', 'true')
274
274
275 #inject some additional parameters that will be available in ui
275 #inject some additional parameters that will be available in ui
276 #for hooks
276 #for hooks
277 for k, v in extras.items():
277 for k, v in extras.items():
278 baseui.setconfig('rhodecode_extras', k, v)
278 baseui.setconfig('rhodecode_extras', k, v)
279
279
280 repoui = make_ui('file', hgrc, False)
280 repoui = make_ui('file', hgrc, False)
281
281
282 if repoui:
282 if repoui:
283 #overwrite our ui instance with the section from hgrc file
283 #overwrite our ui instance with the section from hgrc file
284 for section in ui_sections:
284 for section in ui_sections:
285 for k, v in repoui.configitems(section):
285 for k, v in repoui.configitems(section):
286 baseui.setconfig(section, k, v)
286 baseui.setconfig(section, k, v)
287
287
General Comments 0
You need to be logged in to leave comments. Login now