##// END OF EJS Templates
hgweb: centralize permission checks for protocol commands...
Dirkjan Ochtman -
r6779:d3147b4e default
parent child Browse files
Show More
@@ -16,6 +16,13 b' from common import HTTP_OK, HTTP_BAD_REQ'
16 from request import wsgirequest
16 from request import wsgirequest
17 import webcommands, protocol, webutil
17 import webcommands, protocol, webutil
18
18
19 perms = {
20 'changegroup': 'pull',
21 'changegroupsubset': 'pull',
22 'unbundle': 'push',
23 'stream_out': 'pull',
24 }
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):
@@ -95,6 +102,8 b' class hgweb(object):'
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
@@ -343,16 +352,39 b' class hgweb(object):'
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
@@ -62,8 +62,6 b' def between(web, req):'
62 def changegroup(web, req):
62 def changegroup(web, req):
63 req.respond(HTTP_OK, HGTYPE)
63 req.respond(HTTP_OK, HGTYPE)
64 nodes = []
64 nodes = []
65 if not web.allowpull:
66 return
67
65
68 if 'roots' in req.form:
66 if 'roots' in req.form:
69 nodes = map(bin, req.form['roots'][0].split(" "))
67 nodes = map(bin, req.form['roots'][0].split(" "))
@@ -82,8 +80,6 b' def changegroupsubset(web, req):'
82 req.respond(HTTP_OK, HGTYPE)
80 req.respond(HTTP_OK, HGTYPE)
83 bases = []
81 bases = []
84 heads = []
82 heads = []
85 if not web.allowpull:
86 return
87
83
88 if 'bases' in req.form:
84 if 'bases' in req.form:
89 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
85 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
@@ -120,28 +116,7 b' def unbundle(web, req):'
120 req.write('0\n')
116 req.write('0\n')
121 req.write(response)
117 req.write(response)
122
118
123 # enforce that you can only unbundle with POST requests
119 proto = req.env.get('wsgi.url_scheme') or 'http'
124 if req.env['REQUEST_METHOD'] != 'POST':
125 headers = {'status': '405 Method Not Allowed'}
126 bail('unbundle requires POST request\n', headers)
127 return
128
129 # require ssl by default, auth info cannot be sniffed and
130 # replayed
131 ssl_req = web.configbool('web', 'push_ssl', True)
132 if ssl_req:
133 if req.env.get('wsgi.url_scheme') != 'https':
134 bail('ssl required\n')
135 return
136 proto = 'https'
137 else:
138 proto = 'http'
139
140 # do not allow push unless explicitly allowed
141 if not web.check_perm(req, 'push', False):
142 bail('push not authorized\n', headers={'status': '401 Unauthorized'})
143 return
144
145 their_heads = req.form['heads'][0].split(' ')
120 their_heads = req.form['heads'][0].split(' ')
146
121
147 def check_heads():
122 def check_heads():
@@ -224,7 +199,5 b' def unbundle(web, req):'
224 os.unlink(tempname)
199 os.unlink(tempname)
225
200
226 def stream_out(web, req):
201 def stream_out(web, req):
227 if not web.allowpull:
228 return
229 req.respond(HTTP_OK, HGTYPE)
202 req.respond(HTTP_OK, HGTYPE)
230 streamclone.stream_out(web.repo, req, untrusted=True)
203 streamclone.stream_out(web.repo, req, untrusted=True)
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
General Comments 0
You need to be logged in to leave comments. Login now