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');  } | 
