59 |
59 |
RedmineDbPass "password"
|
60 |
60 |
## Optional where clause (fulltext search would be slow and
|
61 |
61 |
## database dependant).
|
62 |
|
# RedmineDbWhereClause "and members.role_id IN (1,2)"
|
|
62 |
# RedmineDbWhereClause "AND roles.id IN (1,2)"
|
63 |
63 |
## Optional credentials cache size
|
64 |
64 |
# RedmineCacheCredsMax 50
|
65 |
65 |
</Location>
|
... | ... | |
102 |
102 |
|
103 |
103 |
And you need to upgrade at least reposman.rb (after r860).
|
104 |
104 |
|
|
105 |
=head1 GIT SMART HTTP SUPPORT
|
|
106 |
|
|
107 |
Git's smart HTTP protocol (available since Git 1.7.0) will not work with the
|
|
108 |
above settings. Redmine.pm normally does access control depending on the HTTP
|
|
109 |
method used: read-only methods are OK for everyone in public projects and
|
|
110 |
members with read rights in private projects. The rest require membership with
|
|
111 |
commit rights in the project.
|
|
112 |
|
|
113 |
However, this scheme doesn't work for Git's smart HTTP protocol, as it will use
|
|
114 |
POST even for a simple clone. Instead, read-only requests must be detected using
|
|
115 |
the full URL (including the query string): anything that doesn't belong to the
|
|
116 |
git-receive-pack service is read-only.
|
|
117 |
|
|
118 |
To activate this mode of operation, add this line inside your <Location /git>
|
|
119 |
block:
|
|
120 |
|
|
121 |
RedmineGitSmartHttp yes
|
|
122 |
|
|
123 |
Here's a sample Apache configuration which integrates git-http-backend with
|
|
124 |
a MySQL database and this new option:
|
|
125 |
|
|
126 |
SetEnv GIT_PROJECT_ROOT /var/www/git/
|
|
127 |
SetEnv GIT_HTTP_EXPORT_ALL
|
|
128 |
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
|
|
129 |
<Location /git>
|
|
130 |
Order allow,deny
|
|
131 |
Allow from all
|
|
132 |
|
|
133 |
AuthType Basic
|
|
134 |
AuthName Git
|
|
135 |
Require valid-user
|
|
136 |
|
|
137 |
PerlAccessHandler Apache::Authn::Redmine::access_handler
|
|
138 |
PerlAuthenHandler Apache::Authn::Redmine::authen_handler
|
|
139 |
# for mysql
|
|
140 |
RedmineDSN "DBI:mysql:database=redmine;host=127.0.0.1"
|
|
141 |
RedmineDbUser "redmine"
|
|
142 |
RedmineDbPass "xxx"
|
|
143 |
RedmineGitSmartHttp yes
|
|
144 |
</Location>
|
|
145 |
|
|
146 |
Make sure that all the names of the repositories under /var/www/git/ have a
|
|
147 |
matching identifier for some project: /var/www/git/myproject and
|
|
148 |
/var/www/git/myproject.git will work. You can put both bare and non-bare
|
|
149 |
repositories in /var/www/git, though bare repositories are strongly
|
|
150 |
recommended. You should create them with the rights of the user running Redmine,
|
|
151 |
like this:
|
|
152 |
|
|
153 |
cd /var/www/git
|
|
154 |
sudo -u user-running-redmine mkdir myproject
|
|
155 |
cd myproject
|
|
156 |
sudo -u user-running-redmine git init --bare
|
|
157 |
|
|
158 |
Once you have activated this option, you have three options when cloning a
|
|
159 |
repository:
|
|
160 |
|
|
161 |
- Cloning using "http://user@host/git/repo(.git)" works, but will ask for the password
|
|
162 |
all the time.
|
|
163 |
|
|
164 |
- Cloning with "http://user:pass@host/git/repo(.git)" does not have this problem, but
|
|
165 |
this could reveal accidentally your password to the console in some versions
|
|
166 |
of Git, and you would have to ensure that .git/config is not readable except
|
|
167 |
by the owner for each of your projects.
|
|
168 |
|
|
169 |
- Use "http://host/git/repo(.git)", and store your credentials in the ~/.netrc
|
|
170 |
file. This is the recommended solution, as you only have one file to protect
|
|
171 |
and passwords will not be leaked accidentally to the console.
|
|
172 |
|
|
173 |
IMPORTANT NOTE: It is *very important* that the file cannot be read by other
|
|
174 |
users, as it will contain your password in cleartext. To create the file, you
|
|
175 |
can use the following commands, replacing yourhost, youruser and yourpassword
|
|
176 |
with the right values:
|
|
177 |
|
|
178 |
touch ~/.netrc
|
|
179 |
chmod 600 ~/.netrc
|
|
180 |
echo -e "machine yourhost\nlogin youruser\npassword yourpassword" > ~/.netrc
|
|
181 |
|
105 |
182 |
=cut
|
106 |
183 |
|
107 |
184 |
use strict;
|
... | ... | |
151 |
228 |
args_how => TAKE1,
|
152 |
229 |
errmsg => 'RedmineCacheCredsMax must be decimal number',
|
153 |
230 |
},
|
|
231 |
{
|
|
232 |
name => 'RedmineGitSmartHttp',
|
|
233 |
req_override => OR_AUTHCFG,
|
|
234 |
args_how => TAKE1,
|
|
235 |
},
|
154 |
236 |
);
|
155 |
237 |
|
156 |
238 |
sub RedmineDSN {
|
... | ... | |
188 |
270 |
}
|
189 |
271 |
}
|
190 |
272 |
|
|
273 |
sub RedmineGitSmartHttp {
|
|
274 |
my ($self, $parms, $arg) = @_;
|
|
275 |
$arg = lc $arg;
|
|
276 |
|
|
277 |
if ($arg eq "yes" || $arg eq "true") {
|
|
278 |
$self->{RedmineGitSmartHttp} = 1;
|
|
279 |
} else {
|
|
280 |
$self->{RedmineGitSmartHttp} = 0;
|
|
281 |
}
|
|
282 |
}
|
|
283 |
|
191 |
284 |
sub trim {
|
192 |
285 |
my $string = shift;
|
193 |
286 |
$string =~ s/\s{2,}/ /g;
|
... | ... | |
204 |
297 |
|
205 |
298 |
my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
|
206 |
299 |
|
|
300 |
sub request_is_read_only {
|
|
301 |
my ($r) = @_;
|
|
302 |
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
|
|
303 |
|
|
304 |
# Do we use Git's smart HTTP protocol, or not?
|
|
305 |
if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp}) {
|
|
306 |
my $uri = $r->unparsed_uri;
|
|
307 |
my $location = $r->location;
|
|
308 |
my $is_read_only = $uri !~ m{^$location/*[^/]+/+(info/refs\?service=)?git\-receive\-pack$}o;
|
|
309 |
return $is_read_only;
|
|
310 |
} else {
|
|
311 |
# Old behaviour: check the HTTP method
|
|
312 |
my $method = $r->method;
|
|
313 |
return defined $read_only_methods{$method};
|
|
314 |
}
|
|
315 |
}
|
|
316 |
|
207 |
317 |
sub access_handler {
|
208 |
318 |
my $r = shift;
|
209 |
319 |
|
... | ... | |
212 |
322 |
return FORBIDDEN;
|
213 |
323 |
}
|
214 |
324 |
|
215 |
|
my $method = $r->method;
|
216 |
|
return OK unless defined $read_only_methods{$method};
|
|
325 |
return OK unless request_is_read_only($r);
|
217 |
326 |
|
218 |
327 |
my $project_id = get_project_identifier($r);
|
219 |
328 |
|
... | ... | |
338 |
447 |
|
339 |
448 |
my $pass_digest = Digest::SHA::sha1_hex($redmine_pass);
|
340 |
449 |
|
341 |
|
my $access_mode = defined $read_only_methods{$r->method} ? "R" : "W";
|
|
450 |
my $access_mode = request_is_read_only($r) ? "R" : "W";
|
342 |
451 |
|
343 |
452 |
my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);
|
344 |
453 |
my $usrprojpass;
|
... | ... | |
416 |
525 |
|
417 |
526 |
my $location = $r->location;
|
418 |
527 |
my ($identifier) = $r->uri =~ m{$location/*([^/.]+)};
|
419 |
|
$identifier;
|
|
528 |
my $dbh = connect_database($r);
|
|
529 |
my $sth = $dbh->prepare(
|
|
530 |
"SELECT identifier FROM projects WHERE id = (SELECT project_id FROM repositories WHERE url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?);"
|
|
531 |
);
|
|
532 |
$identifier =~ s/_/\\_/g;
|
|
533 |
$sth->execute('%/'.$identifier,
|
|
534 |
'%/'.$identifier.'.git',
|
|
535 |
'%/'.$identifier.'/%',
|
|
536 |
'%/'.$identifier.'.git/%');
|
|
537 |
my @row = $sth->fetchrow_array;
|
|
538 |
$sth->finish();
|
|
539 |
$dbh->disconnect();
|
|
540 |
$row[0];
|
420 |
541 |
}
|
421 |
542 |
|
422 |
543 |
sub connect_database {
|