##// END OF EJS Templates
wireproto: formalize permissions checking as part of protocol interface...
Gregory Szorc -
r36819:66de4555 default
parent child Browse files
Show More
@@ -350,18 +350,15 b' class hgweb(object):'
350
350
351 # Route it to a wire protocol handler if it looks like a wire protocol
351 # Route it to a wire protocol handler if it looks like a wire protocol
352 # request.
352 # request.
353 protohandler = wireprotoserver.parsehttprequest(rctx.repo, req, query)
353 protohandler = wireprotoserver.parsehttprequest(rctx, req, query,
354 self.check_perm)
354
355
355 if protohandler:
356 if protohandler:
356 try:
357 try:
357 if query:
358 if query:
358 raise ErrorResponse(HTTP_NOT_FOUND)
359 raise ErrorResponse(HTTP_NOT_FOUND)
359
360
360 # TODO fold this into parsehttprequest
361 return protohandler['dispatch']()
361 checkperm = lambda op: self.check_perm(rctx, req, op)
362 protohandler['proto'].checkperm = checkperm
363
364 return protohandler['dispatch'](checkperm)
365 except ErrorResponse as inst:
362 except ErrorResponse as inst:
366 return protohandler['handleerror'](inst)
363 return protohandler['handleerror'](inst)
367
364
@@ -731,13 +731,10 b' def batch(repo, proto, cmds, others):'
731 vals[unescapearg(n)] = unescapearg(v)
731 vals[unescapearg(n)] = unescapearg(v)
732 func, spec = commands[op]
732 func, spec = commands[op]
733
733
734 # If the protocol supports permissions checking, perform that
734 # Validate that client has permissions to perform this command.
735 # checking on each batched command.
735 perm = commands[op].permission
736 # TODO formalize permission checking as part of protocol interface.
736 assert perm in ('push', 'pull')
737 if util.safehasattr(proto, 'checkperm'):
737 proto.checkperm(perm)
738 perm = commands[op].permission
739 assert perm in ('push', 'pull')
740 proto.checkperm(perm)
741
738
742 if spec:
739 if spec:
743 keys = spec.split()
740 keys = spec.split()
@@ -54,9 +54,10 b' def decodevaluefromheaders(req, headerpr'
54 return ''.join(chunks)
54 return ''.join(chunks)
55
55
56 class httpv1protocolhandler(wireprototypes.baseprotocolhandler):
56 class httpv1protocolhandler(wireprototypes.baseprotocolhandler):
57 def __init__(self, req, ui):
57 def __init__(self, req, ui, checkperm):
58 self._req = req
58 self._req = req
59 self._ui = ui
59 self._ui = ui
60 self._checkperm = checkperm
60
61
61 @property
62 @property
62 def name(self):
63 def name(self):
@@ -139,6 +140,9 b' class httpv1protocolhandler(wireprototyp'
139
140
140 return caps
141 return caps
141
142
143 def checkperm(self, perm):
144 return self._checkperm(perm)
145
142 # This method exists mostly so that extensions like remotefilelog can
146 # This method exists mostly so that extensions like remotefilelog can
143 # disable a kludgey legacy method only over http. As of early 2018,
147 # disable a kludgey legacy method only over http. As of early 2018,
144 # there are no other known users, so with any luck we can discard this
148 # there are no other known users, so with any luck we can discard this
@@ -146,7 +150,7 b' class httpv1protocolhandler(wireprototyp'
146 def iscmd(cmd):
150 def iscmd(cmd):
147 return cmd in wireproto.commands
151 return cmd in wireproto.commands
148
152
149 def parsehttprequest(repo, req, query):
153 def parsehttprequest(rctx, req, query, checkperm):
150 """Parse the HTTP request for a wire protocol request.
154 """Parse the HTTP request for a wire protocol request.
151
155
152 If the current request appears to be a wire protocol request, this
156 If the current request appears to be a wire protocol request, this
@@ -156,6 +160,8 b' def parsehttprequest(repo, req, query):'
156
160
157 ``req`` is a ``wsgirequest`` instance.
161 ``req`` is a ``wsgirequest`` instance.
158 """
162 """
163 repo = rctx.repo
164
159 # HTTP version 1 wire protocol requests are denoted by a "cmd" query
165 # HTTP version 1 wire protocol requests are denoted by a "cmd" query
160 # string parameter. If it isn't present, this isn't a wire protocol
166 # string parameter. If it isn't present, this isn't a wire protocol
161 # request.
167 # request.
@@ -174,13 +180,13 b' def parsehttprequest(repo, req, query):'
174 if not iscmd(cmd):
180 if not iscmd(cmd):
175 return None
181 return None
176
182
177 proto = httpv1protocolhandler(req, repo.ui)
183 proto = httpv1protocolhandler(req, repo.ui,
184 lambda perm: checkperm(rctx, req, perm))
178
185
179 return {
186 return {
180 'cmd': cmd,
187 'cmd': cmd,
181 'proto': proto,
188 'proto': proto,
182 'dispatch': lambda checkperm: _callhttp(repo, req, proto, cmd,
189 'dispatch': lambda: _callhttp(repo, req, proto, cmd),
183 checkperm),
184 'handleerror': lambda ex: _handlehttperror(ex, req, cmd),
190 'handleerror': lambda ex: _handlehttperror(ex, req, cmd),
185 }
191 }
186
192
@@ -224,7 +230,7 b' def _httpresponsetype(ui, req, prefer_un'
224 opts = {'level': ui.configint('server', 'zliblevel')}
230 opts = {'level': ui.configint('server', 'zliblevel')}
225 return HGTYPE, util.compengines['zlib'], opts
231 return HGTYPE, util.compengines['zlib'], opts
226
232
227 def _callhttp(repo, req, proto, cmd, checkperm):
233 def _callhttp(repo, req, proto, cmd):
228 def genversion2(gen, engine, engineopts):
234 def genversion2(gen, engine, engineopts):
229 # application/mercurial-0.2 always sends a payload header
235 # application/mercurial-0.2 always sends a payload header
230 # identifying the compression engine.
236 # identifying the compression engine.
@@ -242,7 +248,7 b' def _callhttp(repo, req, proto, cmd, che'
242 'over HTTP'))
248 'over HTTP'))
243 return []
249 return []
244
250
245 checkperm(wireproto.commands[cmd].permission)
251 proto.checkperm(wireproto.commands[cmd].permission)
246
252
247 rsp = wireproto.dispatch(repo, proto, cmd)
253 rsp = wireproto.dispatch(repo, proto, cmd)
248
254
@@ -392,6 +398,9 b' class sshv1protocolhandler(wireprototype'
392 def addcapabilities(self, repo, caps):
398 def addcapabilities(self, repo, caps):
393 return caps
399 return caps
394
400
401 def checkperm(self, perm):
402 pass
403
395 class sshv2protocolhandler(sshv1protocolhandler):
404 class sshv2protocolhandler(sshv1protocolhandler):
396 """Protocol handler for version 2 of the SSH protocol."""
405 """Protocol handler for version 2 of the SSH protocol."""
397
406
@@ -146,3 +146,12 b' class baseprotocolhandler(object):'
146
146
147 Returns a list of capabilities. The passed in argument can be returned.
147 Returns a list of capabilities. The passed in argument can be returned.
148 """
148 """
149
150 @abc.abstractmethod
151 def checkperm(self, perm):
152 """Validate that the client has permissions to perform a request.
153
154 The argument is the permission required to proceed. If the client
155 doesn't have that permission, the exception should raise or abort
156 in a protocol specific manner.
157 """
@@ -18,6 +18,9 b' class proto(object):'
18 names = spec.split()
18 names = spec.split()
19 return [args[n] for n in names]
19 return [args[n] for n in names]
20
20
21 def checkperm(self, perm):
22 pass
23
21 class clientpeer(wireproto.wirepeer):
24 class clientpeer(wireproto.wirepeer):
22 def __init__(self, serverrepo):
25 def __init__(self, serverrepo):
23 self.serverrepo = serverrepo
26 self.serverrepo = serverrepo
General Comments 0
You need to be logged in to leave comments. Login now