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 |
|
355 | def check_perm(self, req, op): | |
347 |
''' |
|
356 | '''Check permission for operation based on request data (including | |
348 |
|
|
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_' |
|
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_' |
|
385 | allow = self.configlist('web', 'allow_push') | |
358 |
re |
|
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