| 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 will not work with the above settings. Redmine.pm | 
  |  | 99 | normally does access control depending on the HTTP method used: read-only | 
  |  | 100 | methods are OK for everyone in public projects and members with read rights | 
  |  | 101 | in private projects. The rest all require membership with write rights in the | 
  |  | 102 | 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 | 
  |  | 106 | using the full URL (including the query string): anything that doesn't belong | 
  |  | 107 | to the 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 | Once you have activated this option, you have two options when cloning a | 
  |  | 115 | repository. Cloning using "http://user@host/git/repo" works, but will ask for | 
  |  | 116 | the password all the time. To avoid being pestered by password requests, it's | 
  |  | 117 | best to create a ~/.netrc file with your username and password, and clone using | 
  |  | 118 | "http://host/git/repo" instead. | 
  |  | 119 |  | 
  |  | 120 | IMPORTANT NOTE: It is *very important* that the file cannot be read by other | 
  |  | 121 | users, as it will contain your password in cleartext. To create the file, you | 
  |  | 122 | can use the following commands, replacing yourhost, youruser and yourpassword | 
  |  | 123 | with the right values: | 
  |  | 124 |  | 
  |  | 125 |   touch ~/.netrc | 
  |  | 126 |   chmod 600 .netrc | 
  |  | 127 |   echo -e "machine yourhost\nlogin youruser\npassword yourpassword" > ~/.netrc | 
  |  | 128 |  | 
  | 96 | 129 | =cut | 
  | 97 | 130 |  | 
  | 98 | 131 | use strict; | 
  | ... | ... |  | 
  | 142 | 174 |     args_how => TAKE1, | 
  | 143 | 175 |     errmsg => 'RedmineCacheCredsMax must be decimal number', | 
  | 144 | 176 |   }, | 
  |  | 177 |   { | 
  |  | 178 |     name => 'RedmineGitSmartHttp', | 
  |  | 179 |     req_override => OR_AUTHCFG, | 
  |  | 180 |     args_how => TAKE1, | 
  |  | 181 |   }, | 
  | 145 | 182 | ); | 
  | 146 | 183 |  | 
  | 147 | 184 | sub RedmineDSN {  | 
  | ... | ... |  | 
  | 178 | 215 |   } | 
  | 179 | 216 | } | 
  | 180 | 217 |  | 
  |  | 218 | sub RedmineGitSmartHttp { | 
  |  | 219 |   my ($self, $parms, $arg) = @_; | 
  |  | 220 |   $arg = lc $arg; | 
  |  | 221 |  | 
  |  | 222 |   if ($arg eq "yes" || $arg eq "true") { | 
  |  | 223 |     $self->{RedmineGitSmartHttp} = 1; | 
  |  | 224 |   } else { | 
  |  | 225 |     $self->{RedmineGitSmartHttp} = 0; | 
  |  | 226 |   } | 
  |  | 227 | } | 
  |  | 228 |  | 
  | 181 | 229 | sub trim { | 
  | 182 | 230 |   my $string = shift; | 
  | 183 | 231 |   $string =~ s/\s{2,}/ /g; | 
  | ... | ... |  | 
  | 191 | 239 |  | 
  | 192 | 240 | Apache2::Module::add(__PACKAGE__, \@directives); | 
  | 193 | 241 |  | 
  | 194 | 242 |  | 
  | 195 | 243 | my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/; | 
  | 196 | 244 |  | 
  |  | 245 | sub request_is_read_only { | 
  |  | 246 |   my ($r) = @_; | 
  |  | 247 |   my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config); | 
  |  | 248 |  | 
  |  | 249 |   # Do we use Git's smart HTTP protocol, or not? | 
  |  | 250 |   if (defined $cfg->{RedmineGitSmartHttp} and $cfg->{RedmineGitSmartHttp}) { | 
  |  | 251 |     my $uri = $r->unparsed_uri; | 
  |  | 252 |     my $is_read_only = $uri !~ /^\/git\/.*\/[^\/]*git\-receive\-pack$/o; | 
  |  | 253 |     return $is_read_only; | 
  |  | 254 |   } else { | 
  |  | 255 |     # Old behaviour: check the HTTP method | 
  |  | 256 |     my $method = $r->method; | 
  |  | 257 |     return defined $read_only_methods{$method}; | 
  |  | 258 |   } | 
  |  | 259 | } | 
  |  | 260 |  | 
  | 197 | 261 | sub access_handler { | 
  | 198 | 262 |   my $r = shift; | 
  | 199 | 263 |  | 
  | ... | ... |  | 
  | 202 | 265 |       return FORBIDDEN; | 
  | 203 | 266 |   } | 
  | 204 | 267 |  | 
  | 205 |  |   my $method = $r->method; | 
  | 206 |  |   return OK unless defined $read_only_methods{$method}; | 
  |  | 268 |   return OK unless request_is_read_only($r); | 
  | 207 | 269 |  | 
  | 208 | 270 |   my $project_id = get_project_identifier($r); | 
  | 209 | 271 |  | 
  | ... | ... |  | 
  | 291 | 353 |  | 
  | 292 | 354 |       unless ($auth_source_id) { | 
  | 293 | 355 | 	  my $method = $r->method; | 
  | 294 |  |           if ($hashed_password eq $pass_digest && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) { | 
  |  | 356 |           if ($hashed_password eq $pass_digest && ((request_is_read_only($r) && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) { | 
  | 295 | 357 |               $ret = 1; | 
  | 296 | 358 |               last; | 
  | 297 | 359 |           } |