[PATCH 1 of 3] Moved hgweb.check_perm outside the class and added multiple permission checking

Martin Vejnar avakar at ratatanek.cz
Mon Sep 28 04:18:31 CDT 2009


# HG changeset patch
# User Martin Vejnar <avakar at ratatanek.cz>
# Date 1254128490 -7200
# Node ID 81f5d7d7ed578d8029f3b1761b1c9716e9a97b2d
# Parent  f3f400b13984d175f44943de2cc348c435c4119e
Moved hgweb.check_perm outside the class and added multiple permission checking.

hgweb_mod.check_perms accepts multiple operations to check permissions for.
The permissions are deduced from web.allow_<op> and web.deny_<op> config items,
the syntax is compatible with original web.allow_push and web.deny_push
(deny access if both lists are empty).

This is to be used by hgwebdir_mod to check permissions to create a new repository.

diff -r f3f400b13984 -r 81f5d7d7ed57 mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py	Wed Sep 23 00:23:50 2009 -0500
+++ b/mercurial/hgweb/hgweb_mod.py	Mon Sep 28 11:01:30 2009 +0200
@@ -21,6 +21,61 @@
     'stream_out': 'pull',
 }
 
+def check_perms(ui, req, ops):
+    '''Check for permission to perform operation based on request data (including
+    authentication info). Return if all of the ops are allowed, else raise an ErrorResponse
+    exception.'''
+    
+    user = req.env.get('REMOTE_USER')
+
+    ui.debug('checking for permission to perform %s as %s\n' % (ops, user))
+
+    deny_read = ui.configlist('web', 'deny_read')
+    if deny_read and (not user or deny_read == ['*'] or user in deny_read):
+        raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
+
+    allow_read = ui.configlist('web', 'allow_read')
+    result = (not allow_read) or (allow_read == ['*'])
+    if not (result or user in allow_read):
+        raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
+
+    if 'pull' in ops and not ui.configbool("web", "allowpull", True):
+        raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized')
+    elif 'pull' in ops:
+        ops.remove('pull')
+
+    if not ops:
+        return
+
+    # handle mutating operations,
+    # enforce that they are performed using POST requests
+    if req.env['REQUEST_METHOD'] != 'POST':
+        msg = '%s requires POST request' % ops[0]
+        raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
+
+    if 'push' in ops:
+        # require ssl by default for pushing, auth info cannot be sniffed
+        # and replayed
+        scheme = req.env.get('wsgi.url_scheme')
+        if ui.configbool('web', 'push_ssl', True) and scheme != 'https':
+            raise ErrorResponse(HTTP_OK, 'ssl required')
+
+    for op in ops:
+        deny = ui.configlist('web', 'deny_%s' % op)
+        if deny and (not user or deny == ['*'] or user in deny):
+            raise ErrorResponse(HTTP_UNAUTHORIZED, '%s not authorized' % op)
+
+        allow = ui.configlist('web', 'allow_%s' % op)
+        result = allow and (allow == ['*'] or user in allow)
+        if not result:
+            raise ErrorResponse(HTTP_UNAUTHORIZED, '%s not authorized' % op)
+
+def check_perm(ui, req, op):
+    if op is None:
+        check_perms(ui, req, [])
+    else:
+        check_perms(ui, req, [op])
+
 class hgweb(object):
     def __init__(self, repo, name=None):
         if isinstance(repo, str):
@@ -112,7 +167,7 @@
             try:
                 if cmd in perms:
                     try:
-                        self.check_perm(req, perms[cmd])
+                        check_perm(self.repo.ui, req, perms[cmd])
                     except ErrorResponse, inst:
                         if cmd == 'unbundle':
                             req.drain()
@@ -168,7 +223,7 @@
 
             # check read permissions non-static content
             if cmd != 'static':
-                self.check_perm(req, None)
+                check_perm(self.repo.ui, req, None)
 
             if cmd == '':
                 req.form['cmd'] = [tmpl.cache['default']]
@@ -272,44 +327,3 @@
         'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
         'zip': ('application/zip', 'zip', '.zip', None),
         }
-
-    def check_perm(self, req, op):
-        '''Check permission for operation based on request data (including
-        authentication info). Return if op allowed, else raise an ErrorResponse
-        exception.'''
-
-        user = req.env.get('REMOTE_USER')
-
-        deny_read = self.configlist('web', 'deny_read')
-        if deny_read and (not user or deny_read == ['*'] or user in deny_read):
-            raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
-
-        allow_read = self.configlist('web', 'allow_read')
-        result = (not allow_read) or (allow_read == ['*'])
-        if not (result or user in allow_read):
-            raise ErrorResponse(HTTP_UNAUTHORIZED, 'read not authorized')
-
-        if op == 'pull' and not self.allowpull:
-            raise ErrorResponse(HTTP_UNAUTHORIZED, 'pull not authorized')
-        elif op == 'pull' or op is None: # op is None for interface requests
-            return
-
-        # enforce that you can only push using POST requests
-        if req.env['REQUEST_METHOD'] != 'POST':
-            msg = 'push requires POST request'
-            raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
-
-        # require ssl by default for pushing, auth info cannot be sniffed
-        # and replayed
-        scheme = req.env.get('wsgi.url_scheme')
-        if self.configbool('web', 'push_ssl', True) and scheme != 'https':
-            raise ErrorResponse(HTTP_OK, 'ssl required')
-
-        deny = self.configlist('web', 'deny_push')
-        if deny and (not user or deny == ['*'] or user in deny):
-            raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')
-
-        allow = self.configlist('web', 'allow_push')
-        result = allow and (allow == ['*'] or user in allow)
-        if not result:
-            raise ErrorResponse(HTTP_UNAUTHORIZED, 'push not authorized')


More information about the Mercurial-devel mailing list