93 |
93 |
|
94 |
94 |
And you need to upgrade at least reposman.rb (after r860).
|
95 |
95 |
|
|
96 |
=head1 GIT SMART HTTP SUPPORT
|
|
97 |
|
|
98 |
Git's smart HTTP protocol (available since Git 1.7.0) will not work with the
|
|
99 |
above settings. Redmine.pm normally does access control depending on the HTTP
|
|
100 |
method used: read-only methods are OK for everyone in public projects and
|
|
101 |
members with read rights in private projects. The rest require membership with
|
|
102 |
commit rights in the project.
|
|
103 |
|
|
104 |
However, this scheme doesn't work for Git's smart HTTP protocol, as it will use
|
|
105 |
POST even for a simple clone. Instead, read-only requests must be detected using
|
|
106 |
the full URL (including the query string): anything that doesn't belong to the
|
|
107 |
git-receive-pack service is read-only.
|
|
108 |
|
|
109 |
To activate this mode of operation, add this line inside your <Location /git>
|
|
110 |
block:
|
|
111 |
|
|
112 |
RedmineGitSmartHttp yes
|
|
113 |
|
|
114 |
Here's a sample Apache configuration which integrates git-http-backend with
|
|
115 |
a MySQL database and this new option:
|
|
116 |
|
|
117 |
SetEnv GIT_PROJECT_ROOT /var/www/git/
|
|
118 |
SetEnv GIT_HTTP_EXPORT_ALL
|
|
119 |
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
|
|
120 |
<Location /git>
|
|
121 |
Order allow,deny
|
|
122 |
Allow from all
|
|
123 |
|
|
124 |
AuthType Basic
|
|
125 |
AuthName Git
|
|
126 |
Require valid-user
|
|
127 |
|
|
128 |
PerlAccessHandler Apache::Authn::Redmine::access_handler
|
|
129 |
PerlAuthenHandler Apache::Authn::Redmine::authen_handler
|
|
130 |
# for mysql
|
|
131 |
RedmineDSN "DBI:mysql:database=redmine;host=127.0.0.1"
|
|
132 |
RedmineDbUser "redmine"
|
|
133 |
RedmineDbPass "xxx"
|
|
134 |
RedmineGitSmartHttp yes
|
|
135 |
</Location>
|
|
136 |
|
|
137 |
Make sure that all the names of the repositories under /var/www/git/ have a
|
|
138 |
matching identifier for some project: /var/www/git/myproject and
|
|
139 |
/var/www/git/myproject.git will work. You can put both bare and non-bare
|
|
140 |
repositories in /var/www/git, though bare repositories are strongly
|
|
141 |
recommended. You should create them with the rights of the user running Redmine,
|
|
142 |
like this:
|
|
143 |
|
|
144 |
cd /var/www/git
|
|
145 |
sudo -u user-running-redmine mkdir myproject
|
|
146 |
cd myproject
|
|
147 |
sudo -u user-running-redmine git init --bare
|
|
148 |
|
|
149 |
Once you have activated this option, you have three options when cloning a
|
|
150 |
repository:
|
|
151 |
|
|
152 |
- Cloning using "http://user@host/git/repo(.git)" works, but will ask for the password
|
|
153 |
all the time.
|
|
154 |
|
|
155 |
- Cloning with "http://user:pass@host/git/repo(.git)" does not have this problem, but
|
|
156 |
this could reveal accidentally your password to the console in some versions
|
|
157 |
of Git, and you would have to ensure that .git/config is not readable except
|
|
158 |
by the owner for each of your projects.
|
|
159 |
|
|
160 |
- Use "http://host/git/repo(.git)", and store your credentials in the ~/.netrc
|
|
161 |
file. This is the recommended solution, as you only have one file to protect
|
|
162 |
and passwords will not be leaked accidentally to the console.
|
|
163 |
|
|
164 |
IMPORTANT NOTE: It is *very important* that the file cannot be read by other
|
|
165 |
users, as it will contain your password in cleartext. To create the file, you
|
|
166 |
can use the following commands, replacing yourhost, youruser and yourpassword
|
|
167 |
with the right values:
|
|
168 |
|
|
169 |
touch ~/.netrc
|
|
170 |
chmod 600 ~/.netrc
|
|
171 |
echo -e "machine yourhost\nlogin youruser\npassword yourpassword" > ~/.netrc
|
|
172 |
|
96 |
173 |
=cut
|
97 |
174 |
|
98 |
175 |
use strict;
|
... | ... | |
142 |
219 |
args_how => TAKE1,
|
143 |
220 |
errmsg => 'RedmineCacheCredsMax must be decimal number',
|
144 |
221 |
},
|
|
222 |
{
|
|
223 |
name => 'RedmineGitSmartHttp',
|
|
224 |
req_override => OR_AUTHCFG,
|
|
225 |
args_how => TAKE1,
|
|
226 |
},
|
145 |
227 |
);
|
146 |
228 |
|
147 |
229 |
sub RedmineDSN {
|
... | ... | |
178 |
260 |
}
|
179 |
261 |
}
|
180 |
262 |
|
|
263 |
sub RedmineGitSmartHttp {
|
|
264 |
my ($self, $parms, $arg) = @_;
|
|
265 |
$arg = lc $arg;
|
|
266 |
|
|
267 |
if ($arg eq "yes" || $arg eq "true") {
|
|
268 |
$self->{RedmineGitSmartHttp} = 1;
|
|
269 |
} else {
|
|
270 |
$self->{RedmineGitSmartHttp} = 0;
|
|
271 |
}
|
|
272 |
}
|
|
273 |
|
181 |
274 |
sub trim {
|
182 |
275 |
my $string = shift;
|
183 |
276 |
$string =~ s/\s{2,}/ /g;
|
... | ... | |
194 |
287 |
|
195 |
288 |
my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
|
196 |
289 |
|
|
290 |
sub request_is_read_only {
|
|
291 |
my ($r) = @_;
|
|
292 |
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
|
|
293 |
|
|
294 |
# Do we use Git's smart HTTP protocol, or not?
|
|
295 |
if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp}) {
|
|
296 |
my $uri = $r->unparsed_uri;
|
|
297 |
my $location = $r->location;
|
|
298 |
my $is_read_only = $uri !~ m{^$location/*[^/]+/+(info/refs\?service=)?git\-receive\-pack$}o;
|
|
299 |
return $is_read_only;
|
|
300 |
} else {
|
|
301 |
# Old behaviour: check the HTTP method
|
|
302 |
my $method = $r->method;
|
|
303 |
return defined $read_only_methods{$method};
|
|
304 |
}
|
|
305 |
}
|
|
306 |
|
197 |
307 |
sub access_handler {
|
198 |
308 |
my $r = shift;
|
199 |
309 |
|
... | ... | |
202 |
312 |
return FORBIDDEN;
|
203 |
313 |
}
|
204 |
314 |
|
205 |
|
my $method = $r->method;
|
206 |
|
return OK unless defined $read_only_methods{$method};
|
|
315 |
return OK unless request_is_read_only($r);
|
207 |
316 |
|
208 |
317 |
my $project_id = get_project_identifier($r);
|
209 |
318 |
|
... | ... | |
321 |
430 |
unless ($auth_source_id) {
|
322 |
431 |
my $method = $r->method;
|
323 |
432 |
my $salted_password = Digest::SHA1::sha1_hex($salt.$pass_digest);
|
324 |
|
if ($hashed_password eq $salted_password && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
|
|
433 |
if ($hashed_password eq $salted_password && ((request_is_read_only($r) && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
|
325 |
434 |
$ret = 1;
|
326 |
435 |
last;
|
327 |
436 |
}
|
... | ... | |
340 |
449 |
filter => "(".$rowldap[6]."=%s)"
|
341 |
450 |
);
|
342 |
451 |
my $method = $r->method;
|
343 |
|
$ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass) && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
|
|
452 |
$ret = 1 if ($ldap->authenticate($redmine_user, $redmine_pass)
|
|
453 |
&& ((request_is_read_only($r) && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/));
|
344 |
454 |
|
345 |
455 |
}
|
346 |
456 |
$sthldap->finish();
|
... | ... | |
374 |
484 |
|
375 |
485 |
my $location = $r->location;
|
376 |
486 |
my ($identifier) = $r->uri =~ m{$location/*([^/]+)};
|
|
487 |
$identifier =~ s/\.git//;
|
377 |
488 |
$identifier;
|
378 |
489 |
}
|
379 |
490 |
|