Revision 738:882917cbe8d0 extra

View differences:

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