#!/usr/bin/perl -w
#
# Tests for basic krenew functionality.
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2015 Russ Allbery <eagle@eyrie.org>
# Copyright 2008, 2009, 2011
#     The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.

use IPC::Open3 qw(open3);
use Test::More;

# The full path to the newly-built krenew client.
our $KRENEW = "$ENV{BUILD}/../krenew";

# The path to our data directory, which contains the keytab to use to test.
our $DATA = "$ENV{BUILD}/data";

# Load our test utility programs.
require "$ENV{SOURCE}/libtest.pl";

# Decide whether we have the configuration to run the tests.
my $principal;
if (not -f "$DATA/test.keytab" or not -f "$DATA/test.principal") {
    plan skip_all => 'no keytab configuration';
    exit 0;
} else {
    $principal = contents ("$DATA/test.principal");
    $ENV{KRB5CCNAME} = 'krb5cc_test';
    unlink 'krb5cc_test';
    unless (kinit ("$DATA/test.keytab", $principal, '-r', '2h', '-l', '10m')) {
        plan skip_all => 'cannot get renewable tickets';
        exit 0;
    }
    plan tests => 36;
}

# We should be okay for five minute tickets without doing anything.
($out, $err, $status) = command ($KRENEW, '-vH', '5');
is ($status, 0, 'krenew -H 5 succeeds without reauthenticating');
is ($err, '', ' with no errors');
is ($out, '', ' and no output');

# We should be able to renew to get 30 minute tickets.
($out, $err, $status) = command ($KRENEW, '-vH', '30');
is ($status, 0, 'krenew -H 30 succeeds with authentication');
is ($err, '', ' with no errors');
like ($out,
      qr/^krenew: renewing credentials for \Q$principal\E(\@\S+)?\n\z/,
      ' and the right output');

# But we fail if we try to get 3 hour tickets, since we can't renew for
# that long.
($out, $err, $status) = command ($KRENEW, '-vH', '180');
is ($status, 1, 'krenew -H 180 fails');
is ($err, "krenew: ticket cannot be renewed for long enough\n",
    ' with the right error');
is ($out, '', ' and no output');

# Test running a command.  klist may fail if we have no K4 tickets since
# we're not giving the -5 option; allow for that (we'll catch real
# failures in the regex match on the output).
($out, $err, $status) = command ($KRENEW, 'klist');
ok ($status == 0 || $status == 1, 'krenew with command succeeds');
ok ($err eq '' || $err eq "klist: You have no tickets cached\n",
    ' with no or expected errors');
like ($out, qr,^\s*(Ticket|Credentials)\ cache:
               \ (FILE:)?/tmp/krb5cc_\d+_\S{6}\n
               \s*(Default\ )?[Pp]rincipal:\ \Q$principal\E(\@\S+)?\n,xm,
      ' and the right output');
($cache) = ($out =~ /cache: (?:FILE:)?(\S+)/);
ok (!$cache || !-f $cache, ' and the new cache file was deleted');
ok (-f 'krb5cc_test', ' but the default one was unaffected');

# Test running a command with --.
($out, $err, $status) = command ($KRENEW, '--', 'klist', '-5');
is ($status, 0, 'krenew with command and -- succeeds');
is ($err, '', ' with no errors');
like ($out, qr,^\s*(Ticket|Credentials)\ cache:
               \ (FILE:)?/tmp/krb5cc_\d+_\S{6}\n
               \s*(Default\ )?[Pp]rincipal:\ \Q$principal\E(\@\S+)?\n,xm,
      ' and the right output');
($cache) = ($out =~ /cache: (?:FILE:)?(\S+)/);
ok (!$cache || !-f $cache, ' and the new cache file was deleted');
ok (-f 'krb5cc_test', ' but the default one was unaffected');

# Test propagation of exit status from a command.
($out, $err, $status)
    = command ($KRENEW, '--', 'sh', '-c', 'klist -5; exit 3');
is ($status, 3, 'krenew of exit 3 returns correct exit status');
is ($err, '', ' with no errors');
like ($out, qr,^\s*(Ticket|Credentials)\ cache:
               \ (FILE:)?/tmp/krb5cc_\d+_\S{6}\n
               \s*(Default\ )?[Pp]rincipal:\ \Q$principal\E(\@\S+)?\n,xm,
      ' and the right output');
($cache) = ($out =~ /cache: (?:FILE:)?(\S+)/);
ok (!$cache || !-f $cache, ' and the new cache file was deleted');
ok (-f 'krb5cc_test', ' and the default cache file was unaffected');

# Check exit status if the ticket cache doesn't exist.
unlink 'krb5cc_test';
($out, $err, $status) = command ($KRENEW);
is ($status, 1, 'krenew fails with no ticket cache');
is ($out, '', ' with no output');
like ($err, qr/^krenew: error reading ticket cache: /, ' and the right error');
($out, $err, $status) = command ($KRENEW, '-vH', 5);
is ($status, 1, 'krenew -H fails with no ticket cache');
is ($out, '', ' with no output');
like ($err, qr/^krenew: error reading ticket cache: /, ' and the right error');

# If -i was given, we keep running and keep trying and have to be explicitly
# killed.
my $pid = open3 ('<&0', \*OUT, \*ERR, $KRENEW, '-vi')
    or BAIL_OUT ("can't run $KRENEW: $!");
select (undef, undef, undef, 1.0);
kill (15, $pid);
$out = <OUT>;
$err = <ERR>;
close OUT;
close ERR;
waitpid ($pid, 0);
$status = ($? >> 8);
is ($status, 1, 'krenew -i fails with no ticket cache');
is ($out, undef, ' with no output');
like ($err, qr/^krenew: error reading ticket cache: /, ' and the right error');
$pid = open3 ('<&0', \*OUT, \*ERR, $KRENEW, '-viH', 5)
    or BAIL_OUT ("can't run $KRENEW: $!");
select (undef, undef, undef, 1.0);
kill (15, $pid);
$out = <OUT>;
$err = <ERR>;
close OUT;
close ERR;
waitpid ($pid, 0);
$status = ($? >> 8);
is ($status, 1, 'krenew -iH fails with no ticket cache');
is ($out, undef, ' with no output');
like ($err, qr/^krenew: error reading ticket cache: /, ' and the right error');
