Apache-AuthCAS

 view release on metacpan or  search on metacpan

lib/Apache/AuthCAS.pm  view on Meta::CPAN

# Apache::AuthCAS
# David Castro, April 2004
# $Revision: 1.7 $
#
# Apache auth module to protect underlying resources using Yale's Central
# Authentication service
package Apache::AuthCAS;

$^W = 1;
use diagnostics;
use warnings;
use strict;
use mod_perl qw(StackedHandlers MethodHandlers Authen Authz);
use constant MP2 => $mod_perl::VERSION >= 1.99;
use vars qw($INITIALIZED $SESSION_CLEANUP_COUNTER);

BEGIN {
	if (MP2) {
		require Apache::Const;
		require APR::URI;
		Apache::Const->import(-compile => qw(FORBIDDEN HTTP_MOVED_TEMPORARILY OK DECLINED HTTP_OK));
	} else {
		require Apache::Constants;
		Apache::Constants->import(qw(FORBIDDEN HTTP_MOVED_TEMPORARILY OK DECLINED HTTP_OK));
	}
}

use Apache::URI;
use Net::SSLeay;
use MIME::Base64;
use DBI;

# logging flags
my $LOG_ERROR = "0";
my $LOG_WARN = "1";
my $LOG_INFO = "2";
my $LOG_DEBUG = "3";
my $LOG_INSANE = "4";
my $DEFAULT_LOG_LEVEL = $LOG_ERROR;
my $LOG_LEVEL = $DEFAULT_LOG_LEVEL;
# the URL the client is redirected to when an error occurs
my $DEFAULT_ERROR_URL="http://localhost/cas/error/";
my $ERROR_URL=$DEFAULT_ERROR_URL;
# error codes
my $DB_ERROR_CODE = "Database Service Error";
my $PGT_ERROR_CODE = "CAS Proxy Service Error";
my $INVALID_ST_ERROR_CODE = "Invalid Service Ticket";
my $INVALID_PGT_ERROR_CODE = "Invalid Proxy Granting Ticket";
my $MISSING_NETID_ERROR_CODE = "CAS failed to return NetID";
my $CAS_CONNECT_ERROR_CODE = "CAS couldn't validate service ticket";
# the URL a client is redirected to after logging in
my $SERVICE="";
# the service proxy tickets will be granted for
my $PROXY_SERVICE="";
# the host name of the CAS server
my $CAS_HOST="";
my $DEVEL_CAS_HOST="devel.localhost";
my $PROD_CAS_HOST="localhost";
# the port number for the CAS server
my $CAS_PORT="";
my $DEVEL_CAS_PORT="443";
my $PROD_CAS_PORT="443";
# CAS login URI
my $DEFAULT_CAS_LOGIN_URI="/cas/login";
my $CAS_LOGIN_URI=$DEFAULT_CAS_LOGIN_URI;
# CAS logout URI
my $DEFAULT_CAS_LOGOUT_URI="/cas/logout";
my $CAS_LOGOUT_URI=$DEFAULT_CAS_LOGOUT_URI;
# CAS proxy URI
my $DEFAULT_CAS_PROXY_URI="/cas/proxy";
my $CAS_PROXY_URI=$DEFAULT_CAS_PROXY_URI;
# CAS proxy validate URI
my $DEFAULT_CAS_PROXY_VALIDATE_URI="/cas/proxyValidate";
my $CAS_PROXY_VALIDATE_URI=$DEFAULT_CAS_PROXY_VALIDATE_URI;
# CAS service validate URI
my $DEFAULT_CAS_SERVICE_VALIDATE_URI="/cas/serviceValidate";
my $CAS_SERVICE_VALIDATE_URI=$DEFAULT_CAS_SERVICE_VALIDATE_URI;
# parameter used to pass in PGTIOU
my $PGT_IOU_PARAM = "pgtIou";
# parameter used to pass in PGT
my $PGT_ID_PARAM = "pgtId";
# number of proxy tickets to give the underlying application
my $DEFAULT_NUM_PROXY_TICKETS = 1;
my $NUM_PROXY_TICKETS = $DEFAULT_NUM_PROXY_TICKETS;
# the name of the cookie that will be used for sessions
my $DEFAULT_SESSION_COOKIE_NAME = "APACHECAS";
my $SESSION_COOKIE_NAME = $DEFAULT_SESSION_COOKIE_NAME;
# the domain the session cookies will be sent for
my $DEFAULT_SESSION_COOKIE_DOMAIN = "";
my $SESSION_COOKIE_DOMAIN = "";
# the max time before a session expires (in seconds)
my $DEFAULT_SESSION_TIMEOUT = 1800;
my $SESSION_TIMEOUT = $DEFAULT_SESSION_TIMEOUT;
# the name of the DBI database driver
my $DB_DRIVER = "";
my $DEVEL_DB_DRIVER = "Pg";
my $PROD_DB_DRIVER = "Pg";
# the host name of the database server
my $DB_HOST = "";
my $DEVEL_DB_HOST = "devel.localhost";
my $PROD_DB_HOST = "localhost";
# the port number of the database server
my $DB_PORT = "";
my $DEVEL_DB_PORT = "5432";
my $PROD_DB_PORT = "5432";
# the name of the database for sessions/pgtiou mapping
my $DB_NAME = "";
my $DEVEL_DB_NAME = "apache_cas";
my $PROD_DB_NAME = "apache_cas";
# the name of the session table
my $DB_SESSION_TABLE = "";
my $DEVEL_DB_SESSION_TABLE = "cas_sessions";
my $PROD_DB_SESSION_TABLE = "cas_sessions";
# the name of the pgtiou to pgt mapping table
my $DB_PGTIOU_TABLE = "";
my $DEVEL_DB_PGTIOU_TABLE = "cas_pgtiou_to_pgt";
my $PROD_DB_PGTIOU_TABLE = "cas_pgtiou_to_pgt";
# the user to connnect to the database with
my $DB_USER = "";
my $DEVEL_DB_USER = "develuser";
my $PROD_DB_USER = "produser";
# the password to connect to the databse with
my $DB_PASS = "";
my $DEVEL_DB_PASS = "develpass";
my $PROD_DB_PASS = "prodpass";
# whether or not we want redirect magic to remove service ticket from URL
my $DEFAULT_REMOVE_TICKET = "0";
my $REMOVE_TICKET = $DEFAULT_REMOVE_TICKET;
# are we running with production config, or other?
my $PRODUCTION = "0";
# session cleanup threshold (1 in N requests, session cleanup will occur for
# each Apache thread or process - i.e. for 10 processes, it may take as many as
# 100 requests before session cleanup is performed for a threshold of 10)
my $SESSION_CLEANUP_THRESHOLD = "10";
# when set to true, this module will attempt to make the underlying authz

lib/Apache/AuthCAS.pm  view on Meta::CPAN

			# make sure the session is still valid
			Apache->warn("$$: CAS: authenticate(): session last_accessed=$last_accessed") unless ($LOG_LEVEL < $LOG_DEBUG);
			if ($last_accessed + $SESSION_TIMEOUT >= time()) {
				# session is still valid
				Apache->warn("$$: CAS: authenticate(): session '$sid' is still valid") unless ($LOG_LEVEL < $LOG_DEBUG);

				# record the last time the session was accessed
				$session_data[1] = time();
				Apache->warn("$$: CAS: authenticate(): setting last accessed time to '".time()."'") unless ($LOG_LEVEL < $LOG_DEBUG);

				# if something bad happened, like database unavailability
				if (!$self->set_session_data(@session_data)) {
					Apache->warn("$$: CAS: authenticate(): problem saving session data, redirecting to the error page") unless ($LOG_LEVEL < $LOG_ERROR);
					return $self->redirect($r, $ERROR_URL, $DB_ERROR_CODE);
				} else {
					Apache->warn("$$: CAS: authenticate(): saved session data: ".join(",",@session_data)) unless ($LOG_LEVEL < $LOG_DEBUG);
				}
				
				# set the pgtiou
				$user = $session_data[2];
				$pgtiou = $session_data[3];

				if ($PROXY_SERVICE) {
					return $self->do_proxy($r, $sid, $pgtiou, $user, 0);
				} else {
					# no proxy stuff, so we are done
					Apache->warn("$$: CAS: authenticate(): no proxy stuff, we are done") unless ($LOG_LEVEL < $LOG_DEBUG);

					Apache->warn("$$: CAS: authenticate(): setting header CAS_FILTER_USER=$user") unless ($LOG_LEVEL < $LOG_DEBUG);
					$r->header_in('CAS_FILTER_USER', $user);

					if ($PRETEND_BASIC_AUTH) {
						# setup this up for underlying authz modules that rely on Basic auth having been performed
						$r->header_in('Authorization', "Basic " . encode_base64($user . ":DUMMYPASS"));
						$r->user($user);
						$r->connection->user($user);
						$r->connection->auth_type("Basic");
					}

					return (MP2 ? Apache::OK : Apache::Constants::OK);
				}
			} else {
				Apache->warn("$$: CAS: authenticate(): session '$sid' has expired") unless ($LOG_LEVEL < $LOG_DEBUG);
				if (!$self->delete_session_data($sid)) {
					Apache->warn("$$: CAS: authenticate(): couldn't delete expired session id='$sid'") unless ($LOG_LEVEL < $LOG_WARN);
				}
				Apache->warn("$$: CAS: authenticate(): deleted expired session '$sid'") unless ($LOG_LEVEL < $LOG_DEBUG);
				
				$sid = "";
			}
		} else {
			Apache->warn("$$: CAS: authenticate(): session '$sid' is invalid") unless ($LOG_LEVEL < $LOG_DEBUG);
			$sid = "";
		}
	}
	# note: not an else if, because we may find an invalid session id and
	#       fallback to ticket

	# if we have a service ticket
	if (($sid eq "") and ($ticket ne "")) {
		# validate service ticket through CAS, since no valid cookie was found
		my %properties = $self->validate_service_ticket($r, $ticket, $PROXY_SERVICE ?"1":"0");
		if ($properties{'error'}) {
			# error occurred validating service ticket
			return $self->redirect($r, $ERROR_URL, $properties{'error'});
		} else {
			Apache->warn("$$: CAS: authenticate(): valid service ticket '$ticket'") unless ($LOG_LEVEL < $LOG_DEBUG);
		}

		$pgtiou = $properties{'pgtiou'} || "";
		$user = $properties{'user'} || "";

		# we should get back a netid when validating a service ticket
		if ($user eq "") {
			return $self->redirect($r, $ERROR_URL, $MISSING_NETID_ERROR_CODE);
		}

		$sid = &create_session_id();

		Apache->warn("$$: CAS: authenticate(): setting sid='$sid' for netid='$user'") unless ($LOG_LEVEL < $LOG_DEBUG);

		# map a new session id to this pgtiou and give the client a cookie
		my $time = time();
		Apache->warn("$$: CAS: authenticate(): trying to save session data: ".join(",",$sid, $time, $user, $pgtiou)) unless ($LOG_LEVEL < $LOG_DEBUG);
		if (!$self->set_session_data($sid, $time, $user, $pgtiou)) {
			# if something bad happened, like database unavailability
			Apache->warn("$$: CAS: authenticate(): problem saving session data, redirecting to the error page") unless ($LOG_LEVEL < $LOG_ERROR);
			return $self->redirect($r, $ERROR_URL, $DB_ERROR_CODE);
		} else {
			Apache->warn("$$: CAS: authenticate(): saved session data: ".join(",",$sid, $time, $user, $pgtiou)) unless ($LOG_LEVEL < $LOG_DEBUG);
		}

		Apache->warn("$$: CAS: authenticate(): sending session cookie") unless ($LOG_LEVEL < $LOG_DEBUG);
		my $cookie = "$SESSION_COOKIE_NAME=$sid;path=/";
		if ($SESSION_COOKIE_DOMAIN ne "") {
			$cookie .= ";domain=.$SESSION_COOKIE_DOMAIN";
		}

		# send the cookie to the browser
		$r->header_out("Set-Cookie" => $cookie);

		# in case we redirect (considered an "error")
		$r->err_header_out("Set-Cookie" => $cookie);
	} else {
		Apache->warn("$$: CAS: authenticate(): no valid session id or ticket") unless ($LOG_LEVEL < $LOG_DEBUG);
		return $self->redirect_login($r);
	}

	Apache->warn("$$: CAS: authenticate(): got user: '$user'") unless ($LOG_LEVEL < $LOG_DEBUG);
	Apache->warn("$$: CAS: authenticate(): got PGTIOU: '$pgtiou'") unless ($LOG_LEVEL < $LOG_DEBUG);

	if ($PROXY_SERVICE) {
		return $self->do_proxy($r, $sid, $pgtiou, $user, 1);
	} else {
		# no proxy stuff, so we are done
		Apache->warn("$$: CAS: authenticate(): no proxy stuff, so we are done") unless ($LOG_LEVEL < $LOG_DEBUG);

		# redirect to this same page minus the ticket
		if (($REMOVE_TICKET eq "true") || ($REMOVE_TICKET eq "1")) {
			Apache->warn("$$: CAS: authenticate(): setting header CAS_FILTER_USER=$user") unless ($LOG_LEVEL < $LOG_DEBUG);
			$r->header_in('CAS_FILTER_USER', $user);

lib/Apache/AuthCAS.pm  view on Meta::CPAN

	return (MP2 ? Apache::HTTP_MOVED_TEMPORARILY : Apache::Constants::HTTP_MOVED_TEMPORARILY);
}

sub redirect_login($$) {
	my $self = shift;
	my $r = shift;

	Apache->warn("$$: CAS: redirect_login()") unless ($LOG_LEVEL < $LOG_DEBUG);

	my $service;
	if ($SERVICE eq "") {
		# use the current URL as the service
		$service = $self->this_url_encoded($r);
	} else {
		# use the static entry point into this service
		$service = $self->urlEncode($SERVICE);
	}
	Apache->warn("$$: CAS: redirect_login(): redirecting to CAS for service: '$service'") unless ($LOG_LEVEL < $LOG_INFO);
	my $redirect_url = "https://$CAS_HOST:$CAS_PORT$CAS_LOGIN_URI?service=$service";
	$r->header_out("Location" => $redirect_url);
	return (MP2 ? Apache::HTTP_MOVED_TEMPORARILY : Apache::Constants::HTTP_MOVED_TEMPORARILY);
}

sub redirect($$) {
	my $self = shift;
	my $r = shift;
	my $url = shift || "";
	my $errcode = shift || "";

	Apache->warn("$$: CAS: redirect()") unless ($LOG_LEVEL < $LOG_DEBUG);

	if ($url) {
		my $service;
		if ($SERVICE eq "") {
			# use the current URL as the service
			$service = $self->this_url_encoded($r);
			Apache->warn("$$: CAS: redirect(): using self as service") unless ($LOG_LEVEL < $LOG_DEBUG);
		} else {
			# use the static entry point into this service
			$service = $self->urlEncode($SERVICE);
			Apache->warn("$$: CAS: redirect(): using configured service") unless ($LOG_LEVEL < $LOG_DEBUG);
		}
		$r->header_out("CAS_FILTER_CAS_HOST", $CAS_HOST);
		$r->header_out("CAS_FILTER_CAS_PORT", $CAS_PORT);
		$r->header_out("CAS_FILTER_CAS_LOGIN_URI", $CAS_LOGIN_URI);
		$r->header_out("CAS_FILTER_SERVICE", $service);
		Apache->warn("$$: CAS: redirect(): redirecting to url: '$url' service: '$service'") unless ($LOG_LEVEL < $LOG_INFO);
		$r->header_out("Location" => "$url?login_url=https://$CAS_HOST:$CAS_PORT$CAS_LOGIN_URI&service=$service&errcode=$errcode");
		return (MP2 ? Apache::HTTP_MOVED_TEMPORARILY : Apache::Constants::HTTP_MOVED_TEMPORARILY);
	} else {
		Apache->warn("$$: CAS: redirect(): no redirect URL, displaying message") unless ($LOG_LEVEL < $LOG_INFO);
		$r->content_type ('text/html');
		$r->print("<html><body>service misconfigured</body></html>");
		$r->rflush;
		return (MP2 ? Apache::HTTP_OK : Apache::Constants::HTTP_OK);
	}
}

# params
#     apache request object
#     ticket to be validated
#     1 or 0, whether we need proxy tickets
# returns a hash with keys on success
# 	  'user', 'pgtiou'
# NULL on failure
sub validate_service_ticket($$) {
	my $self = shift;
	my $r = shift;
	my $ticket = shift;
	my $proxy = shift;

	Apache->warn("$$: CAS: validate_service_ticket(): validating service ticket '$ticket' through CAS") unless ($LOG_LEVEL < $LOG_DEBUG);
	my %properties;

	my $service;
	if ($SERVICE eq "") {
		# use the current URL as the service
		$service = $self->this_url_encoded($r);
	} else {
		# use the static entry point into this service
		$service = $self->urlEncode($SERVICE);
	}

	Apache->warn("$$: CAS: validate_service_ticket(): requesting validation for service: '$service'") unless ($LOG_LEVEL < $LOG_DEBUG);
	my $tmp;
	# FIXME - diff urls for proxy vs. none?
	if ($proxy) {
		$tmp = $CAS_PROXY_VALIDATE_URI . "?service=$service&ticket=$ticket&pgtUrl=$service";
	} else {
		$tmp = $CAS_SERVICE_VALIDATE_URI . "?service=$service&ticket=$ticket";
	}

	Apache->warn("$$: CAS: validate_service_ticket(): request URL: '$tmp'") unless ($LOG_LEVEL < $LOG_DEBUG);

	if ($LOG_LEVEL >= $LOG_INSANE) {
		$Net::SSLeay::trace = 3;  # 0=no debugging, 1=ciphers, 2=trace, 3=dump data
	} else {
		$Net::SSLeay::trace = 0;  # 0=no debugging, 1=ciphers, 2=trace, 3=dump data
	}
	#$Net::SSLeay::linux_debug = 1;

	my ($page, $response, %reply_headers) = Net::SSLeay::get_https($CAS_HOST, $CAS_PORT, $tmp);

	# if we had some type of connection problem
	if (!defined($page)) {
		Apache->warn("$$: CAS: validate_service_ticket(): error validating service");
		$properties{'error'} = $CAS_CONNECT_ERROR_CODE;
		return %properties;
	}

	Apache->warn("$$: CAS: validate_service_ticket(): page: $page") unless ($LOG_LEVEL < $LOG_INSANE);
	Apache->warn("$$: CAS: validate_service_ticket(): response: $response") unless ($LOG_LEVEL < $LOG_INSANE);

	# FIXME - add a check for a 404 error/other errors
	if ($page =~ /<cas:user>([^<]+)<\/cas:user>/) {
		my $user = $1;
		chomp $user;
		Apache->warn("$$: CAS: validate_service_ticket(): valid service ticket, user '$user' authenticated") unless ($LOG_LEVEL < $LOG_DEBUG);
		$properties{'user'} = $user;
	
		# only try to get PGTIOU if we are doing proxy stuff
		if ($proxy) {
			if ($page =~ /<cas:proxyGrantingTicket>([^<]+)<\/cas:proxyGrantingTicket>/) {
				Apache->warn("$$: CAS: validate_service_ticket(): got pgt='$1' for user='$user'") unless ($LOG_LEVEL < $LOG_DEBUG);
				if ($1 ne "") {
					$properties{'pgtiou'} = $1;
				} else {
					Apache->warn("$$: CAS: validate_service_ticket(): empty PGT in response from CAS") unless ($LOG_LEVEL < $LOG_ERROR);
				}
			} else {
				Apache->warn("$$: CAS: validate_service_ticket(): no PGT in response from CAS") unless ($LOG_LEVEL < $LOG_ERROR);
				$properties{'error'} = $PGT_ERROR_CODE;
				return %properties;
			}
		}
	} else {
		Apache->warn("$$: CAS: validate_service_ticket(): invalid service ticket, user denied access") unless ($LOG_LEVEL < $LOG_DEBUG);
		$properties{'error'} = $INVALID_ST_ERROR_CODE;
		return %properties;
	}

	return %properties;
}

sub send_proxysuccess($$) {
	my $self = shift;
	my $r = shift;

	Apache->warn("$$: CAS: send_proxysuccess(): sending proxy success for CAS callback") unless ($LOG_LEVEL < $LOG_DEBUG);

	$r->content_type("text/html");
	$r->print("<casClient:proxySuccess xmlns:casClient=\"http://www.yale.edu/tp/casClient\"/>\n");
	$r->rflush();
	return (MP2 ? Apache::OK : Apache::Constants::OK);
}

sub get_proxy_tickets($$) {
	my $self = shift;
	my $pgt = shift;
	my $target = shift;
	my $num_tickets = shift;

	Apache->warn("$$: CAS: get_proxy_tickets()") unless ($LOG_LEVEL < $LOG_DEBUG);

	my @tickets;
	
	for (my $i=0; $i < $num_tickets; $i++) {
		my $uri = "$CAS_PROXY_URI?pgt=$pgt&targetService=$target";
		Apache->warn("$$: CAS: get_proxy_tickets(): using PGT to obtain PT: calling URL '$uri'") unless ($LOG_LEVEL < $LOG_DEBUG);

		if ($LOG_LEVEL >= $LOG_INSANE) {
			$Net::SSLeay::trace = 3;  # 0=no debugging, 1=ciphers, 2=trace, 3=dump data
		} else {
			$Net::SSLeay::trace = 0;  # 0=no debugging, 1=ciphers, 2=trace, 3=dump data
		}

		my ($page, $response, %reply_headers) = Net::SSLeay::get_https($CAS_HOST, $CAS_PORT, $uri);

		if ($page =~ /<cas:proxySuccess>/) {
			Apache->warn("$$: CAS: get_proxy_tickets(): successful proxy request") unless ($LOG_LEVEL < $LOG_DEBUG);
			if ($page =~ /<cas:proxyTicket>([^<]+)<\/cas:proxyTicket>/) {
				Apache->warn("$$: CAS: get_proxy_tickets(): successfully retrieved proxy ticket") unless ($LOG_LEVEL < $LOG_DEBUG);
				push(@tickets, $1);
			} else {
				Apache->warn("$$: CAS: get_proxy_tickets(): no proxy ticket in response") unless ($LOG_LEVEL < $LOG_DEBUG);
				return qw();
			}
		} else {
			Apache->warn("$$: CAS: get_proxy_tickets(): unsuccessful proxy request") unless ($LOG_LEVEL < $LOG_DEBUG);
			return qw();
		}
	}

	if (@tickets) {
		return @tickets;
	} else {
		return qw();

lib/Apache/AuthCAS.pm  view on Meta::CPAN


    # the port number for the CAS server
    PerlSetVar CASPort "443"

    # are we running with production config or dev config
    PerlSetVar CASProduction "1"

    # the URL a client is redirected to after logging in
    PerlSetVar CASService "https://somedomain.com/email/"

    # the service proxy tickets will be granted for
    PerlSetVar CASProxyService "mail.somedomain.com"

    # number of proxy tickets to give the underlying application
    PerlSetVar CASNumProxyTickets "2"

    # the URL the client is redirected to when an error occurs
    PerlSetVar CASErrorURL "https://somedomain.com/error/"

    # the name of the DBI database driver
    PerlSetVar CASDatabaseDriver "Pg"

    # the host name of the database server
    PerlSetVar CASDatabaseHost "db.somedomain.com"

    # the port number of the database server
    PerlSetVar CASDatabasePort "5433"

    # the name of the database for sessions/pgtiou mapping
    PerlSetVar CASDatabaseName "cas"

    # the user to connnect to the database with
    PerlSetVar CASDatabaseUser "dbuser"

    # the password to connect to the databse with
    PerlSetVar CASDatabasePass "dbpass"

    # the name of the session table
    PerlSetVar CASDatabaseSessionTable "cas_sessions"

    # the name of the pgtiou to pgt mapping table
    PerlSetVar CASDatabasePGTIOUTable "cas_pgtiou_to_pgt"

    # the level of logging
    PerlSetVar CASLogLevel "4"

    # whether we should perform a redirect, stripping the service ticket
    # once we have already created a session for the client
    PerlSetVar CASRemoveTicket "true"

    # the name of the cookie that will be used for sessions
    PerlSetVar CASSessionCookieName "APACHECAS"
    
    # the max time before a session expires (in seconds)
    PerlSetVar CASSessionTimeout "1800"

    # not currently able to override through Apache configuration:
    #   CAS login URI
    #   CAS logout URI
    #   CAS proxy URI
    #   CAS proxy validate URI
    #   CAS service validate URI
    #   parameter used to pass in PGTIOU
    #   parameter used to pass in PGT
    #   session cleanup threshold
    #   basic authentication emulation

=head1 NOTES

Any options that are not set in the Apache configuration will default to the
values preconfigured in the Apache::AuthCAS module.  Either explicitly override
those options that do not match your environment or set them in the module
itself.

=head1 COMPATIBILITY

This module should work in both mod_perl 1 and 2.  For Apache 2/mod_perl 2, the
Apache::compat may need to be loaded in your mod_perl startup script.  This can
be done by adding:

    use Apache::compat;

into the script included by the PerlRequire directive in your Apache
configuration.  For instance, if your Apache configuration includes the line:

    PerlRequire /usr/local/sbin/modperl_startup.pl

then the "use" line mentioned above should be added to this file.  Consult the
mod_perl documentation for more information regarding mod_perl startup scripts.

=head1 SEE ALSO

=head2 Official Yale CAS Website

http://www.yale.edu/tp/auth/

=head2 mod_perl Documentation

http://perl.apache.org/

=head1 AUTHOR

David Castro <dcastro@apu.edu>

=head1 COPYRIGHT

Copyright (C) 2004 David Castro <dcastro@apu.edu>

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA




( run in 2.034 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )