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