Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/hgweb/hgweb_mod.py @ 6779:d3147b4e3e8a
hgweb: centralize permission checks for protocol commands
Consistently enforces authorization checks set up in hgrc up front, so that
the actual commands don't have to worry about them and implementers of
hgweb alternatives can easily implement their own permission checks.
author | Dirkjan Ochtman <dirkjan@ochtman.nl> |
---|---|
date | Sun, 29 Jun 2008 11:35:06 +0200 |
parents | 44c5157474e7 |
children | 4c1d67e0fa8c |
comparison
equal
deleted
inserted
replaced
6778:959efdac4a9c | 6779:d3147b4e3e8a |
---|---|
13 from mercurial import revlog, templater, templatefilters, changegroup | 13 from mercurial import revlog, templater, templatefilters, changegroup |
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 request import wsgirequest | 16 from request import wsgirequest |
17 import webcommands, protocol, webutil | 17 import webcommands, protocol, webutil |
18 | |
19 perms = { | |
20 'changegroup': 'pull', | |
21 'changegroupsubset': 'pull', | |
22 'unbundle': 'push', | |
23 'stream_out': 'pull', | |
24 } | |
18 | 25 |
19 class hgweb(object): | 26 class hgweb(object): |
20 def __init__(self, repo, name=None): | 27 def __init__(self, repo, name=None): |
21 if isinstance(repo, str): | 28 if isinstance(repo, str): |
22 parentui = ui.ui(report_untrusted=False, interactive=False) | 29 parentui = ui.ui(report_untrusted=False, interactive=False) |
93 # protocol bits don't need to create any URLs | 100 # protocol bits don't need to create any URLs |
94 # and the clients always use the old URL structure | 101 # and the clients always use the old URL structure |
95 | 102 |
96 cmd = req.form.get('cmd', [''])[0] | 103 cmd = req.form.get('cmd', [''])[0] |
97 if cmd and cmd in protocol.__all__: | 104 if cmd and cmd in protocol.__all__: |
105 if cmd in perms and not self.check_perm(req, perms[cmd]): | |
106 return | |
98 method = getattr(protocol, cmd) | 107 method = getattr(protocol, cmd) |
99 method(self, req) | 108 method(self, req) |
100 return | 109 return |
101 | 110 |
102 # work with CGI variables to create coherent structure | 111 # work with CGI variables to create coherent structure |
341 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None), | 350 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None), |
342 'gz': ('application/x-tar', 'tgz', '.tar.gz', None), | 351 'gz': ('application/x-tar', 'tgz', '.tar.gz', None), |
343 'zip': ('application/zip', 'zip', '.zip', None), | 352 'zip': ('application/zip', 'zip', '.zip', None), |
344 } | 353 } |
345 | 354 |
346 def check_perm(self, req, op, default): | 355 def check_perm(self, req, op): |
347 '''check permission for operation based on user auth. | 356 '''Check permission for operation based on request data (including |
348 return true if op allowed, else false. | 357 authentication info. Return true if op allowed, else false.''' |
349 default is policy to use if no config given.''' | 358 |
359 def error(status, message): | |
360 req.respond(status, protocol.HGTYPE) | |
361 req.write('0\n%s\n' % message) | |
362 | |
363 if op == 'pull': | |
364 return self.allowpull | |
365 | |
366 # enforce that you can only push using POST requests | |
367 if req.env['REQUEST_METHOD'] != 'POST': | |
368 error('405 Method Not Allowed', 'push requires POST request') | |
369 return False | |
370 | |
371 # require ssl by default for pushing, auth info cannot be sniffed | |
372 # and replayed | |
373 scheme = req.env.get('wsgi.url_scheme') | |
374 if self.configbool('web', 'push_ssl', True) and scheme != 'https': | |
375 error(HTTP_OK, 'ssl required') | |
376 return False | |
350 | 377 |
351 user = req.env.get('REMOTE_USER') | 378 user = req.env.get('REMOTE_USER') |
352 | 379 |
353 deny = self.configlist('web', 'deny_' + op) | 380 deny = self.configlist('web', 'deny_push') |
354 if deny and (not user or deny == ['*'] or user in deny): | 381 if deny and (not user or deny == ['*'] or user in deny): |
382 error('401 Unauthorized', 'push not authorized') | |
355 return False | 383 return False |
356 | 384 |
357 allow = self.configlist('web', 'allow_' + op) | 385 allow = self.configlist('web', 'allow_push') |
358 return (allow and (allow == ['*'] or user in allow)) or default | 386 result = allow and (allow == ['*'] or user in allow) |
387 if not result: | |
388 error('401 Unauthorized', 'push not authorized') | |
389 | |
390 return result |