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 @@ -839,19 +839,59 @@ class hgweb(object): req.httphdr("application/mercurial-0.1", length=len(resp)) req.write(resp) + 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.''' + + user = req.env.get('REMOTE_USER') + + deny = self.repo.ui.config('web', 'deny_' + op, '') + deny = deny.replace(',', ' ').split() + + if deny and (not user or deny == ['*'] or user in deny): + return False + + allow = self.repo.ui.config('web', 'allow_' + op, '') + allow = allow.replace(',', ' ').split() + + return (allow and (allow == ['*'] or user in allow)) or default + def do_unbundle(self, req): + def bail(response, headers={}): + length = int(req.env['CONTENT_LENGTH']) + for s in util.filechunkiter(req, limit=length): + # drain incoming bundle, else client will not see + # response when run outside cgi script + pass + req.httphdr("application/mercurial-0.1", headers=headers) + req.write('0\n') + req.write(response) + + # require ssl by default, auth info cannot be sniffed and + # replayed + ssl_req = self.repo.ui.configbool('web', 'push_ssl', True) + if ssl_req and not req.env.get('HTTPS'): + bail(_('ssl required\n')) + return + + # do not allow push unless explicitly allowed + if not self.check_perm(req, 'push', False): + bail(_('push not authorized\n'), + headers={'status': '401 Unauthorized'}) + return + + req.httphdr("application/mercurial-0.1") + their_heads = req.form['heads'][0].split(' ') def check_heads(): heads = map(hex, self.repo.heads()) return their_heads == [hex('force')] or their_heads == heads - req.httphdr("application/mercurial-0.1") - # fail early if possible if not check_heads(): - req.write('0\n') - req.write(_('unsynced changes\n')) + bail(_('unsynced changes\n')) return # do not lock repo until all changegroup data is