NAME-based restrictions

Gitolite allows you to restrict changes by file/dir name.  The syntax
for this used "PATH/" as a prefix to denote such file/dir patterns.
This has now been changed to "NAME/" because PATH is potentially
confusing.

While this is technically a backward-incompatible change, the feature
itself was hitherto undocumented, and only a few people were using it,
so I guess it's not that bad...

Also added documentation now.
This commit is contained in:
Sitaram Chamarty 2010-01-07 17:59:34 +05:30
parent ab3c861241
commit 7124faa9f3
4 changed files with 54 additions and 10 deletions

View file

@ -165,6 +165,40 @@ repo git
# looking for (`W` or `+`), or a minus (`-`), results in success, or failure,
# respectively. A fallthrough also results in failure
# FILE/DIR NAME BASED RESTRICTIONS
# --------------------------------
# Here's a hopefully self-explanatory example. Assume the project has the
# following contents at the top level: a README, a "doc/" directory, and an
# "src/" directory.
repo foo
RW+ = lead_dev # rule 1
RW = dev1 dev2 dev3 dev4 # rule 2
RW NAME/ = lead_dev # rule 3
RW NAME/doc/ = dev1 dev2 # rule 4
RW NAME/src/ = dev1 dev2 dev3 dev4 # rule 5
# Notes
# - the "NAME/" is part of the syntax; think of it as a keyword if you like
# - file/dir NAME-based restrictions are *in addition* to normal (branch-name
# based) restrictions; they are not a *replacement* for them. This is why
# rule #2 (or something like it, maybe with a more specific branch-name) is
# needed; without it, dev1/2/3/4 cannot push any branches.
# - if a repo has *any* NAME/ rules, then NAME-based restrictions are checked
# for *all* users. This is why rule 3 is needed, even though we don't
# actually have any NAME-based restrictions on lead_dev. Notice the pattern
# on rule 3.
# - *each* file touched by the commits being pushed is checked against those
# rules. So, lead_dev can push changes to any files, dev1/2 can push
# changes to files in "doc/" and "src/" (but not the top level README), and
# dev3/4 can only push changes to files in "src/".
# GITWEB AND DAEMON STUFF
# -----------------------

View file

@ -13,6 +13,7 @@ In this document:
* differences from gitosis
* simpler syntax
* two levels of access rights checking
* file/dir NAME based restrictions
* error checking the config file
* delegating parts of the config file
* easier to specify gitweb "description" and gitweb/daemon access
@ -235,6 +236,15 @@ any of the refexes match, the push succeeds. If none of them match, it fails.
Gitolite also allows "exclude" or "deny" rules. See later in this document
for details.
#### file/dir NAME based restrictions
In addition to branch-name based restrictions, gitolite also allows you to
restrict what files or directories can be involved in changes being pushed.
This basically uses `git diff --name-only` to obtain the list of files being
changed, treating each filename as a "ref" to be matched.
Please see `conf/example.conf` for syntax and examples.
#### error checking the config file
gitosis does not do any. I just found out that if you mis-spell `members` as

View file

@ -200,9 +200,9 @@ sub parse_conf_file
# if no ref is given, this PERM applies to all refs
@refs = qw(refs/.*) unless @refs;
# fully qualify refs that dont start with "refs/" or "PATH/";
# fully qualify refs that dont start with "refs/" or "NAME/";
# prefix them with "refs/heads/"
@refs = map { m(^(refs|PATH)/) or s(^)(refs/heads/); $_ } @refs;
@refs = map { m(^(refs|NAME)/) or s(^)(refs/heads/); $_ } @refs;
# expand the user list, unless it is just "@all"
@users = expand_list ( @users )
@ -239,13 +239,13 @@ sub parse_conf_file
# for 2nd level check, store each "ref, perms" pair in order
for my $ref (@refs)
{
# checking PATH based restrictions is expensive for
# checking NAME based restrictions is expensive for
# the update hook (see the changes to src/hooks/update
# in this commit for why) so we would *very* much like
# to avoid doing it for the large majority of repos
# that do *not* use PATH limits. Setting a flag that
# that do *not* use NAME limits. Setting a flag that
# can be checked right away will help us do that
$repos{$repo}{PATH_LIMITS} = 1 if $ref =~ /^PATH\//;
$repos{$repo}{NAME_LIMITS} = 1 if $ref =~ /^NAME\//;
push @{ $repos{$repo}{$user} }, { $ref => $perms }
unless $rurp_seen{$repo}{$user}{$ref}{$perms}++;
}

View file

@ -65,12 +65,12 @@ push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] };
# prepare the list of refs to be checked
# previously, we just checked $ref -- the ref being updated, which is passed
# to us by git (see man githooks). Now we also have to treat each PATH being
# updated as a potential "ref" and check that, if PATH-based restrictions have
# to us by git (see man githooks). Now we also have to treat each NAME being
# updated as a potential "ref" and check that, if NAME-based restrictions have
# been specified
my @refs = ($ref); # the first ref to check is the real one
if (exists $repos{$ENV{GL_REPO}}{PATH_LIMITS}) {
if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) {
# this is special to git -- the hash of an empty tree
my $empty='4b825dc642cb6eb9a060e54bf8d69288fbee4904';
# well they're not really "trees" but $empty is indeed the empty tree so
@ -78,7 +78,7 @@ if (exists $repos{$ENV{GL_REPO}}{PATH_LIMITS}) {
# diff' only wants trees
my $oldtree = $oldsha eq '0' x 40 ? $empty : $oldsha;
my $newtree = $newsha eq '0' x 40 ? $empty : $newsha;
push @refs, map { chomp; s/^/PATH\//; $_; } `git diff --name-only $oldtree $newtree`;
push @refs, map { chomp; s/^/NAME\//; $_; } `git diff --name-only $oldtree $newtree`;
}
my $refex = '';
@ -111,7 +111,7 @@ sub check_ref {
# and in this version, we have many "refs" to check. The one we print in the
# log is the *first* one (which is a *real* ref, like refs/heads/master),
# while all the rest (if they exist) are like PATH/something. So we do the
# while all the rest (if they exist) are like NAME/something. So we do the
# first one separately to capture it, then run the rest (if any)
my $log_refex = check_ref(shift @refs);
check_ref($_) for @refs;