#!/usr/bin/env perl
#
# Version: $Id$
#
use strict;
use warnings;
use CGI;
use CGI::Carp qw( fatalsToBrowser);
use URI;
#
# constants
#
my @ATTRIBUTES_REQUIRED = qw(
eduPersonPrincipalName:eppn
eduPersonTargetedID:persistent_id
);
my @ATTRIBUTES_OPTIONAL = qw(
eduPersonScopedAffiliation:affiliation:eduPersonAffiliation
cn
displayName
);
# allow override from environment ...
if (exists $ENV{'SHIBTEST_ATTRIBUTES_REQUIRED'}) {
@ATTRIBUTES_REQUIRED = split('\s+', $ENV{'SHIBTEST_ATTRIBUTES_REQUIRED'});
}
if (exists $ENV{'SHIBTEST_ATTRIBUTES_OPTIONAL'}) {
@ATTRIBUTES_OPTIONAL = split('\s+', $ENV{'SHIBTEST_ATTRIBUTES_OPTIONAL'});
}
#
# code below ... nothing to change there ...
#
sub xml_escape {
my $s = shift;
$s =~ s!&!&!gs;
$s =~ s!!>!gs;
return $s;
}
sub render_table_rows {
my $caption = shift;
my $keys = shift;
my $i = 0;
print '
';
if (scalar(@{$keys}) > 0) {
foreach my $key (@{$keys}) {
my $value = $ENV{$key};
$value =~ s!\n*!!gs;
$value =~ s!\s*(;|\$)\s*!\n!gs;
$value = xml_escape($value);
$value =~ s!\n!
!gs;
print '';
print '', $key, ' | ', '', $value, ' | ', '
';
}
}
else {
print '',
'[NONE] |
';
}
}
sub dump_shibboleth_attributes {
my $debug_env = shift;
my @keys = sort(keys(%ENV));
my @attrs = grep(!m/^(HTTPS|SERVER_|SCRIPT_|PATH|QUERY_STRING|GATEWAY|DOCUMENT_ROOT|REMOTE|REQUEST|HTTP_|AUTH_TYPE|Shib_)/i, @keys);
my @shib = grep(m/Shib_/i, @keys);
render_table_rows('Shibboleth Attributes:', \@attrs);
render_table_rows('Shibboleth Enviroment Variables:', \@shib);
if (defined ($debug_env)) {
render_table_rows('All Environment Variables:', \@keys);
}
}
sub dump_shibboleth_assertions {
my $count = shift;
return unless defined($count) && $count > 0;
# try to load LWP and XML::Twig and bail if not available ...
eval {
require LWP;
require XML::Twig;
};
if ($@) {
return;
}
print '';
my $j = 0;
my $browser = LWP::UserAgent->new;
ASSERTION:
for (my $i = 1; $i <= $count; $i++) {
my $url = $ENV{sprintf('Shib_Assertion_%02d', $i)};
next ASSERTION unless defined ($url);
print '';
print 'Assertion ', $i, ' | ';
my $response = $browser->get($url);
if ($response->is_success) {
my $twig = XML::Twig->new(pretty_print => 'indented',
output_encoding => 'utf-8',
no_prolog => '1',
keep_original_prefix => '1');
$twig->parse($response->content);
my $s = $twig->sprint();
$s = xml_escape($s);
$s =~ s! ! !gs;
$s =~ s!\n!
!gs;
print '', $s, ' | ';
}
else {
print '', 'Cannot retieve assertion: ',
xml_escape($response->status_line), '', ' | ';
}
print '
';
}
}
sub make_self_uri {
my $scheme = exists $ENV{'HTTPS'} ? 'https' : 'http';
my $uri = URI->new($scheme . '://' . $ENV{'SERVER_NAME'});
$uri->path($ENV{'REQUEST_URI'});
return $uri->as_string();
}
sub make_shibboleth_uri {
my $path = shift;
# XXX: always assume https for Shibboleth URIs ...
my $uri = URI->new('https://' . $ENV{'SERVER_NAME'});
$uri->path($path);
return $uri;
}
sub make_login_uri {
my $uri = $ENV{'SHIBTEST_LOGIN_URI'};
if (defined($uri)) {
$uri = URI->new($uri);
}
else {
$uri = make_shibboleth_uri('/Shibboleth.sso/Login');
}
$uri->query_form({
target => make_self_uri(),
});
return $uri->as_string();
}
sub make_logout_uri {
my $uri = $ENV{'SHIBTEST_LOGOUT_URI'};
if (defined($uri)) {
$uri = URI->new($uri);
}
else {
$uri = make_shibboleth_uri('/Shibboleth.sso/Logout');
}
$uri->query_form({
return => make_self_uri(),
});
return $uri->as_string();
}
sub scan_attributes {
my $scan_ref = shift;
my $optional = shift;
my $missing = 0;
foreach my $aliases (@{$scan_ref}) {
my $found = undef;
my @attrs = split(':', $aliases);
KEY:
foreach my $b (keys(%ENV)) {
foreach my $a (@attrs) {
if (lc($a) eq lc($b)) {
$found = $b;
last KEY;
}
}
}
if (defined($found)) {
print '',
($optional ? 'Optional'
: 'Required'),
' attribute ', $attrs[0], '
is available',
($found ne $attrs[0] ? " (exported as $found
)"
: ''),
'.
';
}
else {
print '', ($optional ? 'Optional'
: '
Required'),
' attribute ', $attrs[0],
'
is not available.
';
$missing++;
}
}
return $missing;
}
sub main {
my $q = shift;
if (defined($ENV{'Shib_Session_ID'})) {
# logout link
my $idp = $ENV{'Shib_Identity_Provider'};
if (!defined($idp)) {
$idp = '[UNKNOWN]';
}
print '';
print 'A Shibboleth session was established with ', $idp,
'.';
if (defined($ENV{'SHIBTEST_LAZY'})) {
print ' [Logout]
';
print 'NB: if this webserver is configured to always requires ',
'authentication for this page, you will be immediately ',
'redirected to the WAYF/Discovery service after logging out!';
}
print '
';
my $errors = 0;
my $warnings = 0;
# CLARIN required attributes
if (scalar(@ATTRIBUTES_REQUIRED) > 0) {
$errors += scan_attributes(\@ATTRIBUTES_REQUIRED, 0);
}
# CLARIN optional attributes
if (scalar(@ATTRIBUTES_OPTIONAL) > 0) {
$warnings += scan_attributes(\@ATTRIBUTES_OPTIONAL, 1);
}
# remote user
my $user = $ENV{'REMOTE_USER'};
$warnings++ unless defined($user);
print '';
print 'REMOTE_USER: ',
(defined($user) ? $user : 'N/A (not exported by mod_shib?!)');
print '
';
if ($errors == 0) {
print 'Interoperability between your SP ',
'and the IDP is ',
($warnings > 0 ? 'sufficent' : 'optimal'), '. ',
$errors, ' error(s), ',
$warnings, ' warning(s)
';
}
else {
print 'Interoperability between your SP ',
'and the IDP is problematic! ', $errors, ' error(s), ',
$warnings, ' warning(s)
',
'Please check SP config and IDP release policy.
';
}
# attribute / environment variable / assertion
print '';
my $debug_env = (defined($q) && $q->param('debug_env'));
dump_shibboleth_attributes($debug_env);
dump_shibboleth_assertions($ENV{'Shib_Assertion_Count'});
print '
';
}
else {
# login link
print 'No Shibboleth session exists, please Login.
';
}
}
my $style = <