view release on metacpan or search on metacpan
EOF
my $builder = $class->new(
module_name => 'Apache::AppSamurai',
license => 'perl',
dist_author => 'Paul M. Hirsch <paul@voltagenoir.org>',
dist_version_from => 'lib/Apache/AppSamurai.pm',
dist_abstract => 'Web application/reverse proxy authenticating front end',
requires => {
'Test::More' => 0,
'CGI::Cookie' => 0,
'URI' => 0,
'Time::HiRes' => 0,
'MIME::Base64' => 0,
'Carp' => 0,
'Apache::Session' => 0,
'Digest::SHA' => 0,
'Storable' => 0,
'Crypt::CBC' => 2.17,
@extrareq
},
abstract: Web application/reverse proxy authenticating front end
license: perl
resources:
MailingList: mailto:appsamurai-misc@lists.sourceforge.net
bugtracker: http://rt.cpan.org/Public/Dist/Display.html?Name=Apache-AppSamurai
homepage: http://appsamurai.sourceforge.net
license: http://dev.perl.org/licenses/
requires:
Apache::Request: 0
Apache::Session: 0
CGI::Cookie: 0
Carp: 0
Crypt::CBC: 2.17
Digest::SHA: 0
MIME::Base64: 0
Storable: 0
Test::More: 0
Time::HiRes: 0
URI: 0
mod_perl: 1.07
recommends:
examples/conf/appsamurai-owa.conf view on Meta::CPAN
# We with use the auth_name "Owa" for this sample. If you prefer
# "TheMagnificentRonnieWilson" instead, just replace "Owa" with
# that in each PerlSetVar line.
#
# Set to 1 for debugging (only for troubleshooting or non-production testing,
# as this produces a TON of noise, and leaks some semi-sensitive info,
# into the Apache error logs) (Default: 0)
PerlSetVar OwaDebug 0
# Name of authentication cookie
PerlSetVar OwaCookieName ChocholateChipOfDoom
# Path to set on authentication cookie (Default: /)
PerlSetVar OwaPath /
# Point to the form login page/script
PerlSetVar OwaLoginScript /AppSamurai/login.pl
# Must satisfy all authentication checks (Default: All)
PerlSetVar OwaSatisfy All
examples/conf/appsamurai-owa.conf view on Meta::CPAN
# (Default: undef)
PerlSetVar OwaAuthBasicRequireRealm "__OWA_SERVER_LOGIN_REALM__"
# Continue to send the same Authorization: header to the backend server
# after login. (Only use this when the AuthBasic check is run against
# the backend server you are protecting) (Default: 1)
PerlSetVar OwaAuthBasicKeepAuth 1
# Collect cookes from AuthBasic check and send back to the user's browser
# on login (Default: 1)
PerlSetVar OwaAuthBasicPassBackCookies 1
## AppSamurai::AuthRadius options
#
# Set the IP and port to send Radius requests to
PerlSetVar OwaAuthRadiusConnect "__RADIUS_SERVER_IP__:__RADIUS_PORT__"
# Set the RADIUS key to use
PerlSetVar OwaAuthRadiusSecret "__RADIUS_PASSWORD__"
lib/Apache/AppSamurai.pm view on Meta::CPAN
Apache::Constants->import(qw(OK DECLINED REDIRECT HTTP_FORBIDDEN
HTTP_INTERNAL_SERVER_ERROR
HTTP_MOVED_TEMPORARILY HTTP_UNAUTHORIZED
M_GET));
require Apache::Request;
$MP = 1;
}
}
# Non-mod_perl includes
use CGI::Cookie;
use URI;
use Time::HiRes qw(usleep);
use Apache::AppSamurai::Util qw(CreateSessionAuthKey CheckSidFormat
HashPass HashAny ComputeSessionId
CheckUrlFormat CheckHostName
CheckHostIP XHalf);
# Apache::AppSamurai::Session is a replacement for Apache::Session::Flex
# It provides normal Apache::Session::Flex features, plus optional extras
# like alternate session key generators/sizes and record level encryption
use Apache::AppSamurai::Session;
# Apache::AppSamurai::Tracker is a special instance of Session meant to
# be shared between all processes serving an auth_name
use Apache::AppSamurai::Tracker;
### START Apache::AuthSession based methods
# The following lower case methods are directly based on Apache::AuthCookie, or
# are required AuthCookie methods (like authen_cred() and authen_ses_key())
# Note - ($$) syntax, used in mod_perl 1 to induce calling the handler as
# an object, has been eliminated in mod_perl 2. Each handler method called
# directly from Apache must be wrapped to support mod_perl 1 and mod_perl 2
# calls. (Just explaining the mess before you have to read it.)
# Identify the username for the session and set for the request
sub recognize_user_mp1 ($$) { &recognize_user_real }
sub recognize_user_mp2 : method { &recognize_user_real }
*recognize_user = ($MP eq 1) ? \&recognize_user_mp1 : \&recognize_user_mp2;
sub recognize_user_real {
my ($self, $r) = @_;
my ($auth_type, $auth_name) = ($r->auth_type, $r->auth_name);
return DECLINED unless $auth_type and $auth_name;
my $cookie_name = $self->cookie_name($r);
my ($cookie) = $r->headers_in->{'Cookie'} =~ /$cookie_name=([^;]+)/;
if (!$cookie && $r->dir_config("${auth_name}Keysource")) {
# Try to get key text using alternate method then compute the key.
# FetchKeysource returns '' if no custom source is configured, in
# which case the cookie should have been previously set, so non-zero
# output is required.
$cookie = $self->FetchKeysource($r);
if ($cookie) {
$cookie = CreateSessionAuthKey($cookie);
}
}
lib/Apache/AppSamurai.pm view on Meta::CPAN
return OK;
}
# Get the cookie name for this protected area
sub cookie_name {
my ($self, $r) = @_;
my $auth_type = $r->auth_type;
my $auth_name = $r->auth_name;
my $cookie_name = $r->dir_config("${auth_name}CookieName") ||
"${auth_type}_${auth_name}";
return $cookie_name;
}
# Set request cache options (no-cache unless specifically told to cache)
sub handle_cache {
my ($self, $r) = @_;
my $auth_name = $r->auth_name;
return unless $auth_name;
lib/Apache/AppSamurai.pm view on Meta::CPAN
# Backdate cookie to attempt to clear from web browser cookie store
sub remove_cookie {
my ($self, $r) = @_;
my $cookie_name = $self->cookie_name($r);
my $str = $self->cookie_string( request => $r,
key => $cookie_name,
value => '',
expires => 'Mon, 21-May-1971 00:00:00 GMT' );
$r->err_headers_out->add("Set-Cookie" => "$str");
$self->Log($r, ('debug', "remove_cookie(): removed_cookie \"$cookie_name\""));
}
# Convert current POST request to GET
# Note - The use of this is questionable now that Apache::Request is being
# used. May go away in the future.
sub _convert_to_get {
my ($self, $r) = @_;
return unless $r->method eq 'POST';
lib/Apache/AppSamurai.pm view on Meta::CPAN
# Get the hard set destination, or setup to just reload
if ($r->dir_config("${auth_name}LoginDestination")) {
$destination = $r->dir_config("${auth_name}LoginDestination");
} elsif ($ar->param("destination")) {
$destination = $ar->param("destination");
} else {
# Someday something slick could hold the URL, then cut through
# to it. Someday. Today we die.
$self->Log($r, ('warn', "No key 'destination' found in form data"));
$r->subprocess_env('AuthCookieReason', 'no_cookie');
return $auth_type->login_form($r);
}
# Check form nonce and signature
if (defined($ar->param("nonce")) and defined($ar->param("sig"))) {
unless (($nonce = CheckSidFormat($ar->param("nonce"))) and
($sig = CheckSidFormat($ar->param("sig")))) {
$self->Log($r, ('warn', "Missing/invalid form nonce or sig"));
$r->subprocess_env('AuthCookieReason', 'no_cookie');
$r->err_headers_out->{'Location'} = $self->URLErrorCode($destination, 'bad_credentials');
$r->status(REDIRECT);
return REDIRECT;
}
$serverkey = $self->GetServerKey($r) or die("FATAL: Could not fetch valid server key\n");
# Now check!
unless ($sig eq ComputeSessionId($nonce, $serverkey)) {
# Failed!
$self->Log($r, ('warn', "Bad signature on posted form (Possible scripted attack)"));
$r->subprocess_env('AuthCookieReason', 'no_cookie');
$r->err_headers_out->{'Location'} = $self->URLErrorCode($destination, 'bad_credentials');
$r->status(REDIRECT);
return REDIRECT;
}
} else {
# Failed!
$self->Log($r, ('warn', "Missing NONCE and/or SIG in posted form (Possible scripted attack)"));
$r->subprocess_env('AuthCookieReason', 'no_cookie');
$r->err_headers_out->{'Location'} = $self->URLErrorCode($destination, 'bad_credentials');
$r->status(REDIRECT);
return REDIRECT;
}
# Get the credentials from the data posted by the client
while ($tc = $ar->param("credential_" . scalar(@credentials))) {
push(@credentials, $tc);
($tc) ? ($tc =~ s/^(.).*$/$1/s) : ($tc = ''); # Only pull first char
lib/Apache/AppSamurai.pm view on Meta::CPAN
$self->CheckTracker($r, 'IPFailures', $r->dir_config("${auth_name}IPFailures"), $r->connection->get_remote_host);
}
}
# Append special error message code and try to redirect to the entry
# point. (Avoids having the LOGIN URL show up in the browser window)
$r->err_headers_out->{'Location'} = $self->URLErrorCode($destination, 'bad_credentials');
$r->status(REDIRECT);
return REDIRECT;
# Handle this ol' style - XXX remove?
#$r->subprocess_env('AuthCookieReason', 'bad_credentials');
#$r->uri($destination);
#return $auth_type->login_form($r);
}
# Special version of login that handles Basic Auth login instead of form
# Can be called by authenticate() if there is no valid session but a
# Authorization: Basic header is detected. Can also be called directly,
# just like login() for targeted triggering
sub loginBasic_mp1 ($$) { &loginBasic_real }
sub loginBasic_mp2 : method { &loginBasic_real }
lib/Apache/AppSamurai.pm view on Meta::CPAN
sub logout_mp1 ($$) { &logout_real }
sub logout_mp2 : method { &logout_real }
*logout = ($MP eq 1) ? \&logout_mp1 : \&logout_mp2;
sub logout_real {
my $self = shift;
my $r = shift;
my $auth_name = $r->auth_name;
my $redirect = shift || "";
my ($sid, %sess, $sessconfig, $username, $alterlist);
# Get the Cookie header. If there is a session key for this realm, strip
# off everything but the value of the cookie.
my $cookie_name = $self->cookie_name($r);
my ($key) = $r->headers_in->{'Cookie'} =~ /$cookie_name=([^;]+)/;
# Try custom keysource if no cookie is present and Keysource is configured
if (!$key && $auth_name && $r->dir_config("${auth_name}Keysource")) {
# Pull in key text
$key = $self->FetchKeysource($r);
# Non-empty, so use to generate the real session auth key
if ($key) {
$key = CreateSessionAuthKey($key);
}
}
lib/Apache/AppSamurai.pm view on Meta::CPAN
if ($@) {
$self->Log($r, ('debug', "logout(): Unable to open session \"$sid\": $@"));
} else {
$username = $sess{'username'};
# Load alterlist
$alterlist = $self->AlterlistLoad(\%sess);
# Re-apply passback cookies to which were cleared and backdated
# after session creation. (This clears the passback cookies)
if (defined($alterlist->{cookie})) {
$self->AlterlistPassBackCookie($alterlist, $r);
}
$self->DestroySession($r, \%sess);
untie(%sess);
$self->Log($r, ('notice', "LOGOUT: username=\"$username\", session=\"$sid\", reason=logout"));
}
} else {
$self->Log($r, ('error', 'logout(): Invalid Session ID Format'));
}
} else {
lib/Apache/AppSamurai.pm view on Meta::CPAN
}
# AuthType is $auth_type which we handle, Check the authentication realm
my $auth_name = $r->auth_name;
$self->Log($r, ('debug', "authenticate(): auth_name " . $auth_name));
unless ($auth_name) {
$r->log_reason("AuthName not set, AuthType=$self", $r->uri);
return HTTP_INTERNAL_SERVER_ERROR;
}
# Get the Cookie header. If there is a session key for this realm, strip
# off everything but the value of the cookie.
my $cookie_name = $self->cookie_name($r);
my ($ses_key_cookie) = ($r->headers_in->{"Cookie"} || "") =~ /$cookie_name=([^;]+)/;
$foundcookie = 0;
if ($ses_key_cookie) {
# If cookie found and not "", set $foundcookie to note auth key source
$foundcookie = 1;
} elsif ($r->dir_config("${auth_name}Keysource")) {
# Try custom keysource if no cookie is present and Keysource is configured
# Pull in key text
$ses_key_cookie = $self->FetchKeysource($r);
lib/Apache/AppSamurai.pm view on Meta::CPAN
key => $cookie_name,
value => $ses_key,
%$cookie_args );
# add P3P header if user has configured it.
my $auth_name = $r->auth_name;
if (my $p3p = $r->dir_config("${auth_name}P3P")) {
$r->err_headers_out->{'P3P'} = $p3p;
}
$r->err_headers_out->add("Set-Cookie" => $cookie);
}
# Convert cookie store to header ready string
sub cookie_string {
my $self = shift;
# if passed 3 args, we have old-style call.
if (scalar(@_) == 3) {
carp "cookie_string(): deprecated old style call to ".__PACKAGE__."::cookie_string()";
my ($r, $key, $value) = @_;
lib/Apache/AppSamurai.pm view on Meta::CPAN
return $string;
}
# Retrieve session cookie value
sub key {
my ($self, $r) = @_;
my $auth_name = $r->auth_name;
my $key = "";
my $allcook = ($r->headers_in->{"Cookie"} || "");
my $cookie_name = $self->cookie_name($r);
($key) = $allcook =~ /(?:^|\s)$cookie_name=([^;]*)/;
# Try custom keysource if no cookie is present and Keysource is configured
if (!$key && $auth_name && $r->dir_config("${auth_name}Keysource")) {
# Pull in key text
$key = $self->FetchKeysource($r);
# Non-empty, so use to generate the real session auth key
if ($key) {
$key = CreateSessionAuthKey($key);
lib/Apache/AppSamurai.pm view on Meta::CPAN
# server sent a 401 to the client. We need to kill the session to
# get things in line again.
$reason = "basic_auth_change";
}
if ($reason) {
# Oh no! They gave us a reason... It's ON! (well, off)
# Remove passback and session cookies first
if (defined($alterlist->{cookie})) {
$self->AlterlistPassBackCookie($alterlist, $r);
}
$self->remove_cookie($r);
$self->handle_cache($r);
# Wake up. Time to die.
$self->DestroySession($r, \%sess);
untie(%sess);
$self->Log($r, ('notice', "LOGOUT: username=\"$username\", session=\"$sid\", reason=$reason"));
# If serving basic auth, return undef instead of triggering login form
if ($r->auth_type =~ /^basic$/i) {
return undef;
} else {
# Use Apache::AuthCookie based custom_errors feature, which will
# call back into our custom_errors() method. (expired_cookie
# applies as an acceptable error for all of these cases.)
return('login', 'expired_cookie');
}
}
# Apply header and cookie alterations to request headed for backend server
$self->AlterlistApply($alterlist, $r);
$self->Log($r, ('debug', "authen_ses_key(): Loaded and applied alterlist groups " . join(",", keys %{$alterlist})));
lib/Apache/AppSamurai.pm view on Meta::CPAN
# Codes in all caps with an underscore are assumed to be Apache
# response codes
($message) && ($r->custom_response($code, $message));
return $code;
} else {
# What was that? Die out.
die "custom_errors(): Invalid code passed to custom_errors: \"$code\"";
}
}
## END Apache::AuthCookie based methods
# Everything past this point is not an overridden/modified Apache::AuthCookie
# function.
# Taking a request, try to get the <AuthName>AuthMethods list for the resource
sub GetAuthMethods {
my ($self, $r) = @_;
my ($authname, $authmethlist);
my @authmethods = ();
# Get the auth name
($authname = $r->auth_name()) || (die("GetAuthMethods(): No auth name set for this request!\n"));
lib/Apache/AppSamurai.pm view on Meta::CPAN
}
# Set hard expiration time if Expire is set
if ($sessconfig->{Expire}) {
$sess{'etime'} = $sess{'ctime'} + $sessconfig->{Expire};
$sess{'Expire'} = $sessconfig->{Expire};
}
# Apply passback cookies to response, and pull in updated alterlist
if (defined($alterlist->{cookie})) {
$alterlist = $self->AlterlistPassBackCookie($alterlist, $r);
}
# If present, save Authorization header to detect future changes,
# then prepend an alterlist rule to delete the header to prevent
# pass though to the backend server. (If needed, a separate
# alterlist rule to add an Authorization header should be set
# by a auth module.)
if ($r->headers_in->{"Authorization"}) {
$sess{'Authorization'} = $r->headers_in->{"Authorization"};
# Stick it in front in case we have an existing add
lib/Apache/AppSamurai.pm view on Meta::CPAN
# NAME - Header name (or regex match for delete)
# VALUE - New value of header for add or replace, else optional regex filter
# for delete (Prefix pattern with ! for negation)
#
# cookie
# ------
# @{$self->{alterlist}->{cookie}} - One or more cookie transforms, with the
# syntax:
# ACTION:NAME:VALUE
# ACTION - add, replace, delete, or passback
# NAME - Cookie name (or regex match for delete)
# VALUE - New value of cookie, or regex filter for delete action (Prefix
# pattern with ! for negation)
#
# Note - delete rules with optional value match pattern will delete only values
# of a multi-value cookie that match the value pattern
#
# The special "passback" action passes cookies back to the web browser on
# login, This allows us to gather cookies from backend servers on login, but
# have the web browser maintain them.
#
lib/Apache/AppSamurai.pm view on Meta::CPAN
(defined($alterlist)) || (return 0);
if (defined($alterlist->{header})) {
# Run through headers (saving off alter count)
$self->AlterlistApplyHeader($alterlist, $r);
$self->Log($r, ('debug', "AlterlistApply(): Applied alterlist for header"));
}
if (defined($alterlist->{cookie})) {
# Run through cookies (saving off alter count)
$self->AlterlistApplyCookie($alterlist, $r);
$self->Log($r, ('debug', "AlterlistApply(): Applied alterlist for cookie"));
}
return $alterlist;
}
# Apply alterlist rules to request headers.
sub AlterlistApplyHeader {
my ($self, $alterlist, $r) = @_;
(defined($alterlist->{header})) || (return 0);
lib/Apache/AppSamurai.pm view on Meta::CPAN
}
}
}
}
return $alterlist;
}
# Apply alterlist rules to request cookies.
# Note - Does not handle "passback" cookie. Use AlterlistPassBackCookie() to
# retrieve and clear passback cookies)
sub AlterlistApplyCookie {
my ($self, $alterlist, $r) = @_;
(defined($alterlist->{cookie})) || (return 0);
my ($t, %c, $cl, $act, $key, $val, $tk, $tv, @ta, @td);
my $alterred = 0;
# Grab any cookies any put into a hash of CGI::Cookies, or just make an
# empty cookie hash for now.
%c = CGI::Cookie->fetch($r);
(%c) || (%c = ());
# Build \n deliminated lookup string to fast match against
$cl = "\n" . join("\n", keys(%c)) . "\n";
foreach $t (@{$alterlist->{cookie}}) {
# Note - : or = allowed between NAME and VALUE to make life easier
($t =~ /^(add|replace|rep|delete|del|passback|set):([\w\d\-]+)(?:\:|\=)(.*?)$/i) || (($self->Log($r, ('debug', "AlterlistApplyCookie(): Skipping illegal cookie transform \"$t\""))) && (next));
$act = $1;
$key = $2;
$val = $3;
if ($act =~ /^passback|set$/) {
# passback not handled in this method
next;
} elsif ($act =~ /^add$/) {
# Blindly add the cookie
@ta = split('&', $val);
# Add a new CGI::Cookie to the hash
$c{$key} = new CGI::Cookie(-name => $key, -value => \@ta);
# Log obscured value
$self->Log($r, ('debug', "AlterlistApplyCookie(): COOKIE ADD: $key=" . XHalf($val)));
$alterred++;
} else {
# Replace and delete allow for regex cookie name matches
while ($cl =~ /($key)/igm) {
# Update
$tk = $1;
if ($act =~ /^replace|rep$/) {
# Blindly delete then add the cookie back with new value
# Save old value for log
$tv = join('&', $c{$tk}->value);;
delete($c{$tk});
@ta = split('&', $val);
$c{$tk} = new CGI::Cookie(-name => $tk, -value => \@ta);
# Log obscured values
$self->Log($r, ('debug', "AlterlistApplyCookie(): COOKIE REPLACE: $tk: " . XHalf($tv) . " -> " . XHalf($val)));
$alterred++;
} elsif ($act =~ /^delete|del$/) {
# Check for extra content match
if ($val) {
@ta = ();
@td = ();
# Cycle through multi-values
foreach $tv ($c{$tk}->value) {
# Handle negation
if ($val =~ s/^\!//) {
lib/Apache/AppSamurai.pm view on Meta::CPAN
push(@td, $tv);
$alterred++;
}
# Kill!
if (scalar @ta) {
# Some values left not deleted, so set those back
$c{$tk}->value(\@ta);
$tv = join('&', @td);
# Log obscured value
$self->Log($r, ('debug', "AlterlistApplyCookie(): COOKIE DELETE PARTIAL: $tk=" . XHalf($tv)));
} else {
# Nothing left inside. KILL!
delete($c{$tk});
$tv = join('&', @td);
# Obscure values for logging
$tv =~ s/([^X])[\w\d]/${1}X/gs;
$self->Log($r, ('debug', "AlterlistApplyCookie(): COOKIE DELETE FULL: $tk=$tv"));
}
} else {
# Kill Em All
$tv = $c{$key}->value;
delete($c{$key});
# Obscure values for logging
$tv =~ s/([^X])[\w\d]/${1}X/gs;
$self->Log($r, ('debug', "AlterlistApplyCookie(): COOKIE DELETE FULL: $key=$tv"));
$alterred++;
}
}
}
}
}
# Unset, then add cookies to header if changes were made
if ($alterred) {
$r->headers_in->unset('Cookie');
$t = '';
foreach $tk (keys %c) {
# Cookie to list in string form.
$t .= $c{$tk}->name . "=" . join('&', $c{$tk}->value) . "; ";
}
# Kill trailing '; '
$t =~ s/\; $//s;
# Ship it
$r->headers_in->add('Cookie' => $t);
}
return $alterlist;
}
# Add a Set-cookie: header to r for all alterlist "passback" cookies and return
# a modified alterlist with the passback cookie values cleared and expired.
#
# Unlike normal alterlist rules, passback cookies are sent BACK to the client.
# The only time this can occur is upon login/redirect. The purpose of passback
# cookies is to set the same cookies in the browser as they would have set
# if they were connecting directly to the backend server(s).
#
# The return should be used to update the alterlist. When
# AlterlistPassBackCookie is applied again, it will UNSET the passback cookies.
# This should be done on logout.
sub AlterlistPassBackCookie() {
my ($self, $alterlist, $r) = @_;
(defined($alterlist->{cookie})) || (return 0);
my ($t, $key, $val, $opt, $tdomain, $tpath, $texpire);
my @ct = ();
my %c = ();
foreach $t (@{$alterlist->{cookie}}) {
# Note - : or = allowed between NAME and VALUE to make life easier
($t =~ /^(passback|set):([\w\d\-]+)(?:\:|\=)([^;]*)(;.*)?$/i) || ((push(@ct, $t)) && (next));
$key = $2;
$val = $3;
$opt = $4;
$tdomain = $tpath = $texpire = '';
# Unlike AlterlistApplyCookie which just needs to parse name and
# value, the PassBack cookies are Set-Cookie items which may
# have options. Also, only process the last cookie value if
# a multi-value cookie is passed
# Add a new CGI::Cookie to the hash
$c{$key} = new CGI::Cookie(-name => $key,
-value => $val,
);
# Set further options (only Expires and Path currently passed through)
foreach $t (split(';', $opt)) {
if ($t =~ /^\s*expires=([\w\d \:\;\-,]+)\s*$/) {
$c{$key}->expires($1);
} elsif ($t =~ /^\s*path=(\/.*?)\s*$/) {
$c{$key}->path($1);
}
}
lib/Apache/AppSamurai.pm view on Meta::CPAN
# cookie. I don't see a need.)
my $auth_name = $r->auth_name;
if ($r->dir_config("${auth_name}Domain")) {
$c{$key}->domain($r->dir_config("${auth_name}Domain"));
}
if (!$r->dir_config("${auth_name}Secure") || ($r->dir_config("${auth_name}Secure") == 1)) {
$c{$key}->secure(1);
}
$r->err_headers_out->add('Set-Cookie' => $c{$key});
# Clean up and log
$t = $c{$key};
$t =~ /($key\s*\=\s*)(.*?)(;|$)/;
$self->Log($r, ('debug', "AlterlistPassBackCookie(): COOKIE PASSBACK: " . $1 . XHalf($2) . $3));
# Save an empty/expired cookie so next call to AlterlistPassBackCookie
# with this alterlist will unset the cookie
$c{$key}->value('');
$c{$key}->expires('Thu, 1-Jan-1970 00:00:00 GMT');
push(@ct, "passback:" . $c{$key});
}
# Save updated cookie array
@{$alterlist->{cookie}} = @ct;
return $alterlist;
}
# Append an error code to the list of query args in a given URL. (Used to
# pass friendly error messages to users in external redirects. (Note that
# AuthCookie used subprocess_env() to pass that info, but since that will only
# work in the same main request, it won't pass into an external redirect.)
sub URLErrorCode {
my $self = shift;
my $uri = (shift) || (return undef);
my $ecode = (shift) || ('');
($uri = new URI($uri)) || (return undef);
# Error codes must contain only letters, numbers, and/or _ chars.
# Your login.pl script should read them in CAREFULLY and make sure
lib/Apache/AppSamurai.pm view on Meta::CPAN
to local or proxied applications with limited authentication options.
Unauthenticated users are presented with either a login form, or a basic
authentication popup (depending on configuration.) User supplied credentials
are checked against one or more authentication systems before the user's
session is created and a session authentication cookie is passed back to the
browser. Only authenticated and authorized requests are proxied through
to the backend server.
Apache::AppSamurai is based on, and includes some code from,
L<Apache::AuthCookie|Apache::AuthCookie>.
Upon that core is added a full authentication and session handling framework.
(No coding required.) Features include:
=over 4
=item *
B<Modular authentication> - Uses authentication sub-modules for the easy
addition custom authentication methods
lib/Apache/AppSamurai.pm view on Meta::CPAN
=head2 GENERAL CONFIGURATION
=head3 I<Debug> C<0|1>
(Default: 0)
Set to 1 to send debugging output to the Apache logs. (Note - you must have
a log configured to catch errors, including debug level errors, to see the
output.)
=head3 I<CookieName> C<NAME>
(Default:AUTHTYPE_AUTHNAME)
The name of the session cookie to send to the browser.
=head3 I<LoginScript> C<PATH>
(Default: undef)
The URL path (location) of the proxy's login page for form based login.
(Sample script provided with the Apache::AppSamurai distribution.)
lib/Apache/AppSamurai.pm view on Meta::CPAN
PerlTaintCheck On
PerlModule Apache::Registry
#*FOR MODPERL2 USE:
# PerlSwitches -wT
# PerlModule ModPerl::Registry
# Load the main module and define configuration options for the
# "Example" auth_name
PerlModule Apache::AppSamurai
PerlSetVar ExampleDebug 0
PerlSetVar ExampleCookieName MmmmCookies
PerlSetVar ExamplePath /
PerlSetVar ExampleLoginScript /login.pl
# Defaults to All by may also be Any
#PerlSetVar ExampleSatisty All
# Optional session cookie domain (Avoid unless absolutely needed.)
#PerlSetVar ExampleDomain ".thing.er"
# Require secure sessions (default: 1)
lib/Apache/AppSamurai.pm view on Meta::CPAN
## Apache::AppSamurai::AuthBasic options.##
# (Note - See L<Apache::AppSamurai::AuthBasic> for more info)
# Set the URL to send Basic auth checks to
PerlSetVar ExampleAuthBasicLoginUrl "https://ex.amp.le/thing/login"
# Always send Basic authentication header to backend server
PerlSetVar ExampleAuthBasicKeepAuth 1
# Capture cookies from AuthBasic login and set in client browser
PerlSetVar ExampleAuthBasicPassBackCookies 1
# Abort the check unless the "realm" returned by the server matches
PerlSetVar ExampleAuthBasicRequireRealm "blah.bleh.blech"
# Pass the named header directly through to the AuthBasic server
PerlSetVar ExampleAuthBasicUserAgent "header:User-Agent"
## Session storage options ##
# (Note - See L<Apache::AppSamurai::Session> and L<Apache::Session> for
lib/Apache/AppSamurai.pm view on Meta::CPAN
Additional authentication modules, tracking features, and other options
can be added to Apache::AppSamurai. In the case of authentication modules,
all that is required is creating a new module that inherits from
L<Apache::AppSamurai::AuthBase|Apache::AppSamurai::AuthBase>.
Other features may be more difficult to add. (Apache::AppSamurai could
use some refactoring.)
Interface and utility methods are not documented at this time. Please
consult the code, and also the L<Apache::AuthCookie|Apache::AuthCookie>
documentation.
=head1 FILES
=over 4
=item F<APPSAMURAI_CONTENT/>
Directory that holds Apache::AppSamurai login/logout pages and related
content. This must be served by Apache and reachable. (This is
lib/Apache/AppSamurai.pm view on Meta::CPAN
L<http://www.voltagenoir.org/AppSamurai/>
=item * AnnoCPAN: Annotated CPAN documentation
L<http://annocpan.org/dist/Apache-AppSamurai>
=back
=head1 ACKNOWLEDGEMENTS
AppSamurai.pm (the main Apache::AppSamurai module), contains some code
from Apache::AuthCookie, which was developed by Ken Williams and others.
The included Apache::AuthCookie code is under the same licenses as Perl
and under the following copyright:
Copyright (c) 2000 Ken Williams. All rights reserved.
=head1 COPYRIGHT & LICENSE
Copyright 2008 Paul M. Hirsch, all rights reserved.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
lib/Apache/AppSamurai/AuthBasic.pm view on Meta::CPAN
my $conft = $self->{conf};
# Initial configuration. Put defaults here before the @_ args are
# pulled in.
$self->{conf} = { %{$conft},
LoginUrl => 'https://127.0.0.1', # URL to authenticate
# aginst
KeepAuth => 0, # Keep Authorization: Basic XXX header
# and continue to send to the proxied
# servers. BE CAREFUL!
PassBackCookies => 0, # Pass all Set-Cookies back to
# client browser
AllowRedirect => 0, # Follow redirects (Keep off and get
# the URL right!)
UserAgent => '', # The User-Agent: header to report
RequireRealm => '', # If set, this realm must match that
# returned by the backend server
SuccessCode => 200, # Auth considered a failure unless
# this code is returned after login
Timeout => 10, # Timeout for connecting to auth server
PassMin => 3,
lib/Apache/AppSamurai/AuthBasic.pm view on Meta::CPAN
}
# Set credentials in request
$self->{request}->authorization_basic($user, $pass);
# Connect with beans and check return
$response = $self->{client}->request($self->{request});
if ($response->code() == $self->{conf}{SuccessCode}) {
# YAY! Now collect and store cookies and headers as directed
if (($self->{conf}{PassBackCookies}) && (@tmp = $response->header('set-cookie'))) {
foreach (@tmp) {
# Trim whitespace
s/^\s*(.*?)\s*$/$1/;
# Add to cookie alterlist for instance (will be sent back to web browser)
push(@{$self->{alterlist}->{cookie}}, "passback:$1");
}
}
if ($self->{conf}{KeepAuth}) {
push(@{$self->{alterlist}->{header}}, "add:Authorization:Basic " . encode_base64($user . ":" . $pass, ''));
lib/Apache/AppSamurai/AuthBasic.pm view on Meta::CPAN
# this string. (Optional)
PerlSetVar fredAuthBasicRequireRealm "Fred World Login"
# Continue to send the same Authorization: header to the backend server
# after login. (Only use this when the AuthBasic check is run against
# the backend server you are protecting)
PerlSetVar fredAuthBasicKeepAuth 1
# Collect cookies from AuthBasic check and send back to the user's browser
# on login (This is the default behaviour)
PerlSetVar fredAuthBasicPassBackCookies 1
=head1 DESCRIPTION
This L<Apache::AppSamurai|Apache::AppSamurai> authentication module checks a
username and password against a backend webserver, (referred to as the "auth
server" below), using HTTP basic authentication (as defined in
L<RFC 2617|http://www.faqs.org/rfcs/rfc2617.html>). In general, the
auth server is the same as the server Apache::AppSamurai is protecting,
though it does not have to be.
lib/Apache/AppSamurai/AuthBasic.pm view on Meta::CPAN
enable this feature unless you are certain all the servers and applications
being protected by this Apache::AppSapurai instance should be receiving users'
usernames and passwords!>
B<Session Storage Warning:>
By default Apache::AppSamurai uses AES (Rijndael) to encrypt session data before storing it to disk, greatly reducing the risk of keeping
the basic auth header, If you use this feature, please leave the
L<Apache::AppSamurai::Session::Serialize::CryptBase64|Apache::AppSamurai::Session::Serialize::CryptBase64> module configured as the session serialization
module.
=head2 I<PassBackCookies> C<0|1>
(Default: 0)
If 1, collects set cookies from the auth server and, upon successful login,
set them in the client web browser.
Even when using basic auth, many apps set cookies for various reasons.
This feature is most useful then the auth server and the protected
backend webserver are the same. It may also be useful in the case of
using a ticket issuer of some sort as the auth server.
lib/Apache/AppSamurai/AuthBasic.pm view on Meta::CPAN
A second request is sent, this time with the username and password (credential)
included.
=item *
The return code is checked against C<SuccessCode>
=item *
If C<PassBackCookies> is 1, the cookies set by the auth server are saved
in the alterlist cookie hash with "passback" rules.
=item *
If C<KeepAuth> is 1, the authorization header (containing the username and
password) are saved in the alterlist header hash with an "add" rule.
=item *
If all checks have succeeded, 1 is returned.
lib/Apache/AppSamurai/Util.pm view on Meta::CPAN
@EXPORT_OK = qw(expires CreateSessionAuthKey CheckSidFormat
HashPass HashAny ComputeSessionId CheckUrlFormat CheckHostName
CheckHostIP XHalf);
# $IDLEN defines the byte length for all IDs (Session IDs, Keys, etc).
# This should be the byte length of the main digest function used.
# (Provided in case something other than SHA256 is used.)
$IDLEN = 32;
# -- expires() shamelessly taken from CGI::Util
## -- And this expires shamelessly taken from Apache::AuthCookie::Util ;)
sub expires {
my($time,$format) = @_;
$format ||= 'http';
my(@MON) = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
my(@WDAY) = qw/Sun Mon Tue Wed Thu Fri Sat/;
# pass through preformatted dates for the sake of expire_calc()
$time = _expire_calc($time);
return $time unless $time =~ /^\d+$/;
t/conf/extra.conf.in view on Meta::CPAN
PerlSwitches -I@ServerRoot@/lib
</IfDefine>
PerlRequire @ServerRoot@/startup.pl
PerlModule Apache::AppSamurai
PerlSetVar WhatEverPath /
PerlSetVar WhatEverLoginScript /docs/login.pl
PerlSetVar WhatEverDebug 3
PerlSetVar WhatEverCookieName CakeNotCookie
PerlSetVar WhatEverSecure 1
# Map Basic auth password into 3 credentials, separated by
# semicolons and reverse mapped for fun!
PerlSetVar WhatEverBasicAuthMap "3,2,1=(.+);([^;]+);([^;]+)"
# Please, don't use these auth modules... they kinda suck.
# (Note that the names hint to the static password for each.
# You know that ain't cool.)
PerlSetVar WhatEverAuthMethods "AuthTestFLUFFY,AuthTestPASSWORD,AuthTest123456"