diff --git a/src/gitolite.pm b/src/gitolite.pm index e0c2af4..e78c882 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -255,28 +255,53 @@ sub new_repo # 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 wild_repo_rights { - my ($repo, $user) = @_; - # creator - my $c = ''; - if ( -f "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-creater") { - my $fh = wrap_open("<", "$ENV{GL_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 "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms") { - my $fh = wrap_open("<", "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms"); - my $perms = join ("", <$fh>); - if ($perms) { - $r = $user if $perms =~ /^\s*R(?=\s).*\s(\@all|$user)(\s|$)/m; - $w = $user if $perms =~ /^\s*RW(?=\s).*\s(\@all|$user)(\s|$)/m; - } - } + # the following sub needs some persistent data, so we make a closure + my $cache_filled = 0; + my %cached_groups; - return ($c, $r, $w); + # "who created this repo", "am I on the R list", and "am I on the RW list"? + sub wild_repo_rights + { + my ($repo, $user) = @_; + # pull in basic group info + unless ($cache_filled) { + local(%repos, %groups); + # read group info from compiled config. At the time we're called + # this info has not yet been pulled in by the rest of the code, so + # we need to do this specially here. However, the info we're + # looking for is not subject to variable substitutions so we don't + # really care; we just pull it in once and save it for the rest of + # the run + do $GL_CONF_COMPILED; + %cached_groups = %groups; + $cache_filled++; + } + # creator + my $c = ''; + if ( -f "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-creater") { + my $fh = wrap_open("<", "$ENV{GL_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 "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms") { + my $fh = wrap_open("<", "$ENV{GL_REPO_BASE_ABS}/$repo.git/gl-perms"); + my $perms = join ("", <$fh>); + # $perms is say "R alice @foo @bar\nRW bob @baz" (the entire gl-perms + # file). We replace each @foo with $user if $cached_groups{'@foo'}{$user} + # exists (i.e., $user is a member of @foo) + for my $g ($perms =~ /\s(\@\S+)/g) { + $perms =~ s/ $g(?!\S)/ $user/ if $cached_groups{$g}{$user}; + } + if ($perms) { + $r = $user if $perms =~ /^\s*R(?=\s).*\s(\@all|$user)(\s|$)/m; + $w = $user if $perms =~ /^\s*RW(?=\s).*\s(\@all|$user)(\s|$)/m; + } + } + + return ($c, $r, $w); + } } # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index a128dc8..0ccd7d5 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -393,7 +393,7 @@ $dumped_data .= Data::Dumper->Dump([\%repo_config], [qw(*repo_config)]) if %repo # much... $dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g; print $compiled_fh $dumped_data; -if ($GL_BIG_CONFIG and %groups) { +if (%groups) { $dumped_data = Data::Dumper->Dump([\%groups], [qw(*groups)]); $dumped_data =~ s/\bCREAT[EO]R\b/\$creator/g; $dumped_data =~ s/'(?=[^']*\$(?:creator|readers|writers|gl_user))~?(.*?)'/"$1"/g; diff --git a/t/out/t01-repo-groups.1b b/t/out/t01-repo-groups.1b new file mode 100644 index 0000000..dac62f2 --- /dev/null +++ b/t/out/t01-repo-groups.1b @@ -0,0 +1,105 @@ +$data_version = '1.5'; +%repos = ( + 'aa' => { + 'R' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'W' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'u1' => [ + [ + 2, + 'refs/.*', + 'RW+' + ] + ], + 'u2' => [ + [ + 4, + 'refs/.*', + 'RW' + ] + ], + 'u3' => [ + [ + 5, + 'refs/.*', + 'RW' + ] + ] + }, + 'bb' => { + 'R' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'W' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'u1' => [ + [ + 3, + 'refs/.*', + 'RW+' + ] + ], + 'u2' => [ + [ + 6, + 'refs/.*', + 'RW' + ] + ], + 'u3' => [ + [ + 7, + 'refs/.*', + 'RW' + ] + ] + }, + 'gitolite-admin' => { + 'R' => { + 'tester' => 1 + }, + 'W' => { + 'tester' => 1 + }, + 'tester' => [ + [ + 0, + 'refs/.*', + 'RW+' + ] + ] + }, + 'testing' => { + '@all' => [ + [ + 1, + 'refs/.*', + 'RW+' + ] + ], + 'R' => { + '@all' => 1 + }, + 'W' => { + '@all' => 1 + } + } +); +%groups = ( + '@g1' => { + 'aa' => 'master', + 'bb' => 'master' + } +); diff --git a/t/out/t02-user-groups.1b b/t/out/t02-user-groups.1b new file mode 100644 index 0000000..cfae71a --- /dev/null +++ b/t/out/t02-user-groups.1b @@ -0,0 +1,80 @@ +$data_version = '1.5'; +%repos = ( + 'aa' => { + 'R' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'W' => { + 'u1' => 1, + 'u2' => 1, + 'u3' => 1 + }, + 'u1' => [ + [ + 2, + 'refs/.*', + 'RW+' + ] + ], + 'u2' => [ + [ + 3, + 'refs/.*', + 'RW' + ] + ], + 'u3' => [ + [ + 4, + 'refs/.*', + 'RW' + ] + ] + }, + 'gitolite-admin' => { + 'R' => { + 'tester' => 1 + }, + 'W' => { + 'tester' => 1 + }, + 'tester' => [ + [ + 0, + 'refs/.*', + 'RW+' + ] + ] + }, + 'testing' => { + '@all' => [ + [ + 1, + 'refs/.*', + 'RW+' + ] + ], + 'R' => { + '@all' => 1 + }, + 'W' => { + '@all' => 1 + } + } +); +%groups = ( + '@g1' => { + 'u1' => 'master' + }, + '@g2' => { + 'u2' => 'master', + 'u3' => 'master' + }, + '@g3' => { + 'u4' => 'master', + 'u5' => 'master', + 'u6' => 'master' + } +); diff --git a/t/t01-repo-groups b/t/t01-repo-groups index d738def..367e29d 100644 --- a/t/t01-repo-groups +++ b/t/t01-repo-groups @@ -26,7 +26,7 @@ echo " " | ugc catconf -expect_filesame $TESTDIR/out/t01-repo-groups.1 +expect_filesame $TESTDIR/out/t01-repo-groups.1b # ---------- $TESTDIR/rollback || die "rollback failed" diff --git a/t/t02-user-groups b/t/t02-user-groups index bc17e17..7b673cc 100644 --- a/t/t02-user-groups +++ b/t/t02-user-groups @@ -29,7 +29,7 @@ echo " " | ugc catconf -expect_filesame $TESTDIR/out/t02-user-groups.1 +expect_filesame $TESTDIR/out/t02-user-groups.1b # ---------- $TESTDIR/rollback || die "rollback failed" diff --git a/t/t61-setperms-wild b/t/t61-setperms-wild new file mode 100644 index 0000000..aa4d8c7 --- /dev/null +++ b/t/t61-setperms-wild @@ -0,0 +1,72 @@ +# vim: syn=sh: +for bc in 0 1 +do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS 1 + editrc GL_BIG_CONFIG $bc + + name "INTERNAL" + echo " + @leads = u1 u2 + @devs = u1 u2 u3 u4 + + @gbar = bar/CREATOR/..* + repo @gbar + C = @leads + RW+ = CREATOR + RW = WRITERS + R = READERS + " | ugc + name "nothing set yet" + expect_push_ok "master -> master" + + name "u1 auto-creates a repo" + runlocal git ls-remote u1:bar/u1/try1 + expect "Initialized empty Git repository in /home/gitolite-test/repositories/bar/u1/try1.git/" + name "default permissions for u2 and u4" + runlocal ssh u1 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u2 expand + notexpect R.*W.*u1.*bar/u1/try1 + runlocal ssh u4 expand + notexpect R.*W.*u1.*bar/u1/try1 + + name "@leads can RW try1" + echo RW @leads | runlocal ssh u1 setperms bar/u1/try1 + expect "RW @leads" + runlocal ssh u1 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u2 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u4 expand + notexpect R.*W.*u1.*bar/u1/try1 + + name "@devs can R try1" + echo R @devs | runlocal ssh u1 setperms bar/u1/try1 + expect "R @devs" + notexpect "RW @leads" + runlocal ssh u1 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u2 expand + notexpect R.*W.*u1.*bar/u1/try1 + expect R.*u1.*bar/u1/try1 + runlocal ssh u4 expand + notexpect R.*W.*u1.*bar/u1/try1 + expect R.*u1.*bar/u1/try1 + + name "combo of previous 2" + printf "R @devs\nRW @leads\n" | runlocal ssh u1 setperms bar/u1/try1 + expect "R @devs" + expect "RW @leads" + runlocal ssh u1 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u2 expand + expect R.*W.*u1.*bar/u1/try1 + runlocal ssh u4 expand + notexpect R.*W.*u1.*bar/u1/try1 + expect R.*u1.*bar/u1/try1 + + name "INTERNAL" + +done