a3ffc9d8fd
We previously said all mirroring features are disabled if GL_HOSTNAME is not set. But what if, after mirroring has been setup, and master/slaves defined for a repo, a slave admin fat-fingers the RC file and accidentally comments out GL_HOSTNAME? We might end up violating RULE NUMBER ONE!
193 lines
7.8 KiB
Perl
Executable file
193 lines
7.8 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# ssh mode
|
|
# - started by sshd
|
|
# - one optional flag, "-s", for "shell allowed" people
|
|
# - one argument, the "user" name
|
|
# - one env var, SSH_ORIGINAL_COMMAND, containing the command
|
|
# - command typically: git-(receive|upload)-pack 'reponame(.git)?'
|
|
# - special gitolite commands: info, expand, (get|set)(perms|desc)
|
|
# - special non-gitolite commands: rsync, svnserve, htpasswd
|
|
# - other commands: anything in $GL_ADC_PATH if defined (see rc file)
|
|
#
|
|
# (smart) http mode
|
|
# - started by apache (httpd)
|
|
# - no arguments
|
|
# - REQUEST_URI contains verb and repo, REMOTE_USER contains username
|
|
# - REQUEST_URI looks like /path/reponame.git/(info/refs\?service=)?git-(receive|upload)-pack
|
|
# - no special processing commands currently handled
|
|
# ----------------------------------------------------------------------------
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# find the rc file, then pull the libraries in
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# this (gl-auth-command) is one of the two valid starting points for all of
|
|
# gitolite for normal operations (the other being gl-time). All other
|
|
# programs are invoked either from this, or from something else (typically
|
|
# git-*-pack) in between). They thus get the benefit of the environment
|
|
# variables that this code sets up.
|
|
BEGIN {
|
|
# find and set bin dir
|
|
$0 =~ m|^(/)?(.*)/| and $ENV{GL_BINDIR} = ($1 || "$ENV{PWD}/") . $2;
|
|
}
|
|
|
|
# our libraries are either in the same place the scripts are, or, as with
|
|
# RPM/DEB install, in some 'system' location that is already in perl's @INC
|
|
# anyway
|
|
use lib $ENV{GL_BINDIR};
|
|
|
|
use gitolite_rc; # this does a "do" of the rc file
|
|
use gitolite_env;
|
|
use gitolite;
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# start...
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# these two options are mutually exclusive. And this program is not supposed
|
|
# to be called manually anyway
|
|
my $shell_allowed = (@ARGV and $ARGV[0] eq '-s' and shift);
|
|
my $program = (@ARGV and $ARGV[0] eq '-e' and shift);
|
|
|
|
# setup the environment for the kids so they don't need to embark on the
|
|
# voyage of self-discovery above ;-) [environment also means things like
|
|
# nice, umask, etc., not just the environment *variables*]
|
|
setup_environment();
|
|
|
|
# if one of the other programs is being invoked (see doc/hacking.mkd), exec it
|
|
exec(@ARGV) if $program;
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# set up GL_USER and (if reqd) SSH_ORIGINAL_COMMAND and SSH_CONNECTION
|
|
# ----------------------------------------------------------------------------
|
|
|
|
my $user;
|
|
if ($ENV{REQUEST_URI}) {
|
|
die "fallback to DAV not supported\n" if $ENV{REQUEST_METHOD} eq 'PROPFIND';
|
|
|
|
# fake out SSH_ORIGINAL_COMMAND and SSH_CONNECTION when called via http,
|
|
# so the rest of the code stays the same (except the exec at the end).
|
|
simulate_ssh_connection();
|
|
|
|
$ENV{REMOTE_USER} ||= $GL_HTTP_ANON_USER; # see doc/http-backend.mkd
|
|
$user = $ENV{GL_USER} = $ENV{REMOTE_USER};
|
|
} else {
|
|
# no (more) arguments given in ssh mode? default user is $USER
|
|
# (fedorahosted works like this, and it is harmless for others)
|
|
@ARGV = ($ENV{USER}) unless @ARGV;
|
|
$user = $ENV{GL_USER} = shift;
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# SSH_ORIGINAL_COMMAND
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# no SSH_ORIGINAL_COMMAND given: shell out or default to 'info'
|
|
unless ($ENV{SSH_ORIGINAL_COMMAND}) {
|
|
shell_out() if $shell_allowed; # doesn't return ('exec's out)
|
|
$ENV{SSH_ORIGINAL_COMMAND} = 'info';
|
|
}
|
|
|
|
# admin defined commands; please see doc/admin-defined-commands.mkd
|
|
if ($GL_ADC_PATH and -d $GL_ADC_PATH) {
|
|
try_adc(); # if it succeeds, this also 'exec's out
|
|
}
|
|
|
|
# get/set perms/desc for wild repos; also the 'expand' command
|
|
my $CUSTOM_COMMANDS=qr/^\s*(expand|(get|set)(perms|desc))\b/;
|
|
# note that all the subs called here chdir somewhere else and do not come
|
|
# back; they all blithely take advantage of the fact that processing custom
|
|
# commands is sort of a dead end for normal (git) processing
|
|
if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) {
|
|
die "wildrepos disabled, sorry\n" unless $GL_WILDREPOS;
|
|
run_custom_command($user);
|
|
exit 0;
|
|
}
|
|
|
|
# non-git commands: if the command does NOT fit the pattern of a normal git
|
|
# command, send it off somewhere else...
|
|
|
|
# side notes on detecting a normal git command: the pattern we check allows
|
|
# old style as well as new style ("git-subcommand arg" or "git subcommand
|
|
# arg"). Currently, this is how git sends across the command (including the
|
|
# single quotes):
|
|
# git-receive-pack 'reponame.git'
|
|
|
|
my ($verb, $repo) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/);
|
|
unless ( $verb and ( $verb eq 'git-init' or $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) {
|
|
special_cmd ($shell_allowed);
|
|
exit 0;
|
|
}
|
|
|
|
# some final sanity checks
|
|
die "$repo ends with a slash; I don't like that\n" if $repo =~ /\/$/;
|
|
die "$repo has two consecutive periods; I don't like that\n" if $repo =~ /\.\./;
|
|
|
|
# save the reponame; too many things need this
|
|
$ENV{GL_REPO}=$repo;
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# the real git commands (git-receive-pack, etc...)
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# we know the user and repo; we just need to know what perm he's trying for
|
|
# (aa == attempted access; setting this makes some later logic simpler)
|
|
my $aa = ($verb =~ $R_COMMANDS ? 'R' : 'W');
|
|
|
|
# writes may get redirected under certain conditions
|
|
if ( $aa eq 'W' and mirror_mode($repo) =~ /^slave of (\S+)/ ) {
|
|
my $master = $1;
|
|
die "$ABRT GL_HOSTNAME not set; rejecting push to non-local repo\n" unless $GL_HOSTNAME;
|
|
die "$ABRT $GL_HOSTNAME not the master, please push to $master\n" unless mirror_redirectOK($repo, $GL_HOSTNAME);
|
|
print STDERR "$GL_HOSTNAME ==== $user ($repo) ===> $master\n";
|
|
exec("ssh", $master, "USER=$user", "SOC=$ENV{SSH_ORIGINAL_COMMAND}");
|
|
}
|
|
|
|
# first level permissions check
|
|
|
|
my ($perm, $creator, $wild);
|
|
if ( $GL_ALL_READ_ALL and $verb =~ $R_COMMANDS and -d "$REPO_BASE/$repo.git") {
|
|
$perm = 'R';
|
|
} else {
|
|
($perm, $creator, $wild) = repo_rights($repo);
|
|
}
|
|
# it was missing, and you have create perms, so create it
|
|
new_wild_repo($repo, $user) if ($perm =~ /C/);
|
|
|
|
die "$aa access for $repo DENIED to $user
|
|
(Or there may be no repository at the given path. Did you spell it correctly?)\n" unless $perm =~ /$aa/;
|
|
|
|
# check if repo is write-enabled
|
|
check_repo_write_enabled($repo) if $aa eq 'W';
|
|
|
|
# run the pre-git hook if present (do this last, just before actually handing
|
|
# off to git). Force its output to go to STDERR so the git client does not
|
|
# get confused, in case the code in the pre-git hook forgot. To make it
|
|
# simple for the script, send in $aa (which will be 'R' or 'W') so now they
|
|
# have all three: GL_USER and GL_REPO in the env, and $aa as arg-1.
|
|
if (-x "$REPO_BASE/$repo.git/hooks/gl-pre-git") {
|
|
system("cd $REPO_BASE/$repo.git; hooks/gl-pre-git $aa >&2");
|
|
die "gl-pre-git hook failed ($?)\n" if $?;
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# over to git now
|
|
# ----------------------------------------------------------------------------
|
|
|
|
if ($ENV{REQUEST_URI}) {
|
|
log_it($ENV{REQUEST_URI});
|
|
exec $ENV{GIT_HTTP_BACKEND};
|
|
# the GIT_HTTP_BACKEND env var should be set either by the rc file, or as
|
|
# a SetEnv in the apache config somewhere
|
|
}
|
|
|
|
log_it();
|
|
|
|
$repo = "'$REPO_BASE/$repo.git'";
|
|
exec("git", "shell", "-c", "$verb $repo") unless $verb eq 'git-init';
|