2012-03-08 09:00:13 +01:00
|
|
|
package Gitolite::Conf::Load;
|
|
|
|
|
|
|
|
# load conf data from stored files
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
@EXPORT = qw(
|
|
|
|
load
|
|
|
|
access
|
2012-03-16 09:59:45 +01:00
|
|
|
git_config
|
2012-03-11 03:06:42 +01:00
|
|
|
vrefs
|
2012-03-12 16:24:30 +01:00
|
|
|
lister_dispatch
|
2012-03-08 09:00:13 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
use Exporter 'import';
|
|
|
|
|
|
|
|
use Gitolite::Common;
|
|
|
|
use Gitolite::Rc;
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
# our variables, because they get loaded by a 'do'
|
|
|
|
our $data_version = '';
|
|
|
|
our %repos;
|
|
|
|
our %one_repo;
|
|
|
|
our %groups;
|
|
|
|
our %configs;
|
|
|
|
our %one_config;
|
|
|
|
our %split_conf;
|
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
my $subconf = 'master';
|
|
|
|
|
|
|
|
my %listers = (
|
|
|
|
'list-groups' => \&list_groups,
|
|
|
|
'list-users' => \&list_users,
|
|
|
|
'list-repos' => \&list_repos,
|
|
|
|
'list-memberships' => \&list_memberships,
|
|
|
|
'list-members' => \&list_members,
|
|
|
|
);
|
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
# helps maintain the "cache" in both "load_common" and "load_1"
|
|
|
|
my $last_repo = '';
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
{
|
|
|
|
my $loaded_repo = '';
|
|
|
|
|
|
|
|
sub load {
|
|
|
|
my $repo = shift or _die "load() needs a reponame";
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 3, "$repo" );
|
2012-03-08 09:00:13 +01:00
|
|
|
if ( $repo ne $loaded_repo ) {
|
2012-03-08 08:01:01 +01:00
|
|
|
load_common();
|
|
|
|
load_1($repo);
|
2012-03-08 09:00:13 +01:00
|
|
|
$loaded_repo = $repo;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub access {
|
|
|
|
my ( $repo, $user, $aa, $ref ) = @_;
|
|
|
|
load($repo);
|
|
|
|
|
|
|
|
my @rules = rules( $repo, $user );
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 2, scalar(@rules) . " rules found" );
|
2012-03-08 09:00:13 +01:00
|
|
|
for my $r (@rules) {
|
2012-03-15 15:34:30 +01:00
|
|
|
my $perm = $r->[1];
|
|
|
|
my $refex = $r->[2]; $refex =~ s(/USER/)(/$user/);
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 3, "perm=$perm, refex=$refex" );
|
2012-03-08 09:00:13 +01:00
|
|
|
|
|
|
|
# skip 'deny' rules if the ref is not (yet) known
|
2012-03-09 09:03:32 +01:00
|
|
|
next if $perm eq '-' and $ref eq 'any';
|
2012-03-08 09:00:13 +01:00
|
|
|
|
2012-03-09 09:03:32 +01:00
|
|
|
# rule matches if ref matches or ref is any (see gitolite-shell)
|
|
|
|
next unless $ref =~ /^$refex/ or $ref eq 'any';
|
2012-03-08 09:00:13 +01:00
|
|
|
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 2, "DENIED by $refex" ) if $perm eq '-';
|
2012-03-09 09:03:32 +01:00
|
|
|
return "$aa $ref $repo $user DENIED by $refex" if $perm eq '-';
|
2012-03-08 09:00:13 +01:00
|
|
|
|
|
|
|
# $perm can be RW\+?(C|D|CD|DC)?M?. $aa can be W, +, C or D, or
|
|
|
|
# any of these followed by "M".
|
|
|
|
( my $aaq = $aa ) =~ s/\+/\\+/;
|
|
|
|
$aaq =~ s/M/.*M/;
|
|
|
|
# as far as *this* ref is concerned we're ok
|
|
|
|
return $refex if ( $perm =~ /$aaq/ );
|
|
|
|
}
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 2, "DENIED by fallthru" );
|
2012-03-09 09:03:32 +01:00
|
|
|
return "$aa $ref $repo $user DENIED by fallthru";
|
2012-03-08 09:00:13 +01:00
|
|
|
}
|
|
|
|
|
2012-03-16 09:59:45 +01:00
|
|
|
sub git_config {
|
|
|
|
my ( $repo, $key ) = @_;
|
|
|
|
$key ||= '.';
|
|
|
|
|
|
|
|
load($repo);
|
|
|
|
|
|
|
|
# read comments bottom up
|
|
|
|
my %ret =
|
|
|
|
# and take the second and third elements to make up your new hash
|
|
|
|
map { $_->[1] => $_->[2] }
|
|
|
|
# keep only the ones where the second element matches your key
|
|
|
|
grep { $_->[1] =~ qr($key) }
|
|
|
|
# sort this list of listrefs by the first element in each list ref'd to
|
|
|
|
sort { $a->[0] <=> $b->[0] }
|
|
|
|
# dereference it (into a list of listrefs)
|
|
|
|
map { @$_ }
|
|
|
|
# take the value of that entry
|
|
|
|
map { $configs{$_} }
|
|
|
|
# if it has an entry in %configs
|
|
|
|
grep { $configs{$_} }
|
|
|
|
# for each "repo" that represents us
|
2012-03-16 17:09:26 +01:00
|
|
|
memberships('repo', $repo);
|
2012-03-16 09:59:45 +01:00
|
|
|
|
|
|
|
# %configs looks like this (for each 'foo' that is in memberships())
|
|
|
|
# 'foo' => [ [ 6, 'foo.bar', 'repo' ], [ 7, 'foodbar', 'repoD' ], [ 8, 'foo.czar', 'jule' ] ],
|
|
|
|
# the first map gets you the value
|
|
|
|
# [ [ 6, 'foo.bar', 'repo' ], [ 7, 'foodbar', 'repoD' ], [ 8, 'foo.czar', 'jule' ] ],
|
|
|
|
# the deref gets you
|
|
|
|
# [ 6, 'foo.bar', 'repo' ], [ 7, 'foodbar', 'repoD' ], [ 8, 'foo.czar', 'jule' ]
|
|
|
|
# the sort rearranges it (in this case it's already sorted but anyway...)
|
|
|
|
# the grep gets you this, assuming the key is foo.bar (and "." is regex ".')
|
|
|
|
# [ 6, 'foo.bar', 'repo' ], [ 7, 'foodbar', 'repoD' ]
|
|
|
|
# and the final map does this:
|
|
|
|
# 'foo.bar'=>'repo' , 'foodbar'=>'repoD'
|
|
|
|
|
|
|
|
return \%ret;
|
|
|
|
}
|
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
sub load_common {
|
|
|
|
|
2012-03-16 05:29:45 +01:00
|
|
|
_chdir( $rc{GL_ADMIN_BASE} );
|
2012-03-08 08:01:01 +01:00
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
# we take an unusual approach to caching this function!
|
|
|
|
# (requires that first call to load_common is before first call to load_1)
|
|
|
|
if ( $last_repo and $split_conf{$last_repo} ) {
|
|
|
|
delete $repos{$last_repo};
|
|
|
|
delete $configs{$last_repo};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $cc = "conf/gitolite.conf-compiled.pm";
|
|
|
|
|
|
|
|
_die "parse $cc failed: " . ( $! or $@ ) unless do $cc;
|
|
|
|
|
|
|
|
if ( data_version_mismatch() ) {
|
2012-03-10 14:26:29 +01:00
|
|
|
_system("gitolite setup");
|
2012-03-08 09:00:13 +01:00
|
|
|
_die "parse $cc failed: " . ( $! or $@ ) unless do $cc;
|
|
|
|
_die "data version update failed; this is serious" if data_version_mismatch();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub load_1 {
|
|
|
|
my $repo = shift;
|
2012-03-15 01:37:41 +01:00
|
|
|
return if $repo =~ /^\@/;
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 3, $repo );
|
2012-03-08 09:00:13 +01:00
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
_chdir("$rc{GL_REPO_BASE}/$repo.git");
|
2012-03-08 08:01:01 +01:00
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
if ( $repo eq $last_repo ) {
|
|
|
|
$repos{$repo} = $one_repo{$repo};
|
|
|
|
$configs{$repo} = $one_config{$repo} if $one_config{$repo};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-10 18:27:01 +01:00
|
|
|
if ( -f "gl-conf" ) {
|
2012-03-08 09:00:13 +01:00
|
|
|
_die "split conf not set, gl-conf present for $repo" if not $split_conf{$repo};
|
|
|
|
|
2012-03-10 18:27:01 +01:00
|
|
|
my $cc = "gl-conf";
|
2012-03-08 09:00:13 +01:00
|
|
|
_die "parse $cc failed: " . ( $! or $@ ) unless do $cc;
|
|
|
|
|
|
|
|
$last_repo = $repo;
|
|
|
|
$repos{$repo} = $one_repo{$repo};
|
|
|
|
$configs{$repo} = $one_config{$repo} if $one_config{$repo};
|
|
|
|
} else {
|
|
|
|
_die "split conf set, gl-conf not present for $repo" if $split_conf{$repo};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-11 03:06:42 +01:00
|
|
|
{
|
|
|
|
my $lastrepo = '';
|
|
|
|
my $lastuser = '';
|
2012-03-12 16:24:30 +01:00
|
|
|
my @cached = ();
|
2012-03-11 03:06:42 +01:00
|
|
|
|
|
|
|
sub rules {
|
|
|
|
my ( $repo, $user ) = @_;
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 3, "repo=$repo, user=$user" );
|
2012-03-11 03:06:42 +01:00
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
return @cached if ( $lastrepo eq $repo and $lastuser eq $user and @cached );
|
2012-03-11 03:06:42 +01:00
|
|
|
|
|
|
|
my @rules = ();
|
2012-03-08 09:00:13 +01:00
|
|
|
|
2012-03-16 17:09:26 +01:00
|
|
|
my @repos = memberships('repo', $repo);
|
|
|
|
my @users = memberships('user', $user);
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 3, "memberships: " . scalar(@repos) . " repos and " . scalar(@users) . " users found" );
|
2012-03-08 09:00:13 +01:00
|
|
|
|
2012-03-11 03:06:42 +01:00
|
|
|
for my $r (@repos) {
|
|
|
|
for my $u (@users) {
|
|
|
|
push @rules, @{ $repos{$r}{$u} } if exists $repos{$r}{$u};
|
|
|
|
}
|
2012-03-08 09:00:13 +01:00
|
|
|
}
|
2012-03-11 03:06:42 +01:00
|
|
|
|
|
|
|
@rules = sort { $a->[0] <=> $b->[0] } @rules;
|
|
|
|
|
|
|
|
$lastrepo = $repo;
|
|
|
|
$lastuser = $user;
|
2012-03-12 16:24:30 +01:00
|
|
|
@cached = @rules;
|
2012-03-11 03:06:42 +01:00
|
|
|
|
|
|
|
return @rules;
|
2012-03-08 09:00:13 +01:00
|
|
|
}
|
|
|
|
|
2012-03-11 03:06:42 +01:00
|
|
|
sub vrefs {
|
|
|
|
my ( $repo, $user ) = @_;
|
|
|
|
# fill the cache if needed
|
2012-03-12 16:24:30 +01:00
|
|
|
rules( $repo, $user ) unless ( $lastrepo eq $repo and $lastuser eq $user and @cached );
|
2012-03-08 09:00:13 +01:00
|
|
|
|
2012-03-11 03:06:42 +01:00
|
|
|
my %seen;
|
|
|
|
my @vrefs = grep { /^VREF\// and not $seen{$_}++ } map { $_->[2] } @cached;
|
|
|
|
return @vrefs;
|
|
|
|
}
|
2012-03-08 09:00:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub memberships {
|
2012-03-16 17:09:26 +01:00
|
|
|
my $type = shift;
|
2012-03-08 09:00:13 +01:00
|
|
|
my $item = shift;
|
2012-03-16 17:09:26 +01:00
|
|
|
my $item2 = '';
|
2012-03-08 09:00:13 +01:00
|
|
|
|
|
|
|
my @ret = ( $item, '@all' );
|
|
|
|
|
2012-03-16 17:09:26 +01:00
|
|
|
if ($type eq 'repo') {
|
|
|
|
my $f = "$rc{GL_REPO_BASE}/$item.git/gl-creator";
|
|
|
|
if (-f $f) {
|
|
|
|
my $creator;
|
|
|
|
chomp($creator = slurp($f));
|
|
|
|
($item2 = $item) =~ s(/$creator/)(/CREATOR/);
|
|
|
|
$item2 = '' if $item2 eq $item; # no change
|
|
|
|
}
|
|
|
|
for my $i (keys %repos) {
|
|
|
|
if ($item eq $i or $item =~ /^$i$/ or $item2 and ( $item2 eq $i or $item2 =~ /^$i$/ )) {
|
|
|
|
push @ret, $i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for my $i (keys %groups) {
|
|
|
|
if ($item eq $i or $item =~ /^$i$/ or $item2 and ( $item2 eq $i or $item2 =~ /^$i$/ )) {
|
|
|
|
push @ret, @{ $groups{$i} };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@ret = @{ sort_u(\@ret) };
|
|
|
|
dbg(\@ret);
|
2012-03-08 09:00:13 +01:00
|
|
|
return @ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub data_version_mismatch {
|
2012-03-16 05:29:45 +01:00
|
|
|
return $data_version ne glrc('current-data-version');
|
2012-03-08 09:00:13 +01:00
|
|
|
}
|
|
|
|
|
2012-03-08 07:43:50 +01:00
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
# api functions
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
sub lister_dispatch {
|
|
|
|
my $command = shift;
|
|
|
|
|
|
|
|
my $fn = $listers{$command} or _die "unknown gitolite sub-command";
|
|
|
|
return $fn;
|
|
|
|
}
|
|
|
|
|
|
|
|
=for list_groups
|
2012-03-08 11:44:13 +01:00
|
|
|
Usage: gitolite list-groups
|
|
|
|
|
|
|
|
- lists all group names in conf
|
|
|
|
- no options, no flags
|
2012-03-12 16:24:30 +01:00
|
|
|
=cut
|
2012-03-08 11:44:13 +01:00
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
sub list_groups {
|
|
|
|
usage() if @_;
|
2012-03-08 07:43:50 +01:00
|
|
|
|
|
|
|
load_common();
|
|
|
|
|
|
|
|
my @g = ();
|
2012-03-16 05:29:45 +01:00
|
|
|
while ( my ( $k, $v ) = each(%groups) ) {
|
|
|
|
push @g, @{$v};
|
2012-03-08 07:43:50 +01:00
|
|
|
}
|
2012-03-16 05:29:45 +01:00
|
|
|
return ( sort_u( \@g ) );
|
2012-03-08 07:43:50 +01:00
|
|
|
}
|
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
=for list_users
|
2012-03-08 11:44:13 +01:00
|
|
|
Usage: gitolite list-users
|
|
|
|
|
|
|
|
- lists all users/user groups in conf
|
|
|
|
- no options, no flags
|
|
|
|
- WARNING: may be slow if you have thousands of repos
|
2012-03-12 16:24:30 +01:00
|
|
|
=cut
|
2012-03-08 11:44:13 +01:00
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
sub list_users {
|
|
|
|
usage() if @_;
|
|
|
|
my $count = 0;
|
|
|
|
my $total = 0;
|
2012-03-08 10:36:05 +01:00
|
|
|
|
|
|
|
load_common();
|
|
|
|
|
2012-03-16 05:29:45 +01:00
|
|
|
my @u = map { keys %{$_} } values %repos;
|
|
|
|
$total = scalar( keys %split_conf );
|
2012-03-08 10:36:05 +01:00
|
|
|
warn "WARNING: you have $total repos to check; this could take some time!\n" if $total > 100;
|
|
|
|
for my $one ( keys %split_conf ) {
|
|
|
|
load_1($one);
|
2012-03-16 05:29:45 +01:00
|
|
|
$count++; print STDERR "$count / $total\r" if not( $count % 100 ) and timer(5);
|
|
|
|
push @u, map { keys %{$_} } values %one_repo;
|
2012-03-08 10:36:05 +01:00
|
|
|
}
|
2012-03-12 16:24:30 +01:00
|
|
|
print STDERR "\n" if $count >= 100;
|
2012-03-16 05:29:45 +01:00
|
|
|
return ( sort_u( \@u ) );
|
2012-03-08 10:36:05 +01:00
|
|
|
}
|
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
=for list_repos
|
2012-03-08 11:44:13 +01:00
|
|
|
Usage: gitolite list-repos
|
|
|
|
|
|
|
|
- lists all repos/repo groups in conf
|
|
|
|
- no options, no flags
|
2012-03-12 16:24:30 +01:00
|
|
|
=cut
|
2012-03-08 11:44:13 +01:00
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
sub list_repos {
|
|
|
|
usage() if @_;
|
2012-03-08 11:44:13 +01:00
|
|
|
|
|
|
|
load_common();
|
|
|
|
|
|
|
|
my @r = keys %repos;
|
|
|
|
push @r, keys %split_conf;
|
|
|
|
|
2012-03-16 05:29:45 +01:00
|
|
|
return ( sort_u( \@r ) );
|
2012-03-08 11:44:13 +01:00
|
|
|
}
|
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
=for list_memberships
|
2012-03-08 12:30:27 +01:00
|
|
|
Usage: gitolite list-memberships <name>
|
|
|
|
|
|
|
|
- list all groups a name is a member of
|
|
|
|
- takes one user/repo name
|
2012-03-12 16:24:30 +01:00
|
|
|
=cut
|
2012-03-08 12:30:27 +01:00
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
sub list_memberships {
|
|
|
|
usage() if @_ and $_[0] eq '-h' or not @_;
|
2012-03-08 12:30:27 +01:00
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
my $name = shift;
|
2012-03-08 12:30:27 +01:00
|
|
|
|
|
|
|
load_common();
|
2012-03-16 17:09:26 +01:00
|
|
|
my @m = memberships('', $name);
|
2012-03-16 05:29:45 +01:00
|
|
|
return ( sort_u( \@m ) );
|
2012-03-08 12:30:27 +01:00
|
|
|
}
|
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
=for list_members
|
2012-03-08 13:29:44 +01:00
|
|
|
Usage: gitolite list-members <group name>
|
|
|
|
|
|
|
|
- list all members of a group
|
|
|
|
- takes one group name
|
2012-03-12 16:24:30 +01:00
|
|
|
=cut
|
2012-03-08 13:29:44 +01:00
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
sub list_members {
|
|
|
|
usage() if @_ and $_[0] eq '-h' or not @_;
|
2012-03-08 13:29:44 +01:00
|
|
|
|
2012-03-12 16:24:30 +01:00
|
|
|
my $name = shift;
|
2012-03-08 13:29:44 +01:00
|
|
|
|
|
|
|
load_common();
|
|
|
|
|
|
|
|
my @m = ();
|
2012-03-16 05:29:45 +01:00
|
|
|
while ( my ( $k, $v ) = each(%groups) ) {
|
|
|
|
for my $g ( @{$v} ) {
|
2012-03-08 13:29:44 +01:00
|
|
|
push @m, $k if $g eq $name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-16 05:29:45 +01:00
|
|
|
return ( sort_u( \@m ) );
|
2012-03-08 13:29:44 +01:00
|
|
|
}
|
|
|
|
|
2012-03-08 10:36:05 +01:00
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
{
|
|
|
|
my $start_time = 0;
|
|
|
|
|
|
|
|
sub timer {
|
|
|
|
unless ($start_time) {
|
|
|
|
$start_time = time();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
my $elapsed = shift;
|
|
|
|
return 0 if time() - $start_time < $elapsed;
|
|
|
|
$start_time = time();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
1;
|
|
|
|
|