package Bric::App::AccessHandler; =head1 NAME Bric::App::AccessHandler - Handles Authentication and Session setup during the Apache Access phase. =head1 VERSION $LastChangedRevision$ =cut # Grab the Version Number. require Bric; our $VERSION = Bric->VERSION; =head1 DATE $LastChangedDate: 2006-03-18 01:26:34 +0100 (Sat, 18 Mar 2006) $ =head1 SYNOPSIS use lib '/usr/local/bricolage/lib'; PerlModule Bric::App::AccessHandler SetHandler default-handler PerlModule Bric::App::Handler PerlFreshRestart On DocumentRoot "/usr/local/bricolage/comp" Options Indexes FollowSymLinks MultiViews AllowOverride None Order allow,deny Allow from all SetHandler perl-script PerlHandler Bric::App::Handler PerlAccessHandler Bric::App::AccessHandler =head1 DESCRIPTION This module handles the Access phase of an Apache request. It authenticates users to Bricolage, and sets up Session handling. =cut ################################################################################ # Dependencies ################################################################################ # Standard Dependencies use strict; ################################################################################ # Programmatic Dependencies use Apache::Constants qw(:common :http); use Apache::Log; use Bric::App::Session; use Bric::App::Util qw(:redir :history); use Bric::App::Auth qw(auth logout); use Bric::Config qw(:err :ssl :cookies); ################################################################################ # Inheritance ################################################################################ ################################################################################ # Function and Closure Prototypes ################################################################################ ################################################################################ # Constants ################################################################################ ################################################################################ # Fields ################################################################################ # Public Class Fields ################################################################################ # Private Class Fields my $port = LISTEN_PORT == 80 ? '' : ':' . LISTEN_PORT; my $ssl_port = SSL_PORT == 443 ? '' : ':' . SSL_PORT; ################################################################################ ################################################################################ # Instance Fields ################################################################################ # Class Methods ################################################################################ =head1 INTERFACE =head2 Constructors NONE. =head2 Destructors NONE. =head2 Public Class Methods NONE. =head2 Public Functions =over 4 =item my $status = handler($r) Sets up the user session and checks authentication. If the authentication is current, it returns OK and the request continues. Otherwise, it caches the requested URI in the session and returns FORBIDDEN. B NONE. B NONE. B NONE. =cut sub handler { my $r = shift; my $ret = eval { # Silently zap foolish user access to http when SSL is always required # by web master. if (ALWAYS_USE_SSL && SSL_ENABLE && LISTEN_PORT == $r->get_server_port) { $r->custom_response(FORBIDDEN, 'https://'. $r->hostname . $ssl_port . '/logout'); return FORBIDDEN; } # Propagate SESSION and AUTH cookies if we switched server ports my %qs = $r->args; my %cookies = Apache::Cookie->fetch; # work around multiple servers if login event if ( exists $qs{&AUTH_COOKIE} && ! $cookies{&AUTH_COOKIE} ) { foreach(&COOKIE, &AUTH_COOKIE) { if (exists $qs{$_} && $qs{$_}) { # hmmm.... Apache is in @INC or we would not have $r my $cook = Apache::unescape_url($qs{$_}); $cookies{$_} = $cook; # insert / overwrite value # propagate this particular cookie back to the browser with # all properties $r->err_headers_out->add('Set-Cookie',$_ . '=' . $cook); } } my $http_cook = ''; while(my($k,$v) = each %cookies) { # Reconstitute the input cookie $http_cook .= '; ' if $http_cook; $v = (split('; ',$v))[0]; $http_cook .= $k .'='. $v; } $r->header_in('Cookie', $http_cook); # Replacement HTTP_COOKIE string } # Continue, the session is not the wiser about inserted cookies IN. # Set up the user's session data. Bric::App::Session::setup_user_session($r); my ($res, $msg) = auth($r); return OK if $res; # If we're here, the user needs to authenticate. Figure out where they # wanted to go so we can redirect them there after they've logged in. $r->log_reason($msg) if $msg; # my $uri = $r->uri; # my $args = $r->args; # $uri = "$uri?$args" if $args; # set_redirect($uri); # Commented out the above and set the login to always redirect to "/". # This is because the session might otherwise get screwed up. The # del_redirect() function in Bric::App::Util depends on this # knowledge, so if we ever change this, we'll need to make sure we fix # that function, too. # set_redirect('/'); my $hostname = $r->hostname; if (SSL_ENABLE) { $r->custom_response(FORBIDDEN, "https://$hostname$ssl_port/login"); } else { $r->custom_response(FORBIDDEN, "http://$hostname$port/login"); } return FORBIDDEN; }; return $@ ? handle_err($r, $@) : $ret; } ################################################################################ =item my $status = logout_handler($r) Logs the user out. B NONE. B NONE. B NONE. =cut sub logout_handler { my $r = shift; my $ret = eval { # Set up the user's session data. Bric::App::Session::setup_user_session($r); # Logout. logout($r); # Expire the user's session. Bric::App::Session::expire_session($r); # Redirect to the login page. my $hostname = $r->hostname; if (SSL_ENABLE) { # if SSL and logging out of server #1, make sure and logout of # server #2 if (scalar $r->args =~ /goodbye/) { $r->custom_response(FORBIDDEN, "https://$hostname$ssl_port/login"); } elsif ($r->get_server_port == &SSL_PORT) { $r->custom_response(HTTP_MOVED_TEMPORARILY, "http://$hostname$port/logout?goodbye"); return HTTP_MOVED_TEMPORARILY; } else { $r->custom_response(HTTP_MOVED_TEMPORARILY, "https://$hostname$ssl_port/logout?goodbye"); return HTTP_MOVED_TEMPORARILY; } } else { $r->custom_response(FORBIDDEN, "http://$hostname$port/login"); } return FORBIDDEN; }; return $@ ? handle_err($r, $@) : $ret; } ################################################################################ =item my $status = okay($r) This handler should B be used for the '/login' location of the SSL virtual host. It simply sets up the user session and returns OK. B NONE. B NONE. B NONE. =cut sub okay { my $r = shift; my $ret = eval { # Set up the user's session data. Bric::App::Session::setup_user_session($r); return OK; }; return $@ ? handle_err($r, $@) : $ret; } ################################################################################ =item my $status = handle_err($r, $err) Handles errors for the other handlers in this class. B NONE. B NONE. B NONE. =cut sub handle_err { my ($r, $err) = @_; # Set the filename for the error element. my $uri = $r->uri; (my $fn = $r->filename) =~ s/$uri/${\ERROR_URI}/; $r->uri(ERROR_URI); $r->filename($fn); $err = Bric::Util::Fault::Exception::AP->new( error => 'Error executing AccessHandler', payload => $err, ); $r->pnotes('BRIC_EXCEPTION' => $err); # Send the error to the apache error log. $r->log->error($err->full_message); # Exception::Class::Base provides trace->as_string, but trace_as_text is # not guaranteed. Use print STDERR to avoid escaping newlines. print STDERR $err->can('trace_as_text') ? $err->trace_as_text : join ("\n", map {sprintf " [%s:%d]", $_->filename, $_->line } $err->trace->frames), "\n"; # Return OK so that Mason can handle displaying the error element. return OK; } ################################################################################ =back =head1 PRIVATE =head2 Private Class Methods NONE. =head2 Private Instance Methods NONE. =head2 Private Functions NONE. =cut 1; __END__ =head1 NOTES NONE. =head1 AUTHOR David Wheeler =head1 SEE ALSO L =cut