summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSitaram Chamarty <sitaram@atc.tcs.com>2013-03-03 18:56:41 +0530
committerSitaram Chamarty <sitaram@atc.tcs.com>2013-03-05 11:55:59 +0530
commit79714c9c85d1321e6f4b0b3a3617bdb3503a98e1 (patch)
tree6347f476fb3e95ab08e7867279c8c97037f73af4
parentrc file format change: some inline doc/help text fixups (diff)
downloadgitolite-gentoo-79714c9c85d1321e6f4b0b3a3617bdb3503a98e1.tar.gz
gitolite-gentoo-79714c9c85d1321e6f4b0b3a3617bdb3503a98e1.tar.bz2
gitolite-gentoo-79714c9c85d1321e6f4b0b3a3617bdb3503a98e1.zip
refex-expr
-rwxr-xr-xsrc/VREF/refex-expr104
-rw-r--r--src/lib/Gitolite/Rc.pm3
-rw-r--r--src/lib/Gitolite/Triggers/RefexExpr.pm2
-rw-r--r--src/syntactic-sugar/refex-expr39
-rwxr-xr-xt/refex-expr-test-162
-rwxr-xr-xt/refex-expr-test-260
-rwxr-xr-xt/refex-expr-test-355
-rwxr-xr-xt/refex-expr-test-9107
8 files changed, 390 insertions, 42 deletions
diff --git a/src/VREF/refex-expr b/src/VREF/refex-expr
index d0a51b7..8403469 100755
--- a/src/VREF/refex-expr
+++ b/src/VREF/refex-expr
@@ -2,6 +2,9 @@
use strict;
use warnings;
+# see bottom of this file for instructons and IMPORTANT WARNINGS!
+# ----------------------------------------------------------------------
+
my $rule = $ARGV[7];
die "\n\nFATAL: GL_REFEX_EXPR_ doesn't exist\n(your admin probably forgot the rc file change needed for this to work)\n\n"
unless exists $ENV{"GL_REFEX_EXPR_" . $rule};
@@ -12,64 +15,85 @@ exit 0;
__END__
-Documentation for the refex-expression evaluation feature
+------------------------------------------------------------------------
+IMPORTANT WARNINGS:
+ * has not been tested heavily
+ * SO PLEASE TEST YOUR SPECIFIC USE CASE THOROUGHLY!
+ * read the NOTES section below
+ * syntax and semantics are to be considered beta and may change as I find
+ better use cases
+------------------------------------------------------------------------
-First, make sure you have both the VREF and the trigger scripts
-(src/VREF/refex-expr and src/lib/Gitolite/Triggers/RefexExpr.pm)
+Refex expressions, like VREFs, are best used as additional "deny" rules, to
+deny combinations that the normal ruleset cannot detect.
-Next, add this to the ACCESS_2 list in the rc file:
+To enable this, uncomment 'refex-expr' in the ENABLE list in the rc file.
- 'RefexExpr::access_2',
+It allows you to say things like "don't allow users u3 and u4 to change the
+Makefile in the master branch" (i.e., they can change any other file in
+master, or the Makefile in any other branch, but not that specific combo).
-For the rest, we'll use this example:
+ repo foo
+ RW+ = u1 u2 # line 1
- * user u1 can push foo to some other branch, and anything else to the master
- branch, but not foo to the master branch
+ RW+ master = u3 u4 # line 2
+ RW+ = u3 u4 # line 3
+ RW+ VREF/NAME/Makefile = u3 u4 # line 4
+ - master and VREF/NAME/Makefile = u3 u4 # line 5
- * user u2 is allowed to push either 'doc/' or 'src/' but not both
+Line 5 is a "refex expression". Here are the rules:
+
+ * for each refex in the expression ("master" and "VREF/NAME/Makefile" in
+ this example), a count is kept of the number of times the EXACT refex was
+ matched and allowed in the *normal* rules (here, lines 2 and 4) during
+ this push.
-Here's the conf file extract:
+ * the expression is evaluated based on these counts. 0 is false, and
+ any non-zero is true (see more examples later). The truth value of the
+ expression determines whether the refex expression matched.
- repo testing
- RW+ master = u1 # line 1
- RW+ = @all # line 2
+ You can use any logical or arithmetic expression using refexes as operands
+ and using these operators:
- RW+ VREF/NAME/foo = u1
- RW+ VREF/NAME/doc/ = u2
- RW+ VREF/NAME/src/ = u2
+ not and or xor + - == -lt -gt -eq -le -ge -ne
- # set up 2 refex expressions, named e1, e2
- option refex-expr.e1 = master and VREF/NAME/foo
- option refex-expr.e2 = VREF/NAME/doc/ and VREF/NAME/src/
+ Parens are not allowed. Precedence is as you might expect for those
+ operators. It's actually perl that is evaluating it (you can guess what
+ the '-lt' etc., get translated to) so if in doubt, check 'man perlop'.
- # now deny users if the corresponding expression is true
- - VREF/refex-expr/e1 = u1
- - VREF/refex-expr/e2 = u2
+ * the refexes that form the terms of the expression (in this case, lines 2
+ and 4) MUST come before the expression itself (i.e., line 5).
-Here are some IMPORTANT notes:
+ * note the words "EXACT refex was matched" above.
- * You MUST place VREF/refex-expr rules at the end. (Only 'partial-copy', if
- you use it, must come later).
+ Let's say you add "u3" to line 1. Then the refex expression in line 5
+ would never match for u3. This is because line 1 prevents line 2 from
+ matching (being more general *and* appearing earlier), so the count for
+ the "master" refex would be 0. If "master" is 0 (false), then "master and
+ <anything>" is also false.
- * You MUST explicitly permit the refexes used in your refex expressions. If
- you have more generic rules, the specific ones must come first.
+ (Same thing is you swap lines 2 and 3; i.e., put the "RW+ = ..." before
+ the "RW+ master = ...").
- For example, without line 1, the refex recorded for user u1 will come from
- line 2, (so it will be 'refs/.*'), and 'master' in the refex expressions
- will never have a true value.
+ Put another way, the terms in the refex expression are refexes, not refs.
+ Merely pushing the master branch does not mean the count for "master"
+ increases; it has to *match* on a line that has "master" as the refex.
- * (corollary) make sure you use the exact same refex in the expression as
- you did on the original rule line. E.g., a missing slash at the end will
- mess things up.
+Here are some more examples:
+
+ * user u2 is allowed to push either 'doc/' or 'src/' but not both
- * You can use any logical expression using refexes as operands and using
- these operators:
+ repo foo
+ RW+ = u1 u2 u3
- and not xor or
+ RW+ VREF/NAME/doc/ = u2
+ RW+ VREF/NAME/src/ = u2
+ - VREF/NAME/doc/ and VREF/NAME/src/ = u2
- Parens are not allowed.
+ * user u3 is allowed to push at most 2 files to conf/
- If a refex has passed, it will have a 'true' value, else it will be false.
+ repo foo
+ RW+ = u1 u2 u3
- The result of the evaluation, after these substitutions, will be the
- result of the refex-expr VREF.
+ RW+ VREF/NAME/conf/ = u3
+ - VREF/NAME/conf/ -gt 2 = u3
diff --git a/src/lib/Gitolite/Rc.pm b/src/lib/Gitolite/Rc.pm
index a408698..7d1f53d 100644
--- a/src/lib/Gitolite/Rc.pm
+++ b/src/lib/Gitolite/Rc.pm
@@ -374,6 +374,7 @@ BEGIN { $non_core = "
continuation-lines SYNTACTIC_SUGAR .
keysubdirs-as-groups SYNTACTIC_SUGAR .
macros SYNTACTIC_SUGAR .
+ refex-expr SYNTACTIC_SUGAR .
renice PRE_GIT .
@@ -388,7 +389,7 @@ BEGIN { $non_core = "
Mirroring PRE_GIT ::
Mirroring POST_GIT ::
- RefexExpr ACCESS_2 ::
+ refex-expr ACCESS_2 RefexExpr::access_2
RepoUmask PRE_GIT ::
RepoUmask POST_CREATE ::
diff --git a/src/lib/Gitolite/Triggers/RefexExpr.pm b/src/lib/Gitolite/Triggers/RefexExpr.pm
index 687cb9e..5e2114f 100644
--- a/src/lib/Gitolite/Triggers/RefexExpr.pm
+++ b/src/lib/Gitolite/Triggers/RefexExpr.pm
@@ -4,7 +4,7 @@ use warnings;
# track refexes passed and evaluate expressions on them
# ----------------------------------------------------------------------
-# see instructions for use at the bottom of src/VREF/refex-expr
+# see src/VREF/refex-expr for instructions and WARNINGS!
use Gitolite::Easy;
diff --git a/src/syntactic-sugar/refex-expr b/src/syntactic-sugar/refex-expr
new file mode 100644
index 0000000..0713579
--- /dev/null
+++ b/src/syntactic-sugar/refex-expr
@@ -0,0 +1,39 @@
+# vim: syn=perl:
+
+# "sugar script" (syntactic sugar helper) for gitolite3
+# ----------------------------------------------------------------------
+# see src/VREF/refex-expr for instructions and WARNINGS!
+
+my $perm = qr(-|R|RW\+?C?D?M?);
+
+my $seq = 1;
+sub sugar_script {
+ my $lines = shift;
+
+ Gitolite::Common::dd ['lines', $lines];
+
+ # my @out = ();
+ for my $l (@$lines) {
+ push @out, $l;
+
+ # quick check
+ next unless $l =~ /^($perm) /;
+ # more detailed check
+ next unless $l =~ /^($perm) (\S.*) = (\S.*)$/;
+ my ($perm, $refexes, $users) = ($1, $2, $3);
+ next unless $refexes =~ / (and|not|or|xor|\+|-|==|-lt|-gt|-eq|-le|-ge|-ne) /;
+
+ print STDERR ">>>> $l\n";
+ pop @out; # we need to replace that last line
+
+ my @words = grep { $_ !~ /^(and|not|or|xor|\+|-|==|-lt|-gt|-eq|-le|-ge|-ne)$/ } split ' ', $refexes;
+ push @out, map { "RW+ $_ = $users" } @words;
+ push @out, "option refex-expr.sugar$seq = $refexes";
+ push @out, "$perm VREF/refex-expr/sugar$seq = $users";
+
+ $seq++;
+ }
+
+ Gitolite::Common::dd ['out', \@out];
+ return \@out;
+}
diff --git a/t/refex-expr-test-1 b/t/refex-expr-test-1
new file mode 100755
index 0000000..1372a1e
--- /dev/null
+++ b/t/refex-expr-test-1
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+# not part of the official test suite (yet); just some q&d testing
+
+# to be run from ~/gitolite as ./$0
+
+set -e
+exec 3>&2
+exec > /dev/null
+exec 2> /dev/null
+print2() { echo -n "$@" >&3; }
+say2() { echo "$@" >&3; }
+die() { echo FATAL: "$@" >&3; exit 1; }
+
+export od=$PWD
+export tmp=$(mktemp -d)
+echo $tmp >&3
+trap "rm -rf $tmp" 0
+cd $tmp
+
+print2 setting up...
+( cd $od; t/reset )
+echo "push @{ \$RC{ENABLE} }, 'refex-expr';" >> ~/.gitolite.rc
+cat <<EOF >> ~/.gitolite/conf/gitolite.conf
+
+repo r1
+ RW+ = u1 u2 # line 1
+
+ RW+ master = u3 u4 # line 2
+ RW+ = u3 u4 # line 3
+ RW+ VREF/NAME/Makefile = u3 u4 # line 4
+ - master and VREF/NAME/Makefile = u3 u4 # line 5
+
+EOF
+gitolite setup
+say2 done
+
+# ----------------------------------------------------------------------
+
+rm -rf u1
+git clone u1:r1 u1
+cd u1
+tsh 'tc f1'
+git push u1:r1 master
+tsh 'tc f2'
+git push u2:r1 master
+tsh 'tc f3'
+git push u3:r1 master
+tsh 'tc f4'
+git push u4:r1 master
+say2 everyone master no Makefile
+
+tsh 'tc f5 Makefile'
+git push u1:r1 master
+tsh 'tc f5 Makefile'
+git push u1:r1 master:m1
+say2 u1 Makefile master
+
+tsh 'tc f5 Makefile'
+git push u3:r1 master && die u3 r1 master should have failed
+git push u3:r1 master:m2
+say2 u3 Makefile master fail m2 pass
diff --git a/t/refex-expr-test-2 b/t/refex-expr-test-2
new file mode 100755
index 0000000..773e42c
--- /dev/null
+++ b/t/refex-expr-test-2
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# not part of the official test suite (yet); just some q&d testing
+
+# to be run from ~/gitolite as ./$0
+
+set -e
+exec 3>&2
+exec > /dev/null
+exec 2> /dev/null
+print2() { echo -n "$@" >&3; }
+say2() { echo "$@" >&3; }
+die() { echo FATAL: "$@" >&3; exit 1; }
+
+export od=$PWD
+export tmp=$(mktemp -d)
+echo $tmp >&3
+trap "rm -rf $tmp" 0
+cd $tmp
+
+print2 setting up...
+( cd $od; t/reset )
+echo "push @{ \$RC{ENABLE} }, 'refex-expr';" >> ~/.gitolite.rc
+cat <<EOF >> ~/.gitolite/conf/gitolite.conf
+
+ repo r2
+ RW+ = @all
+
+ RW+ VREF/NAME/doc/ = u2
+ RW+ VREF/NAME/src/ = u2
+ - VREF/NAME/doc/ and VREF/NAME/src/ = u2
+
+EOF
+gitolite setup
+say2 done
+
+# ----------------------------------------------------------------------
+
+git clone u2:r2
+cd r2
+
+tsh 'tc aa'
+git push origin master
+say2 aa pass
+
+mkdir doc src
+
+tsh 'tc doc/d1'
+git push origin master
+say2 doc pass
+
+tsh 'tc src/s1'
+tsh 'tc src/s2'
+git push origin master
+say2 src src pass
+
+tsh 'tc doc/d2 src/s3'
+git push origin master && die 1
+git push u1:r2 master
+say2 doc src u2 fail u1 pass
diff --git a/t/refex-expr-test-3 b/t/refex-expr-test-3
new file mode 100755
index 0000000..47599eb
--- /dev/null
+++ b/t/refex-expr-test-3
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+# not part of the official test suite (yet); just some q&d testing
+
+# to be run from ~/gitolite as ./$0
+
+set -e
+exec 3>&2
+exec > /dev/null
+exec 2> /dev/null
+print2() { echo -n "$@" >&3; }
+say2() { echo "$@" >&3; }
+die() { echo FATAL: "$@" >&3; exit 1; }
+
+export od=$PWD
+export tmp=$(mktemp -d)
+echo $tmp >&3
+trap "rm -rf $tmp" 0
+cd $tmp
+
+print2 setting up...
+( cd $od; t/reset )
+echo "push @{ \$RC{ENABLE} }, 'refex-expr';" >> ~/.gitolite.rc
+cat <<EOF >> ~/.gitolite/conf/gitolite.conf
+
+ repo r3
+ RW+ = u1 u2 u3
+
+ RW+ VREF/NAME/conf/ = u3
+ - VREF/NAME/conf/ -gt 2 = u3
+
+EOF
+gitolite setup
+say2 done
+
+# ----------------------------------------------------------------------
+
+git clone u3:r3
+cd r3
+
+tsh 'tc aa'
+git push origin master
+say2 aa pass
+
+mkdir doc conf
+
+tsh 'tc doc/d1 doc/d2 doc/d3 doc/d4 conf/c1'
+git push origin master
+say2 4 doc 1 conf pass
+
+tsh 'tc conf/c2 conf/c3 conf/c4'
+git push origin master && die 1
+
+git push u2:r3 master
+say2 3 conf u3 fail u2 pass
diff --git a/t/refex-expr-test-9 b/t/refex-expr-test-9
new file mode 100755
index 0000000..b3a9f09
--- /dev/null
+++ b/t/refex-expr-test-9
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+# not part of the official test suite (yet); just some q&d testing
+
+# to be run from ~/gitolite as ./$0
+
+set -e
+exec 3>&2
+exec > /dev/null
+exec 2> /dev/null
+print2() { echo -n "$@" >&3; }
+say2() { echo "$@" >&3; }
+die() { echo FATAL: "$@" >&3; exit 1; }
+
+export od=$PWD
+export tmp=$(mktemp -d)
+echo $tmp >&3
+trap "rm -rf $tmp" 0
+cd $tmp
+
+print2 setting up...
+( cd $od; t/reset )
+echo "push @{ \$RC{ENABLE} }, 'refex-expr';" >> ~/.gitolite.rc
+cat <<EOF >> ~/.gitolite/conf/gitolite.conf
+
+repo r9
+
+ RW+ = u3 u4
+
+ # u1 and u2 have some restrictions
+
+ # cant push master
+ - master = u1 u2
+ # cant push versioned tags, but other tags are fine
+ - refs/tags/v[0-9] = u1 u2
+ # everything else is fine, but we need to recognise when they're pushing
+ # tags, so that the refex expr will have the correct info
+ RW+ refs/tags/ = u1 u2
+ RW+ = u1 u2
+
+ # can push files in "foo/" only to a tag
+ RW+ VREF/NAME/foo/ = u1 u2
+
+ RW+ VREF/NAME/foo/ and refs/tags/ = u1 u2
+ - VREF/NAME/foo/ and not refs/tags/ = u1 u2
+
+EOF
+gitolite setup
+say2 done
+
+# ----------------------------------------------------------------------
+
+# make sure u3 is not constrained in any way
+
+git clone u3:r9 refex-test.repo
+cd refex-test.repo
+
+tsh 'tc u3-f1'
+git pom
+
+mkdir bar foo
+tsh 'tc bar/thr'
+git pom
+git tag v3
+git push origin v3
+tsh 'tc foo/rht'
+git pom
+git tag 3v
+git push origin 3v
+
+say2 u3 no limits
+
+# now test u1's constraints
+
+cd ..
+rm -rf refex-test.repo
+
+rm -rf ~/repositories/r9.git
+gitolite setup
+
+git clone u1:r9 refex-test.repo
+cd refex-test.repo
+
+tsh 'tc u1-f1'
+# cant push master
+git pom && die 1
+# can push other branches
+git push origin master:m1
+say2 master fail m1 pass
+
+mkdir bar foo
+tsh 'tc bar/one'
+git push origin master:m1
+git tag v1
+# cant push v-tag
+git push origin v1 && die 2
+say2 v-tag fail
+
+# cant push foo/ to a branch
+tsh 'tc foo/eno'
+git push origin master:m1 && die 3
+say2 foo/ m1 fail
+
+# but can push to a non-v-tag
+git tag 1v
+git push origin 1v
+say2 foo/ non-v-tag pass