wildrepos: teach auth and update hook about wildcard repos
- new_repo now takes a "creater" parameter; if given, this user is recorded (in a file called "gl-creater") as the creater of the repo. Only applicable to wildcards - repo_rights reads "gl-creater" and "gl-perms" to tell you who created it, and whether you (the $user) are in the list of READERS or WRITERS **NOTE** that the mechanism to create/update gl-perms has not been written yet... (as of this commit) - parse_acl takes 4 more arguments, all optional. The repo name we're interested in (set by all except the access reporting function), and the names to be interpolated as $creater, $readers, writers - report_basic now knows about the "C" permission and shows it - auth now autovivifies a repo if the user has "C" and it's a wildcard match, or (the old case) the user has "W" and it's not a wildcard. In the former case, the creater is also set IMPLEMENTATION NOTES: - the Dumper code now uses a custom hash key sort to make sure $creater etc land up at the *end* - a wee bit of duplication exists in the update hook; it borrows a little code from parse_acl. I dont (yet) want to include all of gitolite.pm for that little piece...
This commit is contained in:
parent
77306567e9
commit
f49eddd660
4 changed files with 121 additions and 24 deletions
|
@ -81,7 +81,7 @@ sub where_is_rc
|
|||
# NOTE: this sub will change your cwd; caller beware!
|
||||
sub new_repo
|
||||
{
|
||||
my ($repo, $hooks_dir) = @_;
|
||||
my ($repo, $hooks_dir, $creater) = @_;
|
||||
|
||||
umask($REPO_UMASK);
|
||||
|
||||
|
@ -89,19 +89,81 @@ sub new_repo
|
|||
# erm, note that's "and die" not "or die" as is normal in perl
|
||||
wrap_chdir("$repo.git");
|
||||
system("git --bare init >&2");
|
||||
system("echo $creater > gl-creater") if $creater;
|
||||
# propagate our own, plus any local admin-defined, hooks
|
||||
system("cp $hooks_dir/* hooks/");
|
||||
chmod 0755, "hooks/update";
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# metaphysics (like, "is there a god?", "who created me?", etc)
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# "who created this repo", "am I on the R list", and "am I on the RW list"?
|
||||
sub repo_rights
|
||||
{
|
||||
my ($repo_base_abs, $repo, $user) = @_;
|
||||
# creater
|
||||
my $c = '';
|
||||
if ( -f "$repo_base_abs/$repo.git/gl-creater") {
|
||||
my $fh = wrap_open("<", "$repo_base_abs/$repo.git/gl-creater");
|
||||
chomp($c = <$fh>);
|
||||
}
|
||||
# $user's R and W rights
|
||||
my ($r, $w); $r = ''; $w = '';
|
||||
if ($user and -f "$repo_base_abs/$repo.git/gl-perms") {
|
||||
my $fh = wrap_open("<", "$repo_base_abs/$repo.git/gl-perms");
|
||||
my $perms = join ("", <$fh>);
|
||||
if ($perms) {
|
||||
$r = $user if $perms =~ /^\s*R(?=\s).*\s$user(\s|$)/m;
|
||||
$w = $user if $perms =~ /^\s*RW(?=\s).*\s$user(\s|$)/m;
|
||||
}
|
||||
}
|
||||
|
||||
return ($c, $r, $w);
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# parse the compiled acl
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
sub parse_acl
|
||||
{
|
||||
my $GL_CONF_COMPILED = shift;
|
||||
# IMPLEMENTATION NOTE: a wee bit of this is duplicated in the update hook;
|
||||
# please update that also if the interface or the env vars change
|
||||
|
||||
my ($GL_CONF_COMPILED, $repo, $c, $r, $w) = @_;
|
||||
|
||||
# void $r if same as $w (otherwise "readers" overrides "writers"; this is
|
||||
# the same problem that needed a sort sub for the Dumper in the compile
|
||||
# script, but localised to just $readers and $writers)
|
||||
$r = "" if $r eq $w;
|
||||
|
||||
# set up the variables for a parse to interpolate stuff from the dumped
|
||||
# hash (remember the selective conversion of single to double quotes?).
|
||||
|
||||
# if they're not passed in, then we look for an env var of that name, else
|
||||
# we default to "NOBODY" (we hope there isn't a real user called NOBODY!)
|
||||
# And in any case, we set those env vars so level 2 can redo the last
|
||||
# parse without any special code
|
||||
|
||||
our $creater = $ENV{GL_CREATER} = $c || $ENV{GL_CREATER} || "NOBODY";
|
||||
our $readers = $ENV{GL_READERS} = $r || $ENV{GL_READERS} || "NOBODY";
|
||||
our $writers = $ENV{GL_WRITERS} = $w || $ENV{GL_WRITERS} || "NOBODY";
|
||||
|
||||
die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED;
|
||||
|
||||
# access reporting doesn't send $repo, and doesn't need to
|
||||
return unless $repo;
|
||||
|
||||
return $ENV{GL_REPOPATT} = "" if $repos{$repo};
|
||||
my @matched = grep { $repo =~ /^$_$/ } sort keys %repos;
|
||||
die "$repo has no matches\n" unless @matched;
|
||||
die "$repo has multiple matches\n@matched\n" if @matched > 1;
|
||||
# found exactly one pattern that matched, copy its ACL
|
||||
$repos{$repo} = $repos{$matched[0]};
|
||||
# and return the pattern
|
||||
return $ENV{GL_REPOPATT} = $matched[0];
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
@ -114,16 +176,18 @@ sub report_basic
|
|||
{
|
||||
my($GL_ADMINDIR, $GL_CONF_COMPILED, $user) = @_;
|
||||
|
||||
&parse_acl($GL_CONF_COMPILED);
|
||||
&parse_acl($GL_CONF_COMPILED, "", "CREATER", "READERS", "WRITERS");
|
||||
|
||||
# send back some useful info if no command was given
|
||||
print "hello $user, the gitolite version here is ";
|
||||
system("cat", "$GL_ADMINDIR/src/VERSION");
|
||||
print "\ryou have the following permissions:\n\r";
|
||||
for my $r (sort keys %repos) {
|
||||
my $perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{$r}{R}{$user} ? ' R' : '' ) );
|
||||
$perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{$r}{W}{$user} ? ' W' : '' ) );
|
||||
print "$perm\t$r\n\r" if $perm;
|
||||
my $perm .= ( $repos{$r}{C}{'@all'} ? ' @' : ( $repos{$r}{C}{$user} ? ' C' : ' ' ) );
|
||||
$perm .= ( $repos{$r}{R}{'@all'} ? ' @' : ( $repos{$r}{R}{$user} ? ' R' : ' ' ) );
|
||||
$perm .= ( $repos{$r}{W}{'@all'} ? ' @' : ( $repos{$r}{W}{$user} ? ' W' : ' ' ) );
|
||||
print "$perm\t$r\n\r" if $perm =~ /\S/;
|
||||
}
|
||||
}
|
||||
1;
|
||||
|
||||
|
|
|
@ -78,8 +78,28 @@ die "bad command: $cmd. Make sure the repo name is exactly as in your config\n"
|
|||
# first level permissions check
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# parse the compiled acl; goes into %repos (global)
|
||||
&parse_acl($GL_CONF_COMPILED);
|
||||
my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
|
||||
|
||||
if ( -d "$repo_base_abs/$repo.git" ) {
|
||||
# existing repo
|
||||
my ($creater, $user_R, $user_W) = &repo_rights($repo_base_abs, $repo, $user);
|
||||
my $patt = &parse_acl($GL_CONF_COMPILED, $repo, $creater, $user_R, $user_W);
|
||||
} else {
|
||||
my $patt = &parse_acl($GL_CONF_COMPILED, $repo, $user, $user, $user);
|
||||
# parse_acl returns "" if the repo was non-wildcard, or the pattern
|
||||
# that matched if it was a wildcard
|
||||
|
||||
# auto-vivify new repo; 2 situations allow autoviv -- normal repos
|
||||
# with W access (the old mode), and wildcard repos with C access
|
||||
my $W_ok = $repos{$repo}{W}{$user} || $repos{$repo}{W}{'@all'};
|
||||
my $C_ok = $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'};
|
||||
if ($W_ok and not $patt or $C_ok and $patt) {
|
||||
wrap_chdir("$repo_base_abs");
|
||||
# for wildcard repos, we also want to set the "creater"
|
||||
new_repo($repo, "$GL_ADMINDIR/src/hooks", ( $patt ? $user : ""));
|
||||
wrap_chdir($ENV{HOME});
|
||||
}
|
||||
}
|
||||
|
||||
# we know the user and repo; we just need to know what perm he's trying
|
||||
my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W');
|
||||
|
@ -88,16 +108,6 @@ die "$perm access for $repo DENIED to $user\n"
|
|||
unless $repos{$repo}{$perm}{$user}
|
||||
or $repos{$repo}{$perm}{'@all'};
|
||||
|
||||
# create the repo if it doesn't already exist and the user has "W" access
|
||||
my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
|
||||
if ( not -d "$repo_base_abs/$repo.git" ) {
|
||||
if ( $repos{$repo}{W}{$user} or $repos{$repo}{W}{'@all'} ) {
|
||||
wrap_chdir("$repo_base_abs");
|
||||
new_repo($repo, "$GL_ADMINDIR/src/hooks");
|
||||
wrap_chdir($ENV{HOME});
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# logging, timestamp. also setup env vars for later
|
||||
# ----------------------------------------------------------------------------
|
||||
|
@ -113,7 +123,7 @@ for ($s, $min, $h, $d, $m) {
|
|||
}
|
||||
$ENV{GL_TS} = "$y-$m-$d.$h:$min:$s";
|
||||
|
||||
# substitute template parameters and set the logfile name
|
||||
# substitute template parameters and set the logfile name
|
||||
$GL_LOGT =~ s/%y/$y/g;
|
||||
$GL_LOGT =~ s/%m/$m/g;
|
||||
$GL_LOGT =~ s/%d/$d/g;
|
||||
|
|
|
@ -5,6 +5,15 @@ use warnings;
|
|||
use Data::Dumper;
|
||||
$Data::Dumper::Indent = 1;
|
||||
$Data::Dumper::Sortkeys = 1;
|
||||
$Data::Dumper::Sortkeys = sub { return [ reverse sort keys %{$_[0]} ]; };
|
||||
# this is to make sure that $creater etc go to the end of the dumped hash.
|
||||
# Without this, a setup that has something like
|
||||
# @team = u1 u2 u3
|
||||
# repo priv/CREATER/.+
|
||||
# RW+ = CREATER
|
||||
# RW = @team
|
||||
# has a problem. The RW overrides the RW+ when the dumped hash is read in
|
||||
# (simply going by sequence), so creater's special privs are lost
|
||||
|
||||
# === add-auth-keys ===
|
||||
|
||||
|
@ -352,6 +361,7 @@ wrap_chdir("$repo_base_abs");
|
|||
|
||||
for my $repo (sort keys %repos) {
|
||||
next unless $repo =~ $REPONAME_PATT;
|
||||
print STDERR "creating $repo...\n";
|
||||
unless (-d "$repo.git") {
|
||||
new_repo($repo, "$GL_ADMINDIR/src/hooks");
|
||||
# new_repo would have chdir'd us away; come back
|
||||
|
|
|
@ -30,8 +30,21 @@ our %repos;
|
|||
|
||||
# we should already have the GL_RC env var set when we enter this hook
|
||||
die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC};
|
||||
# then "do" the compiled config file, whose name we now know
|
||||
die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED;
|
||||
# then "do" the compiled config file, whose name we now know. Before doing
|
||||
# that we setup the creater etc from environment variables so that the parse
|
||||
# interpolates them. We've minimised the duplication but this *does*
|
||||
# duplicate a bit of parse_acl from gitolite.pm; we don't want to include that
|
||||
# file here just for that little bit
|
||||
{
|
||||
our $creater = $ENV{GL_CREATER};
|
||||
our $readers = $ENV{GL_READERS};
|
||||
our $writers = $ENV{GL_WRITERS};
|
||||
|
||||
die "parse $GL_CONF_COMPILED failed: " . ($! or $@) unless do $GL_CONF_COMPILED;
|
||||
|
||||
$repos{$ENV{GL_REPO}} = $repos{$ENV{GL_REPOPATT}} if ( $ENV{GL_REPOPATT} );
|
||||
}
|
||||
my $reported_repo = $ENV{GL_REPO} . ( $ENV{GL_REPOPATT} ? " ($ENV{GL_REPOPATT})" : "" );
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# start...
|
||||
|
@ -101,12 +114,12 @@ sub check_ref {
|
|||
$refex = (keys %$ar)[0];
|
||||
# refex? sure -- a regex to match a ref against :)
|
||||
next unless $ref =~ /$refex/;
|
||||
die "$perm $ref $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-';
|
||||
die "$perm $ref $reported_repo $ENV{GL_USER} DENIED by $refex\n" if $ar->{$refex} eq '-';
|
||||
|
||||
# as far as *this* ref is concerned we're ok
|
||||
return $refex if ($ar->{$refex} =~ /\Q$perm/);
|
||||
}
|
||||
die "$perm $ref $ENV{GL_REPO} $ENV{GL_USER} DENIED by fallthru\n";
|
||||
die "$perm $ref $reported_repo $ENV{GL_USER} DENIED by fallthru\n";
|
||||
}
|
||||
|
||||
# and in this version, we have many "refs" to check. The one we print in the
|
||||
|
@ -123,6 +136,6 @@ check_ref($_) for @refs;
|
|||
open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!\n";
|
||||
print $log_fh "$ENV{GL_TS} $perm\t" .
|
||||
substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14) .
|
||||
"\t$ENV{GL_REPO}\t$ref\t$ENV{GL_USER}\t$log_refex\n";
|
||||
"\t$reported_repo\t$ref\t$ENV{GL_USER}\t$log_refex\n";
|
||||
close $log_fh or die "close log failed: $!\n";
|
||||
exit 0;
|
||||
|
|
Loading…
Reference in a new issue