diff --git a/src/VREF/refex-expr b/src/VREF/refex-expr new file mode 100755 index 0000000..3ff4bf4 --- /dev/null +++ b/src/VREF/refex-expr @@ -0,0 +1,73 @@ +#!/usr/bin/perl +use strict; +use warnings; + +my $rule = $ARGV[7]; +my $res = $ENV{"GL_REFEX_EXPR_" . $rule} || 0; +print "$ARGV[6] ($res)\n" if $res; + +exit 0; + +__END__ + +Documentation for the refex-expression evaluation feature + +First, make sure you have both the VREF and the trigger scripts +(src/VREF/refex-expr and src/lib/Gitolite/Triggers/RefexExpr.pm) + +Next, add this to the ACCESS_2 list in the rc file: + + 'RefexExpr::access_2', + +For the rest, we'll use this example: + + * user u1 can push foo to some other branch, and anything else to the master + branch, but not foo to the master branch + + * user u2 is allowed to push either 'doc/' or 'src/' but not both + +Here's the conf file extract: + + repo testing + RW+ master = u1 # line 1 + RW+ = @all # line 2 + + RW+ VREF/NAME/foo = u1 + RW+ VREF/NAME/doc/ = u2 + RW+ VREF/NAME/src/ = u2 + + # 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/ + + # now deny users if the corresponding expression is true + - VREF/refex-expr/e1 = u1 + - VREF/refex-expr/e2 = u2 + +Here are some IMPORTANT notes: + + * You MUST place VREF/refex-expr rules at the end. (Only 'partial-copy', if + you use it, must come later). + + * You MUST explicitly permit the refexes used in your refex expressions. If + you have more generic rules, the specific ones must come first. + + 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. + + * (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. + + * You can use any logical expression using refexes as operands and using + these operators: + + and not xor or + + Parens are not allowed. + + If a refex has passed, it will have a 'true' value, else it will be false. + + The result of the evaluation, after these substitutions, will be the + result of the refex-expr VREF. diff --git a/src/lib/Gitolite/Triggers/RefexExpr.pm b/src/lib/Gitolite/Triggers/RefexExpr.pm new file mode 100644 index 0000000..687cb9e --- /dev/null +++ b/src/lib/Gitolite/Triggers/RefexExpr.pm @@ -0,0 +1,80 @@ +package Gitolite::Triggers::RefexExpr; +use strict; +use warnings; + +# track refexes passed and evaluate expressions on them +# ---------------------------------------------------------------------- +# see instructions for use at the bottom of src/VREF/refex-expr + +use Gitolite::Easy; + +my %passed; +my %rules; +my $init_done = 0; + +sub access_2 { + # get out quick for repos that don't have any rules + return if $init_done and not %rules; + + # but we don't really know that the first time, heh! + if (not $init_done) { + my $repo = $_[1]; + init($repo); + return unless %rules; + } + + my $refex = $_[5]; + return if $refex =~ /DENIED/; + + $passed{$refex}++; + + # evaluate the rules each time; it's not very expensive + for my $k (sort keys %rules) { + $ENV{"GL_REFEX_EXPR_" . $k} = eval_rule($rules{$k}); + } +} + +sub eval_rule { + my $rule = shift; + + my $e; + $e = join " ", map { convert($_) } split ' ', $rule; + + my $ret = eval $e; + _die "eval '$e' -> '$@'" if $@; + Gitolite::Common::trace(1, "RefexExpr", "'$rule' -> '$e' -> '$ret'"); + + return "'$rule' -> '$e'" if $ret; +} + +my %constant; +%constant = map { $_ => $_ } qw(1 not and or xor + - ==); +$constant{'-lt'} = '<'; +$constant{'-gt'} = '>'; +$constant{'-eq'} = '=='; +$constant{'-le'} = '<='; +$constant{'-ge'} = '>='; +$constant{'-ne'} = '!='; + +sub convert { + my $i = shift; + return $i if $i =~ /^-?\d+$/; + return $constant{$i} || $passed{$i} || $passed{"refs/heads/$i"} || 0; +} + +# called only once +sub init { + $init_done = 1; + my $repo = shift; + + # find all the rule expressions + my %t = config($repo, "^gitolite-options\\.refex-expr\\."); + my ($k, $v); + # get rid of the cruft and store just the rule name as the key + while ( ($k, $v) = each %t) { + $k =~ s/^gitolite-options\.refex-expr\.//; + $rules{$k} = $v; + } +} + +1;