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 @@ -360,8 +360,10 @@ class hgweb(object): try: if query: raise ErrorResponse(HTTP_NOT_FOUND) + + req.checkperm = lambda op: self.check_perm(rctx, req, op) if cmd in perms: - self.check_perm(rctx, req, perms[cmd]) + req.checkperm(perms[cmd]) return protocol.call(rctx.repo, req, cmd) except ErrorResponse as inst: # A client that sends unbundle without 100-continue will diff --git a/mercurial/hgweb/protocol.py b/mercurial/hgweb/protocol.py --- a/mercurial/hgweb/protocol.py +++ b/mercurial/hgweb/protocol.py @@ -52,6 +52,7 @@ class webproto(wireproto.abstractserverp self.response = '' self.ui = ui self.name = 'http' + self.checkperm = req.checkperm def getargs(self, args): knownargs = self._args() diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py --- a/mercurial/wireproto.py +++ b/mercurial/wireproto.py @@ -689,6 +689,8 @@ def wireprotocommand(name, args=''): return func return register +# TODO define a more appropriate permissions type to use for this. +permissions['batch'] = 'pull' @wireprotocommand('batch', 'cmds *') def batch(repo, proto, cmds, others): repo = repo.filtered("served") @@ -701,6 +703,17 @@ def batch(repo, proto, cmds, others): n, v = a.split('=') vals[unescapearg(n)] = unescapearg(v) func, spec = commands[op] + + # If the protocol supports permissions checking, perform that + # checking on each batched command. + # TODO formalize permission checking as part of protocol interface. + if util.safehasattr(proto, 'checkperm'): + # Assume commands with no defined permissions are writes / for + # pushes. This is the safest from a security perspective because + # it doesn't allow commands with undefined semantics from + # bypassing permissions checks. + proto.checkperm(permissions.get(op, 'push')) + if spec: keys = spec.split() data = {} diff --git a/tests/test-http-permissions.t b/tests/test-http-permissions.t --- a/tests/test-http-permissions.t +++ b/tests/test-http-permissions.t @@ -83,13 +83,12 @@ web.deny_read=* prevents access to wire read not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases' - 200 Script output follows + 401 read not authorized - cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1 - publishing True (no-eol) + 0 + read not authorized + [1] TODO custom commands don't check permissions @@ -143,13 +142,12 @@ web.deny_read=* with REMOTE_USER set sti read not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases' - 200 Script output follows + 401 read not authorized - cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1 - publishing True (no-eol) + 0 + read not authorized + [1] TODO custom commands don't check permissions @@ -201,13 +199,12 @@ web.deny_read= denies access to un read not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases' - 200 Script output follows + 401 read not authorized - cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1 - publishing True (no-eol) + 0 + read not authorized + [1] TODO custom commands don't check permissions @@ -254,13 +251,12 @@ web.deny_read= denies access to us read not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases' - 200 Script output follows + 401 read not authorized - cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1 - publishing True (no-eol) + 0 + read not authorized + [1] TODO custom commands don't check permissions @@ -461,13 +457,12 @@ web.allow_read= does not allow una read not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases' - 200 Script output follows + 401 read not authorized - cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1 - publishing True (no-eol) + 0 + read not authorized + [1] TODO custom commands don't check permissions @@ -514,13 +509,12 @@ web.allow_read= does not allow use read not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases' - 200 Script output follows + 401 read not authorized - cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1 - publishing True (no-eol) + 0 + read not authorized + [1] TODO custom commands don't check permissions @@ -621,13 +615,12 @@ web.deny_read takes precedence over web. read not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases' - 200 Script output follows + 401 read not authorized - cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1 - publishing True (no-eol) + 0 + read not authorized + [1] TODO custom commands don't check permissions @@ -686,13 +679,12 @@ web.allow-pull=false denies read access pull not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases' - 200 Script output follows + 401 pull not authorized - cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1 - publishing True (no-eol) + 0 + pull not authorized + [1] TODO custom commands don't check permissions @@ -742,16 +734,18 @@ Attempting a write command with HTTP GET push requires POST request [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b' - 200 Script output follows + 405 push requires POST request - 1 + 0 + push requires POST request + [1] $ hg bookmarks - bm 0:cb9a9f314b8b + no bookmarks set $ hg bookmark -d bm + abort: bookmark 'bm' does not exist + [255] TODO custom commands don't check permissions @@ -781,16 +775,18 @@ Attempting a write command with an unkno push requires POST request [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b' - 200 Script output follows + 405 push requires POST request - 1 + 0 + push requires POST request + [1] $ hg bookmarks - bm 0:cb9a9f314b8b + no bookmarks set $ hg bookmark -d bm + abort: bookmark 'bm' does not exist + [255] TODO custom commands don't check permissions @@ -823,16 +819,15 @@ Pushing on a plaintext channel is disabl ssl required [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b' - 200 Script output follows + 403 ssl required - 1 + 0 + ssl required + [1] $ hg bookmarks - bm 0:cb9a9f314b8b - $ hg book -d bm + no bookmarks set TODO custom commands don't check permissions @@ -887,16 +882,15 @@ web.deny_push=* denies pushing to unauth push not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b' - 200 Script output follows + 401 push not authorized - 1 + 0 + push not authorized + [1] $ hg bookmarks - bm 0:cb9a9f314b8b - $ hg book -d bm + no bookmarks set TODO custom commands don't check permissions @@ -945,16 +939,15 @@ web.deny_push=* denies pushing to authen push not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b' - 200 Script output follows + 401 push not authorized - 1 + 0 + push not authorized + [1] $ hg bookmarks - bm 0:cb9a9f314b8b - $ hg book -d bm + no bookmarks set TODO custom commands don't check permissions @@ -1009,16 +1002,15 @@ web.deny_push= denies pushing to u push not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b' - 200 Script output follows + 401 push not authorized - 1 + 0 + push not authorized + [1] $ hg bookmarks - bm 0:cb9a9f314b8b - $ hg book -d bm + no bookmarks set TODO custom commands don't check permissions @@ -1067,16 +1059,15 @@ web.deny_push= denies pushing to u push not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b' - 200 Script output follows + 401 push not authorized - 1 + 0 + push not authorized + [1] $ hg bookmarks - bm 0:cb9a9f314b8b - $ hg book -d bm + no bookmarks set TODO custom commands don't check permissions @@ -1241,16 +1232,15 @@ web.allow-push= denies push to use push not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b' - 200 Script output follows + 401 push not authorized - 1 + 0 + push not authorized + [1] $ hg bookmarks - bm 0:cb9a9f314b8b - $ hg book -d bm + no bookmarks set TODO custom commands don't check permissions @@ -1367,16 +1357,15 @@ web.deny_push takes precedence over web. push not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b' - 200 Script output follows + 401 push not authorized - 1 + 0 + push not authorized + [1] $ hg bookmarks - bm 0:cb9a9f314b8b - $ hg book -d bm + no bookmarks set TODO custom commands don't check permissions @@ -1432,16 +1421,15 @@ web.allow-push has no effect if web.deny read not authorized [1] -TODO batch command doesn't check permissions - $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b' - 200 Script output follows + 401 read not authorized - 1 + 0 + read not authorized + [1] $ hg bookmarks - bm 0:cb9a9f314b8b - $ hg book -d bm + no bookmarks set TODO custom commands don't check permissions