comparison mercurial/hgweb/hgweb_mod.py @ 6926:57b954d8d003

hgweb: raise ErrorResponses to communicate protocol errors
author Dirkjan Ochtman <dirkjan@ochtman.nl>
date Tue, 22 Jul 2008 18:23:20 +0200
parents 95f35b553ae6
children 2cfdabe235fb
comparison
equal deleted inserted replaced
6925:87abfefafe02 6926:57b954d8d003
11 from mercurial.repo import RepoError 11 from mercurial.repo import RepoError
12 from mercurial import mdiff, ui, hg, util, patch, hook 12 from mercurial import mdiff, ui, hg, util, patch, hook
13 from mercurial import revlog, templater, templatefilters 13 from mercurial import revlog, templater, templatefilters
14 from common import get_mtime, style_map, paritygen, countgen, ErrorResponse 14 from common import get_mtime, style_map, paritygen, countgen, ErrorResponse
15 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR 15 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
16 from common import HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED
16 from request import wsgirequest 17 from request import wsgirequest
17 import webcommands, protocol, webutil 18 import webcommands, protocol, webutil
18 19
19 perms = { 20 perms = {
20 'changegroup': 'pull', 21 'changegroup': 'pull',
86 # protocol bits don't need to create any URLs 87 # protocol bits don't need to create any URLs
87 # and the clients always use the old URL structure 88 # and the clients always use the old URL structure
88 89
89 cmd = req.form.get('cmd', [''])[0] 90 cmd = req.form.get('cmd', [''])[0]
90 if cmd and cmd in protocol.__all__: 91 if cmd and cmd in protocol.__all__:
91 if cmd in perms and not self.check_perm(req, perms[cmd]): 92 try:
92 return [] 93 if cmd in perms:
93 method = getattr(protocol, cmd) 94 self.check_perm(req, perms[cmd])
94 return method(self.repo, req) 95 method = getattr(protocol, cmd)
96 return method(self.repo, req)
97 except ErrorResponse, inst:
98 req.respond(inst.code, protocol.HGTYPE)
99 if not inst.message:
100 return []
101 return '0\n%s\n' % inst.message,
95 102
96 # work with CGI variables to create coherent structure 103 # work with CGI variables to create coherent structure
97 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME 104 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
98 105
99 req.url = req.env['SCRIPT_NAME'] 106 req.url = req.env['SCRIPT_NAME']
342 349
343 def check_perm(self, req, op): 350 def check_perm(self, req, op):
344 '''Check permission for operation based on request data (including 351 '''Check permission for operation based on request data (including
345 authentication info. Return true if op allowed, else false.''' 352 authentication info. Return true if op allowed, else false.'''
346 353
347 def error(status, message): 354 if op == 'pull' and not self.allowpull:
348 req.respond(status, protocol.HGTYPE) 355 raise ErrorResponse(HTTP_OK, '')
349 req.write('0\n%s\n' % message) 356 elif op == 'pull':
350 357 return
351 if op == 'pull':
352 return self.allowpull
353 358
354 # enforce that you can only push using POST requests 359 # enforce that you can only push using POST requests
355 if req.env['REQUEST_METHOD'] != 'POST': 360 if req.env['REQUEST_METHOD'] != 'POST':
356 error('405 Method Not Allowed', 'push requires POST request') 361 msg = 'push requires POST request'
357 return False 362 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
358 363
359 # require ssl by default for pushing, auth info cannot be sniffed 364 # require ssl by default for pushing, auth info cannot be sniffed
360 # and replayed 365 # and replayed
361 scheme = req.env.get('wsgi.url_scheme') 366 scheme = req.env.get('wsgi.url_scheme')
362 if self.configbool('web', 'push_ssl', True) and scheme != 'https': 367 if self.configbool('web', 'push_ssl', True) and scheme != 'https':
363 error(HTTP_OK, 'ssl required') 368 raise ErrorResponse(HTTP_OK, 'ssl required')
364 return False
365 369
366 user = req.env.get('REMOTE_USER') 370 user = req.env.get('REMOTE_USER')
367 371
368 deny = self.configlist('web', 'deny_push') 372 deny = self.configlist('web', 'deny_push')
369 if deny and (not user or deny == ['*'] or user in deny): 373 if deny and (not user or deny == ['*'] or user in deny):
370 error('401 Unauthorized', 'push not authorized') 374 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
371 return False
372 375
373 allow = self.configlist('web', 'allow_push') 376 allow = self.configlist('web', 'allow_push')
374 result = allow and (allow == ['*'] or user in allow) 377 result = allow and (allow == ['*'] or user in allow)
375 if not result: 378 if not result:
376 error('401 Unauthorized', 'push not authorized') 379 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
377
378 return result