0001-Redmine.pm-add-Git-smart-HTTP-support.patch

Revised patch with a better regexp which allows for trailing slashes in Location block and is stricter on the service URLs - Antonio García-Domínguez, 2010-07-20 10:03

Download (6.42 KB)

View differences:

extra/svn/Redmine.pm
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/ match
138
exactly the identifier for some project: /var/www/git/myproject.git won't work,
139
due to the way this module extracts the identifier from the URL.
140
/var/www/git/myproject will work, though. You can put both bare and non-bare
141
repositories in /var/www/git, though bare repositories are strongly
142
recommended. You should create them with the rights of the user running Redmine,
143
like this:
144

  
145
  cd /var/www/git
146
  sudo -u user-running-redmine mkdir myproject
147
  cd myproject
148
  sudo -u user-running-redmine git init --bare
149

  
150
Once you have activated this option, you have three options when cloning a
151
repository:
152

  
153
- Cloning using "http://user@host/git/repo" works, but will ask for the password
154
  all the time.
155

  
156
- Cloning with "http://user:pass@host/git/repo" does not have this problem, but
157
  this could reveal accidentally your password to the console in some versions
158
  of Git, and you would have to ensure that .git/config is not readable except
159
  by the owner for each of your projects.
160

  
161
- Use "http://host/git/repo", and store your credentials in the ~/.netrc
162
  file. This is the recommended solution, as you only have one file to protect
163
  and passwords will not be leaked accidentally to the console.
164

  
165
  IMPORTANT NOTE: It is *very important* that the file cannot be read by other
166
  users, as it will contain your password in cleartext. To create the file, you
167
  can use the following commands, replacing yourhost, youruser and yourpassword
168
  with the right values:
169

  
170
    touch ~/.netrc
171
    chmod 600 ~/.netrc
172
    echo -e "machine yourhost\nlogin youruser\npassword yourpassword" > ~/.netrc
173

  
96 174
=cut
97 175

  
98 176
use strict;
......
142 220
    args_how => TAKE1,
143 221
    errmsg => 'RedmineCacheCredsMax must be decimal number',
144 222
  },
223
  {
224
    name => 'RedmineGitSmartHttp',
225
    req_override => OR_AUTHCFG,
226
    args_how => TAKE1,
227
  },
145 228
);
146 229

  
147 230
sub RedmineDSN { 
......
178 261
  }
179 262
}
180 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

  
181 275
sub trim {
182 276
  my $string = shift;
183 277
  $string =~ s/\s{2,}/ /g;
......
194 288

  
195 289
my %read_only_methods = map { $_ => 1 } qw/GET PROPFIND REPORT OPTIONS/;
196 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

  
197 308
sub access_handler {
198 309
  my $r = shift;
199 310

  
......
202 313
      return FORBIDDEN;
203 314
  }
204 315

  
205
  my $method = $r->method;
206
  return OK unless defined $read_only_methods{$method};
316
  return OK unless request_is_read_only($r);
207 317

  
208 318
  my $project_id = get_project_identifier($r);
209 319

  
......
320 430

  
321 431
      unless ($auth_source_id) {
322 432
	  my $method = $r->method;
323
          if ($hashed_password eq $pass_digest && ((defined $read_only_methods{$method} && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
433
          if ($hashed_password eq $pass_digest && ((request_is_read_only($r) && $permissions =~ /:browse_repository/) || $permissions =~ /:commit_access/) ) {
324 434
              $ret = 1;
325 435
              last;
326 436
          }
......
339 449
                filter  =>      "(".$rowldap[6]."=%s)"
340 450
            );
341 451
            my $method = $r->method;
342
            $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/));
343 454

  
344 455
          }
345 456
          $sthldap->finish();
346
-