#!/usr/bin/perl use strict; use warnings; use utf8; use Mojo::File qw(curfile); use lib curfile()->dirname()->sibling('lib')->to_string; use Config::Tiny; use Data::Dumper; use DateTime; use English qw(-no_match_vars); use Getopt::Long qw(:config auto_help); use Pod::Usage; use AccountManager::Account; use AccountManager::Metadata; use AccountManager::Entity; use AccountManager::Token; use AccountManager::Tools; my %options; GetOptions( \%options, 'profile=s', 'configuration=s', 'contacts=s', 'delete', 'displayname=s', 'email_address=s', 'expired', '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_file = $ENV{MOJO_CONFIG} || 'conf/manager.conf'; my $configuration = Config::Tiny->read($configuration_file); if (!$configuration) { die Config::Tiny->errstr() . "\n"; } AccountManager::DB->register_db( driver => $configuration->{database}->{type}, database => $configuration->{database}->{name}, host => $configuration->{database}->{host}, password => $configuration->{database}->{password}, username => $configuration->{database}->{username}, options => [ split(/, */, $configuration->{database}->{options}) ] ); my $db = AccountManager::DB->new(); SWITCH: { if ($action eq 'add_account') { add_account(); last SWITCH; } if ($action eq 'list_accounts') { list_accounts(); last SWITCH; } if ($action eq 'add_service') { add_service(); last SWITCH; } if ($action eq 'list_services') { list_services(); last SWITCH; } if ($action eq 'add_token') { add_token(); last SWITCH; } if ($action eq 'get_token') { get_token(); last SWITCH; } if ($action eq 'list_tokens') { list_tokens(); last SWITCH; } if ($action eq 'parse_metadata') { parse_metadata(); last SWITCH; } 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 entityid option, aborting\n", -verbose => 0 ) unless $options{entityid}; my $entity = $options{entityid}; my $validity_period = $configuration->{$entity}->{account_validity_period} || $configuration->{service}->{account_validity_period}; my $password = AccountManager::Tools::generate_password(); my $account = AccountManager::Account->new( db => $db, profile => $options{profile}, entityid => $options{entityid}, scope => $configuration->{idp}->{scope}, password => $password, password_hash => AccountManager::Tools::sha256_hash($password), creation_date => DateTime->now(), expiration_date => DateTime->now()->add(days => $validity_period) ); die "Failed to save test account\n" unless $account->save(); printf "Account created:\n\tuserid: user%d\n\tpassword: %s\n", $account->id(), $account->password(); } sub list_accounts { my %args; if ($options{entityid}) { push @{ $args{query} }, entityid => $options{entityid}; } if ($options{profile}) { push @{ $args{query} }, profile => $options{profile}; } if ($options{expired}) { push @{ $args{query} }, expiration_date => { lt => DateTime->now() }; } my $accounts = AccountManager::Account->get_accounts(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 = AccountManager::Account->get_accounts( db => $db ); eval { AccountManager::Tools::update_ssp_authsources( $configuration->{setup}->{templates_dir}, $configuration->{setup}->{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 = AccountManager::Metadata->new( file => $configuration->{setup}->{federation_metadata_file} ); }; die "unable to load federation metadata: $EVAL_ERROR" if $EVAL_ERROR; my $data = $federation_metadata->parse(id => $options{entityid}); printf "Document %s parsed\n", $configuration->{setup}->{federation_metadata_file}; ## List SAML entities printf "Hashref representing the metadata:\n"; print Dumper->Dump($data); } sub add_service { pod2usage( -message => "missing entityid option, aborting\n", -verbose => 0 ) unless $options{entityid}; pod2usage( -message => "missing contacts option, aborting\n", -verbose => 0 ) unless $options{contacts}; ## Check if entry already exists in DB first my $provider = AccountManager::Entity->new( db => $db, entityid => $options{entityid} ); if ($provider->load(speculative => 1)) { printf "Entry for %s already in DB; update it with new data\n", $options{entityid}; $provider->contacts($options{contacts}); $provider->displayname($options{displayname}) if $options{displayname}; } else { $provider = AccountManager::Entity->new( db => $db, entityid => $options{entityid}, contacts => $options{contacts}, displayname => $options{displayname} ); } $provider->save() or die "failed to save service provider"; printf "Service Provider created\n"; } sub list_services { my %args; my $providers = AccountManager::Entity->get_entities(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{entityid}) { push @{ $args{query} }, entityid => $options{entityid}; } if ($options{token}) { push @{ $args{query} }, token => $options{token}; } if ($options{expired}) { push @{ $args{query} }, expiration_date => { lt => DateTime->now() }; } my $tokens = AccountManager::Token->get_tokens(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 = AccountManager::Token->new(db => $db, %args); die "No corresponding token found in DB\n" unless $token->load(); if ($options{entityid}) { die "Authentication token cannot be used for this SP\n" unless $token->get('entityid') eq $options{entityid}; } $token->print(); } sub add_token { pod2usage( -message => "missing email_address option, aborting\n", -verbose => 0 ) unless $options{email_address}; pod2usage( -message => "missing entityid option, aborting\n", -verbose => 0 ) unless $options{entityid}; # delete any previous token for the same email/service couple my $old_token = AccountManager::Token->new( db => $db, email_address => $options{email_address}, entityid => $options{entityid} ); if ($old_token->load(speculative => 1)) { $old_token->delete() or die "failed to delete authentication token\n"; } # compute a new token my $validity_period = $configuration->{service}->{tokens_validity_period}; my $token = AccountManager::Token->new( db => $db, email_address => $options{email_address}, entityid => $options{entityid}, creation_date => DateTime->now(), expiration_date => DateTime->now()->add(hours => $validity_period), token => AccountManager::Tools::generate_token() ); $token->save() or die "failed to save authentication token\n"; printf "Authentication token created:\n\token %s\n", $token->token(); } __END__ =head1 NAME access-check-manager.pl - Command line client to the Test IdP Account manager =head1 SYNOPSIS access-check-manager.pl [options] add_account Options: --profile <string> --entityid <string> access-check-manager.pl [options] list_accounts Options: --profile <string> --entityid <string> --expired --delete access-check-manager.pl [options] parse_metadata Options: --entityid <string> access-check-manager.pl [options] add_service Options: --entityid <string> --contact <string> --displayname <string> access-check-manager.pl [options] list_services Options: --delete access-check-manager.pl [options] list_tokens Options: --entityid <string> --token <string> --expired --delete access-check-manager.pl [options] get_token Options: --entityid <string> --token <string> access-check-manager.pl [options] add_token Options: --entityid <string> --email_address <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 $> access-check-manager.pl add_account \ --entityid https://test.federation.renater.fr/test/ressource \ --profile student1 Adds a new test account. $> access-check-manager.pl list_accounts \ --entityid https://test.federation.renater.fr/test/ressource \ --profile student1 List all test accounts. Criterias can be added to filter test accounts. $> access-check-manager.pl list_accounts --expired List all expired test accounts. $> access-check-manager.pl list_accounts --expired --delete Remove all expired test accounts from DB. $> access-check-manager.pl parse_metadata Parses the SAML metadata file, as defined by the C<federation_metadata_file> configuration parameter. $> access-check-manager.pl list_tokens \ --entityid https://test.federation.renater.fr/test/ressource \ --token dhj67sjJ List all authentication tokens. Criterias can be added to filter tokens. $> access-check-manager.pl list_tokens --expired List all expired authentication tokens. $> access-check-manager.pl list_tokens --expired --delete Remove all expired authentication tokens from DB. $> access-check-manager.pl get_token --token dhj67sjJ Get informations on a token. $> access-check-manager.pl add_token --email_address john@my.fqdn \ --entityid https://test.federation.renater.fr/test/ressource Adds a new test account. $> access-check-manager.pl add_service \ --entityid https://test.federation.renater.fr/test/ressource \ --displayname 'Test SP' --contacts email1@dom,email2@dom Adds a new service provider