From 1e744ae787312413fb8600dd622b5fd7374b45ae Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Sat, 22 Jun 2013 20:05:42 +0300 Subject: gitaclhook: Improve CLI. --- gitaclhook | 152 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 96 insertions(+), 56 deletions(-) (limited to 'gitaclhook') diff --git a/gitaclhook b/gitaclhook index c2dfdb8..b8b5513 100755 --- a/gitaclhook +++ b/gitaclhook @@ -18,6 +18,7 @@ use strict; use File::Spec; use Pod::Man; use Pod::Usage; +use Getopt::Long qw(:config gnu_getopt no_ignore_case); =head1 NAME @@ -234,14 +235,14 @@ Sergey Poznyakoff, =cut -my $debug = $ENV{GIT_UPDATE_DEBUG} > 0; +my $debug_level = $ENV{GIT_UPDATE_DEBUG} > 0; my $logfile; my $quiet; my ($user_name) = getpwuid $<; my $git_dir = $ENV{GIT_DIR}; -my $ref = $ARGV[0]; -my $old = $ARGV[1]; -my $new = $ARGV[2]; +my $ref; +my $old; +my $new; my $project_name; my $op; @@ -251,6 +252,13 @@ my %opstr = ('C' => 'create', 'U' => 'update', 'R' => 'rewind/rebase'); +sub debug($$) { + my ($level,$msg) = @_; + if ($level <= $debug_level) { + print STDERR "debug: $msg\n"; + } +} + sub logmsg($$;$) { return 0 unless $logfile; @@ -276,7 +284,7 @@ sub deny($;$) { "$project_name:$user_name:$opstr{$op}:$ref:$old:$new: $msg", $loc); - print STDERR "debug: denied by $loc\n" if ($debug and $loc); + debug(1, "denied by $loc") if $loc; print STDERR "denied: $msg\n" unless $quiet; exit 1; } @@ -285,13 +293,13 @@ sub allow($) { logmsg("ALLOW", "$project_name:$user_name:$opstr{$op}:$ref:$old:$new", $_[0]); - print STDERR "debug: allow $_[0]\n" if $debug; + debug(1, "allow $_[0]"); exit 0; } sub info($) { logmsg("INFO", $_[0]); - print STDERR "info: $_[0]\n" if $debug; + print STDERR "info: $_[0]\n" if $debug_level; } sub project_name($) { @@ -334,16 +342,38 @@ sub match_ref($) { return $ref eq $expr; } -sub check_acl($$$) { - my $project = shift; - my $op = shift; - my $ref = shift; +sub match_tuple($) { + my $tuple = shift; + my @x = @{$tuple}; + + return ( \&deny, "malformed line" ) unless $#x >= 2; + return ( \&deny, "unknown keyword" ) + unless ($x[0] eq 'allow' || $x[0] eq 'deny'); + + return ( 0, "project mismatch" ) + if ($x[1] ne "*" and $x[1] ne $project_name); + return ( 0, "user mismatch" ) + unless match_user($x[2]); + return ( 0, "op mismatch" ) + if ($#x >= 3 && index(uc $x[3], $op) == -1); + return ( 0, "ref mismatch" ) + if ($#x == 4 && !match_ref($x[4])); + if ($x[0] eq 'allow') { + return ( \&allow ); + } else { + my $s = "you are not permitted to " . $opstr{$op} . " $ref"; + return ( \&deny, $s ); + } +} + + +sub check_acl { my $fd; my $line = 0; my @ret; my $filename = git_value('config', 'hooks.aclfile'); - allow("no ACL configured for $project") + allow("no ACL configured for $project_name") unless defined($filename); open($fd, "<", $filename) or deny("cannot open configuration file: $!"); @@ -356,19 +386,18 @@ sub check_acl($$$) { next if ($_ eq ""); my @x = split(/\s+/, $_, 5); - deny("unknown keyword", "$filename:$line") - unless ($x[0] eq 'allow' || $x[0] eq 'deny'); - deny("malformed line", "$filename:$line") - unless $#x >= 2; - - next if ($x[1] ne "*" and $x[1] ne $project); - next unless match_user($x[2]); - next if ($#x >= 3 && index(uc $x[3], $op) == -1); - next if ($#x == 4 && !match_ref($x[4])); - - allow("$filename:$line") if ($x[0] eq 'allow'); - deny("you are not permitted to " . $opstr{$op} . " $ref", - "$filename:$line"); + my @res = match_tuple(\@x); + if ($res[0] == 0) { + debug(2, "$filename:$line: $res[1]"); + next; + } + close($fd); + if ($res[1]) { + $res[0]->($res[1], "$filename:$line"); + } else { + $res[0]->("$filename:$line"); + } + exit(127); } close($fd); allow("default rule"); @@ -377,27 +406,50 @@ sub check_acl($$$) { #### # Sanity checks -unless ($git_dir) { - pod2usage(-exitstatus => 0, -verbose => 2) if ($ref eq "--help"); - if ($ref eq "--test") { - deny("--test requires four arguments") unless ($#ARGV == 4); - $ENV{GIT_DIR} = $ARGV[1]; - $user_name = $ARGV[2]; - $op = $ARGV[3]; - deny("invalid op") unless defined($opstr{$op}); - $ref = $ARGV[4]; - check_acl(project_name($ARGV[1]), $op, $ref); - exit(0); +if ($git_dir) { + $ref = $ARGV[0]; + $old = $ARGV[1]; + $new = $ARGV[2]; + + deny "bad old value $old" unless $old =~ /^[a-z0-9]{40}$/; + deny "bad new value $new" unless $new =~ /^[a-z0-9]{40}$/; + + if ($old =~ /^0{40}$/) { + $op = 'C'; + } elsif ($new =~ /^0{40}$/) { + $op = 'D'; + } elsif ($ref =~ m,^heads/, && $old eq git_value('merge-base',$old,$new)) { + $op = 'U'; + } else { + $op = 'R'; } - deny "try \"$0 --help\" for fore info" -} -$debug = git_value('config', '--bool', 'hooks.acldebug') unless ($debug); -$logfile = git_value('config', 'hooks.acllog'); -if ($logfile && $logfile !~ /[>|]/) { - $logfile = ">>$logfile"; + $debug_level = git_value('config', '--bool', 'hooks.acldebug') + unless ($debug_level); + $logfile = git_value('config', 'hooks.acllog'); + if ($logfile && $logfile !~ /[>|]/) { + $logfile = ">>$logfile"; + } + $quiet = git_value('config', 'hooks.aclquiet') unless ($debug_level); +} else { + my $test; + GetOptions("help|h" => sub { pod2usage(-exitstatus => 0, -verbose => 2); }, + "debug|d+" => \$debug_level, + "test|t" => \$test) + or exit (3); + if ($test) { + abend("--test requires four arguments") unless ($#ARGV == 3); + $git_dir = $ENV{GIT_DIR} = $ARGV[0]; + $user_name = $ARGV[1]; + $op = $ARGV[2]; + deny("invalid op") unless defined($opstr{$op}); + $ref = $ARGV[3]; + $old = '0000000000000000000000000000000000000000'; + $new = '0000000000000000000000000000000000000001'; + } else { + deny "try \"$0 --help\" for fore info" + } } -$quiet = git_value('config', 'hooks.aclquiet') unless ($debug); my $httpdusr = git_value('config', 'hooks.httpd-user'); if (defined($httpdusr) and $user_name eq $httpdusr) { @@ -407,26 +459,14 @@ if (defined($httpdusr) and $user_name eq $httpdusr) { deny "need a ref name" unless $ref; deny "bogus ref $ref" unless $ref =~ s,^refs/,,; -deny "bad old value $old" unless $old =~ /^[a-z0-9]{40}$/; -deny "bad new value $new" unless $new =~ /^[a-z0-9]{40}$/; deny "no such user" unless $user_name; allow "no change requested" if $old eq $new; $project_name = project_name($git_dir); -if ($old =~ /^0{40}$/) { - $op = 'C'; -} elsif ($new =~ /^0{40}$/) { - $op = 'D'; -} elsif ($ref =~ m,^heads/, && $old eq git_value('merge-base',$old,$new)) { - $op = 'U'; -} else { - $op = 'R'; -} - info "$user_name requested $opstr{$op} on $ref in $project_name"; -check_acl($project_name, $op, $ref); +&check_acl; # Finis -- cgit v1.2.1