Revision 738:882917cbe8d0 extra/soundsoftware
| extra/soundsoftware/SoundSoftware.pm | ||
|---|---|---|
| 110 | 110 |
req_override => OR_AUTHCFG, |
| 111 | 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 | 120 |
sub SoundSoftwareDSN {
|
| ... | ... | |
| 143 | 148 |
} |
| 144 | 149 |
} |
| 145 | 150 |
|
| 151 |
sub SoundSoftwareSslRequired { set_val('SoundSoftwareSslRequired', @_); }
|
|
| 152 |
|
|
| 146 | 153 |
sub trim {
|
| 147 | 154 |
my $string = shift; |
| 148 | 155 |
$string =~ s/\s{2,}/ /g;
|
| ... | ... | |
| 184 | 191 |
|
| 185 | 192 |
my $project_id = get_project_identifier($dbh, $r); |
| 186 | 193 |
|
| 187 |
if (!defined $read_only_methods{$method}) {
|
|
| 188 |
print STDERR "SoundSoftware.pm:$$: Method is not read-only\n"; |
|
| 189 |
if (project_repo_is_readonly($dbh, $project_id, $r)) {
|
|
| 190 |
print STDERR "SoundSoftware.pm:$$: Project repo is read-only, refusing access\n"; |
|
| 191 |
return FORBIDDEN; |
|
| 192 |
} else {
|
|
| 193 |
print STDERR "SoundSoftware.pm:$$: Project repo is read-write, authentication handler required\n"; |
|
| 194 |
return OK; |
|
| 195 |
} |
|
| 196 |
} |
|
| 194 |
# We want to delegate most of the work to the authentication |
|
| 195 |
# handler (to ensure that user is asked to login even for |
|
| 196 |
# nonexistent projects -- so they can't tell whether a private |
|
| 197 |
# project exists or not without authenticating). So |
|
| 198 |
# |
|
| 199 |
# * if the project is public |
|
| 200 |
# - if the method is read-only |
|
| 201 |
# + set handler to OK, no auth needed |
|
| 202 |
# - if the method is not read-only |
|
| 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 | 212 |
my $status = get_project_status($dbh, $project_id, $r); |
| 213 |
my $readonly = project_repo_is_readonly($dbh, $project_id, $r); |
|
| 199 | 214 |
|
| 200 | 215 |
$dbh->disconnect(); |
| 201 | 216 |
undef $dbh; |
| 202 | 217 |
|
| 203 |
if ($status == 0) { # nonexistent
|
|
| 204 |
print STDERR "SoundSoftware.pm:$$: Project does not exist, refusing access\n"; |
|
| 205 |
return FORBIDDEN; |
|
| 206 |
} elsif ($status == 1) { # public
|
|
| 207 |
print STDERR "SoundSoftware.pm:$$: Project is public, no restriction here\n"; |
|
| 208 |
$r->set_handlers(PerlAuthenHandler => [\&OK]) |
|
| 209 |
} else { # private
|
|
| 210 |
print STDERR "SoundSoftware.pm:$$: Project is private, authentication handler required\n"; |
|
| 218 |
my $auth_ssl_reqd = will_require_ssl_auth($r); |
|
| 219 |
|
|
| 220 |
if ($status == 1) { # public
|
|
| 221 |
|
|
| 222 |
print STDERR "SoundSoftware.pm:$$: Project is public\n"; |
|
| 223 |
|
|
| 224 |
if (!defined $read_only_methods{$method}) {
|
|
| 225 |
|
|
| 226 |
print STDERR "SoundSoftware.pm:$$: Method is not read-only\n"; |
|
| 227 |
|
|
| 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 |
|
| 211 | 258 |
} |
| 212 | 259 |
|
| 213 |
return OK |
|
| 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 | 270 |
sub authen_handler {
|
| ... | ... | |
| 237 | 291 |
|
| 238 | 292 |
print STDERR "SoundSoftware.pm:$$: User is " . $r->user . ", got password\n"; |
| 239 | 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 |
} |
|
| 303 |
|
|
| 240 | 304 |
my $permitted = is_permitted($dbh, $project_id, $r->user, $redmine_pass, $r); |
| 241 | 305 |
|
| 242 | 306 |
$dbh->disconnect(); |
| ... | ... | |
| 279 | 343 |
$ret; |
| 280 | 344 |
} |
| 281 | 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 |
} |
|
| 368 |
} |
|
| 369 |
|
|
| 282 | 370 |
sub project_repo_is_readonly {
|
| 283 | 371 |
my $dbh = shift; |
| 284 | 372 |
my $project_id = shift; |
| ... | ... | |
| 368 | 456 |
} |
| 369 | 457 |
$sthldap->finish(); |
| 370 | 458 |
undef $sthldap; |
| 459 |
last if ($ret); |
|
| 371 | 460 |
} |
| 372 | 461 |
} else {
|
| 373 | 462 |
print STDERR "SoundSoftware.pm:$$: User $redmine_user lacks required role for this project\n"; |
| ... | ... | |
| 383 | 472 |
sub get_project_identifier {
|
| 384 | 473 |
my $dbh = shift; |
| 385 | 474 |
my $r = shift; |
| 386 |
|
|
| 387 | 475 |
my $location = $r->location; |
| 388 |
my ($repo) = $r->uri =~ m{$location/*([^/]+)};
|
|
| 476 |
my ($repo) = $r->uri =~ m{$location/*([^/]*)};
|
|
| 389 | 477 |
|
| 390 | 478 |
return $repo if (!$repo); |
| 391 | 479 |
|
| 392 | 480 |
$repo =~ s/[^a-zA-Z0-9\._-]//g; |
| 393 |
|
|
| 481 |
|
|
| 394 | 482 |
# The original Redmine.pm returns the string just calculated as |
| 395 | 483 |
# the project identifier. That won't do for us -- we may have |
| 396 | 484 |
# (and in fact already do have, in our test instance) projects |
| ... | ... | |
| 410 | 498 |
|
| 411 | 499 |
my $prefix = $cfg->{SoundSoftwareRepoPrefix};
|
| 412 | 500 |
if (!defined $prefix) { $prefix = '%/'; }
|
| 413 |
|
|
| 414 | 501 |
my $identifier = ''; |
| 415 | 502 |
|
| 416 | 503 |
$sth->execute($prefix . $repo); |
| ... | ... | |
| 449 | 536 |
# to project identifier if any are found |
| 450 | 537 |
if ($name =~ m/[^\w\d\s\._-]/) {
|
| 451 | 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 | 553 |
my $realm = '"Mercurial repository for ' . "'$name'" . '"'; |
Also available in: Unified diff