Add git-smart-http support to apache perl module #245

Latest patch from http://www.redmine.org/issues/4905 and other changes improvements, compare also with https://orga.fachschaften.org/projects/redmine-fsenorg/repository/revisions/fsenorg/changes/extra/svn/Redmine.pm .
This commit is contained in:
Felix Schäfer 2011-03-20 18:45:15 +01:00
parent 904897ac90
commit 9fe45cfe1f
1 changed files with 114 additions and 6 deletions

View File

@ -93,6 +93,84 @@ S<them :>
And you need to upgrade at least reposman.rb (after r860).
=head1 GIT SMART HTTP SUPPORT
Git's smart HTTP protocol (available since Git 1.7.0) will not work with the
above settings. Redmine.pm normally does access control depending on the HTTP
method used: read-only methods are OK for everyone in public projects and
members with read rights in private projects. The rest require membership with
commit rights in the project.
However, this scheme doesn't work for Git's smart HTTP protocol, as it will use
POST even for a simple clone. Instead, read-only requests must be detected using
the full URL (including the query string): anything that doesn't belong to the
git-receive-pack service is read-only.
To activate this mode of operation, add this line inside your <Location /git>
block:
RedmineGitSmartHttp yes
Here's a sample Apache configuration which integrates git-http-backend with
a MySQL database and this new option:
SetEnv GIT_PROJECT_ROOT /var/www/git/
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
<Location /git>
Order allow,deny
Allow from all
AuthType Basic
AuthName Git
Require valid-user
PerlAccessHandler Apache::Authn::Redmine::access_handler
PerlAuthenHandler Apache::Authn::Redmine::authen_handler
# for mysql
RedmineDSN "DBI:mysql:database=redmine;host=127.0.0.1"
RedmineDbUser "redmine"
RedmineDbPass "xxx"
RedmineGitSmartHttp yes
</Location>
Make sure that all the names of the repositories under /var/www/git/ match
exactly the identifier for some project: /var/www/git/myproject.git won't work,
due to the way this module extracts the identifier from the URL.
/var/www/git/myproject will work, though. You can put both bare and non-bare
repositories in /var/www/git, though bare repositories are strongly
recommended. You should create them with the rights of the user running Redmine,
like this:
cd /var/www/git
sudo -u user-running-redmine mkdir myproject
cd myproject
sudo -u user-running-redmine git init --bare
Once you have activated this option, you have three options when cloning a
repository:
- Cloning using "http://user@host/git/repo" works, but will ask for the password
all the time.
- Cloning with "http://user:pass@host/git/repo" does not have this problem, but
this could reveal accidentally your password to the console in some versions
of Git, and you would have to ensure that .git/config is not readable except
by the owner for each of your projects.
- Use "http://host/git/repo", and store your credentials in the ~/.netrc
file. This is the recommended solution, as you only have one file to protect
and passwords will not be leaked accidentally to the console.
IMPORTANT NOTE: It is *very important* that the file cannot be read by other
users, as it will contain your password in cleartext. To create the file, you
can use the following commands, replacing yourhost, youruser and yourpassword
with the right values:
touch ~/.netrc
chmod 600 ~/.netrc
echo -e "machine yourhost\nlogin youruser\npassword yourpassword" > ~/.netrc
=cut
use strict;
@ -142,6 +220,11 @@ my @directives = (
args_how => TAKE1,
errmsg => 'RedmineCacheCredsMax must be decimal number',
},
{
name => 'RedmineGitSmartHttp',
req_override => OR_AUTHCFG,
args_how => TAKE1,
},
);
sub RedmineDSN {
@ -178,6 +261,17 @@ sub RedmineCacheCredsMax {
}
}
sub RedmineGitSmartHttp {
my ($self, $parms, $arg) = @_;
$arg = lc $arg;
if ($arg eq "yes" || $arg eq "true") {
$self->{RedmineGitSmartHttp} = 1;
} else {
$self->{RedmineGitSmartHttp} = 0;
}
}
sub trim {
my $string = shift;
$string =~ s/\s{2,}/ /g;
@ -194,6 +288,23 @@ Apache2::Module::add(__PACKAGE__, \@directives);
my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
sub request_is_read_only {
my ($r) = @_;
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
# Do we use Git's smart HTTP protocol, or not?
if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp}) {
my $uri = $r->unparsed_uri;
my $location = $r->location;
my $is_read_only = $uri !~ m{^$location/*[^/]+/+(info/refs\?service=)?git\-receive\-pack$}o;
return $is_read_only;
} else {
# Old behaviour: check the HTTP method
my $method = $r->method;
return defined $read_only_methods{$method};
}
}
sub access_handler {
my $r = shift;
@ -202,8 +313,7 @@ sub access_handler {
return FORBIDDEN;
}
my $method = $r->method;
return OK unless defined $read_only_methods{$method};
return OK unless request_is_read_only($r);
my $project_id = get_project_identifier($r);
@ -320,7 +430,7 @@ sub is_member {
unless ($auth_source_id) {
my $method = $r->method;
if ($hashed_password eq $pass_digest && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
if ($hashed_password eq $pass_digest && ((request_is_read_only($r) && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
$ret = 1;
last;
}
@ -338,9 +448,7 @@ sub is_member {
bindpw => $rowldap[4] ? $rowldap[4] : "",
filter => "(".$rowldap[6]."=%s)"
);
my $method = $r->method;
$ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
$ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && ((request_is_read_only($r) && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
}
$sthldap->finish();
undef $sthldap;