comparison mercurial/hgweb/common.py @ 9910:6f92997dbdca

hgweb: add support for extension-provided permission hooks This allows extensions to hook into permission checking, providing both authentication and authorization as needed. The existing authorization function has been changed to a hook, which is added by default.
author Sune Foldager <cryo@cyanite.org>
date Mon, 23 Nov 2009 11:03:55 +0100
parents 8269fe2d48f6
children 97c75ad3b1a0
comparison
equal deleted inserted replaced
9905:95517eb3c9a7 9910:6f92997dbdca
13 HTTP_UNAUTHORIZED = 401 13 HTTP_UNAUTHORIZED = 401
14 HTTP_FORBIDDEN = 403 14 HTTP_FORBIDDEN = 403
15 HTTP_NOT_FOUND = 404 15 HTTP_NOT_FOUND = 404
16 HTTP_METHOD_NOT_ALLOWED = 405 16 HTTP_METHOD_NOT_ALLOWED = 405
17 HTTP_SERVER_ERROR = 500 17 HTTP_SERVER_ERROR = 500
18
19 # Hooks for hgweb permission checks; extensions can add hooks here. Each hook
20 # is invoked like this: hook(hgweb, request, operation), where operation is
21 # either read, pull or push. Hooks should either raise an ErrorResponse
22 # exception, or just return.
23 # It is possible to do both authentication and authorization through this.
24 permhooks = []
25
26 def checkauthz(hgweb, req, op):
27 '''Check permission for operation based on request data (including
28 authentication info). Return if op allowed, else raise an ErrorResponse
29 exception.'''
30
31 user = req.env.get('REMOTE_USER')
32
33 deny_read = hgweb.configlist('web', 'deny_read')
34 if deny_read and (not user or deny_read == ['*'] or user in deny_read):
35 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
36
37 allow_read = hgweb.configlist('web', 'allow_read')
38 result = (not allow_read) or (allow_read == ['*'])
39 if not (result or user in allow_read):
40 raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
41
42 if op == 'pull' and not hgweb.allowpull:
43 raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized')
44 elif op == 'pull' or op is None: # op is None for interface requests
45 return
46
47 # enforce that you can only push using POST requests
48 if req.env['REQUEST_METHOD'] != 'POST':
49 msg = 'push requires POST request'
50 raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
51
52 # require ssl by default for pushing, auth info cannot be sniffed
53 # and replayed
54 scheme = req.env.get('wsgi.url_scheme')
55 if hgweb.configbool('web', 'push_ssl', True) and scheme != 'https':
56 raise ErrorResponse(HTTP_OK, 'ssl required')
57
58 deny = hgweb.configlist('web', 'deny_push')
59 if deny and (not user or deny == ['*'] or user in deny):
60 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
61
62 allow = hgweb.configlist('web', 'allow_push')
63 result = allow and (allow == ['*'] or user in allow)
64 if not result:
65 raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
66
67 # Add the default permhook, which provides simple authorization.
68 permhooks.append(checkauthz)
69
18 70
19 class ErrorResponse(Exception): 71 class ErrorResponse(Exception):
20 def __init__(self, code, message=None, headers=[]): 72 def __init__(self, code, message=None, headers=[]):
21 Exception.__init__(self) 73 Exception.__init__(self)
22 self.code = code 74 self.code = code