aboutsummaryrefslogtreecommitdiff
path: root/perl
diff options
context:
space:
mode:
Diffstat (limited to 'perl')
-rw-r--r--perl/lib/Wallet/Config.pm6
-rw-r--r--perl/lib/Wallet/Object/Duo/LDAPProxy.pm2
-rw-r--r--perl/t/data/duo/integration-ldap.json11
-rw-r--r--perl/t/data/duo/integration-radius.json11
-rw-r--r--perl/t/object/duo-ldap.t160
-rw-r--r--perl/t/object/duo-pam.t159
-rw-r--r--perl/t/object/duo-radius.t165
-rwxr-xr-xperl/t/object/duo.t7
8 files changed, 513 insertions, 8 deletions
diff --git a/perl/lib/Wallet/Config.pm b/perl/lib/Wallet/Config.pm
index 527658c..2eb57f9 100644
--- a/perl/lib/Wallet/Config.pm
+++ b/perl/lib/Wallet/Config.pm
@@ -217,9 +217,9 @@ our $DUO_KEY_FILE;
=item DUO_TYPE
-The type of integration to create. Currently, only one type of integration
-can be created by one wallet configuration. This restriction may be relaxed
-in the future. The default value is C<unix> to create UNIX integrations.
+The type of integration to create. The default value is C<unix> to create
+UNIX integrations, since this was the first integration created and users
+may rely on it to still be the default.
=cut
diff --git a/perl/lib/Wallet/Object/Duo/LDAPProxy.pm b/perl/lib/Wallet/Object/Duo/LDAPProxy.pm
index cd32523..757a61a 100644
--- a/perl/lib/Wallet/Object/Duo/LDAPProxy.pm
+++ b/perl/lib/Wallet/Object/Duo/LDAPProxy.pm
@@ -42,7 +42,7 @@ sub create {
$time ||= time;
my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host,
- $time, 'ldap');
+ $time, 'ldapproxy');
return $self;
}
diff --git a/perl/t/data/duo/integration-ldap.json b/perl/t/data/duo/integration-ldap.json
new file mode 100644
index 0000000..78a4c9f
--- /dev/null
+++ b/perl/t/data/duo/integration-ldap.json
@@ -0,0 +1,11 @@
+{
+ "enroll_policy": "enroll",
+ "greeting": "",
+ "groups_allowed": [],
+ "integration_key": "DIRWIH0ZZPV4G88B37VQ",
+ "name": "Integration for LDAP proxy",
+ "notes": "",
+ "secret_key": "QO4ZLqQVRIOZYkHfdPDORfcNf8LeXIbCWwHazY7o",
+ "type": "ldap",
+ "visual_style": "default"
+}
diff --git a/perl/t/data/duo/integration-radius.json b/perl/t/data/duo/integration-radius.json
new file mode 100644
index 0000000..514a33e
--- /dev/null
+++ b/perl/t/data/duo/integration-radius.json
@@ -0,0 +1,11 @@
+{
+ "enroll_policy": "enroll",
+ "greeting": "",
+ "groups_allowed": [],
+ "integration_key": "DIRWIH0ZZPV4G88B37VQ",
+ "name": "Integration for Radius proxy",
+ "notes": "",
+ "secret_key": "QO4ZLqQVRIOZYkHfdPDORfcNf8LeXIbCWwHazY7o",
+ "type": "radius",
+ "visual_style": "default"
+}
diff --git a/perl/t/object/duo-ldap.t b/perl/t/object/duo-ldap.t
new file mode 100644
index 0000000..3648eba
--- /dev/null
+++ b/perl/t/object/duo-ldap.t
@@ -0,0 +1,160 @@
+#!/usr/bin/perl
+#
+# Tests for the Duo Auth proxy LDAP integration object implementation.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2014
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# See LICENSE for licensing terms.
+
+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::LDAPProxy');
+}
+
+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::LDAPProxy->new ('duo-ldap', 'test', $schema);
+};
+is ($object, undef, 'Wallet::Object::Duo::LDAPProxy new with no config failed');
+is ($@, "duo object implementation not configured\n", '...with correct error');
+$object = eval {
+ Wallet::Object::Duo::LDAPProxy->create ('duo-ldap', 'test', $schema,
+ @trace);
+};
+is ($object, undef, 'Wallet::Object::Duo::LDAPProxy 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 (ldapproxy)',
+ notes => 'Managed by wallet',
+ type => 'ldapproxy',
+};
+$mock->expect (
+ {
+ method => 'POST',
+ uri => '/admin/v1/integrations',
+ content => $expected,
+ response_file => 't/data/duo/integration.json',
+ }
+);
+$object = Wallet::Object::Duo::LDAPProxy->create ('duo-ldap', 'test', $schema,
+ @trace);
+isa_ok ($object, 'Wallet::Object::Duo::LDAPProxy');
+
+# Check the metadata about the new wallet object.
+$expected = <<"EOO";
+ Type: duo-ldap
+ 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-ldap.json',
+ }
+);
+my $data = $object->get (@trace);
+ok (defined ($data), 'Retrieval succeeds');
+$expected = <<'EOO';
+[ldap_server_challenge]
+ikey = DIRWIH0ZZPV4G88B37VQ
+skey = QO4ZLqQVRIOZYkHfdPDORfcNf8LeXIbCWwHazY7o
+api_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-ldap: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::LDAPProxy->new ('duo-ldap', '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::LDAPProxy->new ('duo-ldap', '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 ();
diff --git a/perl/t/object/duo-pam.t b/perl/t/object/duo-pam.t
new file mode 100644
index 0000000..7b88787
--- /dev/null
+++ b/perl/t/object/duo-pam.t
@@ -0,0 +1,159 @@
+#!/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
+#
+# See LICENSE for licensing terms.
+
+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::PAM');
+}
+
+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::PAM->new ('duo-pam', 'test', $schema);
+};
+is ($object, undef, 'Wallet::Object::Duo::PAM new with no config failed');
+is ($@, "duo object implementation not configured\n", '...with correct error');
+$object = eval {
+ Wallet::Object::Duo::PAM->create ('duo-pam', 'test', $schema, @trace);
+};
+is ($object, undef, 'Wallet::Object::Duo::PAM 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::PAM->create ('duo-pam', 'test', $schema,
+ @trace);
+isa_ok ($object, 'Wallet::Object::Duo::PAM');
+
+# 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::PAM->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::PAM->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 ();
diff --git a/perl/t/object/duo-radius.t b/perl/t/object/duo-radius.t
new file mode 100644
index 0000000..f258518
--- /dev/null
+++ b/perl/t/object/duo-radius.t
@@ -0,0 +1,165 @@
+#!/usr/bin/perl
+#
+# Tests for the Duo Auth proxy Radius integration object implementation.
+#
+# Written by Russ Allbery <eagle@eyrie.org>
+# Copyright 2014
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# See LICENSE for licensing terms.
+
+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::RadiusProxy');
+}
+
+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::RadiusProxy->new ('duo-raduys', 'test', $schema);
+};
+is ($object, undef,
+ 'Wallet::Object::Duo::RadiusProxy new with no config failed');
+is ($@, "duo object implementation not configured\n", '...with correct error');
+$object = eval {
+ Wallet::Object::Duo::RadiusProxy->create ('duo-radius', 'test', $schema,
+ @trace);
+};
+is ($object, undef,
+ 'Wallet::Object::Duo::RadiusProxy 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 (radius)',
+ notes => 'Managed by wallet',
+ type => 'radius',
+};
+$mock->expect (
+ {
+ method => 'POST',
+ uri => '/admin/v1/integrations',
+ content => $expected,
+ response_file => 't/data/duo/integration-radius.json',
+ }
+);
+$object = Wallet::Object::Duo::RadiusProxy->create ('duo-radius', 'test',
+ $schema, @trace);
+isa_ok ($object, 'Wallet::Object::Duo::RadiusProxy');
+
+# Check the metadata about the new wallet object.
+$expected = <<"EOO";
+ Type: duo-radius
+ 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-radius.json',
+ }
+);
+my $data = $object->get (@trace);
+ok (defined ($data), 'Retrieval succeeds');
+$expected = <<'EOO';
+[radius_server_challenge]
+ikey = DIRWIH0ZZPV4G88B37VQ
+skey = QO4ZLqQVRIOZYkHfdPDORfcNf8LeXIbCWwHazY7o
+api_host = example-admin.duosecurity.com
+client = radius_client
+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-radius: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::RadiusProxy->new ('duo-radius', '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::RadiusProxy->new ('duo-radius', '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 ();
diff --git a/perl/t/object/duo.t b/perl/t/object/duo.t
index f73fe7e..a975597 100755
--- a/perl/t/object/duo.t
+++ b/perl/t/object/duo.t
@@ -108,10 +108,9 @@ $mock->expect (
my $data = $object->get (@trace);
ok (defined ($data), 'Retrieval succeeds');
$expected = <<'EOO';
-[duo]
-ikey = DIRWIH0ZZPV4G88B37VQ
-skey = QO4ZLqQVRIOZYkHfdPDORfcNf8LeXIbCWwHazY7o
-host = example-admin.duosecurity.com
+Integration key: DIRWIH0ZZPV4G88B37VQ
+Secret key: QO4ZLqQVRIOZYkHfdPDORfcNf8LeXIbCWwHazY7o
+Host: example-admin.duosecurity.com
EOO
is ($data, $expected, '...and integration data is correct');