diff options
Diffstat (limited to 'perl/Wallet/Object/Duo.pm')
-rw-r--r-- | perl/Wallet/Object/Duo.pm | 331 |
1 files changed, 0 insertions, 331 deletions
diff --git a/perl/Wallet/Object/Duo.pm b/perl/Wallet/Object/Duo.pm deleted file mode 100644 index e5773c8..0000000 --- a/perl/Wallet/Object/Duo.pm +++ /dev/null @@ -1,331 +0,0 @@ -# Wallet::Object::Duo -- Duo integration object implementation for the wallet. -# -# Written by Russ Allbery <eagle@eyrie.org> -# Copyright 2014 -# The Board of Trustees of the Leland Stanford Junior University -# -# See LICENSE for licensing terms. - -############################################################################## -# Modules and declarations -############################################################################## - -package Wallet::Object::Duo; -require 5.006; - -use strict; -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::Base; - -@ISA = qw(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.01'; - -############################################################################## -# Core methods -############################################################################## - -# Override attr_show to display the Duo integration key attribute. -sub attr_show { - my ($self) = @_; - my $output = ''; - 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; - } - return sprintf ("%15s: %s\n", 'Duo key', $key); -} - -# Override new to start by creating a Net::Duo::Admin object for subsequent -# calls. -sub new { - my ($class, $type, $name, $schema) = @_; - - # We have to have a Duo integration key file set. - if (not $Wallet::Config::DUO_KEY_FILE) { - die "duo object implementation not configured\n"; - } - my $key_file = $Wallet::Config::DUO_KEY_FILE; - my $agent = $Wallet::Config::DUO_AGENT; - - # Construct the Net::Duo::Admin object. - require Net::Duo::Admin; - my $duo = Net::Duo::Admin->new ( - { - key_file => $key_file, - user_agent => $agent, - } - ); - - # Construct the object. - my $self = $class->SUPER::new ($type, $name, $schema); - $self->{duo} = $duo; - return $self; -} - -# Override create to start by creating a new integration in Duo, and only -# create the entry in the database if that succeeds. Error handling isn't -# 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) = @_; - - # We have to have a Duo integration key file set. - if (not $Wallet::Config::DUO_KEY_FILE) { - die "duo object implementation not configured\n"; - } - my $key_file = $Wallet::Config::DUO_KEY_FILE; - my $agent = $Wallet::Config::DUO_AGENT; - - # Construct the Net::Duo::Admin object. - require Net::Duo::Admin; - my $duo = Net::Duo::Admin->new ( - { - key_file => $key_file, - user_agent => $agent, - } - ); - - # Create the object in Duo. - require Net::Duo::Admin::Integration; - my %data = ( - name => $name, - notes => 'Managed by wallet', - type => $Wallet::Config::DUO_TYPE, - ); - my $integration = Net::Duo::Admin::Integration->create ($duo, \%data); - - # Create the object in wallet. - my @trace = ($creator, $host, $time); - my $self = $class->SUPER::create ($type, $name, $schema, @trace); - $self->{duo} = $duo; - - # Add the integration key to the object metadata. - my $guard = $self->{schema}->txn_scope_guard; - eval { - my %record = ( - du_name => $name, - du_key => $integration->integration_key, - ); - $self->{schema}->resultset ('Duo')->create (\%record); - $guard->commit; - }; - if ($@) { - my $id = $self->{type} . ':' . $self->{name}; - $self->error ("cannot set Duo key for $id: $@"); - return; - } - - # Done. Return the object. - return $self; -} - -# Override destroy to delete the integration out of Duo as well. -sub destroy { - my ($self, $user, $host, $time) = @_; - my $id = $self->{type} . ':' . $self->{name}; - if ($self->flag_check ('locked')) { - $self->error ("cannot destroy $id: object is locked"); - return; - } - my $schema = $self->{schema}; - my $guard = $schema->txn_scope_guard; - eval { - my %search = (du_name => $self->{name}); - my $row = $schema->resultset ('Duo')->find (\%search); - my $key = $row->get_column ('du_key'); - my $int = Net::Duo::Admin::Integration->new ($self->{duo}, $key); - $int->delete; - $row->delete; - $guard->commit; - }; - if ($@) { - $self->error ($@); - return; - } - return $self->SUPER::destroy ($user, $host, $time); -} - -# Our get implementation. Retrieve the integration information from Duo and -# construct the configuration file expected by the Duo 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); - 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 - Duo integration object implementation for wallet - -=head1 SYNOPSIS - - my @name = qw(duo host.example.com); - my @trace = ($user, $host, time); - my $object = Wallet::Object::Duo->create (@name, $schema, @trace); - my $config = $object->get (@trace); - $object->destroy (@trace); - -=head1 DESCRIPTION - -Wallet::Object::Duo is a representation of Duo integrations the wallet. -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. - -Currently, only one configured integration type can be managed by the -wallet, and 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::Base. 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 is a class method and should be called on the Wallet::Object::Duo -class. It creates a new object with the given TYPE and NAME (TYPE is -normally C<duo> 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. The new integration's type is controlled by the DUO_TYPE -configuration variable, which defaults to C<unix>. See L<Wallet::Config> -for more information. - -If create() fails, it throws an exception. - -=item destroy(PRINCIPAL, HOSTNAME [, DATETIME]) - -Destroys a Duo integration object by removing it from the database and -deleting the integration from Duo. If deleting the Duo integration fails, -destroy() fails. Returns true on success and false on failure. The -caller should call error() to get the error message after a failure. -PRINCIPAL, HOSTNAME, and DATETIME are stored as history information. -PRINCIPAL should be the user who is destroying the object. If DATETIME -isn't given, the current time is used. - -=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. -Currently, only one Duo integration type is supported as well. Further -development should expand the available integration types, possibly as -additional wallet object types. - -=head1 SEE ALSO - -Net::Duo(3), Wallet::Config(3), Wallet::Object::Base(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 AUTHOR - -Russ Allbery <eagle@eyrie.org> - -=cut |