Mercurial > public > mercurial-scm > hg
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 |