comparison extra/soundsoftware/SoundSoftware.pm @ 812:b5474c68c433 luisf

Merge from branch "live"
author luisf <luis.figueira@eecs.qmul.ac.uk>
date Wed, 23 Nov 2011 15:29:25 +0000
parents 1ce6efe3db0e
children cf4cc816278a
comparison
equal deleted inserted replaced
761:0bdd601c124a 812:b5474c68c433
108 { 108 {
109 name => 'SoundSoftwareRepoPrefix', 109 name => 'SoundSoftwareRepoPrefix',
110 req_override => OR_AUTHCFG, 110 req_override => OR_AUTHCFG,
111 args_how => TAKE1, 111 args_how => TAKE1,
112 }, 112 },
113 {
114 name => 'SoundSoftwareSslRequired',
115 req_override => OR_AUTHCFG,
116 args_how => TAKE1,
117 },
113 ); 118 );
114 119
115 sub SoundSoftwareDSN { 120 sub SoundSoftwareDSN {
116 my ($self, $parms, $arg) = @_; 121 my ($self, $parms, $arg) = @_;
117 $self->{SoundSoftwareDSN} = $arg; 122 $self->{SoundSoftwareDSN} = $arg;
141 if ($arg) { 146 if ($arg) {
142 $self->{SoundSoftwareRepoPrefix} = $arg; 147 $self->{SoundSoftwareRepoPrefix} = $arg;
143 } 148 }
144 } 149 }
145 150
151 sub SoundSoftwareSslRequired { set_val('SoundSoftwareSslRequired', @_); }
152
146 sub trim { 153 sub trim {
147 my $string = shift; 154 my $string = shift;
148 $string =~ s/\s{2,}/ /g; 155 $string =~ s/\s{2,}/ /g;
149 return $string; 156 return $string;
150 } 157 }
182 189
183 print STDERR "Connected to db, dbh is " . $dbh . "\n"; 190 print STDERR "Connected to db, dbh is " . $dbh . "\n";
184 191
185 my $project_id = get_project_identifier($dbh, $r); 192 my $project_id = get_project_identifier($dbh, $r);
186 193
187 if (!defined $read_only_methods{$method}) { 194 # We want to delegate most of the work to the authentication
188 print STDERR "SoundSoftware.pm:$$: Method is not read-only\n"; 195 # handler (to ensure that user is asked to login even for
189 if (project_repo_is_readonly($dbh, $project_id, $r)) { 196 # nonexistent projects -- so they can't tell whether a private
190 print STDERR "SoundSoftware.pm:$$: Project repo is read-only, refusing access\n"; 197 # project exists or not without authenticating). So
191 return FORBIDDEN; 198 #
192 } else { 199 # * if the project is public
193 print STDERR "SoundSoftware.pm:$$: Project repo is read-write, authentication handler required\n"; 200 # - if the method is read-only
194 return OK; 201 # + set handler to OK, no auth needed
195 } 202 # - if the method is not read-only
196 } 203 # + if the repo is read-only, return forbidden
204 # + else require auth
205 # * if the project is not public or does not exist
206 # + require auth
207 #
208 # If we are requiring auth and are not currently https, and
209 # https is required, then we must return a redirect to https
210 # instead of an OK.
197 211
198 my $status = get_project_status($dbh, $project_id, $r); 212 my $status = get_project_status($dbh, $project_id, $r);
213 my $readonly = project_repo_is_readonly($dbh, $project_id, $r);
199 214
200 $dbh->disconnect(); 215 $dbh->disconnect();
201 undef $dbh; 216 undef $dbh;
202 217
203 if ($status == 0) { # nonexistent 218 my $auth_ssl_reqd = will_require_ssl_auth($r);
204 print STDERR "SoundSoftware.pm:$$: Project does not exist, refusing access\n"; 219
205 return FORBIDDEN; 220 if ($status == 1) { # public
206 } elsif ($status == 1) { # public 221
207 print STDERR "SoundSoftware.pm:$$: Project is public, no restriction here\n"; 222 print STDERR "SoundSoftware.pm:$$: Project is public\n";
208 $r->set_handlers(PerlAuthenHandler => [\&OK]) 223
209 } else { # private 224 if (!defined $read_only_methods{$method}) {
210 print STDERR "SoundSoftware.pm:$$: Project is private, authentication handler required\n"; 225
211 } 226 print STDERR "SoundSoftware.pm:$$: Method is not read-only\n";
212 227
213 return OK 228 if ($readonly) {
229 print STDERR "SoundSoftware.pm:$$: Project repo is read-only, refusing access\n";
230 return FORBIDDEN;
231 } else {
232 print STDERR "SoundSoftware.pm:$$: Project repo is read-write, auth required\n";
233 # fall through, this is the normal case
234 }
235
236 } elsif ($auth_ssl_reqd and $r->unparsed_uri =~ m/cmd=branchmap/) {
237
238 # A hac^H^H^Hspecial case. We want to ensure we switch to
239 # https (if it will be necessarily for authentication)
240 # before the first POST request, and this is what I think
241 # will give us suitable warning for Mercurial.
242
243 print STDERR "SoundSoftware.pm:$$: Switching to HTTPS in preparation\n";
244 # fall through, this is the normal case
245
246 } else {
247 # Public project, read-only method -- this is the only
248 # case we can decide for certain to accept in this function
249 print STDERR "SoundSoftware.pm:$$: Method is read-only, no restriction here\n";
250 $r->set_handlers(PerlAuthenHandler => [\&OK]);
251 return OK;
252 }
253
254 } else { # status != 1, i.e. nonexistent or private -- equivalent here
255
256 print STDERR "SoundSoftware.pm:$$: Project is private or nonexistent, auth required\n";
257 # fall through
258 }
259
260 if ($auth_ssl_reqd) {
261 my $redir_to = "https://" . $r->hostname() . $r->unparsed_uri();
262 print STDERR "SoundSoftware.pm:$$: Need to switch to HTTPS, redirecting to $redir_to\n";
263 $r->headers_out->add('Location' => $redir_to);
264 return REDIRECT;
265 } else {
266 return OK;
267 }
214 } 268 }
215 269
216 sub authen_handler { 270 sub authen_handler {
217 my $r = shift; 271 my $r = shift;
218 272
234 undef $dbh; 288 undef $dbh;
235 return $res; 289 return $res;
236 } 290 }
237 291
238 print STDERR "SoundSoftware.pm:$$: User is " . $r->user . ", got password\n"; 292 print STDERR "SoundSoftware.pm:$$: User is " . $r->user . ", got password\n";
293
294 my $status = get_project_status($dbh, $project_id, $r);
295 if ($status == 0) {
296 # nonexistent, behave like private project you aren't a member of
297 print STDERR "SoundSoftware.pm:$$: Project doesn't exist, not permitted\n";
298 $dbh->disconnect();
299 undef $dbh;
300 $r->note_auth_failure();
301 return AUTH_REQUIRED;
302 }
239 303
240 my $permitted = is_permitted($dbh, $project_id, $r->user, $redmine_pass, $r); 304 my $permitted = is_permitted($dbh, $project_id, $r->user, $redmine_pass, $r);
241 305
242 $dbh->disconnect(); 306 $dbh->disconnect();
243 undef $dbh; 307 undef $dbh;
275 } 339 }
276 $sth->finish(); 340 $sth->finish();
277 undef $sth; 341 undef $sth;
278 342
279 $ret; 343 $ret;
344 }
345
346 sub will_require_ssl_auth {
347 my $r = shift;
348
349 my $cfg = Apache2::Module::get_config
350 (__PACKAGE__, $r->server, $r->per_dir_config);
351
352 if ($cfg->{SoundSoftwareSslRequired} eq "on") {
353 if ($r->dir_config('HTTPS') eq "on") {
354 # already have ssl
355 return 0;
356 } else {
357 # require ssl for auth, don't have it yet
358 return 1;
359 }
360 } elsif ($cfg->{SoundSoftwareSslRequired} eq "off") {
361 # don't require ssl for auth
362 return 0;
363 } else {
364 print STDERR "WARNING: SoundSoftware.pm:$$: SoundSoftwareSslRequired should be either 'on' or 'off'\n";
365 # this is safer
366 return 1;
367 }
280 } 368 }
281 369
282 sub project_repo_is_readonly { 370 sub project_repo_is_readonly {
283 my $dbh = shift; 371 my $dbh = shift;
284 my $project_id = shift; 372 my $project_id = shift;
366 $ret = 1; 454 $ret = 1;
367 } 455 }
368 } 456 }
369 $sthldap->finish(); 457 $sthldap->finish();
370 undef $sthldap; 458 undef $sthldap;
459 last if ($ret);
371 } 460 }
372 } else { 461 } else {
373 print STDERR "SoundSoftware.pm:$$: User $redmine_user lacks required role for this project\n"; 462 print STDERR "SoundSoftware.pm:$$: User $redmine_user lacks required role for this project\n";
374 } 463 }
375 } 464 }
381 } 470 }
382 471
383 sub get_project_identifier { 472 sub get_project_identifier {
384 my $dbh = shift; 473 my $dbh = shift;
385 my $r = shift; 474 my $r = shift;
386
387 my $location = $r->location; 475 my $location = $r->location;
388 my ($repo) = $r->uri =~ m{$location/*([^/]+)}; 476 my ($repo) = $r->uri =~ m{$location/*([^/]*)};
389 477
390 return $repo if (!$repo); 478 return $repo if (!$repo);
391 479
392 $repo =~ s/[^a-zA-Z0-9\._-]//g; 480 $repo =~ s/[^a-zA-Z0-9\._-]//g;
393 481
394 # The original Redmine.pm returns the string just calculated as 482 # The original Redmine.pm returns the string just calculated as
395 # the project identifier. That won't do for us -- we may have 483 # the project identifier. That won't do for us -- we may have
396 # (and in fact already do have, in our test instance) projects 484 # (and in fact already do have, in our test instance) projects
397 # whose repository names differ from the project identifiers. 485 # whose repository names differ from the project identifiers.
398 486
408 my $cfg = Apache2::Module::get_config 496 my $cfg = Apache2::Module::get_config
409 (__PACKAGE__, $r->server, $r->per_dir_config); 497 (__PACKAGE__, $r->server, $r->per_dir_config);
410 498
411 my $prefix = $cfg->{SoundSoftwareRepoPrefix}; 499 my $prefix = $cfg->{SoundSoftwareRepoPrefix};
412 if (!defined $prefix) { $prefix = '%/'; } 500 if (!defined $prefix) { $prefix = '%/'; }
413
414 my $identifier = ''; 501 my $identifier = '';
415 502
416 $sth->execute($prefix . $repo); 503 $sth->execute($prefix . $repo);
417 my $ret = 0; 504 my $ret = 0;
418 if (my @row = $sth->fetchrow_array) { 505 if (my @row = $sth->fetchrow_array) {
447 534
448 # be timid about characters not permitted in auth realm and revert 535 # be timid about characters not permitted in auth realm and revert
449 # to project identifier if any are found 536 # to project identifier if any are found
450 if ($name =~ m/[^\w\d\s\._-]/) { 537 if ($name =~ m/[^\w\d\s\._-]/) {
451 $name = $project_id; 538 $name = $project_id;
539 } elsif ($name =~ m/^\s*$/) {
540 # empty or whitespace
541 $name = $project_id;
542 }
543
544 if ($name =~ m/^\s*$/) {
545 # nothing even in $project_id -- probably a nonexistent project.
546 # use repo name instead (don't want to admit to user that project
547 # doesn't exist)
548 my $location = $r->location;
549 my ($repo) = $r->uri =~ m{$location/*([^/]*)};
550 $name = $repo;
452 } 551 }
453 552
454 my $realm = '"Mercurial repository for ' . "'$name'" . '"'; 553 my $realm = '"Mercurial repository for ' . "'$name'" . '"';
455 554
456 $realm; 555 $realm;