# -----------------------------------------------------------------------------
# web_auth / logoff.pl
# note - see http://stackoverflow.com/questions/233507/how-to-log-out-user-from-web-site-using-basic-authentication
# for a possible alternative
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# date description
# ---------- -----------------------------------------------------------------
# -----------------------------------------------------------------------------
use strict;
use warnings;
use Apache2::Const -compile => qw(AUTH_REQUIRED);
use Apache2::Request;
use Apache2::RequestIO;
use Apache2::RequestUtil;
# -----------------------------------------------------------------------------
#
# How the logoff links are implemented
# ------------------------------------
#
# A typical logoff link in our web pages looks like this:
#
# Log off
# or
# Log off
#
# where:
#
# id is "logoff" optionally followed by an underscore and one or more digits.
#
# href should be to "logoff.pl" in a password protected directory. It can be
# followed by extra path information and a query string - see details below.
#
# main.js attaches an event handler to links which have the logoff* id. The event
# handler tries to execute the ClearAuthenticationCache command, which will only
# succeed in IE.
#
# If the ClearAuthenticationCache succeeded, the logoff.pl page is loaded. If
# it did not succeed, the handler first modifies the url to include a user name
# and password of "Unknown:Invalid"; this is done by inserting "Unknown:Invalid@"
# between the "//" and the hostname in the full url (see note). For example,
#
# http://scott-dev.ito.lacoe.edu/data_collection/logoff.pl
# becomes
# http://Unknown:Invalid@scott-dev.ito.lacoe.edu/data_collection/logoff.pl
#
# note: even though the href in the example link above is relative, in javascript
# the href property of the link returns a full url.
#
# Because the user:password is invalid, a 401 error is returned and all tested
# browsers re-prompt for a username and password.
#
# There are a couple of issues with this technique. The minor one is that the
# browser will prefill the user name in the prompt with "Unknown" (Safari).
#
# The larger issue, which this program deals with, is that the browser might
# keep the "Unknown:Invalid@" as part of the url for the page, so if you log
# in from the password prompt, all relative urls on the page will have the
# invalid credential included.
#
# To solve this issue, this program does a redirect to a full url; since the
# url doesn't contain the username and password, this becomes the base url
# for the browser.
#
# Generally, either this program would be used dorectly, or there would be a
# symlink in the protected application directory e.g., htdocs/workshops/admin/
# to this program in htdocs/web_auth/
#
# *** NOTE NOTE NOTE NOTE NOTE ***
#
# In order to use this program directly, the AuthName for the application you
# are logging out from must be the same as the AuthName for /web_auth/, which is
# AuthName "ITO"
#
# Details on the href and the redirect url
# ----------------------------------------
#
# Ideally, the user would exit the browser after logging out. However, they,
# or another user at the same computer, might want to log back in with different
# credentials. The appropriate landing page after the new login would probably
# be a menu page (perhaps password protected) for the same application. This
# can be done in one of two ways, depending on how the application directory is
# configured:
#
# 1. If there is directory index for the protected application directory,
# that is what should be displayed, the href would simply be to "login.pl"
# in the directory (login.pl would be a symlink as described above)
#
# e.g., to go to the (public) index for TPB after logging out, the href
# would just be "logoff.pl", assuming that the base url for the web page
# with the logout link is /TPB/.
#
# 2. If you want to execute a specific program in the application directory
# (for example, if the directory index isn't password protected and you
# want instead to invoke the application with the password protected
# index) the href would be "logoff.pl" followed by extra path info
# (and optionally a query string) to the application.
#
# e.g., in /data_collection/ or /data_collection/eett/, you could use
# "logoff.pl/data_collection.pl?run_mode=menu", or, since the default
# run mode for data_collection.pl is "menu", just "logoff.pl/data_collection.pl"
#
# Note that these two methods require that the desired landing page is a
# password protected directory. If it isn't (e.g., to go to /staff after leaving
# /workshops/admin), you can instead use a special query string. If the query
# string starts with a slash it is treated as the full path to the new landing
# page. So, for example, to go to /staff when logging out from /workshops/admin,
# the href would be:
#
# "logoff.pl?/staff"
#
# I don't know if this would ever be appropriate, but you can even include a
# query string. So if you don't want to bother with a symlink, you could do:
#
# "/web_auth/logoff.pl?/data_collection/eett/data_collection.pl?run_mode=menu
#
# -----------------------------------------------------------------------------
my $r = Apache2::RequestUtil->request;
my $hostname = $r->hostname;
my $port = $ENV{'SERVER_PORT'};
my $uri = $r->uri;
my $path_info = $r->path_info;
my $query_string = $r->args || '';
# determine if this is http or https
my $protocol = ($port == 443) ? 'https' : 'http';
# start building the redirect location
my $location = "${protocol}://${hostname}";
# add port number if it is non-standard
if ($port != 80 && $port != 443) {
$location .= ":${port}";
}
# now add the new path and query string
if ($query_string =~ /^\//) {
# if the query string starts with a slash, that is the new path
$location .= $query_string;
}
else {
# remove this application from the uri, then add the query string, if any
(my $path = $uri) =~ s!/logoff.pl.*$!!;
$location .= $path . $path_info;
if ($query_string) {
$location .= '?' . $query_string;
}
}
#warn("
#login after logoff:
# host $hostname
# port $port
# uri $uri
# path info $path_info
# query string $query_string
# protocol $protocol
# location $location
#");
$r->headers_out->set(Location => $location);
$r->status(Apache2::Const::REDIRECT);
return Apache2::Const::REDIRECT;
# -----------------------------------------------------------------------------