Mercurial > hg > soundsoftware-site
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; |