##// END OF EJS Templates
acl: use reST syntax for literal blocks
Martin Geisler -
r9201:3d6c9659 default
parent child Browse files
Show More
@@ -1,105 +1,105 b''
1 # acl.py - changeset access control for mercurial
1 # acl.py - changeset access control for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7 #
7 #
8
8
9 '''hooks for controlling repository access
9 '''hooks for controlling repository access
10
10
11 This hook makes it possible to allow or deny write access to portions of a
11 This hook makes it possible to allow or deny write access to portions of a
12 repository when receiving incoming changesets.
12 repository when receiving incoming changesets.
13
13
14 The authorization is matched based on the local user name on the system where
14 The authorization is matched based on the local user name on the system where
15 the hook runs, and not the committer of the original changeset (since the
15 the hook runs, and not the committer of the original changeset (since the
16 latter is merely informative).
16 latter is merely informative).
17
17
18 The acl hook is best used along with a restricted shell like hgsh, preventing
18 The acl hook is best used along with a restricted shell like hgsh, preventing
19 authenticating users from doing anything other than pushing or pulling. The
19 authenticating users from doing anything other than pushing or pulling. The
20 hook is not safe to use if users have interactive shell access, as they can
20 hook is not safe to use if users have interactive shell access, as they can
21 then disable the hook. Nor is it safe if remote users share an account,
21 then disable the hook. Nor is it safe if remote users share an account,
22 because then there is no way to distinguish them.
22 because then there is no way to distinguish them.
23
23
24 To use this hook, configure the acl extension in your hgrc like this:
24 To use this hook, configure the acl extension in your hgrc like this::
25
25
26 [extensions]
26 [extensions]
27 hgext.acl =
27 hgext.acl =
28
28
29 [hooks]
29 [hooks]
30 pretxnchangegroup.acl = python:hgext.acl.hook
30 pretxnchangegroup.acl = python:hgext.acl.hook
31
31
32 [acl]
32 [acl]
33 # Check whether the source of incoming changes is in this list
33 # Check whether the source of incoming changes is in this list
34 # ("serve" == ssh or http, "push", "pull", "bundle")
34 # ("serve" == ssh or http, "push", "pull", "bundle")
35 sources = serve
35 sources = serve
36
36
37 The allow and deny sections take a subtree pattern as key (with a glob syntax
37 The allow and deny sections take a subtree pattern as key (with a glob syntax
38 by default), and a comma separated list of users as the corresponding value.
38 by default), and a comma separated list of users as the corresponding value.
39 The deny list is checked before the allow list is.
39 The deny list is checked before the allow list is. ::
40
40
41 [acl.allow]
41 [acl.allow]
42 # If acl.allow is not present, all users are allowed by default.
42 # If acl.allow is not present, all users are allowed by default.
43 # An empty acl.allow section means no users allowed.
43 # An empty acl.allow section means no users allowed.
44 docs/** = doc_writer
44 docs/** = doc_writer
45 .hgtags = release_engineer
45 .hgtags = release_engineer
46
46
47 [acl.deny]
47 [acl.deny]
48 # If acl.deny is not present, no users are refused by default.
48 # If acl.deny is not present, no users are refused by default.
49 # An empty acl.deny section means all users allowed.
49 # An empty acl.deny section means all users allowed.
50 glob pattern = user4, user5
50 glob pattern = user4, user5
51 ** = user6
51 ** = user6
52 '''
52 '''
53
53
54 from mercurial.i18n import _
54 from mercurial.i18n import _
55 from mercurial import util, match
55 from mercurial import util, match
56 import getpass, urllib
56 import getpass, urllib
57
57
58 def buildmatch(ui, repo, user, key):
58 def buildmatch(ui, repo, user, key):
59 '''return tuple of (match function, list enabled).'''
59 '''return tuple of (match function, list enabled).'''
60 if not ui.has_section(key):
60 if not ui.has_section(key):
61 ui.debug(_('acl: %s not enabled\n') % key)
61 ui.debug(_('acl: %s not enabled\n') % key)
62 return None
62 return None
63
63
64 pats = [pat for pat, users in ui.configitems(key)
64 pats = [pat for pat, users in ui.configitems(key)
65 if user in users.replace(',', ' ').split()]
65 if user in users.replace(',', ' ').split()]
66 ui.debug(_('acl: %s enabled, %d entries for user %s\n') %
66 ui.debug(_('acl: %s enabled, %d entries for user %s\n') %
67 (key, len(pats), user))
67 (key, len(pats), user))
68 if pats:
68 if pats:
69 return match.match(repo.root, '', pats)
69 return match.match(repo.root, '', pats)
70 return match.exact(repo.root, '', [])
70 return match.exact(repo.root, '', [])
71
71
72
72
73 def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
73 def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
74 if hooktype != 'pretxnchangegroup':
74 if hooktype != 'pretxnchangegroup':
75 raise util.Abort(_('config error - hook type "%s" cannot stop '
75 raise util.Abort(_('config error - hook type "%s" cannot stop '
76 'incoming changesets') % hooktype)
76 'incoming changesets') % hooktype)
77 if source not in ui.config('acl', 'sources', 'serve').split():
77 if source not in ui.config('acl', 'sources', 'serve').split():
78 ui.debug(_('acl: changes have source "%s" - skipping\n') % source)
78 ui.debug(_('acl: changes have source "%s" - skipping\n') % source)
79 return
79 return
80
80
81 user = None
81 user = None
82 if source == 'serve' and 'url' in kwargs:
82 if source == 'serve' and 'url' in kwargs:
83 url = kwargs['url'].split(':')
83 url = kwargs['url'].split(':')
84 if url[0] == 'remote' and url[1].startswith('http'):
84 if url[0] == 'remote' and url[1].startswith('http'):
85 user = urllib.unquote(url[3])
85 user = urllib.unquote(url[3])
86
86
87 if user is None:
87 if user is None:
88 user = getpass.getuser()
88 user = getpass.getuser()
89
89
90 cfg = ui.config('acl', 'config')
90 cfg = ui.config('acl', 'config')
91 if cfg:
91 if cfg:
92 ui.readconfig(cfg, sections = ['acl.allow', 'acl.deny'])
92 ui.readconfig(cfg, sections = ['acl.allow', 'acl.deny'])
93 allow = buildmatch(ui, repo, user, 'acl.allow')
93 allow = buildmatch(ui, repo, user, 'acl.allow')
94 deny = buildmatch(ui, repo, user, 'acl.deny')
94 deny = buildmatch(ui, repo, user, 'acl.deny')
95
95
96 for rev in xrange(repo[node], len(repo)):
96 for rev in xrange(repo[node], len(repo)):
97 ctx = repo[rev]
97 ctx = repo[rev]
98 for f in ctx.files():
98 for f in ctx.files():
99 if deny and deny(f):
99 if deny and deny(f):
100 ui.debug(_('acl: user %s denied on %s\n') % (user, f))
100 ui.debug(_('acl: user %s denied on %s\n') % (user, f))
101 raise util.Abort(_('acl: access denied for changeset %s') % ctx)
101 raise util.Abort(_('acl: access denied for changeset %s') % ctx)
102 if allow and not allow(f):
102 if allow and not allow(f):
103 ui.debug(_('acl: user %s not allowed on %s\n') % (user, f))
103 ui.debug(_('acl: user %s not allowed on %s\n') % (user, f))
104 raise util.Abort(_('acl: access denied for changeset %s') % ctx)
104 raise util.Abort(_('acl: access denied for changeset %s') % ctx)
105 ui.debug(_('acl: allowing changeset %s\n') % ctx)
105 ui.debug(_('acl: allowing changeset %s\n') % ctx)
General Comments 0
You need to be logged in to leave comments. Login now