summaryrefslogtreecommitdiff
path: root/tests/tools/backend-t
blob: 3da82ccba67c416f11f0c07c324ce73f4d2b1230 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/perl
#
# Test suite for krb5-strength-backend.
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2013
#     The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.

use 5.006;
use strict;
use warnings;

use lib "$ENV{SOURCE}/tap/perl";

use File::Path qw(remove_tree);
use POSIX qw(strftime);
use Test::More tests => 30;
use Test::RRA qw(use_prereq);
use Test::RRA::Automake qw(test_file_path test_tmpdir);

use_prereq('IPC::Run',     'run');
use_prereq('Perl6::Slurp', 'slurp');

# Not actually used by the test, but required by krb5-sync-backend.
use_prereq('Net::Remctl::Backend');

# Run krb5-sync-backend and return the status, output, and error output as a
# list.  Always uses a directory named 'queue' under test_tmpdir() as the
# queue directory.
#
# @args - Command-line arguments to pass in
#
# Returns: Exit status, stdout, and stderr as a list
#  Throws: Text exception on failure to create the queue or run the program
sub run_backend {
    my ($action, @args) = @_;

    # Ensure the queue directory exists.
    my $queue = test_tmpdir() . '/queue';
    if (!-d $queue) {
        mkdir($queue, 0777) or BAIL_OUT("cannot create $queue: $!");
    }

    # Add the queue option to the start of the arguments.
    unshift(@args, '-d', $queue);

    # If the action is password, we have to pass the password on standard
    # input.
    my $in;
    if ($action eq 'password') {
        $in = pop(@args);
    }

    # Run the command and capture the output.  We add the queue option after
    # the initial argument.
    my $backend = test_file_path('../tools/krb5-sync-backend');
    my ($out, $err);
    run([$backend, $action, @args], \$in, \$out, \$err);
    my $status = ($? >> 8);

    # Return the results.
    return ($status, $out, $err);
}

# Run a krb5-sync-backend command that should exit with success and produce no
# output and check those results with Test::More functions.  Always uses a
# directory named 'queue' under test_tmpdir() as the queue directory.
#
# @args - Command-line arguments to pass in
#
# Returns: undef
#  Throws: Text exception on failure to create the queue or run the program
sub run_backend_checked {
    my (@args) = @_;

    # Run the program and capture status and output.
    my ($status, $out, $err) = run_backend(@args);

    # Check that everything is as expected.
    is($status, 0,   "krb5-sync-backend @args succeeded");
    is($out,    q{}, '...with no output');
    is($err,    q{}, '...and no errors');
    return;
}

# Given a user, an action, and (for a password change) the new password,
# locate that queued action and check that the correct information was stored
# for it.  Expects the action to have been queued in the five seconds.
# Reports results with Test::More functions.  Always uses a directory named
# 'queue' under test_tmpdir() as the queue directory.
#
# $user     - User for which the action should be queued
# $action   - The queued action, chosen from enable, disable, or password
# $password - For password actions, the queued password
#
# Returns: undef
#  Throws: Text exception on system failures such as reading files
sub check_queued_action {
    my ($user, $action, $password) = @_;
    my $queue = test_tmpdir() . '/queue';

    # Build the base portion of the expected filename, without the timestamp.
    my $type = ($action eq 'disable') ? 'enable' : $action;
    my $base = $queue . "/$user-ad-$type-";

    # Locate the queue file.  This doesn't deal with multiple files created
    # with the same timestamp with a non-zero sequence number.
    my $now = time;
    my $path;
    for my $time ($now - 10 .. $now + 1) {
        my $candidate = $base . strftime('%Y%m%dT%H%M%SZ-00', gmtime($time));
        if (-f $candidate) {
            $path = $candidate;
            last;
        }
    }
    ok(defined($path), 'Queued change found');

    # If we found a file, check the contents and delete the file.
  SKIP: {
        if (!defined($path)) {
            my $count = ($action eq 'password') ? 5 : 4;
            skip('No queued change found', $count);
        }
        my @data = slurp($path, { chomp => 1 });
        is($data[0], $user,   '...queued user is correct');
        is($data[1], 'ad',    '...queued domain is correct');
        is($data[2], $action, '...queued operation is correct');
        if ($action eq 'password') {
            is(scalar(@data), 4,         '...no extraneous data');
            is($data[3],      $password, '...queued password is correct');
        } else {
            is(scalar(@data), 3, '...no extraneous data');
        }

        # Unlink the file after checking.  This lets us check later that no
        # extraneous files were created in the queue.
        if (!unlink($path)) {
            diag("cannot delete queued change $path: $!");
        }
    }
    return;
}

# Clean out any existing queue if one already exists.
my $queue = test_tmpdir() . '/queue';
if (-d $queue) {
    remove_tree($queue);
}

# Start of testing.  Try creating each type of change.
run_backend_checked('disable',  'test');
run_backend_checked('enable',   'longtest');
run_backend_checked('password', 'test', 'foobar');

# Check that the list output is now what we would expect.  We can't check the
# timestamp directly without messing about with various improbable but
# possible time transitions, so just make sure it's in the correct format.
my $timestamp = qr{ \d{4}-\d\d-\d\d [ ] \d\d:\d\d:\d\d [ ] UTC }xms;
my $expected  = qr{
    \A
    longtest [ ]{2} enable   [ ]{4} ad [ ]{4} $timestamp \n
    test     [ ]{6} disable  [ ]{3} ad [ ]{4} $timestamp \n
    test     [ ]{6} password [ ]{2} ad [ ]{4} $timestamp \n
    \z
}xms;
my ($status, $out, $err) = run_backend('list');
is($status, 0, 'krb5-sync-backend list succeeded');
like($out, $expected, '...with correct output');
is($err, q{}, '...and no errors');

# Now check that the created queue files are all correct.
check_queued_action('test',     'disable');
check_queued_action('test',     'password', 'foobar');
check_queued_action('longtest', 'enable');

# Verify that the lock file exists and that there are no other queued files by
# removing the queue.
ok(unlink("$queue/.lock"), 'Lock file exists and can be removed');
ok(rmdir($queue),          'No extraneous files in the queue');