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