#!/usr/bin/perl use strict; use warnings; use utf8; use lib qw(@modulesdir@); use feature "switch"; no warnings 'experimental::smartmatch'; use Data::Dumper; use English qw(-no_match_vars); use Getopt::Long qw(:config auto_help); use Log::Any::Adapter; use Pod::Usage; use IdPAccountManager::AuthenticationToken; use IdPAccountManager::AuthenticationToken::Manager; use IdPAccountManager::ServiceProvider; use IdPAccountManager::ServiceProvider::Manager; use IdPAccountManager::TestAccount; use IdPAccountManager::TestAccount::Manager; use IdPAccountManager::Configuration; use IdPAccountManager::SAMLMetadata; use IdPAccountManager::Tools; my %options; GetOptions( \%options, 'profile=s', 'contacts=s', 'delete', 'displayname=s', 'email_address=s', 'filter_expired', 'sp_entityid=s', 'token=s', ) or pod2usage( -message => "unknown option, aborting\n", -verbose => 0 ); my $action = $ARGV[0]; pod2usage( -message => "no action given, aborting\n", -verbose => 0 ) unless $action; my $configuration = IdPAccountManager::Configuration->new( file => '@sysconfdir@/manager.conf' ); IdPAccountManager::DB->register_db( driver => $configuration->{database_type}, database => $configuration->{database_name}, host => $configuration->{database_host}, password => $configuration->{database_password}, username => $configuration->{database_user} ); my $db = IdPAccountManager::DB->new(); for ($action) { when ('add_account') { add_account() } when ('list_accounts') { list_accounts() } when ('add_provider') { add_provider() } when ('list_providers') { list_providers() } when ('add_token') { add_token() } when ('get_token') { get_token() } when ('list_tokens') { list_tokens() } when ('parse_metadata') { parse_metadata() } default { pod2usage( -message => "invalid action '$action', aborting\n", -verbose => 0 ); } } sub add_account { pod2usage( -message => "missing profile option, aborting\n", -verbose => 0 ) unless $options{profile}; pod2usage( -message => "missing sp_entityid option, aborting\n", -verbose => 0 ) unless $options{sp_entityid}; my $test_account = IdPAccountManager::TestAccount->new( db => $db, profile => $options{profile}, sp_entityid => $options{sp_entityid}, scope => $configuration->{idp_scope}, ); die "Failed to create test account\n" unless $test_account; die "Failed to save test account\n" unless $test_account->save( accounts_validity_period => $configuration->{accounts_validity_period} ); printf "Account created:\n\tuserid: user%d\n\tpassword: %s\n", $test_account->id(), $test_account->password(); } sub list_accounts { my %args; if ($options{sp_entityid}) { push @{ $args{query} }, sp_entityid => $options{sp_entityid}; } if ($options{profile}) { push @{ $args{query} }, profile => $options{profile}; } if ($options{filter_expired}) { push @{ $args{query} }, expiration_date => { lt => time }; } my $accounts = IdPAccountManager::TestAccount::Manager->get_testaccounts(db => $db, %args); if (! @$accounts) { printf "No matching test account in DB\n"; } foreach my $account (@$accounts) { $account->print(); } if ($options{delete}) { foreach my $account (@$accounts) { $account->delete() or die "failed to delete test account\n"; } printf "%d accounts removed\n", scalar @$accounts; $accounts = IdPAccountManager::TestAccount::Manager->get_testaccounts( db => $db ); eval { IdPAccountManager::Tools::update_ssp_authsources( $configuration->{templates_dir}, $configuration->{idp_accounts_file}, $accounts ); }; die "failed to update simpleSAMLphp configuration file: $EVAL_ERROR" if $EVAL_ERROR; printf "Update simpleSamlPhp configuration file...\n"; } } sub parse_metadata { my $federation_metadata; eval { $federation_metadata = IdPAccountManager::SAMLMetadata->new( file => $configuration->{federation_metadata_file} ); }; die "unable to load federation metadata: $EVAL_ERROR" if $EVAL_ERROR; my $data = $federation_metadata->parse(id => $options{sp_entityid}); printf "Document %s parsed\n", $configuration->{federation_metadata_file}; ## List SAML entities printf "Hashref representing the metadata:\n"; print Dumper->Dump($data); } sub add_provider { pod2usage( -message => "missing sp_entityid option, aborting\n", -verbose => 0 ) unless $options{sp_entityid}; pod2usage( -message => "missing contacts option, aborting\n", -verbose => 0 ) unless $options{contacts}; ## Check if entry already exists in DB first my $provider = IdPAccountManager::ServiceProvider->new( db => $db, entityid => $options{sp_entityid} ); if ($provider->load(speculative => 1)) { printf "Entry for %s already in DB; update it with new data\n", $options{sp_entityid}; $provider->contacts($options{contacts}); $provider->displayname($options{displayname}) if $options{displayname}; } else { $provider = IdPAccountManager::ServiceProvider->new( db => $db, entityid => $options{sp_entityid}, contacts => $options{contacts}, displayname => $options{displayname} ); die "failed to create service provider\n" unless $provider; } $provider->save() or die "failed to save service provider"; printf "Service Provider created\n"; } sub list_providers { my %args; my $providers = IdPAccountManager::ServiceProvider::Manager->get_serviceproviders(db => $db, %args); if (! @$providers) { printf "No service provider in DB\n"; } foreach my $provider (@$providers) { $provider->print(); } if ($options{delete}) { foreach my $provider (@$providers) { $provider->delete() or die "failed to delete authentication token\n"; } printf "%d providers removed\n", scalar @$providers; } } sub list_tokens { my %args; if ($options{sp_entityid}) { push @{ $args{query} }, sp_entityid => $options{sp_entityid}; } if ($options{token}) { push @{ $args{query} }, token => $options{token}; } if ($options{filter_expired}) { push @{ $args{query} }, creation_date => { lt => time - ($configuration->{tokens_validity_period} * 3600) }; } my $tokens = IdPAccountManager::AuthenticationToken::Manager->get_authenticationtokens(db => $db, %args); if (!@$tokens) { printf "No corresponding token found in DB\n"; } foreach my $token (@$tokens) { $token->print(); } if ($options{delete}) { foreach my $token (@$tokens) { $token->delete() or die "failed to delete authentication token\n"; } printf "%d tokens removed\n", scalar @$tokens; } } sub get_token { my %args; if ($options{token}) { $args{token} = $options{token}; } my $token = IdPAccountManager::AuthenticationToken->new(db => $db, %args); die "No corresponding token found in DB\n" unless $token->load(); if ($options{sp_entityid}) { die "Authentication token cannot be used for this SP\n" unless $token->get('sp_entityid') eq $options{sp_entityid}; } $token->print(); } sub add_token { pod2usage( -message => "missing email_address option, aborting\n", -verbose => 0 ) unless $options{email_address}; pod2usage( -message => "missing sp_entityid option, aborting\n", -verbose => 0 ) unless $options{sp_entityid}; my $token = IdPAccountManager::AuthenticationToken->new( db => $db, email_address => $options{email_address}, sp_entityid => $options{sp_entityid} ); die "failed to create authentication token\n" unless $token; ## First remove token if on exist for this email+SP if ($token->load(speculative => 1)) { $token->delete() or die "failed to delete authentication token\n"; $token = IdPAccountManager::AuthenticationToken->new( db => $db, email_address => $options{email_address}, sp_entityid => $options{sp_entityid} ); die "failed to create authentication token\n" unless $token; } $token->save() or die "failed to save authentication token\n"; $token->print(); } __END__ =head1 NAME account-manager-client.pl - Command line client to the Test IdP Account manager =head1 SYNOPSIS B<account-manager-client.pl> B<add_account> S<B<--profile> I<string>> S<B<--sp_entityid> I<string>> B<account-manager-client.pl> B<list_accounts> S<[B<--profile> I<string>]> S<[B<--sp_entityid> I<string>]> [B<--filter_expired>] [B<--delete>] B<account-manager-client.pl> B<parse_metadata> S<[B<--sp_entityid> I<string>]> B<account-manager-client.pl> B<add_provider> S<B<--sp_entityid> I<string>> S<B<--contact> I<string>> S<[B<--displayname> I<string>]> B<account-manager-client.pl> B<list_providers> [B<--delete>] B<account-manager-client.pl> B<list_tokens> S<B<--sp_entityid> I<string>> S<[B<--token> I<string>]> [B<--filter_expired>] [B<--delete>] B<account-manager-client.pl> B<get_token> S<B<--sp_entityid> I<string>> S<[B<--token> I<string>]> B<account-manager-client.pl> B<add_token> S<B<--sp_entityid> I<string>> S<B<--email_address> I<string>> =head1 DESCRIPTION The Test Account manager instanciates test accounts associated to a SAML Identity Provider. This script provides a command-line interface for most functions. =head1 EXAMPLES $> account-manager-client.pl add_account \ --sp_entityid https://test.federation.renater.fr/test/ressource \ --profile student1 Adds a new test account. $> account-manager-client.pl list_accounts \ --sp_entityid https://test.federation.renater.fr/test/ressource \ --profile student1 List all test accounts. Criterias can be added to filter test accounts. $> account-manager-client.pl list_accounts --filter_expired List all expired test accounts. $> account-manager-client.pl list_accounts --filter_expired \ --delete Remove all expired test accounts from DB. $> account-manager-client.pl parse_metadata Parses the SAML metadata file, as defined by the C<federation_metadata_file> configuration parameter. $> account-manager-client.pl list_tokens \ --sp_entityid https://test.federation.renater.fr/test/ressource \ --token dhj67sjJ List all authentication tokens. Criterias can be added to filter tokens. $> account-manager-client.pl list_tokens \ --filter_expired List all expired authentication tokens. $> account-manager-client.pl list_tokens \ --filter_expired --delete Remove all expired authentication tokens from DB. $> account-manager-client.pl get_token \ --token dhj67sjJ Get informations on a token. $> account-manager-client.pl add_token \ --email_address john@my.fqdn \ --sp_entityid https://test.federation.renater.fr/test/ressource Adds a new test account. $> account-manager-client.pl add_provider \ --sp_entityid https://test.federation.renater.fr/test/ressource \ --displayname 'Test SP' --contacts email1@dom,email2@dom Adds a new Service provider