diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py +++ b/mercurial/hgweb/hgweb_mod.py @@ -16,6 +16,13 @@ from common import HTTP_OK, HTTP_BAD_REQ from request import wsgirequest import webcommands, protocol, webutil +perms = { + 'changegroup': 'pull', + 'changegroupsubset': 'pull', + 'unbundle': 'push', + 'stream_out': 'pull', +} + class hgweb(object): def __init__(self, repo, name=None): if isinstance(repo, str): @@ -95,6 +102,8 @@ class hgweb(object): cmd = req.form.get('cmd', [''])[0] if cmd and cmd in protocol.__all__: + if cmd in perms and not self.check_perm(req, perms[cmd]): + return method = getattr(protocol, cmd) method(self, req) return @@ -343,16 +352,39 @@ class hgweb(object): 'zip': ('application/zip', 'zip', '.zip', None), } - def check_perm(self, req, op, default): - '''check permission for operation based on user auth. - return true if op allowed, else false. - default is policy to use if no config given.''' + def check_perm(self, req, op): + '''Check permission for operation based on request data (including + authentication info. Return true if op allowed, else false.''' + + def error(status, message): + req.respond(status, protocol.HGTYPE) + req.write('0\n%s\n' % message) + + if op == 'pull': + return self.allowpull + + # enforce that you can only push using POST requests + if req.env['REQUEST_METHOD'] != 'POST': + error('405 Method Not Allowed', 'push requires POST request') + return False + + # require ssl by default for pushing, auth info cannot be sniffed + # and replayed + scheme = req.env.get('wsgi.url_scheme') + if self.configbool('web', 'push_ssl', True) and scheme != 'https': + error(HTTP_OK, 'ssl required') + return False user = req.env.get('REMOTE_USER') - deny = self.configlist('web', 'deny_' + op) + deny = self.configlist('web', 'deny_push') if deny and (not user or deny == ['*'] or user in deny): + error('401 Unauthorized', 'push not authorized') return False - allow = self.configlist('web', 'allow_' + op) - return (allow and (allow == ['*'] or user in allow)) or default + allow = self.configlist('web', 'allow_push') + result = allow and (allow == ['*'] or user in allow) + if not result: + error('401 Unauthorized', 'push not authorized') + + return result diff --git a/mercurial/hgweb/protocol.py b/mercurial/hgweb/protocol.py --- a/mercurial/hgweb/protocol.py +++ b/mercurial/hgweb/protocol.py @@ -62,8 +62,6 @@ def between(web, req): def changegroup(web, req): req.respond(HTTP_OK, HGTYPE) nodes = [] - if not web.allowpull: - return if 'roots' in req.form: nodes = map(bin, req.form['roots'][0].split(" ")) @@ -82,8 +80,6 @@ def changegroupsubset(web, req): req.respond(HTTP_OK, HGTYPE) bases = [] heads = [] - if not web.allowpull: - return if 'bases' in req.form: bases = [bin(x) for x in req.form['bases'][0].split(' ')] @@ -120,28 +116,7 @@ def unbundle(web, req): req.write('0\n') req.write(response) - # enforce that you can only unbundle with POST requests - if req.env['REQUEST_METHOD'] != 'POST': - headers = {'status': '405 Method Not Allowed'} - bail('unbundle requires POST request\n', headers) - return - - # require ssl by default, auth info cannot be sniffed and - # replayed - ssl_req = web.configbool('web', 'push_ssl', True) - if ssl_req: - if req.env.get('wsgi.url_scheme') != 'https': - bail('ssl required\n') - return - proto = 'https' - else: - proto = 'http' - - # do not allow push unless explicitly allowed - if not web.check_perm(req, 'push', False): - bail('push not authorized\n', headers={'status': '401 Unauthorized'}) - return - + proto = req.env.get('wsgi.url_scheme') or 'http' their_heads = req.form['heads'][0].split(' ') def check_heads(): @@ -224,7 +199,5 @@ def unbundle(web, req): os.unlink(tempname) def stream_out(web, req): - if not web.allowpull: - return req.respond(HTTP_OK, HGTYPE) streamclone.stream_out(web.repo, req, untrusted=True) diff --git a/tests/test-hgweb-commands.out b/tests/test-hgweb-commands.out index a9666456a4e9c97af4b479b399d886234b61ab19..fdbe42f38f282b6fbc29f5d7e0478c864520810e GIT binary patch literal 18057 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 B&RGBe