1# Utility functions for mod_webauth tests. 2# 3# Written by Roland Schemers 4# Rewritten as a module by Jon Robertson <jonrober@stanford.edu> 5# Copyright 2003, 2013, 2014 6# The Board of Trustees of the Leland Stanford Junior University 7# 8# Permission is hereby granted, free of charge, to any person obtaining a copy 9# of this software and associated documentation files (the "Software"), to 10# deal in the Software without restriction, including without limitation the 11# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12# sell copies of the Software, and to permit persons to whom the Software is 13# furnished to do so, subject to the following conditions: 14# 15# The above copyright notice and this permission notice shall be included in 16# all copies or substantial portions of the Software. 17# 18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24# IN THE SOFTWARE. 25 26package WebAuth::Tests; 27 28use 5.006; 29use strict; 30use warnings; 31 32use Carp; 33use CGI qw(:standard); 34use CGI::Cookie; 35use Data::Dumper; 36use Template; 37 38use Exporter qw(import); 39our @EXPORT_OK = qw(build_page run_test app_lifetime_test last_used_test); 40 41our $VERSION; 42 43# This version matches the version of WebAuth with which this module was 44# released, but with two digits for the minor and patch versions. 45BEGIN { 46 $VERSION = '4.0700'; 47} 48 49############################################################################# 50# Internal tests 51############################################################################# 52 53# Creates an array of hash records for all of our regular tests, suited to be 54# passed into our template. 55sub _standard_tests { 56 my $webauth_token_creation = $ENV{WEBAUTH_TOKEN_CREATION}; 57 my $webauth_token_expiration = $ENV{WEBAUTH_TOKEN_EXPIRATION}; 58 my $webauth_user = $ENV{WEBAUTH_USER}; 59 my $remote_user = $ENV{REMOTE_USER}; 60 my $auth_type = $ENV{AUTH_TYPE}; 61 62 my @standard_tests; 63 64 my $record = run_test('AUTH_TYPE', 65 $auth_type eq 'WebAuth', 66 'WebAuth', 67 "not WebAuth, it's $auth_type", 68 1); 69 push (@standard_tests, $record); 70 71 $record = run_test('REMOTE_USER', 72 $remote_user ne '', 73 $remote_user, 74 'not set!', 75 1); 76 push (@standard_tests, $record); 77 78 $record = run_test('WEBAUTH_USER', 79 $webauth_user ne '', 80 $webauth_user, 81 'not set!', 82 1); 83 push (@standard_tests, $record); 84 85 $record = run_test('WEBAUTH_USER == REMOTE_USER', 86 $webauth_user eq $remote_user, 87 'they are equal', 88 'they are not equal!', 89 0); 90 push (@standard_tests, $record); 91 92 $record = run_test('WEBAUTH_TOKEN_CREATION', 93 $webauth_token_creation ne '', 94 scalar(localtime($webauth_token_creation)), 95 'not set!', 96 1); 97 push (@standard_tests, $record); 98 99 $record = run_test('WEBAUTH_TOKEN_EXPIRATION', 100 $webauth_token_expiration ne '', 101 scalar(localtime($webauth_token_expiration)), 102 'not set!', 103 1); 104 push (@standard_tests, $record); 105 106 return \@standard_tests; 107} 108 109# Creates an array of hash records for all of our cookies, suited to be 110# passed into our template. 111sub _cookies { 112 my @cookies; 113 my %cookies = CGI::Cookie->fetch; 114 foreach my $var (sort(keys(%cookies))) { 115 next unless $var =~ /^webauth_/ && $var !~ /^webauth_wpt_/; 116 my ($name, $val) = split('=', $cookies{$var}); 117 118 my %record = (name => $var, 119 value => _truncate_str($val, 40), 120 ); 121 push (@cookies, \%record); 122 } 123 124 return \@cookies; 125} 126 127# Creates an array of hash records for directly relevant environment 128# variables, suited to be passed into our template. 129sub _environment_important { 130 my @environment; 131 foreach my $var (sort(keys(%ENV))) { 132 next unless $var eq 'REMOTE_USER' || $var eq 'AUTH_TYPE' 133 || $var =~ m{^WEBAUTH_}; 134 my %record = (name => $var, 135 value => _truncate_str($ENV{$var}, 80), 136 ); 137 push (@environment, \%record); 138 } 139 140 return \@environment; 141} 142 143# Creates an array of hash records for less important environment variables, 144# suited to be passed into our template. 145sub _environment_misc { 146 my @environment; 147 foreach my $var (sort(keys(%ENV))) { 148 next if $var eq 'REMOTE_USER'; 149 next if $var eq 'AUTH_TYPE'; 150 next if $var =~ m{^WEBAUTH_}; 151 my %record = (name => $var, 152 value => _truncate_str($ENV{$var}, 80), 153 ); 154 push (@environment, \%record); 155 } 156 157 return \@environment; 158} 159 160# Creates an array of hash records for the multifactor configuration and 161# levels. 162sub _multifactor_tests { 163 my $webauth_factors_initial = $ENV{WEBAUTH_FACTORS_INITIAL}; 164 my $webauth_factors_session = $ENV{WEBAUTH_FACTORS_SESSION}; 165 my $webauth_loa = $ENV{WEBAUTH_LOA}; 166 167 my @tests; 168 my $record = run_test('WEBAUTH_FACTORS_INITIAL', 169 $webauth_factors_initial ne '', 170 $webauth_factors_initial, 171 'not set!', 172 1); 173 push (@tests, $record); 174 175 $record = run_test('WEBAUTH_FACTORS_SESSION', 176 $webauth_factors_session ne '', 177 $webauth_factors_session, 178 'not set!', 179 1); 180 push (@tests, $record); 181 182 $record = run_test('WEBAUTH_LOA', 183 $webauth_loa ne '', 184 $webauth_loa, 185 'not set!', 186 1); 187 push (@tests, $record); 188 189 return \@tests; 190} 191 192############################################################################# 193# Internal misc functions 194############################################################################# 195 196# Given a string and a maximum length for it, truncate it and append a message 197# after if the string exceeds the maximum length. Return the string. 198sub _truncate_str { 199 my ($str, $max_length) = @_; 200 if (length($str) > $max_length) { 201 $str = substr($str, 0, $max_length) . '...(truncated)'; 202 } 203 return $str; 204} 205 206############################################################################# 207# External tests 208############################################################################# 209 210# Given a test name, a result test to perform, and a good and bad comments to 211# use on pass/fail, return a hash for the test's status. 212sub run_test { 213 my ($test_name, $result, $good, $bad, $bold) = @_; 214 215 my %record; 216 $record{name} = $test_name; 217 $record{result} = $result; 218 $record{comment} = $result ? $good : $bad; 219 $record{comment_bold} = $bold; 220 return \%record; 221} 222 223# Run all tests for App token lifetime and return as an array for reporting. 224sub app_lifetime_test { 225 my $webauth_token_expiration = $ENV{WEBAUTH_TOKEN_EXPIRATION}; 226 my @tests; 227 my $record = run_test('WEBAUTH_TOKEN_EXPIRATION', 228 $webauth_token_expiration < time()+10, 229 'expires in less then 10 seconds', 230 'does not expire in less then 10 seconds', 231 0); 232 push (@tests, $record); 233 return \@tests; 234} 235 236# Test to make sure that the last used time is outside of a narrow window for 237# 'now'. 238sub last_used_test { 239 my $webauth_token_lastused = $ENV{WEBAUTH_TOKEN_LASTUSED}; 240 my $time = time(); 241 my $low = $time - 10; 242 my $high = $time + 10; 243 244 my @tests; 245 my $record = run_test('WEBAUTH_TOKEN_LASTUSED', 246 ($webauth_token_lastused > $low && 247 $webauth_token_lastused < $high), 248 scalar(localtime($webauth_token_lastused)), 249 'not within the acceptable time window!', 250 1); 251 push (@tests, $record); 252 return \@tests; 253} 254 255############################################################################# 256# External misc functions 257############################################################################# 258 259# Takes a number of args for page title and any extra tests or unusual flags 260# to use. Then grabs several standard tests and tosses them in to create a 261# test page result. 262sub build_page { 263 my ($args) = @_; 264 265 # Set all values to be passed to the template. 266 my %values = (test_num => $args->{test_number}, 267 test_desc => $args->{test_desc}, 268 extended_description => $args->{extended_desc}, 269 standard_tests => _standard_tests(), 270 cookies => _cookies(), 271 environment_important => _environment_important(), 272 environment_misc => _environment_misc(), 273 extra_tests_title => $args->{extra_title}, 274 extra_tests => $args->{extra_tests}, 275 logout => $args->{logout} || '/tests/logout', 276 remote_user => $ENV{REMOTE_USER}, 277 unauth_location => $args->{unauth_loc} || 0, 278 ); 279 280 # Multifactor tests have those as their test type. 281 if ($args->{multifactor}) { 282 $values{extra_tests_title} = 'Performing Multifactor tests'; 283 $values{extra_tests} = _multifactor_tests(); 284 } 285 286 # Build and return the page from a template. 287 my $tt = Template->new (RELATIVE => 1) or carp ("$Template::ERROR\n"); 288 my $output = ''; 289 my $fname = $args->{template} || '../test.tt2'; 290 $tt->process ($fname, \%values, \$output) 291 or carp ($tt->error."\n"); 292 return $output; 293} 294 2951; 296 297__END__ 298 299############################################################################## 300# Documentation 301############################################################################## 302 303=for stopwords 304WebAuth WebLogin multifactor 305 306=head1 NAME 307 308WebAuth::Tests - Assists with constructing WebAuth Apache module tests 309 310=head1 SYNOPSIS 311 312 use WebAuth::Tests qw(build_page); 313 314 my %settings = ( 315 test_number => 1, 316 test_desc => 'basic WebAuth test', 317 extended_desc => \@extended, 318 ); 319 320 print "Content-type: text/html\n\n"; 321 print build_page(\%settings); 322 323=head1 DESCRIPTION 324 325This module provides shared code for the test suite for the mod_webauth 326Apache module. It is used by the individual test programs and test Apache 327configuration to construct a variety of scenarios to exercise most of the 328functionality of mod_webauth and some of the WebLogin and mod_webkdc 329features. Most of the test setup is in the Apache configuration, but each 330test corresponds to a Perl script, which uses this module, that checks 331environment variables to see that the tests did what they should and 332provides useful debugging information if they did not. 333 334This module is primarily intended for use with the tests that are included 335with WebAuth and currently includes some defaults that make it difficult 336to use for other purposes. The goal is to eventually make it more general 337so that it can be used for building additional tests local to a particular 338site. 339 340=head1 FUNCTIONS 341 342None of the following functions are exported by default. They must be 343explicitly requested when using the WebAuth::Tests module. 344 345=over 4 346 347=item build_page(SETTINGS) 348 349Performs the work of building a test page that shows the test number, 350title, information about the test run, and various tables showing current 351status and tests run. SETTINGS should be a reference to a hash, which may 352contain one or more of the following settings: 353 354=over 4 355 356=item test_number 357 358The number of this test, passed to the template. 359 360=item test_desc 361 362The short description of this test, passed to the template. 363 364=item extended_description 365 366The extended description of this test. This should be a reference to an 367array that contains one or more paragraphs of text as strings. Each 368element of the array will be wrapped in <p> tags. 369 370=item extra_tests 371 372An anonymous array of hash references, each of which represents a test 373result. The hash should have three keys: C<name>, C<result>, and 374C<comment>. C<name> should be the name of this test, C<result> should 375be either C<PASS> or C<FAIL>, and C<comment> should provide additional 376information about the test. 377 378=item extra_tests_title 379 380If there are extra tests, this will be used as the heading for that test 381output. If this setting is present, extra_tests should also be present. 382 383=item multifactor 384 385Set this to true to also perform multifactor tests. 386 387=item template 388 389The path (possibly relative) to the template used for generating HTML. 390By default, this is set to F<../test.tt2>, which works for the default 391mod_webauth test suite (and probably not for anything else). 392 393=item unauth_loc 394 395Set this to true if this test will be running without authentication. 396This is used by the template to change some of the boilerplate text. 397 398=back 399 400=item run_test(NAME, RESULT, GOOD, BAD, BOLD) 401 402Performs a test of WebAuth information, returning an anonymous hash 403showing the results of the test in the format required by the 404C<extra_tests> setting to build_page(). It takes the name of the test, a 405boolean value that represents the result of the test, text to display on 406true or false results, and a flag indicating whether to bold true results. 407 408=item app_lifetime_test 409 410Creates an anonymous array of hash references that contains the results of 411a test for the lifetime of an application token. The result is suitable 412for inclusion in C<extra_tests>. 413 414=item last_used_test() 415 416Creates an anonymous array of hash references that contains the results of 417a test for current last-used time for a token. The result is suitable for 418inclusion in C<extra_tests>. 419 420=back 421 422=head1 BUGS 423 424The interactions between this module and its template aren't currently 425completely documented. 426 427No one has yet used this module for anything other than the mod_webauth 428test suite included in the distribution. It will probably need work to 429be usable for writing site-specific tests. 430 431=head1 AUTHOR 432 433Roland Schemers and Jon Robertson <jonrober@stanford.edu>. 434 435=head1 SEE ALSO 436 437Template(3) 438 439This module is part of WebAuth. The current version is available from 440L<http://webauth.stanford.edu/>. 441 442=cut 443