#!/usr/bin/perl -w

# myproxy test script
# written by Jim Basney <jbasney@ncsa.uiuc.edu>

# Requires a valid proxy credential with lifetime of atleast 3 hours.
#
# Test cases are:
#   1. Store a credential on master (myproxy-init).
#   2. Get info on the stored credential (myproxy-info).
#   3. Retrieve stored credential from master (myproxy-get-delegation).
#   4. Replicate to slaves (myproxy-replicate).
#   5. Retrieve stored credential from slave (myproxy-get-delegation). 
#   6. Change passphrase on master (myproxy-change-pass-phrase).
#   7. Retrieve from master with new passphrase.
#   8. Replicate to slaves (myproxy-replicate). 
#   9. Remove credential from repository (myproxy-destroy).
#  10. Verify credential is removed from master(myproxy_info).
#  11. Replicate to slaves (myproxy-replicate).
#  12. Verify credential is removed from slave(myproxy_info).
#
#  13. Store credential (myproxy-store -v -t 1)
#  14. Get info on the stored credential (myproxy-info)
#  15. Create proxy from stored credential (myproxy-get-delegation).
#  16. Replicate to slaves (myproxy-replicate).
#  17. Retrieve stored credential from master (myproxy-retrieve)
#  18. Retrieve stored credential from slave (myproxy-retrieve)
#
#  Test server failure.
#
#  20. Store a credential (myproxy-store -v -t 1)
#  21. Shutdown one slave server and replicate (myproxy-replicate)
#      Should get one failure: STATUS: 256
#                               Unable to connect to 141.142.96.61:60503
#      .myproxy_replicate and .myproxy_deleted should not update
#  22. Restart slave server and and replicate (myproxy-replicate)
#  23. Shutdown one slave server and destroy cred (myproxy-destroy)
#      Should get one failure: STATUS: 256
#                               error in myproxy_init_client(): Unable to connect to 141.142.96.61:60503
#      .myproxy_replicate and .myproxy_deleted should not update
#  24. Restart slave server and and replicate (myproxy-replicate)
#  25. Store a credential (myproxy-store -v -t 1)
#  26. Shutdown one slave server and replicate (myproxy-replicate)
#      Should get one failure: STATUS: 256
#                               Unable to connect to 141.142.96.61:60503
#      .myproxy_replicate and .myproxy_deleted should not update
#  27. Restart slave server and destroy cred (myproxy-destroy) 

use File::Temp qw(tempdir);
use File::Copy;
use IPC::Open3;
use Socket;

$tmpdir = tempdir(CLEANUP => 1);

$PROXYBITS = "-bits 2048";

#
# handle cmdline options
#
$usage = "usage: myproxy-test-replicate [-help] [-verbose] [-keepfiles]\n";
$verbose = 0;
$cleanupfiles = 1;
while (($arg = shift @ARGV)) {
    if ($arg eq "-h" || $arg eq "-help") {
	print STDERR $usage;
	exit 1;
    } elsif ($arg eq "-v" || $arg eq "-verbose") {
	$verbose = 1;
    } elsif ($arg eq "-k" || $arg eq "-keepfiles") {
	$cleanupfiles = 0;
    } else {
	print STDERR $usage;
	exit 1;
    }
}

# Create a private CA and proxy locally and use them.
my $privcerts = "$tmpdir/privcerts.$$" ;
print STDERR "creating a CA in ", $privcerts, "\n" if ($verbose);
mkdir("$privcerts") ||
 die("failed to create $privcerts directory, stopped") ;
open(SSLCNF, ">$privcerts/openssl.cnf") || 
 die("failed to create $privcerts/openssl.cnf: $!");
print SSLCNF <<EOF;
[ ca ]
default_ca     = CA_default                  # The default ca section

[ CA_default ]
dir            = $privcerts                  # top dir
database       = $privcerts/index.txt        # index file.
new_certs_dir  = $privcerts/                 # new certs dir
 
certificate    = $privcerts/cacert.pem       # The CA cert
serial         = $privcerts/serial           # serial no file
private_key    = $privcerts/cakey.pem        # CA private key
RANDFILE       = $privcerts/.rand            # random number file
default_md     = sha256                      # md to use
[ req ]
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
commonName = Common Name (hostname, IP, or your name)
commonName_max = 64

[ v3_ca ]
basicConstraints = CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always

[ policy_anything ]
commonName              = supplied

EOF

close(SSLCNF);
open(CAINDEX,">$privcerts/index.txt") ||
  die("failed to create $privcerts/index.txt");
close(CAINDEX);
open(SERIAL,">$privcerts/serial") ||
  die("failed to create $privcerts/serial");
print SERIAL "01\n";
close(SERIAL);
&runcmd("openssl req -batch -subj '/CN=MyProxy Test CA' -config $privcerts/openssl.cnf -new -x509 -extensions v3_ca -nodes -keyout $privcerts/cakey.pem -out $privcerts/cacert.pem -days 30");

chomp($hash = `openssl x509 -in $privcerts/cacert.pem -hash -noout`) ;
mkdir("$privcerts/grid-security")  ||
 die("failed to create $privcerts/grid-security");
copy("$privcerts/cacert.pem","$privcerts/grid-security/$hash.0") ||
 die("failed to copy $privcerts/cacert.pem","$privcerts/grid-security/$hash.0");
open(POLICY,">$privcerts/grid-security/$hash.signing_policy") ||
 die("failed to create $privcerts/grid-security/$hash.signing_policy");
print POLICY  "access_id_CA            X509    '/CN=MyProxy Test CA'\n";
print POLICY  "pos_rights              globus  CA:sign\n";
print POLICY  "cond_subjects           globus  '\"/*\"'\n";
close(POLICY);
print STDERR "creating a user certificate request ", $privcerts, "\n" if ($verbose);
&runcmd("openssl req -batch -subj '/CN=MyProxy Test User'  -config $privcerts/openssl.cnf -new -nodes -keyout $privcerts/userkey.pem -out $privcerts/usercert.csr -days 7") ;
print STDERR "signing user certificate with by CA in ", $privcerts, "\n" if ($verbose);
&runcmd("openssl ca -batch -days 7 -config $privcerts/openssl.cnf -policy policy_anything -out $privcerts/usercert.pem -infiles $privcerts/usercert.csr");
chmod(oct("0600"),"$privcerts/userkey.pem","$privcerts/usercert.pem") ||
 die("failed to chmod $privcerts/userkey.pem or $privcerts/usercert.pem");
$ENV{'X509_USER_CERT'} = "$privcerts/usercert.pem" ;
$ENV{'X509_USER_KEY'}  = "$privcerts/userkey.pem" ;
$ENV{'X509_CERT_DIR'}  = "$privcerts/grid-security" ;
print STDERR "generating a user proxy\n" if ($verbose);
chomp($grid_proxy_init = `which grid-proxy-init 2>/dev/null`);
  die "grid-proxy-init not found, stopped" if (!(-x $grid_proxy_init));
&runcmd("$grid_proxy_init -debug $PROXYBITS");
#end of generatecerts.

#
# make sure I have a valid proxy
#
chomp($grid_proxy_init = `which grid-proxy-init 2>/dev/null`);
die "grid-proxy-init not found, stopped" if (!(-x $grid_proxy_init));
chomp($grid_proxy_info = `which grid-proxy-info 2>/dev/null`);
die "grid-proxy-info not found, stopped" if (!(-x $grid_proxy_info));
$timeleft = `$grid_proxy_info -timeleft 2>/dev/null`;
if (!defined($timeleft) || $timeleft eq "" || ($timeleft < 60*60*3)) {
    &debug("Problem with proxy.  Will try to create a new one.");
    `$grid_proxy_init -pwstdin </dev/null >/dev/null 2>&1`;
    $timeleft = `$grid_proxy_info -timeleft 2>/dev/null`;
}
die "grid-proxy-info failed, stopped"
    if (!defined($timeleft) || $timeleft eq "");
die "proxy expired, stopped" if ($timeleft < 60);
die "proxy lifetime too short, stopped" if ($timeleft < 60*60*3);
chomp($cert_subject = `$grid_proxy_info -identity`);
die "grid-proxy-info -identity failed, stopped"
    if (!defined($cert_subject) || $cert_subject eq "");

#
# check for the commands I want to run
#
chomp($myproxy_store = `which myproxy-store 2>/dev/null`);
die "myproxy-store not in PATH, stopped" if (!(-x $myproxy_store));
chomp($myproxy_init = `which myproxy-init 2>/dev/null`);
die "myproxy-init not in PATH, stopped" if (!(-x $myproxy_init));
chomp($myproxy_retrieve = `which myproxy-retrieve 2>/dev/null`);
die "myproxy-retrieve not in PATH, stopped" if (!(-x $myproxy_retrieve));
chomp($myproxy_info = `which myproxy-info 2>/dev/null`);
die "myproxy-info not in PATH, stopped" if (!(-x $myproxy_info));
chomp($myproxy_destroy = `which myproxy-destroy 2>/dev/null`);
die "myproxy-destroy not in PATH, stopped" if (!(-x $myproxy_destroy));
chomp($myproxy_get = `which myproxy-get-delegation 2>/dev/null`);
die "myproxy-get-delegation not in PATH, stopped" if (!(-x $myproxy_get));
chomp($myproxy_passwd = `which myproxy-change-pass-phrase 2>/dev/null`);
die "myproxy-change-pass-phrase not in PATH, stopped"
    if (!(-x $myproxy_passwd));

#
# setup environment variables
#
if (!defined($ENV{'X509_USER_PROXY'})) {
    $ENV{'X509_USER_PROXY'} = "/tmp/x509up_u$<";
}
# make proxy from existing proxy, so we don't need to deal with long-term cred
$ENV{'X509_USER_CERT'} = $ENV{'X509_USER_PROXY'};
$ENV{'X509_USER_KEY'} = $ENV{'X509_USER_PROXY'};

srand(time||$$);
$passphrase = sprintf "%010d", int(rand(0x7fffffff));

my $mport   = undef;
my $s1port  = undef;
my $s2port  = undef;
my $s3port  = undef;

my $masterpid     = undef;
my $masterdir     = undef;
my $masterconf    = undef;
my $masterpidfile = undef;

my $slconf        = undef;

my $sl1pid        = undef;
my $sl1dir        = undef;
my $sl1pidfile    = undef;
my $sl1portfile   = undef;

my $sl2pid        = undef;
my $sl2dir        = undef;
my $sl2pidfile    = undef;
my $sl2portfile   = undef;

my $sl3pid        = undef;
my $sl3dir        = undef;
my $sl3pidfile    = undef;
my $sl3portfile   = undef;

#
# start servers
#
$ENV{'MYPROXY_SERVER'} = "localhost";
$ENV{'MYPROXY_SERVER_DN'} = $cert_subject;

chomp($myproxy_server = `which myproxy-server 2>/dev/null`);
die "myproxy-server not in PATH, stopped" if (!(-x $myproxy_server));

$slconf = "$tmpdir/myproxy-test.serverconf.sl.$$";
open(CONF, ">$slconf") ||
    die "failed to open $slconf, stopped";
print CONF "accepted_credentials  \"$ENV{MYPROXY_SERVER_DN}\"\n";
print CONF "authorized_retrievers \"*\"\n";
print CONF "default_retrievers    \"*\"\n";
print CONF "authorized_renewers   \"*\"\n";
print CONF "default_renewers      \"none\"\n";
print CONF "authorized_key_retrievers \"*\"\n";
print CONF "default_key_retrievers    \"*\"\n";
print CONF "allow_self_authorization true\n"; # temporary workaround
close(CONF);

$sl1dir = "$tmpdir/myproxy-test.serverdir.sl1.$$";
mkdir($sl1dir) ||
    die "failed to create $sl1dir, stopped";
chmod(0700, $sl1dir) ||
    die "failed to chmod $sl1dir, stopped";
$sl1pidfile = "$tmpdir/myproxy-test.serverpid.sl1.$$";
$sl1portfile = "$tmpdir/myproxy-test.serverport.sl1.$$";
$servercmd = "$myproxy_server -s $sl1dir -c $slconf";
$servercmd .= " -l $ENV{'MYPROXY_SERVER'} -p 0";
$servercmd .= " -P $sl1pidfile -z $sl1portfile";
&debug("running '$servercmd'");
`$servercmd`;
sleep(2);			# give server a chance to startup
if (open SERVERPIDFILE, $sl1pidfile) {
    $sl1pid = <SERVERPIDFILE>;
    close SERVERPIDFILE;
}
if (!defined($sl1pid) || $sl1pid eq "") {
    print STDERR "failed to start slave 1:\n";
    `$servercmd -d`;	# show output on terminal
    &docleanup();
    exit 1;
}
if (open SERVERPORTFILE, $sl1portfile) {
    chomp($s1port = <SERVERPORTFILE>);
    close SERVERPORTFILE;
}

$sl2dir = "$tmpdir/myproxy-test.serverdir.sl2.$$";
mkdir($sl2dir) ||
    die "failed to create $sl2dir, stopped";
chmod(0700, $sl2dir) ||
    die "failed to chmod $sl2dir, stopped";
$sl2pidfile = "$tmpdir/myproxy-test.serverpid.sl2.$$";
$sl2portfile = "$tmpdir/myproxy-test.serverport.sl2.$$";
$servercmd = "$myproxy_server -s $sl2dir -c $slconf";
$servercmd .= " -p 0 -P $sl2pidfile -z $sl2portfile";
&debug("running '$servercmd'");
`$servercmd`;
sleep(2);			# give server a chance to startup
if (open SERVERPIDFILE, $sl2pidfile) {
    $sl2pid = <SERVERPIDFILE>;
    close SERVERPIDFILE;
}
if (!defined($sl2pid) || $sl2pid eq "") {
    print STDERR "failed to start slave 2:\n";
    `$servercmd -d`;	# show output on terminal
    &docleanup();
    exit 1;
}
if (open SERVERPORTFILE, $sl2portfile) {
    chomp($s2port = <SERVERPORTFILE>);
    close SERVERPORTFILE;
}

start_sl3();

sub start_sl3 {
  $sl3dir = "$tmpdir/myproxy-test.serverdir.sl3.$$";
  if( !(-d $sl3dir) ) {
    mkdir($sl3dir) || 
      die "failed to create $sl3dir, stopped";
  }
  chmod(0700, $sl3dir) ||
    die "failed to chmod $sl3dir, stopped";
  $sl3pidfile = "$tmpdir/myproxy-test.serverpid.sl3.$$";
  $sl3portfile = "$tmpdir/myproxy-test.serverport.sl3.$$";
  $servercmd  = "$myproxy_server -s $sl3dir -c $slconf";
  if (defined($s3port)) {
    $servercmd .= " -p $s3port -P $sl3pidfile";
  } else {
    $servercmd .= " -p 0 -P $sl3pidfile -z $sl3portfile";
  }
  &debug("running '$servercmd'");
  `$servercmd`;
  sleep(2);                     # give server a chance to startup
  if (open SERVERPIDFILE, $sl3pidfile) {
    $sl3pid = <SERVERPIDFILE>;
    close SERVERPIDFILE;
  }
  if (!defined($sl3pid) || $sl3pid eq "") {
    print STDERR "failed to start slave 3:\n";
    `$servercmd -d`;            # show output on terminal
    &docleanup();
    exit 1;
  }
  
  if (!defined($s3port)) {
    if (open SERVERPORTFILE, $sl3portfile) {
      chomp($s3port = <SERVERPORTFILE>);
      close SERVERPORTFILE;
    }
  }
}

$masterconf = "$tmpdir/myproxy-test.serverconf.master.$$";
open(CONF, ">$masterconf") ||
    die "failed to open $masterconf, stopped";
print CONF "accepted_credentials  \"*\"\n";
print CONF "authorized_retrievers \"*\"\n";
print CONF "default_retrievers    \"*\"\n";
print CONF "authorized_renewers   \"*\"\n";
print CONF "default_renewers      \"none\"\n";
print CONF "authorized_key_retrievers \"*\"\n";
print CONF "default_key_retrievers    \"*\"\n";
print CONF "allow_self_authorization true\n"; # temporary workaround
print CONF "slave_servers localhost:$s1port;localhost:$s2port;localhost:$s3port";
close(CONF);

$masterdir = "$tmpdir/myproxy-test.serverdir.master.$$";
mkdir($masterdir) ||
    die "failed to create $masterdir, stopped";
chmod(0700, $masterdir) ||
    die "failed to chmod $masterdir, stopped";
$masterpidfile = "$tmpdir/myproxy-test.serverpid.master.$$";
$masterportfile = "$tmpdir/myproxy-test.serverport.master.$$";
$servercmd = "$myproxy_server -s $masterdir -c $masterconf";
$servercmd .= " -p 0 -P $masterpidfile -z $masterportfile";
&debug("running '$servercmd'");
`$servercmd`;
sleep(2);			# give server a chance to startup
if (open SERVERPIDFILE, $masterpidfile) {
    $masterpid = <SERVERPIDFILE>;
    close SERVERPIDFILE;
}
if (!defined($masterpid) || $masterpid eq "") {
    print STDERR "failed to start master:\n";
    `$servercmd -d`;	# show output on terminal
    &docleanup();
    exit 1;
}
if (open SERVERPORTFILE, $masterportfile) {
    chomp($mport = <SERVERPORTFILE>);
    close SERVERPORTFILE;
}

#
# BEGIN TESTS
#
$SUCCESSES = $FAILURES = 0;

# commands to test: myproxy-init, myproxy-info, myproxy-destroy,
#                   myproxy-get-delegation, and myproxy-change-pass-phrase

##
## Test 1
##
($exitstatus, $output) =
    &runtest("myproxy-init -s localhost -p $mport -v -a -c 1 -t 1 -S -d -k \"test credential\"",
	     $passphrase . "\n");
print "MyProxy Test 1 (store credential): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 2
##
($exitstatus, $output) = &runtest("myproxy-info -s localhost -p $mport -v -d", undef);
print "MyProxy Test 2 (get info for stored credential): ";
if ($exitstatus == 0 && $output =~ /username/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 3
##
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $mport -t 1 -o $tmpdir/myproxy-test.$$ -v -S -d -k \"test credential\"",
	     $passphrase . "\n");
print "MyProxy Test 3 (retrieve stored credential): ";
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&verifyproxy("$tmpdir/myproxy-test.$$");
}
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 4
##
($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
	     undef);
print "MyProxy Test 4 (Replicate master server to all slaves): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
    goto end_of_tests;
}

##
## Test 5
##
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $s1port -t 1 -o $tmpdir/myproxy-test.$$ -v -S -d -k \"test credential\"",
	     $passphrase . "\n");
print "MyProxy Test 5 (retrieve stored credential from slave): ";
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&verifyproxy("$tmpdir/myproxy-test.$$");
}
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 6
##
$old_passphrase = $passphrase;
$passphrase = sprintf "%010d", int(rand(0x7fffffff));
($exitstatus, $output) =
    &runtest("myproxy-change-pass-phrase -s localhost -p $mport -v -S -d -k \"test credential\"",
	     "$old_passphrase\n$passphrase\n");
print "MyProxy Test 6 (change passphrase for credential): ";
if ($exitstatus == 0 && $output =~ /Pass phrase changed/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 7
##
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $mport -t 1 -o $tmpdir/myproxy-test.$$ -v -S -d -k \"test credential\"",
	     $passphrase . "\n");
print "MyProxy Test 7 (verify new passphrase): ";
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&verifyproxy("$tmpdir/myproxy-test.$$");
}
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 8
##
($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
	     undef);
print "MyProxy Test 8 (Replicate master server to all slaves): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 9
##
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $s2port -t 1 -o $tmpdir/myproxy-test.$$ -v -S -d -k \"test credential\"",
	     $passphrase . "\n");
print "MyProxy Test 9 (verify new passphrase has been replicated to slaves): ";
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&verifyproxy("$tmpdir/myproxy-test.$$");
}
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 10
##
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $s3port -a \$X509_USER_PROXY -t 1 -o $tmpdir/myproxy-test.$$ -v -d -k \"test credential\"", undef);
print "MyProxy Test 10 (verify default renewal policy on slave): ";
if ($exitstatus != 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 11
##
($exitstatus, $output) =
    &runtest("myproxy-destroy -s localhost -p $mport -v -d -k \"test credential\"", undef);
print "MyProxy Test 11 (remove credential from master repository): ";
if ($exitstatus == 0 && $output =~ /was successfully removed/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 12
##
($exitstatus, $output) =
    &runtest("myproxy-info -v -d", undef);
print "MyProxy Test 12 (verify credentials are removed from master): ";
if (!($output =~ /default credential/)) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 13
##
($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $tmpdir/myproxy-test.serverconf.$mport.master.$$ -r $masterdir",
	     undef);
print "MyProxy Test 13 (Replicate destroy to all slaves): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 14
##
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $s2port -t 1 -o $tmpdir/myproxy-test.$$ -v -S -d -k \"test credential\"",
	     $passphrase . "\n");
print "MyProxy Test 14 (verify destroy has been replicated to slaves): ";
if ($exitstatus != 0 && $output =~ /exist/) {
    ($exitstatus, $output) =
	&verifyproxy("$tmpdir/myproxy-test.$$");
}
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 15
##
($exitstatus, $output) =
    &runtest("myproxy-init -s localhost -p $mport -v -R 'nobody' -k 'nobody' -c 1 -t 1 -d -S",
	     $passphrase . "\n");
print "MyProxy Test 15 (store credentials with renewal policies): ";
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&runtest("myproxy-init -s localhost -p $mport -v -x -R '$cert_subject' -k 'mine' -c 1 -t 1 -d -S",
		 $passphrase . "\n");
}
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 16
##
($exitstatus, $output) = &runtest("myproxy-info -s localhost -p $mport -v -d", undef);
print "MyProxy Test 16 (get info for stored renewal credentials): ";
if ($exitstatus == 0 && $output =~ /username/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 17
##
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $mport -k 'mine' -a $ENV{'X509_USER_PROXY'} -t 1 -o $tmpdir/myproxy-test.$$ -v -d",
	     undef);
print "MyProxy Test 17 (verify renewal policies): ";
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&verifyproxy("$tmpdir/myproxy-test.$$");
}
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&runtest("myproxy-get-delegation -s localhost -p $mport -k 'nobody' -a $ENV{'X509_USER_PROXY'} -t 1 -o $tmpdir/myproxy-test.$$ -v -d",
		 undef);
    if ($exitstatus != 0) {
	print "SUCCEEDED\n"; $SUCCESSES++;
    } else {
	print "FAILED\n"; $FAILURES++;
	print STDERR "Shouldn't have allowed retrieval.\n";
	print STDERR $output;
    }
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 18
##
($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
             undef);
print "MyProxy Test 18 (Replicate to all slaves): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 19
##
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $s1port -k 'mine' -a $ENV{'X509_USER_PROXY'} -t 1 -o $tmpdir/myproxy-test.$$ -v -d",
	     undef);
print "MyProxy Test 19 (verify renewal policies on slave): ";
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&verifyproxy("$tmpdir/myproxy-test.$$");
}
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&runtest("myproxy-get-delegation -s localhost -p $s1port -k 'nobody' -a $ENV{'X509_USER_PROXY'} -t 1 -o $tmpdir/myproxy-test.$$ -v -d",
		 undef);
    if ($exitstatus != 0) {
	print "SUCCEEDED\n"; $SUCCESSES++;
    } else {
	print "FAILED\n"; $FAILURES++;
	print STDERR "Shouldn't have allowed retrieval.\n";
	print STDERR $output;
    }
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

&runtest("myproxy-destroy -s localhost -p $mport -v -k 'mine' -d", undef);
&runtest("myproxy-destroy -s localhost -p $mport -v -k 'nobody' -d", undef);
&runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir", undef);

##
## Test replication failure handling
##

##
## Test 20
##
($exitstatus, $output) =
    &runtest("myproxy-init -s localhost -p $mport -v -a -c 1 -t 1 -S",
	     $passphrase . "\n");
print "MyProxy Test 20 (store credential with default name): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 21
##
kill('TERM', $sl3pid) if (defined($sl3pid));

($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
	     undef);
print "MyProxy Test 21 (Replicate master server to all slaves): ";

if ($exitstatus != 0 && $output =~ /Unable to connect to/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 22
##
start_sl3();

($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
	     undef);
print "MyProxy Test 22 (Replicate master server to all slaves): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 23
##
kill('TERM', $sl3pid) if (defined($sl3pid));

($exitstatus, $output) =
    &runtest("myproxy-destroy -s localhost -p $mport -v", undef);
print "MyProxy Test 23 (remove credential from master repository): ";
if ($exitstatus == 0 && $output =~ /was successfully removed/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
	     undef);
print "\t(Replicate master server to all slaves): ";
if ($exitstatus != 0 && $output =~ /Unable to connect to/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 24
##
start_sl3();

($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
	     undef);
print "MyProxy Test 24 (Replicate master server to all slaves): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
    goto end_of_tests;
}

##
## Test 25
##
kill('TERM', $sl3pid) if (defined($sl3pid));

($exitstatus, $output) =
    &runtest("myproxy-init -s localhost -p $mport -v -a -c 1 -t 1 -S",
	     $passphrase . "\n");
print "MyProxy Test 25 (store credential with default name): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
	     undef);
print "\t(Replicate master server to all slaves): ";
if ($exitstatus != 0 && $output =~ /Unable to connect to/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

($exitstatus, $output) =
    &runtest("myproxy-destroy -s localhost -p $mport -v", undef);
print "\t(remove credential from master repository): ";
if ($exitstatus == 0 && $output =~ /was successfully removed/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

start_sl3();

($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
	     undef);
print "\t(Replicate master server to all slaves): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

#
# Start of myproxy-store and myproxy-retrieve tests
#

# commands to test: myproxy-store, myproxy-info, myproxy-destroy,
#                   myproxy-get-delegation, myproxy-retrieve, and 
#                   myproxy-change-pass-phrase

# For myproxy-store, we need an encrypted key to store.
# So, let's encrypt our proxy key.
$passphrase = sprintf "%010d", int(rand(0x7fffffff));
$testkey = "$tmpdir/myproxy-test.$$.key";
&runtest("openssl rsa -des3 -passout stdin -in \$X509_USER_KEY -out $testkey",
	 $passphrase . "\n");
chmod(0600, $testkey);

#
# Test 26
#
($exitstatus, $output) =
    &runtest("myproxy-store -s localhost -p $mport -v -t 1 -y $testkey", undef);
print "MyProxy Test 26 (store credential with default name): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} elsif (($output =~ /Error checking authorization/) ||
         ($output =~ /unknown command/)) {
    print "UNSUPPORTED\n"; $FAILURES++;
    print "Server does not support myproxy-store. Skipping futher myproxy-store tests.\n";
    goto end_of_tests;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
    print "Skipping futher myproxy-store tests.\n";
    goto end_of_tests;
}

#
# Test 27
#
($exitstatus, $output) = &runtest("myproxy-info -s localhost -p $mport -v", undef);
print "MyProxy Test 27 (get info for stored credential): ";
if ($exitstatus == 0 && $output =~ /username/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

#
# Test 28
#
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $mport -t 1 -o $tmpdir/myproxy-test.$$ -v -S",
	     $passphrase . "\n");
print "MyProxy Test 28 (create proxy from stored credential on master): ";
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&verifyproxy("$tmpdir/myproxy-test.$$");
}
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

#
# Test 29
#

($exitstatus, $output) =
    &runtest("myproxy-retrieve -s localhost -p $mport -c $tmpdir/myproxy-test.cert.$$ -y $tmpdir/myproxy-test.key.$$ -v -S",
	     $passphrase . "\n");
print "MyProxy Test 29 (retrieve stored credential from master): ";
if ($exitstatus == 0) {
    ($exitstatus, $output) =
	&verifycert("$tmpdir/myproxy-test.cert.$$", "$tmpdir/myproxy-test.key.$$");
}
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}
unlink( "$tmpdir/myproxy-test.cert.$$" );
unlink( "$tmpdir/myproxy-test.key.$$" );

##
## Test 30
##
($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
             undef);
print "MyProxy Test 30 (Replicate to all slaves): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

#
# Test 31
#
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $s1port -t 1 -o $tmpdir/myproxy-test.$$ -v -S", $passphrase . "\n");
print "MyProxy Test 31 (create proxy from stored credential on slave): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

#
# Test 32
#
($exitstatus, $output) =
    &runtest("myproxy-retrieve -s localhost -p $s2port -c $tmpdir/myproxy-test-cert.$$ -y $tmpdir/myproxy-test-key.$$ -v -S",
	     $passphrase . "\n");
print "MyProxy Test 32 (retrieve stored credential from slave): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}
unlink( "$tmpdir/myproxy-test.cert.$$" );
unlink( "$tmpdir/myproxy-test.key.$$" );

#
# Test 33
#
($exitstatus, $output) =
    &runtest("myproxy-destroy -s localhost -p $mport -v", undef);
print "MyProxy Test 33 (remove credential from master repository): ";
if ($exitstatus == 0 && $output =~ /was successfully removed/) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

#
# Test 34
#
($exitstatus, $output) =
    &runtest("myproxy-info -v", undef);
print "MyProxy Test 34 (verify credentials are removed from master): ";
if (!($output =~ /default credential/)) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 35
##
($exitstatus, $output) =
    &runtest("myproxy-replicate -d -v -c $masterconf -r $masterdir",
             undef);
print "MyProxy Test 35 (Replicate to all slaves): ";
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

##
## Test 36
##
($exitstatus, $output) =
    &runtest("myproxy-get-delegation -s localhost -p $s3port -t 1 -o $tmpdir/myproxy-test.$$ -v -S",
	     $passphrase . "\n");
print "MyProxy Test 36 (verify destroy has been replicated to slaves): ";
if ($exitstatus != 0 && $output =~ /exist/) {
    ($exitstatus, $output) =
	&verifyproxy("$tmpdir/myproxy-test.$$");
}
if ($exitstatus == 0) {
    print "SUCCEEDED\n"; $SUCCESSES++;
} else {
    print "FAILED\n"; $FAILURES++; print STDERR $output;
}

end_of_tests:
&runtest("myproxy-destroy -v -k 'mine' -d", undef);
&runtest("myproxy-destroy -v -k 'nobody' -d", undef);
`rm -f $tmpdir/myproxy-test.*.12.*.$$`;
unlink("$tmpdir/myproxy-test.$$.key");

#
# END TESTS
#

&docleanup();

print "MyProxy Tests Complete: ", $SUCCESSES, " tests passed, ";
print $FAILURES, " tests failed\n";
exit $FAILURES;

#
# SUBROUTINES
#

sub runtest {
    local($command, $input) = @_;

    $pid = open3(*Writer, *Reader, 0, "exec $command") ||
	die "failed to run $command";
    print Writer $input if (defined($input));
    close(Writer);
    @output = <Reader>;
    close(Reader);
    waitpid($pid, 0);
    $exitstatus = $?;
    $output = join('', @output);

    return ($exitstatus, $output);
}

#
# verify_proxy
#
# Check to see if user has a valid proxy, and verify proxy is usable
#
# Dependencies: (-x grid-proxy-info)
#
# grid-proxy-info -timeleft
#     die if no output, or output is less than 60 seconds
# $proxy = grid-proxy-info -path
# grid-proxy-init -debug -verify -cert $proxy -key $proxy
#     if $? == 0 then proxy is valid
#     if $? != 0, die, b/c proxy is invalid and won't work
#
sub verifyproxy {
    local($proxyfile) = @_;

    chomp (my $timeleft = `$grid_proxy_info -file $proxyfile -timeleft`);
    if (!defined($timeleft) || $timeleft eq "") {
	$output = "failed to verify proxy\n";
	$output .= "'grid-proxy-info -timeleft' failed\n";
	return (1, $output);
    }
    if ($timeleft < 1) {
	$output = "proxy is expired\n";
	return (1, $output);
    }
    chomp (my $proxytype = `$grid_proxy_info -file $proxyfile -type`);
    local($oldproxy) = "";
    if ($proxytype =~ /legacy/) {
	$oldproxy = "-old";
    }
    local($output) = '$grid_proxy_init $oldproxy -debug -verify -cert $proxyfile -key $proxyfile -valid 0:1 -out $tmpdir/tmpproxy.$$';
    if ($? != 0) {
	$output = "failed to verify proxy\n" . $output;
	unlink("$tmpdir/tmpproxy.$$");
	return (1, $output);
    }

    # remove the new proxy we created for validation
    # NOTE: this does not affect the user's original proxy in any way
    unlink("$tmpdir/tmpproxy.$$");

    return (0, "");
}

#
# verifycert
#
# Check to see if user has a valid usable certificate
#
# Dependencies: (-x grid-proxy-int)
#
# grid-proxy-init -debug -verify -cert $certfile -key $keyfile
#     if $? == 0 then proxy is valid
#     if $? != 0, die, b/c proxy is invalid and won't work
#
sub verifycert {
    local($certfile, $keyfile) = @_;

    local($output) = '$grid_proxy_init $oldproxy -debug -verify -cert $certfile -key $keyfile -valid 0:1 -out $tmpdir/tmpproxy.$$';
    if ($? != 0) {
	$output = "failed to verify certificate from: $certfile and $keyfile\n" . $output;
	unlink("$tmpdir/tmpproxy.$$");
	return (1, $output);
    }

    # remove the new proxy we created for validation
    # NOTE: this does not affect the user's original proxy in any way
    unlink("$tmpdir/tmpproxy.$$");

    return (0, "");
}

sub debug {
    print STDERR join('', @_), "\n" if ($verbose);
}

sub docleanup {
    unlink("$tmpdir/myproxy-test.$$");
    unlink("$tmpdir/myproxy-test-cert.$$");
    unlink("$tmpdir/myproxy-test-key.$$");

    # Kill off servers...
    kill('TERM', $masterpid) if (defined($masterpid));
    kill('TERM', $sl1pid) if (defined($sl1pid));
    kill('TERM', $sl2pid) if (defined($sl2pid));
    kill('TERM', $sl3pid) if (defined($sl3pid));
    
    # Get rid of pid files...
    unlink($masterpidfile) if (defined($masterpidfile));
    unlink($sl1pidfile) if (defined($sl1pidfile));
    unlink($sl2pidfile) if (defined($sl2pidfile));
    unlink($sl3pidfile) if (defined($sl3pidfile));

  if ($cleanupfiles) {
    # Remove configuration files...
    unlink($masterconf) if (defined($masterconf));
    unlink($slconf) if (defined($slconf));

    # Get rid of left over creds and dirs...
    `rm -rf $masterdir` if (defined($masterdir));
    `rm -rf $sl1dir` if (defined($sl1dir));
    `rm -rf $sl2dir` if (defined($sl2dir));
    `rm -rf $sl3dir` if (defined($sl3dir));
  }
}

sub openport {
  $sockaddr = 'S n a4 x8';
  $host = "127.0.0.1";
  local($port) = @_;
  @list = getprotobyname('tcp');
  $proto = $list[2];
  @list = gethostbyaddr(inet_aton($host), AF_INET);
  $addr = $list[4];
  while ($port < 65535) {
    $destaddr = pack($sockaddr, AF_INET, $port, $addr);
    socket(S, AF_INET, SOCK_STREAM, $proto) || die $!;
    connect(S, $destaddr);
    if ($! =~ /Connection refused/) {
      return $port;
    }
    close(S);
    $port++;
  }
  die "failed to find available port";
}

sub runcmd {
    local($command, $input) = @_;

    print STDERR "running: ", $command, "\n" if ($verbose); 
    $pid = open3(*Writer, *Reader, '', "exec $command") ||
	die "failed to run $command";
    print Writer $input if (defined($input));
    close(Writer);
    @output = <Reader>;
    close(Reader);
    waitpid($pid, 0);
    $exitstatus = $?;
    $output = join('', @output);
    print STDERR $output if ($verbose);

    return ($exitstatus, $output);
}
