aboutsummaryrefslogtreecommitdiff
path: root/gitaclhook
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-06-22 20:05:42 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2013-06-22 20:05:42 +0300
commit1e744ae787312413fb8600dd622b5fd7374b45ae (patch)
tree52c94147ac95fcf4e75265f56d15d318a9cb0ac2 /gitaclhook
parent9e6f5e5fe3c9422f5ab7182e88e8cfd05470c86c (diff)
downloadgitaclhook-1e744ae787312413fb8600dd622b5fd7374b45ae.tar.gz
gitaclhook-1e744ae787312413fb8600dd622b5fd7374b45ae.tar.bz2
gitaclhook: Improve CLI.
Diffstat (limited to 'gitaclhook')
-rwxr-xr-xgitaclhook152
1 files changed, 96 insertions, 56 deletions
diff --git a/gitaclhook b/gitaclhook
index c2dfdb8..b8b5513 100755
--- a/gitaclhook
+++ b/gitaclhook
@@ -15,12 +15,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
use strict;
use File::Spec;
use Pod::Man;
use Pod::Usage;
+use Getopt::Long qw(:config gnu_getopt no_ignore_case);
=head1 NAME
gitaclhook - control access to git repositories
=head1 SYNOPSIS
@@ -231,29 +232,36 @@ B<git-receive-pack>(1).
=head1 AUTHOR
Sergey Poznyakoff, <gray@gno.org>
=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;
my %opstr = ('C' => 'create',
'D' => 'delete',
'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;
my $status = shift;
my $message = shift;
my $loc = shift;
@@ -273,28 +281,28 @@ sub deny($;$) {
my $loc = shift;
logmsg("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;
}
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($) {
my $dir = shift;
File::Spec->rel2abs($dir) =~ m,/([^/]+)(?:\.git|/\.git)$,;
@@ -331,105 +339,137 @@ sub match_ref($) {
my $expr = shift;
return ($ref =~ /$expr/) if ($expr =~ /^\^/);
return ("$ref/" eq $expr or index($ref, $expr) == 0) if ($expr =~ /\/$/);
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: $!");
while (<$fd>) {
++$line;
chomp;
s/^\s+//;
s/\s+$//;
s/#.*//;
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");
}
####
# 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) {
deny "need authenticated user" unless $ENV{AUTH_TYPE};
$user_name = $ENV{REMOTE_USER};
}
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

Return to:

Send suggestions and report system problems to the System administrator.