[PATCH] support for deny_read/allow_read options
Nilton Volpato
nilton.volpato at gmail.com
Tue Dec 11 20:56:01 CST 2007
Hi all,
I had implemented some time ago (Oct 20) a feature for allowing
finer-grained access. However the solution was not following much of
the project philosophy. Alexis answered me with some hints, this is a
follow up to that earlier message.
I've implemented a counterpart of deny_push/allow_push options to be
used in the web section of a .hg/hgrc file of a repository: the
deny_read/allow_read.
The patch is below. If a user don't have read permission for a repo,
then it won't show on hgwebdir list and he won't be able to access the
repo dir also.
I've replaced the check_perm method with is_op_allowed function in
common.py and changed when allow_by_default variable matters. I think
it was wrong before, but there was no problem with this because the
old method (check_perm) wasn't called with default=True.
Please, take a look at this patch and if possible merge to the mercurial repo.
-- Nilton
# HG changeset patch
# User Nilton Volpato <nilton.volpato at gmail.com>
# Date 1197426375 7200
# Node ID 4fd078545b10e17a094c6122cbec96f65466d782
# Parent feac5b0bf9bad2c125ebd5f3e133bcd46ecb8c7c
Added support for deny_read/allow_read options for finer grained access control
diff -r feac5b0bf9ba -r 4fd078545b10 mercurial/hgweb/common.py
--- a/mercurial/hgweb/common.py Wed Nov 28 13:58:31 2007 -0800
+++ b/mercurial/hgweb/common.py Wed Dec 12 00:26:15 2007 -0200
@@ -97,3 +97,21 @@ def paritygen(stripecount, offset=0):
parity = 1 - parity
count = 0
+def is_op_allowed(req, ui, op, allow_by_default=False):
+ """Return True if the current authenticated user is allowed
+ to execute operation op, otherwise return False.
+ By setting allow by default, the any user will be allowed to
+ execute op, unless explicitly stated otherwise.
+
+ This is currently used with deny_push/allow_push and
+ deny_read/allow_read config options.
+ """
+ user = req.env.get('REMOTE_USER')
+
+ deny = ui.configlist('web', 'deny_' + op, default=None, untrusted=True)
+ if deny and (not user or deny == ['*'] or user in deny):
+ return False
+
+ if allow_by_default: return allow_by_default
+ allow = ui.configlist('web', 'allow_' + op, default=None, untrusted=True)
+ return allow and (allow == ['*'] or user in allow)
diff -r feac5b0bf9ba -r 4fd078545b10 mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py Wed Nov 28 13:58:31 2007 -0800
+++ b/mercurial/hgweb/hgweb_mod.py Wed Dec 12 00:26:15 2007 -0200
@@ -12,7 +12,8 @@ from mercurial.i18n import gettext as _
from mercurial.i18n import gettext as _
from mercurial import mdiff, ui, hg, util, archival, streamclone, patch
from mercurial import revlog, templater
-from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen
+from common import ErrorResponse, get_mtime, staticfile, style_map, \
+ paritygen, is_op_allowed
def _up(p):
if p[0] != "/":
@@ -843,6 +844,12 @@ class hgweb(object):
})
try:
+ if not is_op_allowed(req, self.repo.ui, 'read',
+ allow_by_default=True):
+ req.respond('403 Forbidden',
+ self.t('error', error='403 Forbidden'))
+ return
+
if not req.form.has_key('cmd'):
req.form['cmd'] = [self.t.cache['default']]
@@ -1071,20 +1078,6 @@ class hgweb(object):
req.httphdr("application/mercurial-0.1", length=len(resp))
req.write(resp)
- def check_perm(self, req, op, default):
- '''check permission for operation based on user auth.
- return true if op allowed, else false.
- default is policy to use if no config given.'''
-
- user = req.env.get('REMOTE_USER')
-
- deny = self.configlist('web', 'deny_' + op)
- if deny and (not user or deny == ['*'] or user in deny):
- return False
-
- allow = self.configlist('web', 'allow_' + op)
- return (allow and (allow == ['*'] or user in allow)) or default
-
def do_unbundle(self, req):
def bail(response, headers={}):
length = int(req.env['CONTENT_LENGTH'])
@@ -1108,9 +1101,9 @@ class hgweb(object):
proto = 'http'
# do not allow push unless explicitly allowed
- if not self.check_perm(req, 'push', False):
+ if not is_op_allowed(req, self.repo.ui, 'push'):
bail(_('push not authorized\n'),
- headers={'status': '401 Unauthorized'})
+ headers={'status': '403 Forbidden'})
return
their_heads = req.form['heads'][0].split(' ')
diff -r feac5b0bf9ba -r 4fd078545b10 mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py Wed Nov 28 13:58:31 2007 -0800
+++ b/mercurial/hgweb/hgwebdir_mod.py Wed Dec 12 00:26:15 2007 -0200
@@ -9,7 +9,8 @@ import os, mimetools, cStringIO
import os, mimetools, cStringIO
from mercurial.i18n import gettext as _
from mercurial import ui, hg, util, templater
-from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen
+from common import ErrorResponse, get_mtime, staticfile, style_map, \
+ paritygen, is_op_allowed
from hgweb_mod import hgweb
# This is a stopgap
@@ -154,6 +155,8 @@ class hgwebdir(object):
if u.configbool("web", "hidden", untrusted=True):
continue
+ if not is_op_allowed(req, u, 'read', allow_by_default=True):
+ continue
url = ('/'.join([req.env["REQUEST_URI"].split('?')[0], name])
.replace("//", "/")) + '/'
# HG changeset patch
# User Nilton Volpato <nilton.volpato at gmail.com>
# Date 1197427772 7200
# Node ID a97ec8971abf2eab851b5cfadf0402ec63ba9a63
# Parent 4fd078545b10e17a094c6122cbec96f65466d782
Fixed semantics of is_op_allowed: allow_by_default matters only if no
explicit allow is given
diff -r 4fd078545b10 -r a97ec8971abf mercurial/hgweb/common.py
--- a/mercurial/hgweb/common.py Wed Dec 12 00:26:15 2007 -0200
+++ b/mercurial/hgweb/common.py Wed Dec 12 00:49:32 2007 -0200
@@ -112,6 +112,7 @@ def is_op_allowed(req, ui, op, allow_by_
if deny and (not user or deny == ['*'] or user in deny):
return False
- if allow_by_default: return allow_by_default
allow = ui.configlist('web', 'allow_' + op, default=None, untrusted=True)
+ if not allow and allow_by_default:
+ return True
return allow and (allow == ['*'] or user in allow)
More information about the Mercurial-devel
mailing list