2006-05-24 01:58:30 +04:00
|
|
|
# acl.py - changeset access control for mercurial
|
|
|
|
#
|
|
|
|
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
|
|
|
|
#
|
2009-04-26 03:08:54 +04:00
|
|
|
# This software may be used and distributed according to the terms of the
|
2010-01-20 07:20:08 +03:00
|
|
|
# GNU General Public License version 2 or any later version.
|
2009-06-21 18:45:47 +04:00
|
|
|
|
2009-06-24 15:42:34 +04:00
|
|
|
'''hooks for controlling repository access
|
2009-06-21 18:45:47 +04:00
|
|
|
|
2009-07-26 03:37:17 +04:00
|
|
|
This hook makes it possible to allow or deny write access to portions
|
2010-04-28 02:02:11 +04:00
|
|
|
of a repository when receiving incoming changesets via pretxnchangegroup and
|
|
|
|
pretxncommit.
|
2009-06-21 18:45:47 +04:00
|
|
|
|
2009-07-26 03:37:17 +04:00
|
|
|
The authorization is matched based on the local user name on the
|
|
|
|
system where the hook runs, and not the committer of the original
|
|
|
|
changeset (since the latter is merely informative).
|
2009-06-21 18:45:47 +04:00
|
|
|
|
2009-07-26 03:37:17 +04:00
|
|
|
The acl hook is best used along with a restricted shell like hgsh,
|
|
|
|
preventing authenticating users from doing anything other than
|
|
|
|
pushing or pulling. The hook is not safe to use if users have
|
|
|
|
interactive shell access, as they can then disable the hook.
|
|
|
|
Nor is it safe if remote users share an account, because then there
|
|
|
|
is no way to distinguish them.
|
2009-06-22 16:49:07 +04:00
|
|
|
|
2010-05-01 16:32:50 +04:00
|
|
|
The deny list is checked before the allow list.
|
2010-04-28 02:02:11 +04:00
|
|
|
|
|
|
|
The allow and deny sections take key-value pairs, having a subtree pattern
|
|
|
|
as key (with a glob syntax by default). The corresponding value can be either:
|
2010-05-01 16:20:17 +04:00
|
|
|
|
2010-04-28 02:02:11 +04:00
|
|
|
1) an asterisk, to match everyone;
|
|
|
|
2) a comma-separated list containing users and groups.
|
|
|
|
|
2010-05-01 16:32:50 +04:00
|
|
|
Group names must be prefixed with an ``@`` symbol.
|
2010-04-28 02:02:11 +04:00
|
|
|
Specifying a group name has the same effect as specifying all the users in
|
|
|
|
that group.
|
|
|
|
|
2010-05-01 16:20:17 +04:00
|
|
|
To use this hook, configure the acl extension in your hgrc like this::
|
2009-06-21 18:45:47 +04:00
|
|
|
|
|
|
|
[extensions]
|
2009-12-22 02:50:03 +03:00
|
|
|
acl =
|
2009-06-21 18:45:47 +04:00
|
|
|
|
|
|
|
[hooks]
|
2010-04-28 02:02:11 +04:00
|
|
|
|
2010-05-01 16:32:50 +04:00
|
|
|
# Use this if you want to check access restrictions at commit time.
|
2010-04-28 02:02:11 +04:00
|
|
|
pretxncommit.acl = python:hgext.acl.hook
|
|
|
|
|
|
|
|
# Use this if you want to check access restrictions for pull, push, bundle
|
|
|
|
# and serve.
|
2009-06-21 18:45:47 +04:00
|
|
|
pretxnchangegroup.acl = python:hgext.acl.hook
|
|
|
|
|
|
|
|
[acl]
|
2010-05-01 16:32:50 +04:00
|
|
|
# Check whether the source of incoming changes is in this list where
|
|
|
|
# "serve" == ssh or http, and "push", "pull" and "bundle" are the
|
|
|
|
# corresponding hg commands.
|
2009-06-22 16:49:07 +04:00
|
|
|
sources = serve
|
2009-06-21 18:45:47 +04:00
|
|
|
|
2010-04-28 02:02:11 +04:00
|
|
|
[acl.deny]
|
|
|
|
# This list is checked first. If a match is found, 'acl.allow' will not be
|
2010-05-01 16:32:50 +04:00
|
|
|
# checked. All users are granted access if acl.deny is not present.
|
|
|
|
# Format for both lists: glob pattern = user, ..., @group, ...
|
2010-04-28 02:02:11 +04:00
|
|
|
|
|
|
|
# To match everyone, use an asterisk for the user:
|
|
|
|
# my/glob/pattern = *
|
|
|
|
|
|
|
|
# user6 will not have write access to any file:
|
|
|
|
** = user6
|
|
|
|
|
|
|
|
# Group "hg-denied" will not have write access to any file:
|
|
|
|
** = @hg-denied
|
|
|
|
|
|
|
|
# Nobody will be able to change "DONT-TOUCH-THIS.txt", despite everyone being
|
|
|
|
# able to change all other files. See below.
|
|
|
|
src/main/resources/DONT-TOUCH-THIS.txt = *
|
2009-06-21 18:45:47 +04:00
|
|
|
|
|
|
|
[acl.allow]
|
2010-04-28 02:02:11 +04:00
|
|
|
# if acl.allow not present, all users allowed by default
|
|
|
|
# empty acl.allow = no users allowed
|
|
|
|
|
|
|
|
# User "doc_writer" has write access to any file under the "docs" folder:
|
2009-06-21 18:45:47 +04:00
|
|
|
docs/** = doc_writer
|
2010-04-28 02:02:11 +04:00
|
|
|
|
|
|
|
# User "jack" and group "designers" have write access to any file under the
|
|
|
|
# "images" folder:
|
|
|
|
images/** = jack, @designers
|
|
|
|
|
|
|
|
# Everyone (except for "user6" - see "acl.deny" above) will have write access
|
2010-05-01 16:32:50 +04:00
|
|
|
# to any file under the "resources" folder (except for 1 file. See "acl.deny"):
|
2010-04-28 02:02:11 +04:00
|
|
|
src/main/resources/** = *
|
|
|
|
|
2009-06-21 18:45:47 +04:00
|
|
|
.hgtags = release_engineer
|
|
|
|
|
|
|
|
'''
|
2006-05-24 01:58:30 +04:00
|
|
|
|
2006-12-15 05:25:19 +03:00
|
|
|
from mercurial.i18n import _
|
2009-05-24 11:56:14 +04:00
|
|
|
from mercurial import util, match
|
2010-04-26 17:55:57 +04:00
|
|
|
import getpass, urllib, grp
|
|
|
|
|
|
|
|
def _getusers(group):
|
|
|
|
return grp.getgrnam(group).gr_mem
|
|
|
|
|
|
|
|
def _usermatch(user, usersorgroups):
|
|
|
|
|
|
|
|
if usersorgroups == '*':
|
|
|
|
return True
|
|
|
|
|
|
|
|
for ug in usersorgroups.replace(',', ' ').split():
|
|
|
|
if user == ug or ug.find('@') == 0 and user in _getusers(ug[1:]):
|
|
|
|
return True
|
|
|
|
|
|
|
|
return False
|
2006-05-24 01:58:30 +04:00
|
|
|
|
2008-06-28 06:45:16 +04:00
|
|
|
def buildmatch(ui, repo, user, key):
|
|
|
|
'''return tuple of (match function, list enabled).'''
|
|
|
|
if not ui.has_section(key):
|
2009-09-19 03:15:38 +04:00
|
|
|
ui.debug('acl: %s not enabled\n' % key)
|
2008-06-28 06:45:16 +04:00
|
|
|
return None
|
2006-05-24 01:58:30 +04:00
|
|
|
|
2008-06-28 06:45:16 +04:00
|
|
|
pats = [pat for pat, users in ui.configitems(key)
|
2010-04-26 17:55:57 +04:00
|
|
|
if _usermatch(user, users)]
|
2009-09-19 03:15:38 +04:00
|
|
|
ui.debug('acl: %s enabled, %d entries for user %s\n' %
|
2008-06-28 06:45:16 +04:00
|
|
|
(key, len(pats), user))
|
|
|
|
if pats:
|
2009-05-24 11:56:14 +04:00
|
|
|
return match.match(repo.root, '', pats)
|
2009-06-01 02:54:18 +04:00
|
|
|
return match.exact(repo.root, '', [])
|
2009-05-24 11:56:14 +04:00
|
|
|
|
2006-05-24 01:58:30 +04:00
|
|
|
|
|
|
|
def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
|
2010-04-07 05:15:43 +04:00
|
|
|
if hooktype not in ['pretxnchangegroup', 'pretxncommit']:
|
2006-05-24 01:58:30 +04:00
|
|
|
raise util.Abort(_('config error - hook type "%s" cannot stop '
|
2010-04-07 05:15:43 +04:00
|
|
|
'incoming changesets nor commits') % hooktype)
|
|
|
|
if (hooktype == 'pretxnchangegroup' and
|
|
|
|
source not in ui.config('acl', 'sources', 'serve').split()):
|
2009-09-19 03:15:38 +04:00
|
|
|
ui.debug('acl: changes have source "%s" - skipping\n' % source)
|
2006-05-24 01:58:30 +04:00
|
|
|
return
|
|
|
|
|
2009-06-07 22:31:38 +04:00
|
|
|
user = None
|
|
|
|
if source == 'serve' and 'url' in kwargs:
|
|
|
|
url = kwargs['url'].split(':')
|
|
|
|
if url[0] == 'remote' and url[1].startswith('http'):
|
2009-07-02 21:53:20 +04:00
|
|
|
user = urllib.unquote(url[3])
|
2009-06-07 22:31:38 +04:00
|
|
|
|
|
|
|
if user is None:
|
|
|
|
user = getpass.getuser()
|
|
|
|
|
2008-06-28 06:45:16 +04:00
|
|
|
cfg = ui.config('acl', 'config')
|
|
|
|
if cfg:
|
2009-04-24 00:40:10 +04:00
|
|
|
ui.readconfig(cfg, sections = ['acl.allow', 'acl.deny'])
|
2008-06-28 06:45:16 +04:00
|
|
|
allow = buildmatch(ui, repo, user, 'acl.allow')
|
|
|
|
deny = buildmatch(ui, repo, user, 'acl.deny')
|
|
|
|
|
|
|
|
for rev in xrange(repo[node], len(repo)):
|
|
|
|
ctx = repo[rev]
|
|
|
|
for f in ctx.files():
|
|
|
|
if deny and deny(f):
|
2009-09-19 03:15:38 +04:00
|
|
|
ui.debug('acl: user %s denied on %s\n' % (user, f))
|
2008-06-28 06:45:16 +04:00
|
|
|
raise util.Abort(_('acl: access denied for changeset %s') % ctx)
|
|
|
|
if allow and not allow(f):
|
2009-09-19 03:15:38 +04:00
|
|
|
ui.debug('acl: user %s not allowed on %s\n' % (user, f))
|
2008-06-28 06:45:16 +04:00
|
|
|
raise util.Abort(_('acl: access denied for changeset %s') % ctx)
|
2009-09-19 03:15:38 +04:00
|
|
|
ui.debug('acl: allowing changeset %s\n' % ctx)
|