logical expressions on refexes :-)

redis
Sitaram Chamarty 2012-06-29 22:19:06 +05:30
parent af437c3a7b
commit db2cf23379
2 changed files with 153 additions and 0 deletions

73
src/VREF/refex-expr Executable file
View File

@ -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.

View File

@ -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;