aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-06-21 12:12:08 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2013-06-21 12:12:08 +0300
commit850a3e03dfc227f058647ff65092a3c791012df8 (patch)
tree67b5505f14b5365e0903f73da6e3c5bf46cc83d1
parent5b089678b03537f27fac73a2a08136ff9b1cbf66 (diff)
downloadgsc-850a3e03dfc227f058647ff65092a3c791012df8.tar.gz
gsc-850a3e03dfc227f058647ff65092a3c791012df8.tar.bz2
Improve gitaclhook.
Reformat and improve documentation. Display it if called with the --help option. Allow for * in the PROJECT field (matches any project name). Work with http(s) transport. New configuration variable httpd-user supplies the name of the user http server runs as.
-rwxr-xr-xgit/gitaclhook205
1 files changed, 150 insertions, 55 deletions
diff --git a/git/gitaclhook b/git/gitaclhook
index f9b3d3a..dd31dd8 100755
--- a/git/gitaclhook
+++ b/git/gitaclhook
@@ -13,91 +13,177 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
use strict;
use File::Spec;
+use Pod::Man;
+use Pod::Usage;
-=doc
-This hook is intended to be run as an "update" hook by git.
-It is called by git-receive-pack with arguments: refname old-sha1 new-sha1.
+=head1 NAME
-If the hooks.aclfile keyword is defined in the repository's config file,
+gitaclhook - control access to git repositories
+
+=head1 SYNOPSIS
+
+B<gitaclhook> I<refname> I<old-sha1> I<new-sha1>
+
+B<gitaclhook --help>
+
+=head1 DESCRIPTION
+
+This program is intended to be run as an "update" hook by git.
+It is called by B<git-receive-pack> with arguments:
+I<refname> I<old-sha1> I<new-sha1>.
+
+If the B<hooks.aclfile> keyword is defined in the repository's config file,
this hook will parse the file and allow or deny update depending on
-its settings. If hooks.aclfile is not defined, update is allowed
+its settings. If B<hooks.aclfile> is not defined, update is allowed
unconditionally.
+=head1 ACL FILE
+
The ACL file has the usual line-oriented syntax. Comments are introduced
by the # sign and extend to the end of the physical line. Comments and
empty lines are ignored.
Non-empty lines introduce ACL rules. The syntax is:
- VERB PROJECT USER [OP REF]
+=over 4
+
+I<VERB> I<PROJECT> I<USER> [I<OP> I<REF>]
+
+=back
where brackets denote optional parts. The parts of an ACL are:
-VERB Either 'allow' or 'deny', to allow or deny the operation,
- correspondingly.
+=over 4
+
+=item I<VERB>
+
+Either B<allow> or B<deny>, to allow or deny the operation, correspondingly.
+
+=item I<PROJECT>
+
+The name of the project. It is obtained by removing the directory
+and suffix parts of the repository pathname. Thus, if the repository
+is located in B</var/gitroot/foobar.git>, then the corresponding name of
+the project is B<foobar>.
-PROJECT The name of the project. It is obtained by removing the directory
- and suffix parts of the repository pathname. Thus, if the repository
- is located in /var/gitroot/foobar.git, then the corresponding name of
- the project is 'foobar'.
+An asterisk matches any project name.
-USER Name of the user. The word 'all' stands for any user, the word 'none'
- matches no one at all. Otherwise, if this part begins with a percent
- sign (%), the rest of characters aretreated as the name of the UNIX
- group to check and the rule matches any user in that group. Otherwise,
- the literal match is assumed.
+=item I<USER>
+
+Name of the user. The word B<all> stands for any user, the word B<none>
+matches no one at all. Otherwise, if this part begins with a percent
+sign (B<%>), the rest of characters are treated as the name of the UNIX
+group to check and the rule matches any user in that group. Otherwise,
+the literal match is assumed.
+
+=back
The optional parts are:
-OP Requested operation codes. It is a string consisting of one or more
- of the following letters (case-insensitive):
-
- C: create new ref
- D: delete existing ref
- U: fast-forward existing ref (no commit loss)
- R: rewind or rebase existing ref (commit loss)
-
-REF Affected ref, relative to the git refs/ directory. If it begins with
- a caret (^), it is treated as a Perl regular expression (with the ^
- being its part). If it ends with a /, it is treated as a prefix match,
- so, e.g., "heads/baz/" matches "refs/heads/baz" and anything below.
- Otherwise, it must match exactly the affected ref.
-
-The rule applies only if its PROJECT and USER parts match the project which is
-being updated and the user who requests the update, its OP contains the opcode
-of the requested operation and REF matches the affected ref. Missing REF
-and/or OP are treated as a match.
+=over 4
+
+=item I<OP>
+
+Requested operation codes. It is a string consisting of one or more
+of the following letters (case-insensitive):
+
+=over 8
+
+=item B<C>
+
+Create new ref.
+
+=item B<D>
+
+Delete existing ref.
+
+=item B<U>
+
+Fast-forward existing ref (no commit loss).
+
+=item B<R>
+
+Rewind or rebase existing ref (commit loss).
+
+=back
+
+=item I<REF>
+
+Affected ref, relative to the git B<refs/> directory. If it begins with
+a caret (B<^>), it is treated as a Perl regular expression (with the B<^>
+being its part). If it ends with a B</>, it is treated as a prefix match,
+so, e.g., B<heads/baz/> matches B<refs/heads/baz> and anything below.
+Otherwise, it must match exactly the affected ref.
+
+=back
+
+=head1 RULE MATCHING
+
+The rule applies only if its I<PROJECT> and I<USER> parts match the project
+which is being updated and the user who requests the update, its I<OP>
+contains the opcode of the requested operation and I<REF> matches the affected
+ref. Missing I<REF> and/or I<OP> are treated as a match.
If no rule applies, the operation is allowed.
For example, assume you have the following ACL file:
-allow myprog %devel U heads/master
-allow myprog %pm CDUR heads/
-allow myprog %pm C ^heads/tags/v\\d+$
-allow myprog admin CDUR
-deny myprog all
+ allow myprog %devel U heads/master
+ allow myprog %pm CDUR heads/
+ allow myprog %pm C ^heads/tags/v\\d+$
+ allow myprog admin CDUR
+ deny myprog all
-Then the users from the 'devel' group will be able to push updates to
-refs/heads/master, the users from the 'pm' group will be allowed to do
-anything with refs under refs/heads and to create tags with names beginning
-with 'v' and containing only digits afterwards, and the user 'admin' will
+Then the users from the B<devel> group will be able to push updates to
+B<refs/heads/master>, the users from the B<pm> group will be allowed to do
+anything with refs under B<refs/heads> and to create tags with names beginning
+with B<v> and containing only digits afterwards, and the user B<admin> will
be allowed to do anything he pleases. No other users will be allowed to
update that repository.
-Configuration settings:
+=head1 CONFIGURATION SETTINGS
+
+=over 4
+
+=item B<hooks.aclfile> STRING
+
+Name of the ACL file.
+
+=item B<hooks.acllog> STRING
+
+Send log info to this file.
+
+=item B<hooks.acldebug> BOOL
-hooks.aclfile STRING Name of the ACL file
-hooks.acllog STRING Send log info to this file
-hooks.acldebug BOOL Enable debugging
-hooks.aclquiet BOOL Suppress diagnostics on stderr
+Enable debugging.
+=item B<hooks.aclquiet> BOOL
+
+Suppress diagnostics on stderr.
+
+=item B<hooks.httpd-user> STRING
+
+Name of the user httpd runs as. Define it if the repository can be
+accessed via HTTP(S). If B<gitaclhook> is run as this user, it will
+get the name of the user on behalf of which the update is performed
+from the environment variable B<REMOTE_USER>.
+
+=back
+
+=head1 SEE ALSO
+
+B<git-receive-pack>(1).
+
+=head1 AUTHOR
+
+Sergey Poznyakoff, <gray@gno.org>
+
=cut
my $debug = $ENV{GIT_UPDATE_DEBUG} > 0;
my $logfile;
my $quiet;
my ($user_name) = getpwuid $<;
@@ -128,39 +214,39 @@ sub logmsg($$;$) {
} else {
print $fd "$status: $message\n";
}
close($fd);
}
-sub deny ($;$) {
+sub deny($;$) {
my $msg = shift;
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);
print STDERR "denied: $msg\n" unless $quiet;
exit 1;
}
-sub allow ($) {
+sub allow($) {
logmsg("ALLOW",
"$project_name:$user_name:$opstr{$op}:$ref:$old:$new",
$_[0]);
print STDERR "debug: allow $_[0]\n" if $debug;
exit 0;
}
-sub info ($) {
+sub info($) {
logmsg("INFO", $_[0]);
print STDERR "info: $_[0]\n" if $debug;
}
-sub git_value (@) {
+sub git_value(@) {
my $fd;
open($fd,'-|','git',@_);
local $_ = <$fd>;
chop;
close($fd);
@@ -214,13 +300,13 @@ sub check_acl($$$) {
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 $project);
+ 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",
@@ -230,21 +316,30 @@ sub check_acl($$$) {
allow("default rule");
}
####
# Sanity checks
-deny "don't run this script from the command line" unless ($git_dir);
+unless ($git_dir) {
+ pod2usage(-exitstatus => 0, -verbose => 2) if ($ref eq "--help");
+ 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";
}
$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 defined($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;

Return to:

Send suggestions and report system problems to the System administrator.