hgext/acl.py
author Martin Geisler <mg@lazybytes.net>
Sun, 13 Dec 2009 23:49:53 +0100
changeset 10065 a1ae0ed78d1a
parent 9467 4c041f1ee1b4
child 10112 703db37d186b
permissions -rw-r--r--
minirst: improve layout of field lists Before, we used the padding following the key to compute where to wrap the text. Long keys would thus give a big indentation. It also required careful alignment of the source text, making it cumbersome to items to the list. We now compute the maximum key length and use that for all items in the list. We also put a cap on the indentation: keys longer than 10 characters are put on their own line. This is similar to how rst2html handles large keys: it uses 14 as the cutoff point, but I felt that 10 was better for monospaced text in the console.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
2344
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
     1
# acl.py - changeset access control for mercurial
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
     2
#
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
     3
# Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
     4
#
8225
46293a0c7e9f updated license to be explicit about GPL version 2
Martin Geisler <mg@lazybytes.net>
parents: 8142
diff changeset
     5
# This software may be used and distributed according to the terms of the
46293a0c7e9f updated license to be explicit about GPL version 2
Martin Geisler <mg@lazybytes.net>
parents: 8142
diff changeset
     6
# GNU General Public License version 2, incorporated herein by reference.
2344
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
     7
#
8873
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
     8
8935
f4f0e902b750 extensions: change descriptions for hook-providing extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8894
diff changeset
     9
'''hooks for controlling repository access
8873
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    10
9250
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    11
This hook makes it possible to allow or deny write access to portions
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    12
of a repository when receiving incoming changesets.
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    13
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    14
The authorization is matched based on the local user name on the
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    15
system where the hook runs, and not the committer of the original
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    16
changeset (since the latter is merely informative).
8873
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    17
9250
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    18
The acl hook is best used along with a restricted shell like hgsh,
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    19
preventing authenticating users from doing anything other than
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    20
pushing or pulling. The hook is not safe to use if users have
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    21
interactive shell access, as they can then disable the hook.
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    22
Nor is it safe if remote users share an account, because then there
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    23
is no way to distinguish them.
8873
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    24
9201
3d6c9659886b acl: use reST syntax for literal blocks
Martin Geisler <mg@lazybytes.net>
parents: 9052
diff changeset
    25
To use this hook, configure the acl extension in your hgrc like this::
8873
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    26
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    27
  [extensions]
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    28
  hgext.acl =
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    29
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    30
  [hooks]
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    31
  pretxnchangegroup.acl = python:hgext.acl.hook
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    32
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    33
  [acl]
8893
cc0593af30d4 acl: help improvements
Cédric Duval <cedricduval@free.fr>
parents: 8873
diff changeset
    34
  # Check whether the source of incoming changes is in this list
cc0593af30d4 acl: help improvements
Cédric Duval <cedricduval@free.fr>
parents: 8873
diff changeset
    35
  # ("serve" == ssh or http, "push", "pull", "bundle")
cc0593af30d4 acl: help improvements
Cédric Duval <cedricduval@free.fr>
parents: 8873
diff changeset
    36
  sources = serve
8873
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    37
9250
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    38
The allow and deny sections take a subtree pattern as key (with a glob
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    39
syntax by default), and a comma separated list of users as the
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    40
corresponding value. The deny list is checked before the allow list
00986b9ed649 acl: wrap docstrings at 70 characters
Martin Geisler <mg@lazybytes.net>
parents: 9201
diff changeset
    41
is. ::
8873
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    42
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    43
  [acl.allow]
8893
cc0593af30d4 acl: help improvements
Cédric Duval <cedricduval@free.fr>
parents: 8873
diff changeset
    44
  # If acl.allow is not present, all users are allowed by default.
cc0593af30d4 acl: help improvements
Cédric Duval <cedricduval@free.fr>
parents: 8873
diff changeset
    45
  # An empty acl.allow section means no users allowed.
8873
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    46
  docs/** = doc_writer
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    47
  .hgtags = release_engineer
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    48
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    49
  [acl.deny]
8893
cc0593af30d4 acl: help improvements
Cédric Duval <cedricduval@free.fr>
parents: 8873
diff changeset
    50
  # If acl.deny is not present, no users are refused by default.
cc0593af30d4 acl: help improvements
Cédric Duval <cedricduval@free.fr>
parents: 8873
diff changeset
    51
  # An empty acl.deny section means all users allowed.
8873
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    52
  glob pattern = user4, user5
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    53
   ** = user6
e872ef2e6758 help: add/fix docstrings for a bunch of extensions
Dirkjan Ochtman <dirkjan@ochtman.nl>
parents: 8846
diff changeset
    54
'''
2344
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
    55
3891
6b4127c7d52a Simplify i18n imports
Matt Mackall <mpm@selenic.com>
parents: 3877
diff changeset
    56
from mercurial.i18n import _
8566
744d6322b05b match: change all users of util.matcher to match.match
Matt Mackall <mpm@selenic.com>
parents: 8225
diff changeset
    57
from mercurial import util, match
8846
b30775386d40 acl: support for getting authenticated user from web server (issue298)
Henrik Stuart <hg@hstuart.dk>
parents: 8682
diff changeset
    58
import getpass, urllib
2344
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
    59
6766
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    60
def buildmatch(ui, repo, user, key):
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    61
    '''return tuple of (match function, list enabled).'''
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    62
    if not ui.has_section(key):
9467
4c041f1ee1b4 do not attempt to translate ui.debug output
Martin Geisler <mg@lazybytes.net>
parents: 9250
diff changeset
    63
        ui.debug('acl: %s not enabled\n' % key)
6766
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    64
        return None
2344
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
    65
6766
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    66
    pats = [pat for pat, users in ui.configitems(key)
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    67
            if user in users.replace(',', ' ').split()]
9467
4c041f1ee1b4 do not attempt to translate ui.debug output
Martin Geisler <mg@lazybytes.net>
parents: 9250
diff changeset
    68
    ui.debug('acl: %s enabled, %d entries for user %s\n' %
6766
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    69
             (key, len(pats), user))
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    70
    if pats:
8567
fea40a677d43 match: add some default args
Matt Mackall <mpm@selenic.com>
parents: 8566
diff changeset
    71
        return match.match(repo.root, '', pats)
8682
cc7da5aae4cd match: remove match.never
Matt Mackall <mpm@selenic.com>
parents: 8567
diff changeset
    72
    return match.exact(repo.root, '', [])
8566
744d6322b05b match: change all users of util.matcher to match.match
Matt Mackall <mpm@selenic.com>
parents: 8225
diff changeset
    73
2344
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
    74
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
    75
def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
    76
    if hooktype != 'pretxnchangegroup':
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
    77
        raise util.Abort(_('config error - hook type "%s" cannot stop '
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
    78
                           'incoming changesets') % hooktype)
6766
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    79
    if source not in ui.config('acl', 'sources', 'serve').split():
9467
4c041f1ee1b4 do not attempt to translate ui.debug output
Martin Geisler <mg@lazybytes.net>
parents: 9250
diff changeset
    80
        ui.debug('acl: changes have source "%s" - skipping\n' % source)
2344
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
    81
        return
ae12e5a2c4a3 add acl extension, to limit who can push to subdirs of central repo.
Vadim Gelfer <vadim.gelfer@gmail.com>
parents:
diff changeset
    82
8846
b30775386d40 acl: support for getting authenticated user from web server (issue298)
Henrik Stuart <hg@hstuart.dk>
parents: 8682
diff changeset
    83
    user = None
b30775386d40 acl: support for getting authenticated user from web server (issue298)
Henrik Stuart <hg@hstuart.dk>
parents: 8682
diff changeset
    84
    if source == 'serve' and 'url' in kwargs:
b30775386d40 acl: support for getting authenticated user from web server (issue298)
Henrik Stuart <hg@hstuart.dk>
parents: 8682
diff changeset
    85
        url = kwargs['url'].split(':')
b30775386d40 acl: support for getting authenticated user from web server (issue298)
Henrik Stuart <hg@hstuart.dk>
parents: 8682
diff changeset
    86
        if url[0] == 'remote' and url[1].startswith('http'):
9018
5ed463d0ebdb acl: read correct index into url for username (issue298)
Henrik Stuart <hg@hstuart.dk>
parents: 8935
diff changeset
    87
            user = urllib.unquote(url[3])
8846
b30775386d40 acl: support for getting authenticated user from web server (issue298)
Henrik Stuart <hg@hstuart.dk>
parents: 8682
diff changeset
    88
b30775386d40 acl: support for getting authenticated user from web server (issue298)
Henrik Stuart <hg@hstuart.dk>
parents: 8682
diff changeset
    89
    if user is None:
b30775386d40 acl: support for getting authenticated user from web server (issue298)
Henrik Stuart <hg@hstuart.dk>
parents: 8682
diff changeset
    90
        user = getpass.getuser()
b30775386d40 acl: support for getting authenticated user from web server (issue298)
Henrik Stuart <hg@hstuart.dk>
parents: 8682
diff changeset
    91
6766
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    92
    cfg = ui.config('acl', 'config')
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    93
    if cfg:
8142
912bfef12ba6 ui: fold readsections into readconfig
Matt Mackall <mpm@selenic.com>
parents: 6766
diff changeset
    94
        ui.readconfig(cfg, sections = ['acl.allow', 'acl.deny'])
6766
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    95
    allow = buildmatch(ui, repo, user, 'acl.allow')
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    96
    deny = buildmatch(ui, repo, user, 'acl.deny')
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    97
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    98
    for rev in xrange(repo[node], len(repo)):
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
    99
        ctx = repo[rev]
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
   100
        for f in ctx.files():
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
   101
            if deny and deny(f):
9467
4c041f1ee1b4 do not attempt to translate ui.debug output
Martin Geisler <mg@lazybytes.net>
parents: 9250
diff changeset
   102
                ui.debug('acl: user %s denied on %s\n' % (user, f))
6766
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
   103
                raise util.Abort(_('acl: access denied for changeset %s') % ctx)
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
   104
            if allow and not allow(f):
9467
4c041f1ee1b4 do not attempt to translate ui.debug output
Martin Geisler <mg@lazybytes.net>
parents: 9250
diff changeset
   105
                ui.debug('acl: user %s not allowed on %s\n' % (user, f))
6766
e81d2bd66908 acl: refactoring
Matt Mackall <mpm@selenic.com>
parents: 6750
diff changeset
   106
                raise util.Abort(_('acl: access denied for changeset %s') % ctx)
9467
4c041f1ee1b4 do not attempt to translate ui.debug output
Martin Geisler <mg@lazybytes.net>
parents: 9250
diff changeset
   107
        ui.debug('acl: allowing changeset %s\n' % ctx)