| 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 { | 
  | ... | ... |  | 
  | 179 | 261 |   } | 
  | 180 | 262 | } | 
  | 181 | 263 |  | 
  |  | 264 | sub RedmineGitSmartHttp { | 
  |  | 265 |   my ($self, $parms, $arg) = @_; | 
  |  | 266 |   $arg = lc $arg; | 
  |  | 267 |  | 
  |  | 268 |   if ($arg eq "yes" || $arg eq "true") { | 
  |  | 269 |     $self->{RedmineGitSmartHttp} = 1; | 
  |  | 270 |   } else { | 
  |  | 271 |     $self->{RedmineGitSmartHttp} = 0; | 
  |  | 272 |   } | 
  |  | 273 | } | 
  |  | 274 |  | 
  | 182 | 275 | sub trim { | 
  | 183 | 276 |   my $string = shift; | 
  | 184 | 277 |   $string =~ s/\s{2,}/ /g; | 
  | ... | ... |  | 
  | 195 | 288 |  | 
  | 196 | 289 | my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/; | 
  | 197 | 290 |  | 
  |  | 291 | sub request_is_read_only { | 
  |  | 292 |   my ($r) = @_; | 
  |  | 293 |   my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config); | 
  |  | 294 |  | 
  |  | 295 |   # Do we use Git's smart HTTP protocol, or not? | 
  |  | 296 |   if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp}) { | 
  |  | 297 |     my $uri = $r->unparsed_uri; | 
  |  | 298 |     my $location = $r->location; | 
  |  | 299 |     my $is_read_only = $uri !~ m{^$location/*[^/]+/+(info/refs\?service=)?git\-receive\-pack$}o; | 
  |  | 300 |     return $is_read_only; | 
  |  | 301 |   } else { | 
  |  | 302 |     # Old behaviour: check the HTTP method | 
  |  | 303 |     my $method = $r->method; | 
  |  | 304 |     return defined $read_only_methods{$method}; | 
  |  | 305 |   } | 
  |  | 306 | } | 
  |  | 307 |  | 
  | 198 | 308 | sub access_handler { | 
  | 199 | 309 |   my $r = shift; | 
  | 200 | 310 |  | 
  | ... | ... |  | 
  | 203 | 313 |       return FORBIDDEN; | 
  | 204 | 314 |   } | 
  | 205 | 315 |  | 
  | 206 |  |   my $method = $r->method; | 
  | 207 |  |   return OK unless defined $read_only_methods{$method}; | 
  |  | 316 |   return OK unless request_is_read_only($r); | 
  | 208 | 317 |  | 
  | 209 | 318 |   my $project_id = get_project_identifier($r); | 
  | 210 | 319 |  | 
  | ... | ... |  | 
  | 329 | 438 |  | 
  | 330 | 439 |   my $pass_digest = Digest::SHA1::sha1_hex($redmine_pass); | 
  | 331 | 440 |  | 
  | 332 |  |   my $access_mode = defined $read_only_methods{$r->method} ? "R" : "W"; | 
  |  | 441 |   my $access_mode = request_is_read_only($r) ? "R" : "W"; | 
  | 333 | 442 |  | 
  | 334 | 443 |   my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config); | 
  | 335 | 444 |   my $usrprojpass; | 
  | ... | ... |  | 
  | 400 | 509 |  | 
  | 401 | 510 |     my $location = $r->location; | 
  | 402 | 511 |     my ($identifier) = $r->uri =~ m{$location/*([^/]+)}; | 
  |  | 512 |     $identifier =~ s/\.git//; | 
  | 403 | 513 |     $identifier; | 
  | 404 | 514 | } | 
  | 405 | 515 |  |