11 from mercurial.repo import RepoError |
11 from mercurial.repo import RepoError |
12 from mercurial import mdiff, ui, hg, util, patch, hook |
12 from mercurial import mdiff, ui, hg, util, patch, hook |
13 from mercurial import revlog, templater, templatefilters |
13 from mercurial import revlog, templater, templatefilters |
14 from common import get_mtime, style_map, paritygen, countgen, ErrorResponse |
14 from common import get_mtime, style_map, paritygen, countgen, ErrorResponse |
15 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR |
15 from common import HTTP_OK, HTTP_BAD_REQUEST, HTTP_NOT_FOUND, HTTP_SERVER_ERROR |
|
16 from common import HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED |
16 from request import wsgirequest |
17 from request import wsgirequest |
17 import webcommands, protocol, webutil |
18 import webcommands, protocol, webutil |
18 |
19 |
19 perms = { |
20 perms = { |
20 'changegroup': 'pull', |
21 'changegroup': 'pull', |
86 # protocol bits don't need to create any URLs |
87 # protocol bits don't need to create any URLs |
87 # and the clients always use the old URL structure |
88 # and the clients always use the old URL structure |
88 |
89 |
89 cmd = req.form.get('cmd', [''])[0] |
90 cmd = req.form.get('cmd', [''])[0] |
90 if cmd and cmd in protocol.__all__: |
91 if cmd and cmd in protocol.__all__: |
91 if cmd in perms and not self.check_perm(req, perms[cmd]): |
92 try: |
92 return [] |
93 if cmd in perms: |
93 method = getattr(protocol, cmd) |
94 self.check_perm(req, perms[cmd]) |
94 return method(self.repo, req) |
95 method = getattr(protocol, cmd) |
|
96 return method(self.repo, req) |
|
97 except ErrorResponse, inst: |
|
98 req.respond(inst.code, protocol.HGTYPE) |
|
99 if not inst.message: |
|
100 return [] |
|
101 return '0\n%s\n' % inst.message, |
95 |
102 |
96 # work with CGI variables to create coherent structure |
103 # work with CGI variables to create coherent structure |
97 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME |
104 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME |
98 |
105 |
99 req.url = req.env['SCRIPT_NAME'] |
106 req.url = req.env['SCRIPT_NAME'] |
342 |
349 |
343 def check_perm(self, req, op): |
350 def check_perm(self, req, op): |
344 '''Check permission for operation based on request data (including |
351 '''Check permission for operation based on request data (including |
345 authentication info. Return true if op allowed, else false.''' |
352 authentication info. Return true if op allowed, else false.''' |
346 |
353 |
347 def error(status, message): |
354 if op == 'pull' and not self.allowpull: |
348 req.respond(status, protocol.HGTYPE) |
355 raise ErrorResponse(HTTP_OK, '') |
349 req.write('0\n%s\n' % message) |
356 elif op == 'pull': |
350 |
357 return |
351 if op == 'pull': |
|
352 return self.allowpull |
|
353 |
358 |
354 # enforce that you can only push using POST requests |
359 # enforce that you can only push using POST requests |
355 if req.env['REQUEST_METHOD'] != 'POST': |
360 if req.env['REQUEST_METHOD'] != 'POST': |
356 error('405 Method Not Allowed', 'push requires POST request') |
361 msg = 'push requires POST request' |
357 return False |
362 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg) |
358 |
363 |
359 # require ssl by default for pushing, auth info cannot be sniffed |
364 # require ssl by default for pushing, auth info cannot be sniffed |
360 # and replayed |
365 # and replayed |
361 scheme = req.env.get('wsgi.url_scheme') |
366 scheme = req.env.get('wsgi.url_scheme') |
362 if self.configbool('web', 'push_ssl', True) and scheme != 'https': |
367 if self.configbool('web', 'push_ssl', True) and scheme != 'https': |
363 error(HTTP_OK, 'ssl required') |
368 raise ErrorResponse(HTTP_OK, 'ssl required') |
364 return False |
|
365 |
369 |
366 user = req.env.get('REMOTE_USER') |
370 user = req.env.get('REMOTE_USER') |
367 |
371 |
368 deny = self.configlist('web', 'deny_push') |
372 deny = self.configlist('web', 'deny_push') |
369 if deny and (not user or deny == ['*'] or user in deny): |
373 if deny and (not user or deny == ['*'] or user in deny): |
370 error('401 Unauthorized', 'push not authorized') |
374 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') |
371 return False |
|
372 |
375 |
373 allow = self.configlist('web', 'allow_push') |
376 allow = self.configlist('web', 'allow_push') |
374 result = allow and (allow == ['*'] or user in allow) |
377 result = allow and (allow == ['*'] or user in allow) |
375 if not result: |
378 if not result: |
376 error('401 Unauthorized', 'push not authorized') |
379 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized') |
377 |
|
378 return result |
|