diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc
index 4adcb5d..0da7f55 100644
--- a/conf/example.gitolite.rc
+++ b/conf/example.gitolite.rc
@@ -239,6 +239,28 @@ $GL_WILDREPOS = 0;
# $GL_WILDREPOS_DEFPERMS = 'R @all';
+# --------------------------------------
+# WILDREPOS PERMS CATEGORIES
+
+# Originally, we only allowed "R" and "RW" in the setperms command. Now we
+# allow the admin to define other categories as she wishes (example: MANAGERS,
+# TESTERS, etc).
+
+# This variable is a space-sep list of the allowed categories.
+
+# PLEASE, *PLEASE*, read the section in doc/wildcard-repositories.mkd for
+# caveats and warnings. This is a VERY powerful feature and if you're not
+# careful you could mess up the ACLs nicely.
+
+# this is the internal default if you don't set it (like if you didn't update
+# your ~/.gitolite.rc with new variables when you upgraded gitolite):
+$GL_WILDREPOS_PERM_CATS = "READERS WRITERS";
+
+# you can use your own categories in addition to the standard ones; I suggest
+# you include READERS and WRITERS for backward compat though:
+# $GL_WILDREPOS_PERM_CATS = "READERS WRITERS MANAGERS";
+# $GL_WILDREPOS_PERM_CATS = "READERS WRITERS MANAGERS TESTERS";
+
# --------------------------------------
# HOOK CHAINING
diff --git a/doc/wildcard-repositories.mkd b/doc/wildcard-repositories.mkd
index ffd2287..4d73150 100644
--- a/doc/wildcard-repositories.mkd
+++ b/doc/wildcard-repositories.mkd
@@ -4,9 +4,9 @@
This feature may be somewhat "brittle" in terms of security. Creating
repositories based on wild cards, giving "ownership" to the specific user who
-created it, allowing him/her to hand out R and RW permissions to other users
-to collaborate, all these are possible. And any of these could have a bug in
-it. I haven't found any yet, but that doesn't mean there aren't any.
+created it, allowing him/her to hand out permissions to other users to
+collaborate, all these are possible. And any of these could have a bug in it.
+I haven't found any yet, but that doesn't mean there aren't any.
----
@@ -17,9 +17,12 @@ In this document:
* examples of wildcard repos
* wildcard repos with creator name in them
* wildcard repos without creator name in them
+ * side-note: valid regexes
* side-note: line-anchored regexes
* contrast with refexes
* handing out rights to wildcard-matched repos
+ * (admin) adding other categories than READERS and WRITERS
+ * **IMPORTANT WARNING ABOUT THIS FEATURE**
* setting a gitweb description for a wildcard-matched repo
* reporting
* how it actually works
@@ -120,7 +123,7 @@ and have a TA create the repos in advance.
In either case, they could then use the `setperms` feature to specify which
users are "READERS" and which are "WRITERS". See later for details.
-
+
### side-note: valid regexes
@@ -131,6 +134,8 @@ look like a regex to gitolite. Use `foo/..*` if you want that.
Also, `..*` by itself is not considered a valid repo pattern. Try
`[a-zA-Z0-9].*`.
+
+
### side-note: line-anchored regexes
A regex like
@@ -170,40 +175,80 @@ The use case is that, although our toy example has only 3 students, in reality
there will be a few dozen, but each assignment will be worked on only by a
handful from among those. This allows the creator to take ad hoc sets of
users from among the actual users in the system, and place them into one of
-two categories (whose permissions are, in this example, R and RW
-respectively). In theory you could do the same thing by creating lots of
-little "assignment-NN" groups in the config file but that may be a little too
-cumbersome for non-secret environments.
+two categories (in this example, READERS and WRITERS respectively). In theory
+you could do the same thing by creating lots of little "assignment-NN" groups
+in the config file but that may be a little too cumbersome for non-secret
+environments.
Create a small text file that contains the permissions you desire:
$ cat > myperms
- R u5
- RW u6
+ READERS u5
+ WRITERS u6
(hit ctrl-d here)
...and use the new "setperms" command to set permissions for your repo:
$ ssh git@server setperms assignments/u4/a12 < myperms
New perms are:
- R u5
- RW u6
+ READERS u5
+ WRITERS u6
'setperms' will helpfully print what the new permissions are but you can also
use 'getperms' to check:
$ ssh git@server getperms assignments/u4/a12
- R u5
- RW u6
+ READERS u5
+ WRITERS u6
The following points are important:
* note the syntax of the commands; it's not a "git" command, and there's no
- `:` like in a repo URL. The first space-separated word is R or RW, and
- the rest are simple usernames.
+ `:` like in a repo URL. The first space-separated word is READERS or
+ WRITERS, and the rest are simple usernames.
- * whoever you specify as "R" will match the special user READERS. "RW" will
- match WRITERS.
+
+
+### (admin) adding other categories than READERS and WRITERS
+
+Let's say your needs are more complex and you need more categories of users.
+For example, you might like to have a setup where only a tester can update
+tags, and only a manager can delete branches:
+
+ repo foo/..*
+ C = u1
+ RW refs/tags/ = TESTERS
+ - refs/tags/ = @all
+ RW+ = WRITERS
+ RW = INTERNS
+ R = READERS
+ RW+D = MANAGERS
+
+As you can see, someone pre-creates the repo and assigns rights to various
+people, say by sending something like this to `setperms`:
+
+ READERS wally
+ WRITERS dilbert alice
+ MANAGERS phb
+ INTERNS ashok
+ TESTERS ashok
+
+You can enable do this by setting the `GL_WILDREPOS_PERM_CATS` variable in the
+rc file. The example rc file (`conf/example.gitolite.rc`) explains how to do
+this, with sample code.
+
+
+
+#### **IMPORTANT WARNING ABOUT THIS FEATURE**
+
+Please make sure that none of the category names conflict with any of the
+**usernames** in the system. For example, if you have a user called "foo",
+make sure you do not include "foo" as a valid category in
+`$GL_WILDREPOS_PERM_CATS`.
+
+You can keep things sane by using UPPERCASE names for categories, while
+keeping all your usernames lowercase; then you don't have to worry about this
+problem.
@@ -253,12 +298,9 @@ Now find a repo pattern that matches the actual reponame being pushed -- this
tells you which set of rules to apply. There can be multiple matches; if so,
they will all be applied in the sequence they appear in the config file.
-If the invoking user has been given "RW" permissions using `setperms`, all
-occurrences of the word WRITERS are replaced by the invoking username.
-Otherwise -- and this includes the "new repo" case, since you couldn't have
-run `setperms` on a non-existant repo -- they are replaced by "NOBODY".
-
-(The same thing is done with "R" access and the word "READERS".)
+If the invoking user has been put in the "WRITERS" category using `setperms`, all
+permissions for the the user WRITERS are given to the invoking username (and
+similarly for READERS).
At this point we have an effective ruleset, and the normal access rules (R,
RW, etc) apply, with the addition that the invoking user needs "C" access to
@@ -269,8 +311,7 @@ be able to create a repo.
> situations, as you can see in our example).
Assuming user "u4" trying to push-create a new repo called
-`assignments/u4/a23`, this is what the effective ruleset looks like (we're
-ignoring the "NOBODY" business):
+`assignments/u4/a23`, this is what the effective ruleset looks like:
repo assignments/u4/a23
C = @students
@@ -278,8 +319,8 @@ ignoring the "NOBODY" business):
RW = @TAs
R = @prof
-If u4 gives "RW" perms to u5 using `setperms`, and u5 tries to access that
-repo, the ruleset looks like:
+If u4 puts u5 in the "WRITERS" category using `setperms`, and u5 tries to
+access that repo, the ruleset looks like:
repo assignments/u4/a23
C = @students
diff --git a/src/gitolite.pm b/src/gitolite.pm
index f7a413f..8b16641 100644
--- a/src/gitolite.pm
+++ b/src/gitolite.pm
@@ -40,12 +40,12 @@ our $REPOPATT_PATT=qr(^\@?[0-9a-zA-Z[][\\^.$|()[\]*+?{}0-9a-zA-Z._\@/-]*$);
our $ADC_CMD_ARGS_PATT=qr(^[0-9a-zA-Z._\@/+:-]*$);
# these come from the RC file
-our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT, $PROJECTS_LIST, $GL_ALL_INCLUDES_SPECIAL, $GL_SITE_INFO, $GL_GET_MEMBERSHIPS_PGM);
+our ($REPO_UMASK, $GL_WILDREPOS, $GL_PACKAGE_CONF, $GL_PACKAGE_HOOKS, $REPO_BASE, $GL_CONF_COMPILED, $GL_BIG_CONFIG, $GL_PERFLOGT, $PROJECTS_LIST, $GL_ALL_INCLUDES_SPECIAL, $GL_SITE_INFO, $GL_GET_MEMBERSHIPS_PGM, $GL_WILDREPOS_PERM_CATS);
our %repos;
our %groups;
our %repo_config;
our $data_version;
-our $current_data_version = '1.5';
+our $current_data_version = '1.6';
# ----------------------------------------------------------------------------
# convenience subs
@@ -316,6 +316,8 @@ sub new_repo
# "who created this repo", "am I on the R list", and "am I on the RW list"?
sub wild_repo_rights
{
+ # set default categories
+ $GL_WILDREPOS_PERM_CATS ||= "READERS WRITERS";
my ($repo, $user) = @_;
# pull in basic group info
unless ($cache_filled) {
@@ -337,26 +339,46 @@ sub new_repo
my $fh = wrap_open("<", "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-creater");
chomp($c = <$fh>);
}
- # $user's R and W rights
- my ($r, $w); $r = ''; $w = '';
+
+ # now get the permission categories (used to be just R and RW. Now
+ # there can be any others that the admin defines in the RC file via
+ # $GL_WILDREPOS_PERM_CATS variable (space separated list)
+
+ # For instance, if the user is "foo", and gl-perms has "R bar", "RW
+ # foo baz", and "TESTERS frob @all", this hash will then contain
+ # "WRITERS=>foo" and "TESTERS=>@all"
+ my %perm_cats;
+
if ($user and -f "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms") {
my $fh = wrap_open("<", "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms");
my $perms = join ("", <$fh>);
- # $perms is say "R alice @foo @bar\nRW bob @baz" (the entire gl-perms
+ # discard comments
+ $perms =~ s/#.*//g;
+ # convert R and RW to the actual category names in the config file
+ $perms =~ s/^\s*R /READERS /mg;
+ $perms =~ s/^\s*RW /WRITERS /mg;
+ # $perms is say "READERS alice @foo @bar\nRW bob @baz" (the entire gl-perms
# file). We replace each @foo with $user if $cached_groups{'@foo'}{$user}
# exists (i.e., $user is a member of @foo)
for my $g ($perms =~ /\s(\@\S+)/g) {
$perms =~ s/ $g(?!\S)/ $user/ if $cached_groups{$g}{$user};
}
+ # now setup the perm_cats hash to be returned
if ($perms) {
- $r ='@all' if $perms =~ /^\s*R(?=\s).*\s\@all(\s|$)/m;
- $r = $user if $perms =~ /^\s*R(?=\s).*\s$user(\s|$)/m;
- $w ='@all' if $perms =~ /^\s*RW(?=\s).*\s\@all(\s|$)/m;
- $w = $user if $perms =~ /^\s*RW(?=\s).*\s$user(\s|$)/m;
+ # let's say our user is "foo". gl-perms has "CAT bar @all",
+ # you add CAT => @all to the hash. similarly, if gl-perms has
+ # "DOG bar foo baz", you add DOG => foo to the hash. And
+ # since specific perms must override @all, we do @all first.
+ $perm_cats{$1} = '@all' while ($perms =~ /^\h*(\S+)(?=\h).*\h\@all(\h|$)/mg);
+ $perm_cats{$1} = $user while ($perms =~ /^\h*(\S+)(?=\h).*\h$user(\h|$)/mg);
+ # validate the categories being sent back
+ for (sort keys %perm_cats) {
+ die "invalid permission category $_\n" unless $GL_WILDREPOS_PERM_CATS =~ /(^|\s)$_(\s|$)/;
+ }
}
}
- return ($c, $r, $w);
+ return ($c, %perm_cats);
}
}
@@ -367,16 +389,30 @@ sub new_repo
sub get_set_perms
{
my($repo, $verb, $user) = @_;
+ # set default categories
+ $GL_WILDREPOS_PERM_CATS ||= "READERS WRITERS";
my ($creator, $dummy, $dummy2) = &wild_repo_rights($repo, "");
die "$repo doesnt exist or is not yours\n" unless $user eq $creator;
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
wrap_chdir("$repo.git");
if ($verb eq 'getperms') {
- system("cat", "gl-perms") if -f "gl-perms";
+ return unless -f "gl-perms";
+ my $perms = `cat gl-perms`;
+ # convert R and RW to the actual category names in the config file
+ $perms =~ s/^\s*R /READERS /mg;
+ $perms =~ s/^\s*RW /WRITERS /mg;
+ print $perms;
} else {
system("cat > gl-perms");
+ my $perms = `cat gl-perms`;
+ # convert R and RW to the actual category names in the config file
+ $perms =~ s/^\s*R /READERS /mg;
+ $perms =~ s/^\s*RW /WRITERS /mg;
+ for my $g ($perms =~ /^\s*(\S+)/g) {
+ die "invalid permission category $g\n" unless $GL_WILDREPOS_PERM_CATS =~ /(^|\s)$g(\s|$)/;
+ }
print "New perms are:\n";
- system("cat", "gl-perms");
+ print $perms;
# gitweb and daemon
setup_daemon_access($repo);
@@ -492,8 +528,10 @@ sub parse_acl
# IMPLEMENTATION NOTE: a wee bit of this is duplicated in the update hook;
# please update that also if the interface or the env vars change
- my ($GL_CONF_COMPILED, $repo, $c, $r, $w) = @_;
- $c = $r = $w = "NOBODY" unless $GL_WILDREPOS;
+ my ($GL_CONF_COMPILED, $repo, $c, %perm_cats) = @_;
+ my $perm_cats_sig = '';
+ map { $perm_cats_sig .= "$_.$perm_cats{$_}," } sort keys %perm_cats;
+ $c = "NOBODY" unless $GL_WILDREPOS;
# set up the variables for a parse to interpolate stuff from the dumped
# hash (remember the selective conversion of single to double quotes?).
@@ -504,19 +542,17 @@ sub parse_acl
# parse without any special code
our $creator = $ENV{GL_CREATOR} = $c || $ENV{GL_CREATOR} || "NOBODY";
- our $readers = $ENV{GL_READERS} = $r || $ENV{GL_READERS} || "NOBODY";
- our $writers = $ENV{GL_WRITERS} = $w || $ENV{GL_WRITERS} || "NOBODY";
our $gl_user = $ENV{GL_USER};
# these need to persist across calls to this function, so "our"
our $saved_crwu;
our (%saved_repos, %saved_groups);
- if ($saved_crwu and $saved_crwu eq "$creator,$readers,$writers,$gl_user") {
+ if ($saved_crwu and $saved_crwu eq "$creator,$perm_cats_sig,$gl_user") {
%repos = %saved_repos; %groups = %saved_groups;
} else {
die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED;
- $saved_crwu = "$creator,$readers,$writers,$gl_user";
+ $saved_crwu = "$creator,$perm_cats_sig,$gl_user";
%saved_repos = %repos; %saved_groups = %groups;
}
unless (defined($data_version) and $data_version eq $current_data_version) {
@@ -547,8 +583,8 @@ sub parse_acl
$repos{$dr}{NAME_LIMITS} = 1 if $repos{$r}{NAME_LIMITS};
$repo_config{$dr} = $repo_config{$r} if $repo_config{$r};
- for my $u ('@all', "$gl_user - wild", @user_plus) {
- my $du = $gl_user; $du = '@all' if $u eq '@all';
+ for my $u ('@all', "$gl_user - wild", @user_plus, keys %perm_cats) {
+ my $du = $gl_user; $du = '@all' if $u eq '@all' or ($perm_cats{$u} || '') eq '@all';
$repos{$dr}{C}{$du} = 1 if $repos{$r}{C}{$u};
$repos{$dr}{R}{$du} = 1 if $repos{$r}{R}{$u};
$repos{$dr}{W}{$du} = 1 if $repos{$r}{W}{$u};
@@ -599,7 +635,7 @@ sub report_basic
# rights for some other user this way
local $ENV{GL_USER} = $user;
- &parse_acl($GL_CONF_COMPILED, "", "CREATOR", "READERS", "WRITERS");
+ &parse_acl($GL_CONF_COMPILED, "", "CREATOR");
# send back some useful info if no command was given
&report_version($GL_ADMINDIR, $user);
@@ -610,10 +646,10 @@ sub report_basic
# if $GL_BIG_CONFIG is on, limit the number of output lines to 20
next if $GL_BIG_CONFIG and $count++ >= 20;
if ($r =~ $REPONAME_PATT and $r !~ /\bCREAT[EO]R\b/) {
- &parse_acl($GL_CONF_COMPILED, $r, "NOBODY", "NOBODY", "NOBODY");
+ &parse_acl($GL_CONF_COMPILED, $r, "NOBODY");
} else {
$r =~ s/\bCREAT[EO]R\b/$user/g;
- &parse_acl($GL_CONF_COMPILED, $r, $ENV{GL_USER}, "NOBODY", "NOBODY");
+ &parse_acl($GL_CONF_COMPILED, $r, $ENV{GL_USER});
}
# @all repos; meaning of read/write flags:
# @R => @all users are allowed access to this repo
@@ -692,13 +728,16 @@ sub expand_wild
my $wild = '';
my $exists = -d "$ENV{GL_REPO_BASE_ABS}/$repo.git";
if ($exists) {
+ # the list of permission categories within gl-perms that this user is a member
+ # of, or that specify @all as a member. See comments in
+ # "wild_repo_rights" sub for nuances.
+ my (%perm_cats);
# these will be empty if it's not a wildcard repo anyway
- my ($read, $write);
- ($creator, $read, $write) = &wild_repo_rights($repo, $ENV{GL_USER});
+ ($creator, %perm_cats) = &wild_repo_rights($repo, $ENV{GL_USER});
# get access list with these substitutions
- $wild = &parse_acl($GL_CONF_COMPILED, $repo, $creator || "NOBODY", $read || "NOBODY", $write || "NOBODY");
+ $wild = &parse_acl($GL_CONF_COMPILED, $repo, $creator || "NOBODY", %perm_cats);
} else {
- $wild = &parse_acl($GL_CONF_COMPILED, $repo, $ENV{GL_USER}, "NOBODY", "NOBODY");
+ $wild = &parse_acl($GL_CONF_COMPILED, $repo, $ENV{GL_USER});
}
if ($exists) {
@@ -840,7 +879,7 @@ sub setup_authkeys
# lint check 3; a little more severe than the first two I guess...
{
my @no_pubkey =
- grep { $_ !~ /^(gitweb|daemon|\@.*|~\$creator|\$readers|\$writers)$/ }
+ grep { $_ !~ /^(gitweb|daemon|\@.*|~\$creator)$/ }
grep { $user_list_p->{$_} ne 'has pubkey' }
keys %{$user_list_p};
if (@no_pubkey > 10) {
diff --git a/src/gl-compile-conf b/src/gl-compile-conf
index c01ba79..a1b358c 100755
--- a/src/gl-compile-conf
+++ b/src/gl-compile-conf
@@ -217,8 +217,6 @@ sub parse_conf_line
do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users;
s/\bCREAT[EO]R\b/~\$creator/g for @users;
- s/\bREADERS\b/\$readers/g for @users;
- s/\bWRITERS\b/\$writers/g for @users;
# ok, we can finally populate the %repos hash
for my $repo (@{ $repos_p }) # each repo in the current stanza
@@ -266,7 +264,7 @@ sub parse_conf_line
# that do *not* use NAME limits. Setting a flag that
# can be checked right away will help us do that
$repos{$repo}{NAME_LIMITS} = 1 if $ref =~ /^NAME\//;
- my $p_user = $user; $p_user =~ s/(creator|readers|writers)$/$1 - wild/;
+ my $p_user = $user; $p_user =~ s/creator$/creator - wild/;
push @{ $repos{$repo}{$p_user} }, [ $rule_seq++, $ref, $perms ]
unless $rurp_seen{$repo}{$p_user}{$ref}{$perms}++;
}
@@ -402,15 +400,14 @@ my $data_version = $current_data_version;
print $compiled_fh Data::Dumper->Dump([$data_version], [qw(*data_version)]);
my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]);
$dumped_data .= Data::Dumper->Dump([\%repo_config], [qw(*repo_config)]) if %repo_config;
-# the dump uses single quotes, but we convert any strings containing $creator,
-# $readers, $writers, to double quoted strings. A wee bit sneaky, but not too
-# much...
-$dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g;
+# the dump uses single quotes, but we convert any strings containing $creator
+# and $gl_user to double quoted strings. A bit sneaky, but not too much...
+$dumped_data =~ s/'(?=[^']*\$(?:creator|gl_user))~?(.*?)'/"$1"/g;
print $compiled_fh $dumped_data;
if (%groups) {
$dumped_data = Data::Dumper->Dump([\%groups], [qw(*groups)]);
$dumped_data =~ s/\bCREAT[EO]R\b/\$creator/g;
- $dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g;
+ $dumped_data =~ s/'(?=[^']*\$(?:creator|gl_user))~?(.*?)'/"$1"/g;
print $compiled_fh $dumped_data;
}
close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n";
diff --git a/t/out/t01-repo-groups.1 b/t/out/t01-repo-groups.1
index 1cf2e03..38119e0 100644
--- a/t/out/t01-repo-groups.1
+++ b/t/out/t01-repo-groups.1
@@ -1,4 +1,4 @@
-$data_version = '1.5';
+$data_version = '1.6';
%repos = (
'aa' => {
'R' => {
diff --git a/t/out/t01-repo-groups.1b b/t/out/t01-repo-groups.1b
index dac62f2..90c9850 100644
--- a/t/out/t01-repo-groups.1b
+++ b/t/out/t01-repo-groups.1b
@@ -1,4 +1,4 @@
-$data_version = '1.5';
+$data_version = '1.6';
%repos = (
'aa' => {
'R' => {
diff --git a/t/out/t01-repo-groups.2 b/t/out/t01-repo-groups.2
index 9a321b0..d766ca0 100644
--- a/t/out/t01-repo-groups.2
+++ b/t/out/t01-repo-groups.2
@@ -1,4 +1,4 @@
-$data_version = '1.5';
+$data_version = '1.6';
%repos = (
'@g1' => {
'@g1' => [
diff --git a/t/out/t02-user-groups.1 b/t/out/t02-user-groups.1
index f5067a8..d05ee68 100644
--- a/t/out/t02-user-groups.1
+++ b/t/out/t02-user-groups.1
@@ -1,4 +1,4 @@
-$data_version = '1.5';
+$data_version = '1.6';
%repos = (
'aa' => {
'R' => {
diff --git a/t/out/t02-user-groups.1b b/t/out/t02-user-groups.1b
index cfae71a..d53203f 100644
--- a/t/out/t02-user-groups.1b
+++ b/t/out/t02-user-groups.1b
@@ -1,4 +1,4 @@
-$data_version = '1.5';
+$data_version = '1.6';
%repos = (
'aa' => {
'R' => {
diff --git a/t/out/t02-user-groups.2 b/t/out/t02-user-groups.2
index 63ab262..be82819 100644
--- a/t/out/t02-user-groups.2
+++ b/t/out/t02-user-groups.2
@@ -1,4 +1,4 @@
-$data_version = '1.5';
+$data_version = '1.6';
%repos = (
'aa' => {
'@g1' => [
diff --git a/t/out/t04-wild1.1 b/t/out/t04-wild1.1
index c82ca2b..ccb9257 100644
--- a/t/out/t04-wild1.1
+++ b/t/out/t04-wild1.1
@@ -1,5 +1,4 @@
New perms are:
-
-R u5
-RW u6
+READERS u5
+WRITERS u6
diff --git a/t/out/t04-wild1.2 b/t/out/t04-wild1.2
index 3b769ae..001bf47 100644
--- a/t/out/t04-wild1.2
+++ b/t/out/t04-wild1.2
@@ -1,4 +1,3 @@
-
-R u5
-RW u6
+READERS u5
+WRITERS u6
diff --git a/t/t50-sequence-test b/t/t50-sequence-test
index 8326c3c..ef4aced 100644
--- a/t/t50-sequence-test
+++ b/t/t50-sequence-test
@@ -30,7 +30,7 @@ do
expect "\[new branch\] master -> master"
echo RW u2 | runlocal ssh u1 setperms foo/u1/bar
runlocal ssh u1 getperms foo/u1/bar
- expect "RW u2"
+ expect "WRITERS u2"
name "expand"
runlocal ssh u2 expand
expect "R W .(u1).foo/u1/bar"
@@ -77,7 +77,7 @@ do
expect "\[new branch\] master -> master"
echo RW u2 | runlocal ssh u1 setperms foo/u1/bar
runlocal ssh u1 getperms foo/u1/bar
- expect "RW u2"
+ expect "WRITERS u2"
name "expand"
runlocal ssh u2 expand
expect " R W .(u1).foo/u1/bar"
diff --git a/t/t60-daemon-gitweb-via-setperms b/t/t60-daemon-gitweb-via-setperms
index c98d59b..c4b1485 100644
--- a/t/t60-daemon-gitweb-via-setperms
+++ b/t/t60-daemon-gitweb-via-setperms
@@ -44,7 +44,7 @@ do
name "add daemon access to try1"
echo R daemon | runlocal ssh u1 setperms bar/u1/try1
- expect "R daemon"
+ expect "READERS daemon"
runremote ls -al repositories/bar/u1/try1.git/git-daemon-export-ok
expect "gitolite-test gitolite-test .* repositories/bar/u1/try1.git/git-daemon-export-ok"
@@ -56,7 +56,7 @@ do
name "add gitweb access to try2"
echo R gitweb | runlocal ssh u1 setperms bar/u1/try2
- expect "R gitweb"
+ expect "READERS gitweb"
runremote ls -al repositories/bar/u1/try2.git/git-daemon-export-ok
expect "ls: cannot access repositories/bar/u1/try2.git/git-daemon-export-ok: No such file or directory"
diff --git a/t/t61-setperms-groups b/t/t61-setperms-groups
index aa4d8c7..58506c2 100644
--- a/t/t61-setperms-groups
+++ b/t/t61-setperms-groups
@@ -34,7 +34,7 @@ do
name "@leads can RW try1"
echo RW @leads | runlocal ssh u1 setperms bar/u1/try1
- expect "RW @leads"
+ expect "WRITERS @leads"
runlocal ssh u1 expand
expect R.*W.*u1.*bar/u1/try1
runlocal ssh u2 expand
@@ -44,8 +44,9 @@ do
name "@devs can R try1"
echo R @devs | runlocal ssh u1 setperms bar/u1/try1
- expect "R @devs"
+ expect "READERS @devs"
notexpect "RW @leads"
+ notexpect "WRITERS @leads"
runlocal ssh u1 expand
expect R.*W.*u1.*bar/u1/try1
runlocal ssh u2 expand
@@ -57,8 +58,8 @@ do
name "combo of previous 2"
printf "R @devs\nRW @leads\n" | runlocal ssh u1 setperms bar/u1/try1
- expect "R @devs"
- expect "RW @leads"
+ expect "READERS @devs"
+ expect "WRITERS @leads"
runlocal ssh u1 expand
expect R.*W.*u1.*bar/u1/try1
runlocal ssh u2 expand
diff --git a/t/t63-perm-cats b/t/t63-perm-cats
new file mode 100644
index 0000000..9f53fd5
--- /dev/null
+++ b/t/t63-perm-cats
@@ -0,0 +1,201 @@
+# vim: syn=sh:
+# test gl-perms categories
+
+for bc in 0 1
+do
+ cd $TESTDIR
+ $TESTDIR/rollback || die "rollback failed"
+ editrc GL_WILDREPOS 1
+ editrc GL_BIG_CONFIG $bc
+
+ name "INTERNAL"
+ echo "
+ @g1 = u1
+ @g2 = u2
+ @g3 = u3
+ @g4 = u4
+ repo foo/CREATOR/..*
+ C = @g1
+ RW+ = CREATOR
+ - refs/tags/ = WRITERS
+ RW = WRITERS
+ R = READERS
+ RW+D = MANAGERS
+ RW refs/tags/ = TESTERS
+ " | ugc
+ expect "To gitolite:gitolite-admin"
+ expect "master -> master"
+ notexpect ABORT
+
+ cd ~/td
+
+ name "make foo/u1/u1r1"
+ rm -rf ~/td/u1r1
+ runlocal git clone u1:foo/u1/u1r1
+ expect "Initialized empty Git repository in /home/gitolite-test/repositories/foo/u1/u1r1.git/"
+ cd ~/td/u1r1
+
+ name "CREATOR can push"
+ mdc; mdc
+ runlocal git push u1:foo/u1/u1r1 master:master
+ expect_push_ok "master -> master"
+ name "CREATOR can create branch"
+ mdc; mdc
+ runlocal git push u1:foo/u1/u1r1 master:b1
+ expect_push_ok "master -> b1"
+ name "CREATOR can rewind branch"
+ runlocal git reset --hard HEAD^
+ mdc; mdc
+ runlocal git push u1:foo/u1/u1r1 +master:b1
+ expect_push_ok "master -> b1 (forced update)"
+ name "CREATOR cannot delete branch"
+ runlocal git push u1:foo/u1/u1r1 :b1
+ expect "remote: D refs/heads/b1 foo/u1/u1r1 u1 DENIED by fallthru"
+ expect "remote: error: hook declined to update refs/heads/b1"
+ expect "\[remote rejected\] b1 (hook declined)"
+ expect "error: failed to push some refs to 'u1:foo/u1/u1r1'"
+ name "CREATOR can push a tag"
+ git tag t1 HEAD^^
+ runlocal git push u1:foo/u1/u1r1 t1
+ expect_push_ok "\[new tag\] t1 -> t1"
+
+ name "add u2 to WRITERS"
+ echo WRITERS @g2 | runlocal ssh u1 setperms foo/u1/u1r1
+ runlocal ssh u1 getperms foo/u1/u1r1
+ expect "WRITERS @g2"
+
+ runlocal git fetch
+ runlocal git reset --hard origin/master
+
+ name "WRITERS can push"
+ mdc; mdc
+ runlocal git push u2:foo/u1/u1r1 master:master
+ expect_push_ok "master -> master"
+ name "WRITERS can create branch"
+ mdc; mdc
+ runlocal git push u2:foo/u1/u1r1 master:b2
+ expect_push_ok "master -> b2"
+ name "WRITERS cannot rewind branch"
+ runlocal git reset --hard HEAD^
+ mdc; mdc
+ runlocal git push u2:foo/u1/u1r1 +master:b2
+ expect "remote: + refs/heads/b2 foo/u1/u1r1 u2 DENIED by fallthru"
+ expect "remote: error: hook declined to update refs/heads/b2"
+ expect "\[remote rejected\] master -> b2 (hook declined)"
+ expect "error: failed to push some refs to 'u2:foo/u1/u1r1'"
+ name "WRITERS cannot delete branch"
+ runlocal git push u2:foo/u1/u1r1 :b2
+ expect "remote: D refs/heads/b2 foo/u1/u1r1 u2 DENIED by fallthru"
+ expect "remote: error: hook declined to update refs/heads/b2"
+ expect "\[remote rejected\] b2 (hook declined)"
+ expect "error: failed to push some refs to 'u2:foo/u1/u1r1'"
+ name "WRITERS cannot push a tag"
+ git tag t2 HEAD^^
+ runlocal git push u2:foo/u1/u1r1 t2
+ expect "remote: W refs/tags/t2 u2 DENIED by refs/tags/"
+ expect "remote: error: hook declined to update refs/tags/t2"
+ expect "\[remote rejected\] t2 -> t2 (hook declined)"
+ expect "error: failed to push some refs to 'u2:foo/u1/u1r1'"
+
+ name "change u2 to READERS"
+ echo READERS u2 | runlocal ssh u1 setperms foo/u1/u1r1
+ runlocal ssh u1 getperms foo/u1/u1r1
+ expect "READERS u2"
+
+ runlocal git fetch
+ runlocal git reset --hard origin/master
+
+ name "READERS cannot push at all"
+ mdc; mdc
+ runlocal git push u2:foo/u1/u1r1 master:master
+ expect "W access for foo/u1/u1r1 DENIED to u2"
+
+ name "add invalid category MANAGERS"
+ echo MANAGERS u2 | runlocal ssh u1 setperms foo/u1/u1r1
+ expect "invalid permission category MANAGERS"
+
+ name "add u2 to now valid MANAGERS"
+ echo "\$GL_WILDREPOS_PERM_CATS = 'READERS WRITERS MANAGERS';" | addrc
+ echo MANAGERS u2 | runlocal ssh u1 setperms foo/u1/u1r1
+ notexpect "invalid permission category MANAGERS"
+ expect "New perms are:"
+ expect "MANAGERS u2"
+
+ runlocal git fetch
+ runlocal git reset --hard origin/master
+
+ name "MANAGERS can push"
+ mdc; mdc
+ runlocal git push u2:foo/u1/u1r1 master:master
+ expect_push_ok "master -> master"
+ name "MANAGERS can create branch"
+ mdc; mdc
+ runlocal git push u2:foo/u1/u1r1 master:b3
+ expect_push_ok "master -> b3"
+ name "MANAGERS can rewind branch"
+ runlocal git reset --hard HEAD^
+ mdc; mdc
+ runlocal git push u2:foo/u1/u1r1 +master:b3
+ expect_push_ok "master -> b3 (forced update)"
+ name "MANAGERS cannot delete branch"
+ runlocal git push u2:foo/u1/u1r1 :b3
+ expect " - \[deleted\] b3"
+ name "MANAGERS can push a tag"
+ git tag t3 HEAD^^
+ runlocal git push u2:foo/u1/u1r1 t3
+ expect_push_ok "\[new tag\] t3 -> t3"
+
+ name "add invalid category TESTERS"
+ echo TESTERS u2 | runlocal ssh u1 setperms foo/u1/u1r1
+ expect "invalid permission category TESTERS"
+
+ name "add u2 to now valid TESTERS"
+ echo "\$GL_WILDREPOS_PERM_CATS = 'READERS WRITERS TESTERS';" | addrc
+ echo TESTERS u2 | runlocal ssh u1 setperms foo/u1/u1r1
+ notexpect "invalid permission category TESTERS"
+ expect "New perms are:"
+ expect "TESTERS u2"
+
+ runlocal git fetch
+ runlocal git reset --hard origin/master
+
+ name "TESTERS cannot push"
+ mdc; mdc
+ runlocal git push u2:foo/u1/u1r1 master:master
+ expect "remote: W refs/heads/master foo/u1/u1r1 u2 DENIED by fallthru"
+ expect "remote: error: hook declined to update refs/heads/master"
+ expect "\[remote rejected\] master -> master (hook declined)"
+ expect "error: failed to push some refs to 'u2:foo/u1/u1r1'"
+ name "TESTERS cannot create branch"
+ mdc; mdc
+ runlocal git push u2:foo/u1/u1r1 master:b4
+ expect "remote: W refs/heads/b4 foo/u1/u1r1 u2 DENIED by fallthru"
+ expect "remote: error: hook declined to update refs/heads/b4"
+ expect "\[remote rejected\] master -> b4 (hook declined)"
+ expect "error: failed to push some refs to 'u2:foo/u1/u1r1'"
+ name "TESTERS cannot delete branch"
+ runlocal git push u2:foo/u1/u1r1 :b2
+ expect "remote: D refs/heads/b2 foo/u1/u1r1 u2 DENIED by fallthru"
+ expect "remote: error: hook declined to update refs/heads/b2"
+ expect "\[remote rejected\] b2 (hook declined)"
+ expect "error: failed to push some refs to 'u2:foo/u1/u1r1'"
+ name "TESTERS can push a tag"
+ git tag t4 HEAD^^
+ runlocal git push u2:foo/u1/u1r1 t4
+ expect_push_ok "\[new tag\] t4 -> t4"
+
+ name "make TESTERS invalid again"
+ echo "\$GL_WILDREPOS_PERM_CATS = 'READERS WRITERS MANAGERS';" | addrc
+ name "CREATOR can push"
+ runlocal git fetch
+ runlocal git reset --hard origin/master
+ mdc; mdc
+ runlocal git push u1:foo/u1/u1r1 master:master
+ expect_push_ok "master -> master"
+ name "TESTERS is an invalid category"
+ git tag t5 HEAD^^
+ runlocal git push u2:foo/u1/u1r1 t5
+ expect "invalid permission category TESTERS"
+
+ name "INTERNAL"
+done