wildrepos: teach compile the new syntax
There's a new "C" permission to let someone *create* a repo that matches the pattern given in the "repo ..." line. If the word CREATER appears in the repo pattern, then that is forced to the actual user performing that operation. Something like this (we'll discuss READERS and WRITERS later): repo personal/CREATER/.+ C = @staff R [foo] = READERS RW [bar] = WRITERS ...various other permissions as usual... Delegation checking also changes quite a bit... see comments in code Implementation: there's also a sneaky little trick we're playing here with the dumped hash
This commit is contained in:
parent
8a4bb453a0
commit
77306567e9
|
@ -27,6 +27,8 @@ $W_COMMANDS=qr/^git[ -]receive-pack$/;
|
||||||
# note that REPONAME_PATT allows a "/" also, which USERNAME_PATT doesn't
|
# note that REPONAME_PATT allows a "/" also, which USERNAME_PATT doesn't
|
||||||
$REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern
|
$REPONAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._/-]*$); # very simple pattern
|
||||||
$USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern
|
$USERNAME_PATT=qr(^\@?[0-9a-zA-Z][0-9a-zA-Z._-]*$); # very simple pattern
|
||||||
|
# same as REPONAME, plus some common regex metas
|
||||||
|
$REPOPATT_PATT=qr(^\@?[0-9a-zA-Z][\\^.$|()[\]*+?{}0-9a-zA-Z._/-]*$);
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# convenience subs
|
# convenience subs
|
||||||
|
|
|
@ -54,7 +54,7 @@ open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q');
|
||||||
# these are set by the "rc" file
|
# these are set by the "rc" file
|
||||||
our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH);
|
our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH);
|
||||||
# and these are set by gitolite.pm
|
# and these are set by gitolite.pm
|
||||||
our ($REPONAME_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN);
|
our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN);
|
||||||
|
|
||||||
# the common setup module is in the same directory as this running program is
|
# the common setup module is in the same directory as this running program is
|
||||||
my $bindir = $0;
|
my $bindir = $0;
|
||||||
|
@ -85,7 +85,8 @@ our %groups = ();
|
||||||
# %repos has two functions.
|
# %repos has two functions.
|
||||||
|
|
||||||
# $repos{repo}{R|W}{user} = 1 if user has R (or W) permissions for at least
|
# $repos{repo}{R|W}{user} = 1 if user has R (or W) permissions for at least
|
||||||
# one branch in repo. This is used by the "level 1 check" (see faq)
|
# one branch in repo. This is used by the "level 1 check" (see faq). There's
|
||||||
|
# also the new "C" (create a repo) permission now
|
||||||
|
|
||||||
# $repos{repo}{user} is a list of {ref, perms} pairs. This is used by the
|
# $repos{repo}{user} is a list of {ref, perms} pairs. This is used by the
|
||||||
# level 2 check. In order to allow "exclude" rules, the order of rules now
|
# level 2 check. In order to allow "exclude" rules, the order of rules now
|
||||||
|
@ -119,9 +120,7 @@ sub expand_list
|
||||||
|
|
||||||
for my $item (@list)
|
for my $item (@list)
|
||||||
{
|
{
|
||||||
# we test with the slightly more relaxed pattern here; we'll catch the
|
die "$ABRT bad user or repo name $item\n" unless $item =~ $REPOPATT_PATT;
|
||||||
# "/" in user name thing later; it doesn't affect security anyway
|
|
||||||
die "$ABRT bad user or repo name $item\n" unless $item =~ $REPONAME_PATT;
|
|
||||||
if ($item =~ /^@/) # nested group
|
if ($item =~ /^@/) # nested group
|
||||||
{
|
{
|
||||||
die "$ABRT undefined group $item\n" unless $groups{$item};
|
die "$ABRT undefined group $item\n" unless $groups{$item};
|
||||||
|
@ -183,9 +182,11 @@ sub parse_conf_file
|
||||||
# grab the list and expand any @stuff in it
|
# grab the list and expand any @stuff in it
|
||||||
@repos = split ' ', $1;
|
@repos = split ' ', $1;
|
||||||
@repos = expand_list ( @repos );
|
@repos = expand_list ( @repos );
|
||||||
|
|
||||||
|
s/\bCREAT[EO]R\b/\$creater/g for @repos;
|
||||||
}
|
}
|
||||||
# actual permission line
|
# actual permission line
|
||||||
elsif (/^(-|R|RW|RW\+) (.* )?= (.+)/)
|
elsif (/^(-|C|R|RW|RW\+) (.* )?= (.+)/)
|
||||||
{
|
{
|
||||||
my $perms = $1;
|
my $perms = $1;
|
||||||
my @refs; @refs = split(' ', $2) if $2;
|
my @refs; @refs = split(' ', $2) if $2;
|
||||||
|
@ -202,21 +203,39 @@ sub parse_conf_file
|
||||||
unless (@users == 1 and $users[0] eq '@all');
|
unless (@users == 1 and $users[0] eq '@all');
|
||||||
do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users;
|
do { die "$ABRT bad username $_\n" unless $_ =~ $USERNAME_PATT } for @users;
|
||||||
|
|
||||||
|
s/\bCREAT[EO]R\b/\$creater/g for @users;
|
||||||
|
s/\bREADERS\b/\$readers/g for @users;
|
||||||
|
s/\bWRITERS\b/\$writers/g for @users;
|
||||||
|
|
||||||
# ok, we can finally populate the %repos hash
|
# ok, we can finally populate the %repos hash
|
||||||
for my $repo (@repos) # each repo in the current stanza
|
for my $repo (@repos) # each repo in the current stanza
|
||||||
{
|
{
|
||||||
# if we're processing a delegated config file (not the master
|
# if we're processing a delegated config file (not the master
|
||||||
# config), and if that fragment name is not the same as the
|
# config), we need to prevent attempts by that admin to obtain
|
||||||
# current repo
|
# rights on stuff outside his domain
|
||||||
if ($fragment ne 'master' and $fragment ne $repo)
|
|
||||||
{
|
# trying to set access for $repo (='foo')...
|
||||||
# then the fragment must be a group name and the repo
|
if (
|
||||||
# being processed must be a member of that "@group".
|
# processing the master config, not a fragment
|
||||||
# Also, the value of the hash for that combination must be
|
( $fragment eq 'master' ) or
|
||||||
# "master", signifying a group created in the master
|
# fragment is also called 'foo' (you're allowed to have a
|
||||||
# config file and not in one of the delegates
|
# fragment that is only concerned with one repo)
|
||||||
unless ( ($groups{"\@$fragment"}{$repo} || '') eq 'master')
|
( $fragment eq $repo ) or
|
||||||
{
|
# fragment is called "bar" and "@bar = foo" has been
|
||||||
|
# defined in the master config
|
||||||
|
( ($groups{"\@$fragment"}{$repo} || '') eq 'master' )
|
||||||
|
) {
|
||||||
|
# all these are fine
|
||||||
|
} else {
|
||||||
|
# this is a little more complex
|
||||||
|
|
||||||
|
# fragment is called "bar", one or more "@bar = regex"
|
||||||
|
# have been specified in master, and "foo" matches some
|
||||||
|
# such "regex"
|
||||||
|
my @matched = grep { $repo =~ /^$_$/ }
|
||||||
|
grep { $groups{"\@$fragment"}{$_} eq 'master' }
|
||||||
|
sort keys %{ $groups{"\@$fragment"} };
|
||||||
|
if (@matched < 1) {
|
||||||
$ignored{$fragment}{$repo} = 1;
|
$ignored{$fragment}{$repo} = 1;
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
@ -226,6 +245,7 @@ sub parse_conf_file
|
||||||
$user_list{$user}++; # only to catch lint, see later
|
$user_list{$user}++; # only to catch lint, see later
|
||||||
|
|
||||||
# for 1st level check (see faq/tips doc)
|
# for 1st level check (see faq/tips doc)
|
||||||
|
$repos{$repo}{C}{$user} = 1, next if $perms eq 'C';
|
||||||
$repos{$repo}{R}{$user} = 1 if $perms =~ /R/;
|
$repos{$repo}{R}{$user} = 1 if $perms =~ /R/;
|
||||||
$repos{$repo}{W}{$user} = 1 if $perms =~ /W/;
|
$repos{$repo}{W}{$user} = 1 if $perms =~ /W/;
|
||||||
|
|
||||||
|
@ -302,7 +322,12 @@ for my $fragment_file (glob("conf/fragments/*.conf"))
|
||||||
}
|
}
|
||||||
|
|
||||||
my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED );
|
my $compiled_fh = wrap_open( ">", $GL_CONF_COMPILED );
|
||||||
print $compiled_fh Data::Dumper->Dump([\%repos], [qw(*repos)]);
|
my $dumped_data = Data::Dumper->Dump([\%repos], [qw(*repos)]);
|
||||||
|
# the dump uses single quotes, but we convert any strings containing $creater,
|
||||||
|
# $readers, $writers, to double quoted strings. A wee bit sneaky, but not too
|
||||||
|
# much...
|
||||||
|
$dumped_data =~ s/'(?=[^']*\$(?:creater|readers|writers))(.*?)'/"$1"/g;
|
||||||
|
print $compiled_fh $dumped_data;
|
||||||
close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n";
|
close $compiled_fh or die "$ABRT close compiled-conf failed: $!\n";
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
@ -326,6 +351,7 @@ my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE"
|
||||||
wrap_chdir("$repo_base_abs");
|
wrap_chdir("$repo_base_abs");
|
||||||
|
|
||||||
for my $repo (sort keys %repos) {
|
for my $repo (sort keys %repos) {
|
||||||
|
next unless $repo =~ $REPONAME_PATT;
|
||||||
unless (-d "$repo.git") {
|
unless (-d "$repo.git") {
|
||||||
new_repo($repo, "$GL_ADMINDIR/src/hooks");
|
new_repo($repo, "$GL_ADMINDIR/src/hooks");
|
||||||
# new_repo would have chdir'd us away; come back
|
# new_repo would have chdir'd us away; come back
|
||||||
|
@ -352,6 +378,7 @@ wrap_chdir("$repo_base_abs");
|
||||||
|
|
||||||
# daemons first...
|
# daemons first...
|
||||||
for my $repo (sort keys %repos) {
|
for my $repo (sort keys %repos) {
|
||||||
|
next unless $repo =~ $REPONAME_PATT;
|
||||||
my $export_ok = "$repo.git/git-daemon-export-ok";
|
my $export_ok = "$repo.git/git-daemon-export-ok";
|
||||||
if ($repos{$repo}{'R'}{'daemon'}) {
|
if ($repos{$repo}{'R'}{'daemon'}) {
|
||||||
system("touch $export_ok");
|
system("touch $export_ok");
|
||||||
|
@ -363,6 +390,7 @@ for my $repo (sort keys %repos) {
|
||||||
my %projlist = ();
|
my %projlist = ();
|
||||||
# ...then gitwebs
|
# ...then gitwebs
|
||||||
for my $repo (sort keys %repos) {
|
for my $repo (sort keys %repos) {
|
||||||
|
next unless $repo =~ $REPONAME_PATT;
|
||||||
my $desc_file = "$repo.git/description";
|
my $desc_file = "$repo.git/description";
|
||||||
# note: having a description also counts as enabling gitweb
|
# note: having a description also counts as enabling gitweb
|
||||||
if ($repos{$repo}{'R'}{'gitweb'} or $desc{"$repo.git"}) {
|
if ($repos{$repo}{'R'}{'gitweb'} or $desc{"$repo.git"}) {
|
||||||
|
@ -422,7 +450,7 @@ for my $pubkey (glob("*"))
|
||||||
# lint check 3; a little more severe than the first two I guess...
|
# lint check 3; a little more severe than the first two I guess...
|
||||||
for my $user (sort keys %user_list)
|
for my $user (sort keys %user_list)
|
||||||
{
|
{
|
||||||
next if $user =~ /^(gitweb|daemon|\@all)$/ or $user_list{$user} eq 'has pubkey';
|
next if $user =~ /^(gitweb|daemon|\@all|\$creater|\$readers|\$writers)$/ or $user_list{$user} eq 'has pubkey';
|
||||||
print STDERR "$WARN user $user in config, but has no pubkey!\n";
|
print STDERR "$WARN user $user in config, but has no pubkey!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue