diff options
Diffstat (limited to 'perl')
-rw-r--r-- | perl/lib/Wallet/Admin.pm | 8 | ||||
-rw-r--r-- | perl/lib/Wallet/Object/Duo.pm | 121 | ||||
-rw-r--r-- | perl/lib/Wallet/Object/Duo/LDAPProxy.pm | 202 | ||||
-rw-r--r-- | perl/lib/Wallet/Object/Duo/PAM.pm | 205 | ||||
-rw-r--r-- | perl/lib/Wallet/Object/Duo/RadiusProxy.pm | 204 | ||||
-rw-r--r-- | perl/sql/wallet-1.3-update-duo.sql | 9 | ||||
-rw-r--r-- | perl/t/object/duo-ldap.t | 21 | ||||
-rw-r--r-- | perl/t/object/duo-pam.t | 20 | ||||
-rw-r--r-- | perl/t/object/duo-radius.t | 21 | ||||
-rw-r--r-- | perl/t/object/duo-rdp.t | 20 |
10 files changed, 162 insertions, 669 deletions
diff --git a/perl/lib/Wallet/Admin.pm b/perl/lib/Wallet/Admin.pm index a8b8368..b38cc94 100644 --- a/perl/lib/Wallet/Admin.pm +++ b/perl/lib/Wallet/Admin.pm @@ -126,10 +126,10 @@ sub default_data { # types default rows. my @record = ([ qw/ty_name ty_class/ ], [ 'duo', 'Wallet::Object::Duo' ], - [ 'duo-ldap', 'Wallet::Object::Duo::LDAPProxy' ], - [ 'duo-pam', 'Wallet::Object::Duo::PAM' ], - [ 'duo-radius', 'Wallet::Object::Duo::RadiusProxy' ], - [ 'duo-rdp', 'Wallet::Object::Duo::RDP' ], + [ 'duo-ldap', 'Wallet::Object::Duo' ], + [ 'duo-pam', 'Wallet::Object::Duo' ], + [ 'duo-radius', 'Wallet::Object::Duo' ], + [ 'duo-rdp', 'Wallet::Object::Duo' ], [ 'file', 'Wallet::Object::File' ], [ 'password', 'Wallet::Object::Password' ], [ 'keytab', 'Wallet::Object::Keytab' ], diff --git a/perl/lib/Wallet/Object/Duo.pm b/perl/lib/Wallet/Object/Duo.pm index d08294b..d0901de 100644 --- a/perl/lib/Wallet/Object/Duo.pm +++ b/perl/lib/Wallet/Object/Duo.pm @@ -29,7 +29,100 @@ use Wallet::Object::Base; # This version should be increased on any code change to this module. Always # use two digits for the minor version with a leading zero if necessary so # that it will sort properly. -$VERSION = '0.02'; +$VERSION = '0.03'; + +# Mappings from our types into what Duo calls the integration types. +our %DUO_TYPES = ( + 'duo' => { + integration => 'unix', + output => \&_output_generic, + }, + 'duo-ldap' => { + integration => 'ldapproxy', + output => \&_output_ldap, + }, + 'duo-pam' => { + integration => 'unix', + output => \&_output_pam, + }, + 'duo-radius' => { + integration => 'radius', + output => \&_output_radius, + }, + ); + +# Extra types to add. These are all just named as the Duo integration name +# with duo- before it and go to the generic output. Put them here to prevent +# pages of settings. These are also not all actually set as types in the +# types table to prevent overpopulation. You should manually create the +# entries in that table for any Duo integrations you want to add. +our @EXTRA_TYPES = ('accountsapi', 'adfs', 'adminapi', 'array', 'barracuda', + 'cisco', 'citrixcag', 'citrixns', 'confluence', 'drupal', + 'f5bigip', 'f5firepass', 'fortinet', 'jira', 'juniper', + 'juniperuac', 'lastpass', 'okta', 'onelogin', 'openvpn', + 'openvpnas', 'owa', 'paloalto', 'rdgateway', 'rdp', + 'rdweb', 'rest', 'rras', 'shibboleth', 'sonicwallsra', + 'splunk', 'tmg', 'uag', 'verify', 'vmwareview', 'websdk', + 'wordpress'); +for my $type (@EXTRA_TYPES) { + my $wallet_type = 'duo-'.$type; + $DUO_TYPES{$wallet_type}{integration} = $type; + $DUO_TYPES{$wallet_type}{output} = \&_output_generic; +}; + +############################################################################## +# Get output methods +############################################################################## + +# Output for any miscellaneous Duo integration, usually those that use a GUI +# to set information and so don't need a custom configuration file. +sub _output_generic { + my ($key, $secret, $hostname) = @_; + + my $output; + $output .= "Integration key: $key\n"; + $output .= "Secret key: $secret\n"; + $output .= "Host: $hostname\n"; + + return $output; +} + +# Output for the Duo unix integration, which hooks into the PAM stack. +sub _output_pam { + my ($key, $secret, $hostname) = @_; + + my $output = "[duo]\n"; + $output .= "ikey = $key\n"; + $output .= "skey = $secret\n"; + $output .= "host = $hostname\n"; + + return $output; +} + +# Output for the radius proxy, which can be plugged into the proxy config. +sub _output_radius { + my ($key, $secret, $hostname) = @_; + + my $output = "[radius_server_challenge]\n"; + $output .= "ikey = $key\n"; + $output .= "skey = $secret\n"; + $output .= "api_host = $hostname\n"; + $output .= "client = radius_client\n"; + + return $output; +} + +# Output for the LDAP proxy, which can be plugged into the proxy config. +sub _output_ldap { + my ($key, $secret, $hostname) = @_; + + my $output = "[ldap_server_challenge]\n"; + $output .= "ikey = $key\n"; + $output .= "skey = $secret\n"; + $output .= "api_host = $hostname\n"; + + return $output; +} ############################################################################## # Core methods @@ -86,7 +179,7 @@ sub new { # great here since we don't have a way to communicate the error back to the # caller. sub create { - my ($class, $type, $name, $schema, $creator, $host, $time, $duo_type) = @_; + my ($class, $type, $name, $schema, $creator, $host, $time) = @_; # We have to have a Duo integration key file set. if (not $Wallet::Config::DUO_KEY_FILE) { @@ -95,6 +188,12 @@ sub create { my $key_file = $Wallet::Config::DUO_KEY_FILE; my $agent = $Wallet::Config::DUO_AGENT; + # Make sure this is actually a type we know about, since this handler + # can handle many types. + if (!exists $DUO_TYPES{$type}) { + die "$type is not a valid duo integration\n"; + } + # Construct the Net::Duo::Admin object. require Net::Duo::Admin; my $duo = Net::Duo::Admin->new ( @@ -106,7 +205,7 @@ sub create { # Create the object in Duo. require Net::Duo::Admin::Integration; - $duo_type ||= $Wallet::Config::DUO_TYPE; + my $duo_type = $DUO_TYPES{$type}{integration}; my %data = ( name => "$name ($duo_type)", notes => 'Managed by wallet', @@ -201,11 +300,17 @@ sub get { my $json = JSON->new->utf8 (1)->relaxed (1); my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE); - # Construct the returned file. - my $output; - $output .= "Integration key: $key\n"; - $output .= 'Secret key: ' . $integration->secret_key . "\n"; - $output .= "Host: $config->{api_hostname}\n"; + # Construct the returned file. Assume the generic handler in case there + # is no valid handler, though that shouldn't happen. + my $output_sub; + my $type = $self->{type}; + if (exists $DUO_TYPES{$type}{output}) { + $output_sub = $DUO_TYPES{$type}{output}; + } else { + $output_sub = \&_output_generic; + } + my $output = $output_sub->($key, $integration->secret_key, + $config->{api_hostname}); # Log the action and return. $self->log_action ('get', $user, $host, $time); diff --git a/perl/lib/Wallet/Object/Duo/LDAPProxy.pm b/perl/lib/Wallet/Object/Duo/LDAPProxy.pm deleted file mode 100644 index 23894ac..0000000 --- a/perl/lib/Wallet/Object/Duo/LDAPProxy.pm +++ /dev/null @@ -1,202 +0,0 @@ -# Wallet::Object::Duo::LDAPProxy -- Duo auth proxy integration for LDAP -# -# Written by Jon Robertson <jonrober@stanford.edu> -# Copyright 2014 -# The Board of Trustees of the Leland Stanford Junior University -# -# See LICENSE for licensing terms. - -############################################################################## -# Modules and declarations -############################################################################## - -package Wallet::Object::Duo::LDAPProxy; -require 5.006; - -use strict; -use warnings; -use vars qw(@ISA $VERSION); - -use JSON; -use Net::Duo::Admin; -use Net::Duo::Admin::Integration; -use Perl6::Slurp qw(slurp); -use Wallet::Config (); -use Wallet::Object::Duo; - -@ISA = qw(Wallet::Object::Duo); - -# This version should be increased on any code change to this module. Always -# use two digits for the minor version with a leading zero if necessary so -# that it will sort properly. -$VERSION = '0.01'; - -############################################################################## -# Core methods -############################################################################## - -# Override create to provide the specific Duo integration type that will be -# used in the remote Duo record. -sub create { - my ($class, $type, $name, $schema, $creator, $host, $time) = @_; - - $time ||= time; - my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host, - $time, 'ldapproxy'); - return $self; -} - -# Override get to output the data in a specific format used for Duo LDAP -# integration -sub get { - my ($self, $user, $host, $time) = @_; - $time ||= time; - - # Check that the object isn't locked. - my $id = $self->{type} . ':' . $self->{name}; - if ($self->flag_check ('locked')) { - $self->error ("cannot get $id: object is locked"); - return; - } - - # Retrieve the integration from Duo. - my $key; - eval { - my %search = (du_name => $self->{name}); - my $row = $self->{schema}->resultset ('Duo')->find (\%search); - $key = $row->get_column ('du_key'); - }; - if ($@) { - $self->error ($@); - return; - } - my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key); - - # We also need the admin server name, which we can get from the Duo object - # configuration with a bit of JSON decoding. - my $json = JSON->new->utf8 (1)->relaxed (1); - my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE); - - # Construct the returned file. - my $output = "[ldap_server_challenge]\n"; - $output .= "ikey = $key\n"; - $output .= 'skey = ' . $integration->secret_key . "\n"; - $output .= "api_host = $config->{api_hostname}\n"; - - # Log the action and return. - $self->log_action ('get', $user, $host, $time); - return $output; -} - -1; -__END__ - -############################################################################## -# Documentation -############################################################################## - -=for stopwords -Allbery Duo integration DBH keytab LDAP auth - -=head1 NAME - -Wallet::Object::Duo::LDAPProxy - Duo auth proxy integration for LDAP - -=head1 SYNOPSIS - - my @name = qw(duo-ldap host.example.com); - my @trace = ($user, $host, time); - my $object = Wallet::Object::Duo::LDAPProxy->create (@name, $schema, @trace); - my $config = $object->get (@trace); - $object->destroy (@trace); - -=head1 DESCRIPTION - -Wallet::Object::Duo::LDAPProxy is a representation of Duo -integrations with the wallet, specifically to output Duo integrations -in a format that can easily be pulled into configuring the Duo -Authentication Proxy for LDAP. It implements the wallet object API -and provides the necessary glue to create a Duo integration, return a -configuration file containing the key and API information for that -integration, and delete the integration from Duo when the wallet object -is destroyed. - -The integration information is always returned in the configuration file -format expected by the Authentication Proxy for Duo in configuring it -for LDAP. - -This object can be retrieved repeatedly without changing the secret key, -matching Duo's native behavior with integrations. To change the keys of -the integration, delete it and recreate it. - -To use this object, at least one configuration parameter must be set. See -L<Wallet::Config> for details on supported configuration parameters and -information about how to set wallet configuration. - -=head1 METHODS - -This object mostly inherits from Wallet::Object::Duo. See the -documentation for that class for all generic methods. Below are only -those methods that are overridden or behave specially for this -implementation. - -=over 4 - -=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME]) - -This will override the Wallet::Object::Duo class with the information -needed to create a specific integration type in Duo. It creates a new -object with the given TYPE and NAME (TYPE is normally C<duo-ldap> and -must be for the rest of the wallet system to use the right class, but -this module doesn't check for ease of subclassing), using DBH as the -handle to the wallet metadata database. PRINCIPAL, HOSTNAME, and -DATETIME are stored as history information. PRINCIPAL should be the -user who is creating the object. If DATETIME isn't given, the current -time is used. - -When a new Duo integration object is created, a new integration will be -created in the configured Duo account and the integration key will be -stored in the wallet object. If the integration already exists, create() -will fail. - -If create() fails, it throws an exception. - -=item get(PRINCIPAL, HOSTNAME [, DATETIME]) - -Retrieves the configuration information for the Duo integration and -returns that information in the format expected by the configuration file -for the Duo UNIX integration. Returns undef on failure. The caller -should call error() to get the error message if get() returns undef. - -The returned configuration look look like: - - [ldap_server_challenge] - ikey = <integration-key> - skey = <secret-key> - api_host = <api-hostname> - -The C<host> parameter will be taken from the configuration file pointed -to by the DUO_KEY_FILE configuration variable. - -PRINCIPAL, HOSTNAME, and DATETIME are stored as history information. -PRINCIPAL should be the user who is downloading the keytab. If DATETIME -isn't given, the current time is used. - -=back - -=head1 LIMITATIONS - -Only one Duo account is supported for a given wallet implementation. - -=head1 SEE ALSO - -Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8) - -This module is part of the wallet system. The current version is -available from L<http://www.eyrie.org/~eagle/software/wallet/>. - -=head1 AUTHORS - -Jon Robertson <jonrober@stanford.edu> - -=cut diff --git a/perl/lib/Wallet/Object/Duo/PAM.pm b/perl/lib/Wallet/Object/Duo/PAM.pm deleted file mode 100644 index d9d17f8..0000000 --- a/perl/lib/Wallet/Object/Duo/PAM.pm +++ /dev/null @@ -1,205 +0,0 @@ -# Wallet::Object::Duo::PAM -- Duo PAM int. object implementation for wallet -# -# Written by Russ Allbery <eagle@eyrie.org> -# Jon Robertson <jonrober@stanford.edu> -# Copyright 2014 -# The Board of Trustees of the Leland Stanford Junior University -# -# See LICENSE for licensing terms. - -############################################################################## -# Modules and declarations -############################################################################## - -package Wallet::Object::Duo::PAM; -require 5.006; - -use strict; -use warnings; -use vars qw(@ISA $VERSION); - -use JSON; -use Net::Duo::Admin; -use Net::Duo::Admin::Integration; -use Perl6::Slurp qw(slurp); -use Wallet::Config (); -use Wallet::Object::Duo; - -@ISA = qw(Wallet::Object::Duo); - -# This version should be increased on any code change to this module. Always -# use two digits for the minor version with a leading zero if necessary so -# that it will sort properly. -$VERSION = '0.01'; - -############################################################################## -# Core methods -############################################################################## - -# Override create to provide the specific Duo integration type that will be -# used in the remote Duo record. -sub create { - my ($class, $type, $name, $schema, $creator, $host, $time) = @_; - - $time ||= time; - my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host, - $time, 'unix'); - return $self; -} - -# Override get to output the data in a specific format used by Duo's PAM -# module. -sub get { - my ($self, $user, $host, $time) = @_; - $time ||= time; - - # Check that the object isn't locked. - my $id = $self->{type} . ':' . $self->{name}; - if ($self->flag_check ('locked')) { - $self->error ("cannot get $id: object is locked"); - return; - } - - # Retrieve the integration from Duo. - my $key; - eval { - my %search = (du_name => $self->{name}); - my $row = $self->{schema}->resultset ('Duo')->find (\%search); - $key = $row->get_column ('du_key'); - }; - if ($@) { - $self->error ($@); - return; - } - my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key); - - # We also need the admin server name, which we can get from the Duo object - # configuration with a bit of JSON decoding. - my $json = JSON->new->utf8 (1)->relaxed (1); - my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE); - - # Construct the returned file. - my $output = "[duo]\n"; - $output .= "ikey = $key\n"; - $output .= 'skey = ' . $integration->secret_key . "\n"; - $output .= "host = $config->{api_hostname}\n"; - - # Log the action and return. - $self->log_action ('get', $user, $host, $time); - return $output; -} - -1; -__END__ - -############################################################################## -# Documentation -############################################################################## - -=for stopwords -Allbery Duo integration DBH keytab - -=head1 NAME - -Wallet::Object::Duo::PAM - Duo PAM int. object implementation for wallet - -=head1 SYNOPSIS - - my @name = qw(duo-pam host.example.com); - my @trace = ($user, $host, time); - my $object = Wallet::Object::Duo::PAM->create (@name, $schema, @trace); - my $config = $object->get (@trace); - $object->destroy (@trace); - -=head1 DESCRIPTION - -Wallet::Object::Duo::PAM is a representation of Duo integrations with -the wallet, specifically to output Duo integrations in a format that -can easily be pulled into configuring the Duo PAM interface. It -implements the wallet object API and provides the necessary glue to -create a Duo integration, return a configuration file containing the key -and API information for that integration, and delete the integration from -Duo when the wallet object is destroyed. - -The integration information is always returned in the configuration file -format expected by the Duo UNIX integration. The results of retrieving -this object will be text, suitable for putting in the UNIX integration -configuration file, containing the integration key, secret key, and admin -hostname for that integration. - -This object can be retrieved repeatedly without changing the secret key, -matching Duo's native behavior with integrations. To change the keys of -the integration, delete it and recreate it. - -To use this object, at least one configuration parameter must be set. See -L<Wallet::Config> for details on supported configuration parameters and -information about how to set wallet configuration. - -=head1 METHODS - -This object mostly inherits from Wallet::Object::Duo. See the -documentation for that class for all generic methods. Below are only -those methods that are overridden or behave specially for this -implementation. - -=over 4 - -=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME]) - -This will override the Wallet::Object::Duo class with the information -needed to create a specific integration type in Duo. It creates a new -object with the given TYPE and NAME (TYPE is normally C<duo-pam> and must -be for the rest of the wallet system to use the right class, but this -module doesn't check for ease of subclassing), using DBH as the handle -to the wallet metadata database. PRINCIPAL, HOSTNAME, and DATETIME are -stored as history information. PRINCIPAL should be the user who is -creating the object. If DATETIME isn't given, the current time is -used. - -When a new Duo integration object is created, a new integration will be -created in the configured Duo account and the integration key will be -stored in the wallet object. If the integration already exists, create() -will fail. - -If create() fails, it throws an exception. - -=item get(PRINCIPAL, HOSTNAME [, DATETIME]) - -Retrieves the configuration information for the Duo integration and -returns that information in the format expected by the configuration file -for the Duo UNIX integration. Returns undef on failure. The caller -should call error() to get the error message if get() returns undef. - -The returned configuration look look like: - - [duo] - ikey = <integration-key> - skey = <secret-key> - host = <api-hostname> - -The C<host> parameter will be taken from the configuration file pointed -to by the DUO_KEY_FILE configuration variable. - -PRINCIPAL, HOSTNAME, and DATETIME are stored as history information. -PRINCIPAL should be the user who is downloading the keytab. If DATETIME -isn't given, the current time is used. - -=back - -=head1 LIMITATIONS - -Only one Duo account is supported for a given wallet implementation. - -=head1 SEE ALSO - -Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8) - -This module is part of the wallet system. The current version is -available from L<http://www.eyrie.org/~eagle/software/wallet/>. - -=head1 AUTHORS - -Russ Allbery <eagle@eyrie.org> -Jon Robertson <eagle@eyrie.org> - -=cut diff --git a/perl/lib/Wallet/Object/Duo/RadiusProxy.pm b/perl/lib/Wallet/Object/Duo/RadiusProxy.pm deleted file mode 100644 index a1f6e24..0000000 --- a/perl/lib/Wallet/Object/Duo/RadiusProxy.pm +++ /dev/null @@ -1,204 +0,0 @@ -# Wallet::Object::Duo::RadiusProxy -- Duo auth proxy integration for radius -# -# Written by Jon Robertson <jonrober@stanford.edu> -# Copyright 2014 -# The Board of Trustees of the Leland Stanford Junior University -# -# See LICENSE for licensing terms. - -############################################################################## -# Modules and declarations -############################################################################## - -package Wallet::Object::Duo::RadiusProxy; -require 5.006; - -use strict; -use warnings; -use vars qw(@ISA $VERSION); - -use JSON; -use Net::Duo::Admin; -use Net::Duo::Admin::Integration; -use Perl6::Slurp qw(slurp); -use Wallet::Config (); -use Wallet::Object::Duo; - -@ISA = qw(Wallet::Object::Duo); - -# This version should be increased on any code change to this module. Always -# use two digits for the minor version with a leading zero if necessary so -# that it will sort properly. -$VERSION = '0.01'; - -############################################################################## -# Core methods -############################################################################## - -# Override create to provide the specific Duo integration type that will be -# used in the remote Duo record. -sub create { - my ($class, $type, $name, $schema, $creator, $host, $time) = @_; - - $time ||= time; - my $self = $class->SUPER::create ($type, $name, $schema, $creator, $host, - $time, 'radius'); - return $self; -} - -# Override get to output the data in a specific format used for Duo radius -# integration -sub get { - my ($self, $user, $host, $time) = @_; - $time ||= time; - - # Check that the object isn't locked. - my $id = $self->{type} . ':' . $self->{name}; - if ($self->flag_check ('locked')) { - $self->error ("cannot get $id: object is locked"); - return; - } - - # Retrieve the integration from Duo. - my $key; - eval { - my %search = (du_name => $self->{name}); - my $row = $self->{schema}->resultset ('Duo')->find (\%search); - $key = $row->get_column ('du_key'); - }; - if ($@) { - $self->error ($@); - return; - } - my $integration = Net::Duo::Admin::Integration->new ($self->{duo}, $key); - - # We also need the admin server name, which we can get from the Duo object - # configuration with a bit of JSON decoding. - my $json = JSON->new->utf8 (1)->relaxed (1); - my $config = $json->decode (scalar slurp $Wallet::Config::DUO_KEY_FILE); - - # Construct the returned file. - my $output = "[radius_server_challenge]\n"; - $output .= "ikey = $key\n"; - $output .= 'skey = ' . $integration->secret_key . "\n"; - $output .= "api_host = $config->{api_hostname}\n"; - $output .= "client = radius_client\n"; - - # Log the action and return. - $self->log_action ('get', $user, $host, $time); - return $output; -} - -1; -__END__ - -############################################################################## -# Documentation -############################################################################## - -=for stopwords -Allbery Duo integration DBH keytab auth - -=head1 NAME - -Wallet::Object::Duo::RadiusProxy - Duo auth proxy integration for RADIUS - -=head1 SYNOPSIS - - my @name = qw(duo-radius host.example.com); - my @trace = ($user, $host, time); - my $object = Wallet::Object::Duo::RadiusProxy->create (@name, $schema, @trace); - my $config = $object->get (@trace); - $object->destroy (@trace); - -=head1 DESCRIPTION - -Wallet::Object::Duo::RadiusProxy is a representation of Duo -integrations with the wallet, specifically to output Duo integrations -in a format that can easily be pulled into configuring the Duo -Authentication Proxy for Radius. It implements the wallet object API -and provides the necessary glue to create a Duo integration, return a -configuration file containing the key and API information for that -integration, and delete the integration from Duo when the wallet object -is destroyed. - -The integration information is always returned in the configuration file -format expected by the Authentication Proxy for Duo in configuring it -for Radius. - -This object can be retrieved repeatedly without changing the secret key, -matching Duo's native behavior with integrations. To change the keys of -the integration, delete it and recreate it. - -To use this object, at least one configuration parameter must be set. See -L<Wallet::Config> for details on supported configuration parameters and -information about how to set wallet configuration. - -=head1 METHODS - -This object mostly inherits from Wallet::Object::Duo. See the -documentation for that class for all generic methods. Below are only -those methods that are overridden or behave specially for this -implementation. - -=over 4 - -=item create(TYPE, NAME, DBH, PRINCIPAL, HOSTNAME [, DATETIME]) - -This will override the Wallet::Object::Duo class with the information -needed to create a specific integration type in Duo. It creates a new -object with the given TYPE and NAME (TYPE is normally C<duo-radius> and -must be for the rest of the wallet system to use the right class, but -this module doesn't check for ease of subclassing), using DBH as the -handle to the wallet metadata database. PRINCIPAL, HOSTNAME, and -DATETIME are stored as history information. PRINCIPAL should be the -user who is creating the object. If DATETIME isn't given, the current -time is used. - -When a new Duo integration object is created, a new integration will be -created in the configured Duo account and the integration key will be -stored in the wallet object. If the integration already exists, create() -will fail. - -If create() fails, it throws an exception. - -=item get(PRINCIPAL, HOSTNAME [, DATETIME]) - -Retrieves the configuration information for the Duo integration and -returns that information in the format expected by the configuration file -for the Duo UNIX integration. Returns undef on failure. The caller -should call error() to get the error message if get() returns undef. - -The returned configuration look look like: - - [radius_server_challenge] - ikey = <integration-key> - skey = <secret-key> - api_host = <api-hostname> - client = radius_client - -The C<host> parameter will be taken from the configuration file pointed -to by the DUO_KEY_FILE configuration variable. - -PRINCIPAL, HOSTNAME, and DATETIME are stored as history information. -PRINCIPAL should be the user who is downloading the keytab. If DATETIME -isn't given, the current time is used. - -=back - -=head1 LIMITATIONS - -Only one Duo account is supported for a given wallet implementation. - -=head1 SEE ALSO - -Net::Duo(3), Wallet::Config(3), Wallet::Object::Duo(3), wallet-backend(8) - -This module is part of the wallet system. The current version is -available from L<http://www.eyrie.org/~eagle/software/wallet/>. - -=head1 AUTHORS - -Jon Robertson <jonrober@stanford.edu> - -=cut diff --git a/perl/sql/wallet-1.3-update-duo.sql b/perl/sql/wallet-1.3-update-duo.sql new file mode 100644 index 0000000..affadcd --- /dev/null +++ b/perl/sql/wallet-1.3-update-duo.sql @@ -0,0 +1,9 @@ +-- +-- Run on installing wallet 1.3 in order to update what the Duo types +-- point to for modules. +-- + +UPDATE types set ty_class='Wallet::Object::Duo' where ty_name='duo-ldap'; +UPDATE types set ty_class='Wallet::Object::Duo' where ty_name='duo-pam'; +UPDATE types set ty_class='Wallet::Object::Duo' where ty_name='duo-radius'; +UPDATE types set ty_class='Wallet::Object::Duo' where ty_name='duo-rdp'; diff --git a/perl/t/object/duo-ldap.t b/perl/t/object/duo-ldap.t index 3648eba..8a00dbb 100644 --- a/perl/t/object/duo-ldap.t +++ b/perl/t/object/duo-ldap.t @@ -26,7 +26,7 @@ BEGIN { BEGIN { use_ok('Wallet::Admin'); use_ok('Wallet::Config'); - use_ok('Wallet::Object::Duo::LDAPProxy'); + use_ok('Wallet::Object::Duo'); } use lib 't/lib'; @@ -53,15 +53,14 @@ 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); + Wallet::Object::Duo->new ('duo-ldap', 'test', $schema); }; -is ($object, undef, 'Wallet::Object::Duo::LDAPProxy new with no config failed'); +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::LDAPProxy->create ('duo-ldap', 'test', $schema, - @trace); + Wallet::Object::Duo->create ('duo-ldap', 'test', $schema, @trace); }; -is ($object, undef, 'Wallet::Object::Duo::LDAPProxy creation with no config failed'); +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. @@ -83,9 +82,8 @@ $mock->expect ( 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'); +$object = Wallet::Object::Duo->create ('duo-ldap', 'test', $schema, @trace); +isa_ok ($object, 'Wallet::Object::Duo'); # Check the metadata about the new wallet object. $expected = <<"EOO"; @@ -127,7 +125,7 @@ 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); +$object = Wallet::Object::Duo->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 @@ -144,8 +142,7 @@ 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) }; + $object = eval { Wallet::Object::Duo->new ('duo-ldap', 'test', $schema) }; is ($object, undef, '...and now object cannot be retrieved'); is ($@, "cannot find duo:test\n", '...with correct error'); } diff --git a/perl/t/object/duo-pam.t b/perl/t/object/duo-pam.t index 7b88787..047343e 100644 --- a/perl/t/object/duo-pam.t +++ b/perl/t/object/duo-pam.t @@ -26,7 +26,7 @@ BEGIN { BEGIN { use_ok('Wallet::Admin'); use_ok('Wallet::Config'); - use_ok('Wallet::Object::Duo::PAM'); + use_ok('Wallet::Object::Duo'); } use lib 't/lib'; @@ -53,14 +53,14 @@ 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); + Wallet::Object::Duo->new ('duo-pam', 'test', $schema); }; -is ($object, undef, 'Wallet::Object::Duo::PAM new with no config failed'); +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::PAM->create ('duo-pam', 'test', $schema, @trace); + Wallet::Object::Duo->create ('duo-pam', 'test', $schema, @trace); }; -is ($object, undef, 'Wallet::Object::Duo::PAM creation with no config failed'); +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. @@ -82,9 +82,8 @@ $mock->expect ( 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'); +$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"; @@ -126,7 +125,7 @@ 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); +$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 @@ -143,8 +142,7 @@ 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) }; + $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'); } diff --git a/perl/t/object/duo-radius.t b/perl/t/object/duo-radius.t index f258518..55cbb9d 100644 --- a/perl/t/object/duo-radius.t +++ b/perl/t/object/duo-radius.t @@ -26,7 +26,7 @@ BEGIN { BEGIN { use_ok('Wallet::Admin'); use_ok('Wallet::Config'); - use_ok('Wallet::Object::Duo::RadiusProxy'); + use_ok('Wallet::Object::Duo'); } use lib 't/lib'; @@ -53,17 +53,16 @@ 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); + Wallet::Object::Duo->new ('duo-radius', 'test', $schema); }; is ($object, undef, - 'Wallet::Object::Duo::RadiusProxy new with no config failed'); + 'Wallet::Object::Duo 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); + Wallet::Object::Duo->create ('duo-radius', 'test', $schema, @trace); }; is ($object, undef, - 'Wallet::Object::Duo::RadiusProxy creation with no config failed'); + 'Wallet::Object::Duo creation with no config failed'); is ($@, "duo object implementation not configured\n", '...with correct error'); # Set up the Duo configuration. @@ -85,9 +84,8 @@ $mock->expect ( 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'); +$object = Wallet::Object::Duo->create ('duo-radius', 'test', $schema, @trace); +isa_ok ($object, 'Wallet::Object::Duo'); # Check the metadata about the new wallet object. $expected = <<"EOO"; @@ -130,8 +128,7 @@ 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); +$object = Wallet::Object::Duo->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 @@ -149,7 +146,7 @@ TODO: { is ($object->destroy (@trace), 1, 'Duo object deletion succeeded'); $object = eval { - Wallet::Object::Duo::RadiusProxy->new ('duo-radius', 'test', $schema); + Wallet::Object::Duo->new ('duo-radius', 'test', $schema); }; is ($object, undef, '...and now object cannot be retrieved'); is ($@, "cannot find duo:test\n", '...with correct error'); diff --git a/perl/t/object/duo-rdp.t b/perl/t/object/duo-rdp.t index 9b2d566..25060ac 100644 --- a/perl/t/object/duo-rdp.t +++ b/perl/t/object/duo-rdp.t @@ -26,7 +26,7 @@ BEGIN { BEGIN { use_ok('Wallet::Admin'); use_ok('Wallet::Config'); - use_ok('Wallet::Object::Duo::RDP'); + use_ok('Wallet::Object::Duo'); } use lib 't/lib'; @@ -53,14 +53,14 @@ 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::RDP->new ('duo-rdp', 'test', $schema); + Wallet::Object::Duo->new ('duo-rdp', 'test', $schema); }; -is ($object, undef, 'Wallet::Object::Duo::RDP new with no config failed'); +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::RDP->create ('duo-rdp', 'test', $schema, @trace); + Wallet::Object::Duo->create ('duo-rdp', 'test', $schema, @trace); }; -is ($object, undef, 'Wallet::Object::Duo::RDP creation with no config failed'); +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. @@ -82,9 +82,8 @@ $mock->expect ( response_file => 't/data/duo/integration-rdp.json', } ); -$object = Wallet::Object::Duo::RDP->create ('duo-rdp', 'test', $schema, - @trace); -isa_ok ($object, 'Wallet::Object::Duo::RDP'); +$object = Wallet::Object::Duo->create ('duo-rdp', 'test', $schema, @trace); +isa_ok ($object, 'Wallet::Object::Duo'); # Check the metadata about the new wallet object. $expected = <<"EOO"; @@ -125,7 +124,7 @@ 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::RDP->new ('duo-rdp', 'test', $schema); +$object = Wallet::Object::Duo->new ('duo-rdp', '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 @@ -142,8 +141,7 @@ TODO: { local $TODO = 'Net::Duo::Mock::Agent not yet capable'; is ($object->destroy (@trace), 1, 'Duo object deletion succeeded'); - $object = eval { Wallet::Object::Duo::RDP->new ('duo-rdp', 'test', - $schema) }; + $object = eval { Wallet::Object::Duo->new ('duo-rdp', 'test', $schema) }; is ($object, undef, '...and now object cannot be retrieved'); is ($@, "cannot find duo:test\n", '...with correct error'); } |