Using Basic Auth with WebGUI (#12198)
Per IRC discussion with preaction, make HTTP auth failures soft failures. Don't attempt to re-auth the user on failure. Otherwise, .htaccess or similar put in place to protect a site and WebGUI get into a skirmish (users are asked to re-auth even if they did the .htaccess correctly, the log gets flooded, cats get radio shows, etc).
This commit is contained in:
parent
ee121e9460
commit
622391b61d
3 changed files with 73 additions and 71 deletions
110
lib/WebGUI.pm
110
lib/WebGUI.pm
|
|
@ -54,9 +54,13 @@ These subroutines are available from this package:
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 authen ( requestObject, [ user, pass, config ])
|
||||
=head2 authen ( requestObject, user || undef, pass || undef, session ])
|
||||
|
||||
HTTP Basic auth for WebGUI.
|
||||
Either called from L<WebGUI::Content::URL> directly or indirectly when pushed back on the L<mod_perl> handler stack.
|
||||
HTTP Basic auth is an alternative authentication mechanism for WebGUI for robots such as RSS feed readers.
|
||||
L<WebGUI::Content::URL> does nothing with the return codes from here, but L<mod_perl> uses them if this routine
|
||||
gets pushed as a handler.
|
||||
|
||||
=head3 requestObject
|
||||
|
||||
|
|
@ -70,16 +74,17 @@ The username to authenticate with. Will pull from the request object if not spec
|
|||
|
||||
The password to authenticate with. Will pull from the request object if not specified.
|
||||
|
||||
=head3 config
|
||||
=head3 session
|
||||
|
||||
A reference to a WebGUI::Config object. One will be created if it isn't specified.
|
||||
A reference to a WebGUI::Session object.
|
||||
|
||||
=cut
|
||||
|
||||
|
||||
sub authen {
|
||||
my ($request, $username, $password, $config) = @_;
|
||||
my ($request, $username, $password, $session) = @_;
|
||||
$request = Apache2::Request->new($request);
|
||||
my $log = $session->log;
|
||||
my $server = Apache2::ServerUtil->server;
|
||||
my $status = Apache2::Const::OK;
|
||||
|
||||
|
|
@ -88,67 +93,46 @@ sub authen {
|
|||
if ($request->auth_type eq "Basic") {
|
||||
($status, $password) = $request->get_basic_auth_pw;
|
||||
$username = $request->user;
|
||||
$username or return Apache2::Const::HTTP_UNAUTHORIZED;
|
||||
}
|
||||
else {
|
||||
return Apache2::Const::HTTP_UNAUTHORIZED;
|
||||
# per http://www.webgui.org/use/bugs/tracker/12198, failures result in the user remaining visitor, not them
|
||||
# being denied access entirely.
|
||||
# $status = Apache2::Const::HTTP_UNAUTHORIZED; # no
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
|
||||
$config ||= WebGUI::Config->new($server->dir_config('WebguiRoot'),$request->dir_config('WebguiConfig'));
|
||||
my $cookies = eval { APR::Request::Apache2->handle($request)->jar(); };
|
||||
if (blessed $@ && $@->isa('APR::Request::Error')) {
|
||||
$cookies = $@->jar;
|
||||
}
|
||||
else {
|
||||
$cookies = {};
|
||||
my $user = WebGUI::User->newByUsername($session, $username);
|
||||
if ( ! defined $user ) {
|
||||
# $status = Apache2::Const::HTTP_UNAUTHORIZED; # no
|
||||
return $status;
|
||||
}
|
||||
|
||||
# determine session id
|
||||
my $sessionId = $cookies->{$config->getCookieName};
|
||||
my $session = WebGUI::Session->open($server->dir_config('WebguiRoot'),$config->getFilename, $request, $server, $sessionId);
|
||||
my $log = $session->log;
|
||||
$request->pnotes(wgSession => $session);
|
||||
my $authMethod = $user->authMethod;
|
||||
if ($authMethod) { # we have an auth method, let's try to instantiate
|
||||
my $auth = eval { WebGUI::Pluggable::instanciate("WebGUI::Auth::".$authMethod, "new", [ $session, $authMethod ] ) };
|
||||
if ($@) { # got an error
|
||||
$log->error($@);
|
||||
return Apache2::Const::SERVER_ERROR;
|
||||
}
|
||||
elsif ($auth->authenticate($username, $password)) { # lets try to authenticate
|
||||
$log->info("BASIC AUTH: authenticated successfully");
|
||||
my $sessionId = $session->db->quickScalar("select sessionId from userSession where userId=?",[$user->userId]);
|
||||
unless (defined $sessionId) { # no existing session found
|
||||
$log->info("BASIC AUTH: creating new session");
|
||||
$sessionId = $session->id->generate;
|
||||
$auth->_logLogin($user->userId, "success (HTTP Basic)");
|
||||
}
|
||||
$session->{_var} = WebGUI::Session::Var->new($session, $sessionId);
|
||||
$session->user({user=>$user});
|
||||
return Apache2::Const::OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $sessionId && $session->user->isRegistered) { # got a session id passed in or from a cookie
|
||||
$log->info("BASIC AUTH: using cookie");
|
||||
return Apache2::Const::OK;
|
||||
}
|
||||
elsif ($status != Apache2::Const::OK) { # prompt the user for their username and password
|
||||
$log->info("BASIC AUTH: prompt for user/pass");
|
||||
return $status;
|
||||
}
|
||||
elsif (defined $username && $username ne "") { # no session cookie, let's try to do basic auth
|
||||
$log->info("BASIC AUTH: using user/pass");
|
||||
my $user = WebGUI::User->newByUsername($session, $username);
|
||||
if (defined $user) {
|
||||
my $authMethod = $user->authMethod;
|
||||
if ($authMethod) { # we have an auth method, let's try to instantiate
|
||||
my $auth = eval { WebGUI::Pluggable::instanciate("WebGUI::Auth::".$authMethod, "new", [ $session, $authMethod ] ) };
|
||||
if ($@) { # got an error
|
||||
$log->error($@);
|
||||
return Apache2::Const::SERVER_ERROR;
|
||||
}
|
||||
elsif ($auth->authenticate($username, $password)) { # lets try to authenticate
|
||||
$log->info("BASIC AUTH: authenticated successfully");
|
||||
$sessionId = $session->db->quickScalar("select sessionId from userSession where userId=?",[$user->userId]);
|
||||
unless (defined $sessionId) { # no existing session found
|
||||
$log->info("BASIC AUTH: creating new session");
|
||||
$sessionId = $session->id->generate;
|
||||
$auth->_logLogin($user->userId, "success (HTTP Basic)");
|
||||
}
|
||||
$session->{_var} = WebGUI::Session::Var->new($session, $sessionId);
|
||||
$session->user({user=>$user});
|
||||
return Apache2::Const::OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
$log->security($username." failed to login using HTTP Basic Authentication");
|
||||
$request->auth_type('Basic');
|
||||
$request->note_basic_auth_failure;
|
||||
return Apache2::Const::HTTP_UNAUTHORIZED;
|
||||
}
|
||||
$log->info("BASIC AUTH: skipping");
|
||||
return Apache2::Const::HTTP_UNAUTHORIZED;
|
||||
$log->security($username." failed to login using HTTP Basic Authentication");
|
||||
# $status = Apache2::Const::HTTP_UNAUTHORIZED; # no
|
||||
return $status;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
@ -175,18 +159,6 @@ sub handler {
|
|||
$matchUri =~ s{^$gateway}{/};
|
||||
my $gotMatch = 0;
|
||||
|
||||
# handle basic auth
|
||||
my $auth = $request->headers_in->{'Authorization'};
|
||||
if ($auth =~ m/^Basic/) { # machine oriented
|
||||
# Get username and password from Apache and hand over to authen
|
||||
$auth =~ s/Basic //;
|
||||
authen($request, split(":", MIME::Base64::decode_base64($auth), 2), $config);
|
||||
}
|
||||
else { # realm oriented
|
||||
$request->push_handlers(PerlAuthenHandler => sub { return WebGUI::authen($request, undef, undef, $config)});
|
||||
}
|
||||
|
||||
|
||||
# url handlers
|
||||
WEBGUI_FATAL: foreach my $handler (@{$config->get("urlHandlers")}) {
|
||||
my ($regex) = keys %{$handler};
|
||||
|
|
|
|||
|
|
@ -64,12 +64,30 @@ to the user, instead of displaying the Page Not Found page.
|
|||
sub handler {
|
||||
my ($request, $server, $config) = @_;
|
||||
$request->push_handlers(PerlResponseHandler => sub {
|
||||
|
||||
my $request = shift;
|
||||
$request = Apache2::Request->new($request);
|
||||
|
||||
my $session = $request->pnotes('wgSession');
|
||||
|
||||
WEBGUI_FATAL: {
|
||||
unless (defined $session) {
|
||||
$session = WebGUI::Session->open($server->dir_config('WebguiRoot'), $config->getFilename, $request, $server);
|
||||
return Apache2::Const::OK if ! defined $session;
|
||||
}
|
||||
|
||||
# if there's no session cookie but there is HTTP auth, try to log in using that
|
||||
my $auth = $request->headers_in->{'Authorization'};
|
||||
if( $session->user->isVisitor and $auth ) {
|
||||
if( $auth =~ m/^Basic/ ) {
|
||||
$auth =~ s/Basic //;
|
||||
WebGUI::authen($request, split(":", MIME::Base64::decode_base64($auth), 2), $session);
|
||||
}
|
||||
else { # realm oriented
|
||||
$request->push_handlers(PerlAuthenHandler => sub { return WebGUI::authen($request, undef, undef, $session)});
|
||||
}
|
||||
}
|
||||
|
||||
WebGUI::Asset::Template->processVariableHeaders($session);
|
||||
foreach my $handler (@{$config->get("contentHandlers")}) {
|
||||
my $output = eval { WebGUI::Pluggable::run($handler, "handler", [ $session ] )};
|
||||
|
|
|
|||
|
|
@ -56,8 +56,13 @@ my ($mech, $redirect, $response, $url);
|
|||
|
||||
# Get the site's base URL
|
||||
my $baseUrl = 'http://' . $session->config->get('sitename')->[0];
|
||||
# $baseUrl .= ':8000'; # no easy way to automatically find this
|
||||
$baseUrl .= $session->config->get('gateway');
|
||||
|
||||
my $httpAuthUrl = 'http://' . $USERNAME . ':' . $IDENTIFIER . '@' . $session->config->get('sitename')->[0];
|
||||
# $httpAuthUrl .= ':8000'; # no easy way to automatically find this
|
||||
$httpAuthUrl .= $session->config->get('gateway');
|
||||
|
||||
# Make an asset we can login on
|
||||
my $asset
|
||||
= $node->addChild({
|
||||
|
|
@ -84,7 +89,7 @@ if ( !$mech->success ) {
|
|||
plan skip_all => "Cannot load URL '$baseUrl'. Will not test.";
|
||||
}
|
||||
|
||||
plan tests => 40; # Increment this number for each test you create
|
||||
plan tests => 42; # Increment this number for each test you create
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# no form: Test logging in on a normal page sends the user back to the same page
|
||||
|
|
@ -276,3 +281,10 @@ $mech->submit_form_ok(
|
|||
);
|
||||
$mech->base_is( $assetUrl, "We don't get redirected" );
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# HTTP basic auth
|
||||
$mech = Test::WWW::Mechanize->new;
|
||||
$mech->get( $httpAuthUrl );
|
||||
$mech->content_contains( "Hello, $USERNAME", "We are greeted by name" );
|
||||
$mech->get( $httpAuthUrl . $asset->get('url') );
|
||||
$mech->content_contains( "ARTICLE", "We are shown the article" );
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue