Browse files

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).
  • Loading branch information...
1 parent ee121e9 commit 622391b61d29f91eb8e12fed9e63870a84f968dd @scrottie scrottie committed Sep 7, 2011
Showing with 72 additions and 70 deletions.
  1. +41 −69 lib/WebGUI.pm
  2. +18 −0 lib/WebGUI/URL/Content.pm
  3. +13 −1 t/Auth/mech.t
View
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;
+ my $user = WebGUI::User->newByUsername($session, $username);
+ if ( ! defined $user ) {
+ # $status = Apache2::Const::HTTP_UNAUTHORIZED; # no
+ return $status;
}
- else {
- $cookies = {};
+
+ 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;
+ }
}
-
- # 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);
- 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};
View
18 lib/WebGUI/URL/Content.pm
@@ -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 ] )};
View
14 t/Auth/mech.t
@@ -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" );

0 comments on commit 622391b

Please sign in to comment.