summaryrefslogtreecommitdiff
path: root/perl/t/object/duo-pam.t
blob: f0c9e618ec58b5e03a8a3e863d3f1c3cd1a984c7 (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
#!/usr/bin/perl
#
# Tests for the Duo PAM integration object implementation.
#
# Written by Russ Allbery <eagle@eyrie.org>
# Copyright 2014
#     The Board of Trustees of the Leland Stanford Junior University
#
# SPDX-License-Identifier: MIT

use strict;
use warnings;

use POSIX qw(strftime);
use Test::More;

BEGIN {
    eval 'use Net::Duo';
    plan skip_all => 'Net::Duo required for testing duo'
      if $@;
    eval 'use Net::Duo::Mock::Agent';
    plan skip_all => 'Net::Duo::Mock::Agent required for testing duo'
      if $@;
}

BEGIN {
    use_ok('Wallet::Admin');
    use_ok('Wallet::Config');
    use_ok('Wallet::Object::Duo');
}

use lib 't/lib';
use Util;

# Some global defaults to use.
my $user = 'admin@EXAMPLE.COM';
my $host = 'localhost';
my @trace = ($user, $host, time);
my $date = strftime ('%Y-%m-%d %H:%M:%S', localtime $trace[2]);

# Flush all output immediately.
$| = 1;

# Use Wallet::Admin to set up the database.
db_setup;
my $admin = eval { Wallet::Admin->new };
is ($@, '', 'Database connection succeeded');
is ($admin->reinitialize ($user), 1, 'Database initialization succeeded');
my $schema = $admin->schema;

# Create a mock object to use for Duo calls.
my $mock = Net::Duo::Mock::Agent->new ({ key_file => 't/data/duo/keys.json' });

# Test error handling in the absence of configuration.
my $object = eval {
    Wallet::Object::Duo->new ('duo-pam', 'test', $schema);
};
is ($object, undef, 'Wallet::Object::Duo new with no config failed');
is ($@, "duo object implementation not configured\n", '...with correct error');
$object = eval {
    Wallet::Object::Duo->create ('duo-pam', 'test', $schema, @trace);
};
is ($object, undef, 'Wallet::Object::Duo creation with no config failed');
is ($@, "duo object implementation not configured\n", '...with correct error');

# Set up the Duo configuration.
$Wallet::Config::DUO_AGENT    = $mock;
$Wallet::Config::DUO_KEY_FILE = 't/data/duo/keys.json';

# Test creating an integration.
note ('Test creating an integration');
my $expected = {
    name  => 'test (unix)',
    notes => 'Managed by wallet',
    type  => 'unix',
};
$mock->expect (
    {
        method        => 'POST',
        uri           => '/admin/v1/integrations',
        content       => $expected,
        response_file => 't/data/duo/integration.json',
    }
);
$object = Wallet::Object::Duo->create ('duo-pam', 'test', $schema, @trace);
isa_ok ($object, 'Wallet::Object::Duo');

# Check the metadata about the new wallet object.
$expected = <<"EOO";
           Type: duo-pam
           Name: test
        Duo key: DIRWIH0ZZPV4G88B37VQ
     Created by: $user
   Created from: $host
     Created on: $date
EOO
is ($object->show, $expected, 'Show output is correct');

# Test retrieving the integration information.
note ('Test retrieving an integration');
$mock->expect (
    {
        method        => 'GET',
        uri           => '/admin/v1/integrations/DIRWIH0ZZPV4G88B37VQ',
        response_file => 't/data/duo/integration.json',
    }
);
my $data = $object->get (@trace);
ok (defined ($data), 'Retrieval succeeds');
$expected = <<'EOO';
[duo]
ikey = DIRWIH0ZZPV4G88B37VQ
skey = QO4ZLqQVRIOZYkHfdPDORfcNf8LeXIbCWwHazY7o
host = example-admin.duosecurity.com
EOO
is ($data, $expected, '...and integration data is correct');

# Ensure that we can't retrieve the object when locked.
is ($object->flag_set ('locked', @trace), 1,
    'Setting object to locked succeeds');
is ($object->get, undef, '...and now get fails');
is ($object->error, 'cannot get duo-pam:test: object is locked',
    '...with correct error');
is ($object->flag_clear ('locked', @trace), 1,
    '...and clearing locked flag works');

# Create a new object by wallet type and name.
$object = Wallet::Object::Duo->new ('duo-pam', 'test', $schema);

# Test deleting an integration.  We can't test this entirely properly because
# currently Net::Duo::Mock::Agent doesn't support stacking multiple expected
# calls and delete makes two calls.
note ('Test deleting an integration');
$mock->expect (
    {
        method        => 'GET',
        uri           => '/admin/v1/integrations/DIRWIH0ZZPV4G88B37VQ',
        response_file => 't/data/duo/integration.json',
    }
);
TODO: {
    local $TODO = 'Net::Duo::Mock::Agent not yet capable';

    is ($object->destroy (@trace), 1, 'Duo object deletion succeeded');
    $object = eval { Wallet::Object::Duo->new ('duo-pam', 'test', $schema) };
    is ($object, undef, '...and now object cannot be retrieved');
    is ($@, "cannot find duo:test\n", '...with correct error');
}

# Clean up.
$admin->destroy;
END {
    unlink ('wallet-db');
}

# Done testing.
done_testing ();