summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG6
-rwxr-xr-xsrc/commands/access10
-rwxr-xr-xsrc/commands/info16
-rwxr-xr-xsrc/gitolite5
-rwxr-xr-xsrc/gitolite-shell7
-rw-r--r--src/lib/Gitolite/Common.pm20
-rw-r--r--src/lib/Gitolite/Hooks/Update.pm2
-rwxr-xr-xsrc/triggers/expand-deny-messages14
-rwxr-xr-xt/access.t53
9 files changed, 116 insertions, 17 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 345f2e2..9c3998a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,9 @@
+2018-08-07 v3.6.9 prevent racy access to repos in process of migration to
+ gitolite
+
+ 'info' learns new '-p' option to show only physical repos
+ (as opposed to wild repos)
+
2018-07-12 v3.6.8 fix bug when deleting *all* hooks for a repo
allow trailing slashes in repo names
diff --git a/src/commands/access b/src/commands/access
index f02e533..7d4a5b9 100755
--- a/src/commands/access
+++ b/src/commands/access
@@ -51,7 +51,7 @@ $ref ||= 'any';
# fq the ref if needed
$ref =~ s(^)(refs/heads/) if $ref and $ref ne 'any' and $ref !~ m(^(refs|VREF)/);
_die "invalid perm" if not( $aa and $aa =~ /^(R|W|\+|C|D|M|\^C)$/ );
-_die "invalid ref name" if not( $ref and $ref =~ $REPONAME_PATT );
+_die "invalid ref name" if not( $ref and $ref =~ $REF_OR_FILENAME_PATT );
my $ret = '';
@@ -61,6 +61,9 @@ if ( $repo ne '%' and $user ne '%' ) {
show($ret) if $s;
+ # adjust for fallthru in VREFs
+ $ret =~ s/DENIED by fallthru/allowed by fallthru/ if $ref =~ m(^VREF/);
+
if ( $ret =~ /DENIED/ ) {
print "$ret\n" unless $q;
exit 1;
@@ -85,8 +88,9 @@ while (<>) {
sub adjust_aa {
my ($repo, $aa) = @_;
- $aa = '+' if $aa eq 'C' and not option($repo, 'CREATE_IS_C');
+ $aa = 'W' if $aa eq 'C' and not option($repo, 'CREATE_IS_C');
$aa = '+' if $aa eq 'D' and not option($repo, 'DELETE_IS_D');
+ $aa = 'W' if $aa eq 'M' and not option($repo, 'MERGE_CHECK');
return $aa;
}
@@ -103,7 +107,7 @@ sub show {
p => skipped due to perm (W, +, etc) not matching,
D => explicitly denied,
A => explicitly allowed,
- F => denied due to fallthru (no rules matched)
+ F => fallthru; access denied for normal refs, allowed for VREFs
";
diff --git a/src/commands/info b/src/commands/info
index 5079cfa..b88e288 100755
--- a/src/commands/info
+++ b/src/commands/info
@@ -12,12 +12,13 @@ use Gitolite::Conf::Load;
=for args
Usage: gitolite info [-lc] [-ld] [-json] [<repo name pattern>]
-List all existing repos you can access, as well as repo name patterns you can
-create repos from (if any).
+List all existing repos you can access, as well as repo name patterns (see
+"wild repos") you have any kind of access to.
'-lc' lists creators as an additional field at the end.
'-ld' lists description as an additional field at the end.
'-json' produce JSON output instead of normal output
+ '-p' limits output to physical repos only (no wild repo regexes!)
The optional pattern is an unanchored regex that will limit the repos
searched, in both cases. It might speed up things a little if you have more
@@ -25,7 +26,7 @@ than a few thousand repos.
=cut
# these are globals
-my ( $lc, $ld, $json, $patt ) = args();
+my ( $lc, $ld, $json, $p, $patt ) = args();
my %out; # holds info to be json'd
$ENV{GL_USER} or _die "GL_USER not set";
@@ -35,8 +36,8 @@ if ($json) {
print greeting();
}
-print_patterns(); # repos he can create for himself
-print_phy_repos(); # repos already created
+print_patterns() unless $p; # repos he can create for himself
+print_phy_repos(); # repos already created
if ( $rc{SITE_INFO} ) {
$json
@@ -49,13 +50,14 @@ print JSON::to_json( \%out, { utf8 => 1, pretty => 1 } ) if $json;
# ----------------------------------------------------------------------
sub args {
- my ( $lc, $ld, $json, $patt ) = ( '', '', '', '' );
+ my ( $lc, $ld, $json, $p, $patt ) = ( '', '', '', '' );
my $help = '';
GetOptions(
'lc' => \$lc,
'ld' => \$ld,
'json' => \$json,
+ 'p' => \$p,
'h' => \$help,
) or usage();
@@ -64,7 +66,7 @@ sub args {
require JSON if $json;
- return ( $lc, $ld, $json, $patt );
+ return ( $lc, $ld, $json, $p, $patt );
}
sub print_patterns {
diff --git a/src/gitolite b/src/gitolite
index 4a4cbf5..c11e047 100755
--- a/src/gitolite
+++ b/src/gitolite
@@ -71,6 +71,11 @@ if ( $command eq 'setup' ) {
compile(@args);
} elsif ( $command eq 'trigger' ) {
+ my $s = $args[0];
+ _die "trigger section '$s' not found in rc"
+ unless $s eq 'POST_COMPILE'
+ or $s eq 'POST_CREATE'
+ or ( exists $rc{$s} and ref( $rc{$s} ) eq 'ARRAY' );
trigger(@args);
} elsif ( my $c = _which( "commands/$command", 'x' ) ) {
diff --git a/src/gitolite-shell b/src/gitolite-shell
index e8efe3d..684d400 100755
--- a/src/gitolite-shell
+++ b/src/gitolite-shell
@@ -113,6 +113,13 @@ sub main {
$ENV{GL_REPO} = $repo;
my $aa = ( $verb =~ 'upload' ? 'R' : 'W' );
+ # catch rare race when moving repos into gitolite control
+ _die "$aa any $repo $user DENIED by fallthru" .
+ "\n(or you mis-spelled the reponame)"
+ unless update_hook_present($repo);
+ # this error message is exactly the same as that from elsewhere in the
+ # code, for the usual reasons (avoid leaking information)
+
# set up env vars from options set for this repo
env_options($repo, $user);
diff --git a/src/lib/Gitolite/Common.pm b/src/lib/Gitolite/Common.pm
index 7a52f4b..3f47b37 100644
--- a/src/lib/Gitolite/Common.pm
+++ b/src/lib/Gitolite/Common.pm
@@ -19,6 +19,8 @@ package Gitolite::Common;
ssh_fingerprint_file
ssh_fingerprint_line
+
+ update_hook_present
);
#>>>
use Exporter 'import';
@@ -235,14 +237,28 @@ sub cleanup_conf_line {
chomp($repo);
$repo =~ s/\.git$//;
$repo =~ s(^\./)();
- push @phy_repos, $repo unless $repo =~ m(/$);
- # tolerate bare repos within ~/repositories but silently ignore them
+ next if $repo =~ m(/$);
+ # tolerate non-bare repos within ~/repositories but silently ignore them
+ next unless update_hook_present($repo);
+ # ignore repos that don't yet have the update hook
+ push @phy_repos, $repo;
}
trace( 3, scalar(@phy_repos) . " physical repos found" );
return sort_u( \@phy_repos );
}
}
+sub update_hook_present {
+ my $repo = shift;
+
+ return 1 unless -d "$ENV{GL_REPO_BASE}/$repo.git"; # non-existent repo is fine
+
+ my $x = readlink("$ENV{GL_REPO_BASE}/$repo.git/hooks/update");
+ return 1 if $x and $x eq "$ENV{GL_ADMIN_BASE}/hooks/common/update";
+
+ return 0;
+}
+
# generate a timestamp
sub gen_ts {
my ( $s, $min, $h, $d, $m, $y ) = (localtime)[ 0 .. 5 ];
diff --git a/src/lib/Gitolite/Hooks/Update.pm b/src/lib/Gitolite/Hooks/Update.pm
index 32cd6e0..2bc43a8 100644
--- a/src/lib/Gitolite/Hooks/Update.pm
+++ b/src/lib/Gitolite/Hooks/Update.pm
@@ -17,6 +17,8 @@ use Gitolite::Conf::Load;
use strict;
use warnings;
+$|++;
+
# ----------------------------------------------------------------------
sub update {
diff --git a/src/triggers/expand-deny-messages b/src/triggers/expand-deny-messages
index a8b2289..107202c 100755
--- a/src/triggers/expand-deny-messages
+++ b/src/triggers/expand-deny-messages
@@ -2,6 +2,8 @@
use strict;
use warnings;
+$|++;
+
# program name: expand-deny-messages
# DOCUMENTATION IS AT THE BOTTOM OF THIS FILE; PLEASE READ
@@ -47,10 +49,14 @@ _info( "Operation", _op( $a12, $aa, $oldsha, $newsha ) );
if ( $ref =~ m((^VREF/[^/]+)) ) {
my $vref = $1;
- my $vref_text = slurp( _which( $vref, 'x' ) );
- my $etag = '(?:help|explain|explanation)';
- $vref_text =~ m(^\s*# $etag.start\n(.*)^\s*# $etag.end\n)sm
- and print STDERR "Explanation for $vref:\n$1";
+ if ($ref =~ s(^VREF/NAME/)()) {
+ print STDERR "You're apparently not allowed to push '$ref'";
+ } else {
+ my $vref_text = slurp( _which( $vref, 'x' ) );
+ my $etag = '(?:help|explain|explanation)';
+ $vref_text =~ m(^\s*# $etag.start\n(.*)^\s*# $etag.end\n)sm
+ and print STDERR "Explanation for $vref:\n$1";
+ }
}
print STDERR "\n";
diff --git a/t/access.t b/t/access.t
index c6c7b22..c3f3341 100755
--- a/t/access.t
+++ b/t/access.t
@@ -9,7 +9,7 @@ use Gitolite::Test;
# test 'gitolite access'
# ----------------------------------------------------------------------
-try "plan 216";
+try "plan 254";
confreset;confadd '
@admins = admin dev1
@@ -200,3 +200,54 @@ try "
gitolite access c1 u2 +; ok
gitolite access c1 u2 C; !ok
";
+
+confreset;confadd '
+ repo foo
+ R = u1
+ RW = u2
+ RW+ = u3
+
+ repo bar
+ R = u1
+ RW = u2
+ RW+ = u3
+ RW+CDM = u6
+
+';
+
+try "ADMIN_PUSH set4; !/FATAL/" or die text();
+
+try "
+ gitolite access foo u1 +; !ok
+ gitolite access foo u2 +; !ok
+ gitolite access foo u3 +; ok
+ gitolite access foo u1 C; !ok
+ gitolite access foo u2 C; ok
+ gitolite access foo u3 C; ok
+ gitolite access foo u1 D; !ok
+ gitolite access foo u2 D; !ok
+ gitolite access foo u3 D; ok
+ gitolite access foo u1 M; !ok
+ gitolite access foo u2 M; ok
+ gitolite access foo u3 M; ok
+
+ gitolite access bar u1 +; !ok
+ gitolite access bar u2 +; !ok
+ gitolite access bar u3 +; ok
+ gitolite access bar u1 C; !ok
+ gitolite access bar u2 C; !ok
+ gitolite access bar u3 C; !ok
+ gitolite access bar u1 D; !ok
+ gitolite access bar u2 D; !ok
+ gitolite access bar u3 D; !ok
+ gitolite access bar u1 M; !ok
+ gitolite access bar u2 M; !ok
+ gitolite access bar u3 M; !ok
+
+ gitolite access bar u6 R; ok
+ gitolite access bar u6 W; ok
+ gitolite access bar u6 +; ok
+ gitolite access bar u6 C; ok
+ gitolite access bar u6 D; ok
+ gitolite access bar u6 M; ok
+";