diff --git a/bin/account-manager-client.pl b/bin/account-manager-client.pl index 8f98bfaa0c6227da24c9d64c3ca2a15ae5012b2f..9e2ecc48e18789fb674f67e89b2a45b643dae237 100755 --- a/bin/account-manager-client.pl +++ b/bin/account-manager-client.pl @@ -17,7 +17,7 @@ use IdPAccountManager::ServiceProvider; use IdPAccountManager::AuthenticationToken; my %options; -unless (&GetOptions(\%options, 'help', 'add_test_account', 'account_profile=s', 'sp_entityid=s', 'list_test_accounts', 'parse_federation_metadata', 'list_service_providers','list_authentication_tokens', 'add_authentication_token','email_address=s')) { +unless (&GetOptions(\%options, 'help', 'add_test_account', 'account_profile=s', 'sp_entityid=s', 'list_test_accounts', 'parse_federation_metadata', 'list_service_providers','list_authentication_tokens', 'get_authentication_token', 'add_authentication_token','email_address=s','token=s')) { die "Unknown options."; } @@ -108,17 +108,45 @@ if ($options{'add_test_account'}) { }elsif ($options{'list_authentication_tokens'}) { my %args; + if ($options{'sp_entityid'}) { + push @{$args{'query'}}, 'sp_entityid' => $options{'sp_entityid'}; + } + if ($options{'token'}) { + push @{$args{'query'}}, 'token' => $options{'token'}; + } my $all = IdPAccountManager::AuthenticationToken::list_authentication_tokens(%args); if ($#{$all} < 0) { - printf "No token in DB\n"; + printf "No corresponding token found in DB\n"; } foreach my $authentication_token (@$all) { $authentication_token->print(); } +}elsif ($options{'get_authentication_token'}) { + + my %args; + if ($options{'token'}) { + $args{'token'} = $options{'token'}; + } + + my $authentication_token = new IdPAccountManager::AuthenticationToken(%args); + + unless ($authentication_token->load()) { + die "No corresponding token found in DB\n"; + } + + if ($options{'sp_entityid'}) { + unless ($authentication_token->get('sp_entityid') eq $options{'sp_entityid'}) { + die "Authentication token cannot be used for this SP\n"; + } + } + + + $authentication_token->print(); + }elsif ($options{'add_authentication_token'}) { unless ($options{'email_address'}) { diff --git a/bin/account-manager-web.pl b/bin/account-manager-web.pl index eaf7e61cf25f0f134e2ccb5c88d0bde0b50cbc26..d7c1f5151ff98f0069f870b4d128eecdb19cae71 100755 --- a/bin/account-manager-web.pl +++ b/bin/account-manager-web.pl @@ -35,6 +35,7 @@ my %format = ( my %actions = ('select_sp' => {'title_en' => 'Select your Service Provider' }, 'get_sp_list' => {'title_en' => 'Select your Service Provider' }, 'generate_token' => {'title_en' => 'Generate an authentication token'}, + 'validate_token' => {'title_en' => 'Validate an authentication token'}, ); ## Gives writes for the group @@ -332,4 +333,68 @@ sub req_generate_token { return 1; } +## Validate an authentication token +## Test accounts get created +## Sample call : dev-edugain.renater.fr/accountmanager?action=validate_token&style=nobanner&sp_entityid=https%3A%2F%2Fsourcesup.cru.fr%2Fshibboleth&authentication_token=c1cfecb51ea40d39a695 +sub req_validate_token { + my $self = shift; + &IdPAccountManager::Tools::do_log('info', ""); + + unless ($self->{'param_in'}{'sp_entityid'}) { + push @{$self->{'param_out'}{'errors'}}, "missing_sp_entityid"; + &IdPAccountManager::Tools::do_log('error', "Missing parameter sp_entityid"); + return undef; + } + + unless ($self->{'param_in'}{'authentication_token'}) { + push @{$self->{'param_out'}{'errors'}}, "missing_authentication_token"; + &IdPAccountManager::Tools::do_log('error', "Missing parameter authentication_token"); + return undef; + } + + my $authentication_token = new IdPAccountManager::AuthenticationToken(token => $self->{'param_in'}{'authentication_token'}); + + unless ($authentication_token->load()) { + push @{$self->{'param_out'}{'errors'}}, "wrong_token"; + &IdPAccountManager::Tools::do_log('error', "Failed to validate authentication token %s for sp_entityid %s", + $self->{'param_in'}{'token'}, $self->{'param_in'}{'sp_entityid'}); + return undef; + } + + unless ($authentication_token->get('sp_entityid') eq $self->{'param_in'}{'sp_entityid'}) { + push @{$self->{'param_out'}{'errors'}}, "wrong_token_for_sp"; + &IdPAccountManager::Tools::do_log('error', "Authentication token %s cannot be used for SP with entityid %s", + $self->{'param_in'}{'token'}, $self->{'param_in'}{'sp_entityid'}); + return undef; + } + + ## delete the token + unless ($authentication_token->delete()) { + &IdPAccountManager::Tools::do_log('error', "Failed to delete authentication token %s", + $self->{'param_in'}{'token'}); + } + + ## create test accounts + my @test_accounts = &IdPAccountManager::TestAccount::create_test_accounts_for_sp(sp_entityid => $self->{'param_in'}{'sp_entityid'}); + + unless (@test_accounts) { + push @{$self->{'param_out'}{'errors'}}, "accounts_creation_failed"; + &IdPAccountManager::Tools::do_log('error', "Failed to create test accounts for SP with entityid %s", + $self->{'param_in'}{'sp_entityid'}); + return undef; + } + + ## Update simpleSAMLphp configuration to enable test accounts + unless (&IdPAccountManager::Tools::update_ssp_authsources()) { + push @{$self->{'param_out'}{'errors'}}, "accounts_creation_failed"; + &IdPAccountManager::Tools::do_log('error', "Failed to create simpleSAMLphp configuration file"); + return undef; + } + + $self->{'param_out'}{'sp_entityid'} = $self->{'param_in'}{'sp_entityid'}; + $self->{'param_out'}{'test_accounts'} = \@test_accounts; + + return 1; +} + diff --git a/lib/IdPAccountManager/AuthenticationToken.pm b/lib/IdPAccountManager/AuthenticationToken.pm index 94ede6f36c78cd9fc34540e38ce4184e91553c02..4399f380a6b2ead2d80f9e6f0694310e278e96da 100644 --- a/lib/IdPAccountManager/AuthenticationToken.pm +++ b/lib/IdPAccountManager/AuthenticationToken.pm @@ -41,6 +41,13 @@ sub new { return $self; } +## Load an authentication token from DB +sub load { + my $self = shift; + + return $self->{'persistent'}->load(speculative => 1); +} + ## Get object parameter sub get { my $self = shift; diff --git a/lib/IdPAccountManager/TestAccount.pm b/lib/IdPAccountManager/TestAccount.pm index 22cf234324979d3ad66b62b512a57b6f3eb6c493..b052b837f113ac4f64986a7b38999e726933cdbb 100644 --- a/lib/IdPAccountManager/TestAccount.pm +++ b/lib/IdPAccountManager/TestAccount.pm @@ -106,6 +106,35 @@ sub list_test_accounts { return $accounts; } +## create test accounts for all active account profiles +sub create_test_accounts_for_sp { + my %args = @_; + my @test_accounts; + + unless ($args{'sp_entityid'}) { + IdPAccountManager::Tools::do_log('error',"Failed to create test account"); + return undef; + } + + foreach my $profile (@{$Conf::global{'account_profiles'}}) { + my $test_account = new IdPAccountManager::TestAccount(account_profile => $profile, + sp_entityid => $args{'sp_entityid'}); + unless (defined $test_account) { + IdPAccountManager::Tools::do_log('error',"Failed to create test account"); + return undef; + } + + unless ($test_account->save()) { + IdPAccountManager::Tools::do_log('error',"Failed to create test account"); + return undef; + } + + push @test_accounts, $test_account; + } + + return @test_accounts; +} + #before 'new' => sub { print "about to call new\n"; }; 1; # Magic true value required at end of module diff --git a/templates/accountProfiles/teacher1.tt2 b/templates/accountProfiles/teacher1.tt2 new file mode 100644 index 0000000000000000000000000000000000000000..a40a063ef54cf15aadc36caaeba994fe0a7c7966 --- /dev/null +++ b/templates/accountProfiles/teacher1.tt2 @@ -0,0 +1,13 @@ +'user[% account.get('id') %]:{SHA256}[% account.get('user_password_hash') %]=' => array( + 'uid' => 'user[% account.get('id') %]', + 'eduPersonAffiliation' => array('member', 'faculty'), + 'eduPersonScopedAffiliation' => array('member@[% conf.idp_scope %]', 'faculty@[% conf.idp_scope %]'), + 'displayName' => 'Peter Smith', + 'cn' => 'Peter Smith', + 'mail' => 'peter.smith@[% conf.idp_scope %]', + 'eduPersonPrincipalName' =>'[% account.get('id') %]@[% conf.idp_scope %]', + 'eduPersonTargetedID' =>'[% conf.idp_entityid %]![% account.get('sp_entityid') %]!X622UR2A7PG1uVhATobBOrMz+Ys=', + 'schacHomeOrganization' => '[% conf.idp_scope %]', + 'schacHomeOrganizationType' => 'urn:schac:homeOrganizationType:int:university', + 'associatedSP' => '[% account.get('sp_entityid') %]', +), diff --git a/templates/mail/send_authentication_token.tt2.eml b/templates/mail/send_authentication_token.tt2.eml index cfc11cd508ea1379a1633f4984e0e12970e76930..aea1425f8e82b9bce6895417b1ecdc205fc2b8a3 100644 --- a/templates/mail/send_authentication_token.tt2.eml +++ b/templates/mail/send_authentication_token.tt2.eml @@ -4,10 +4,10 @@ Subject: [% conf.app_name %] - Test accounts request Content-type: text/plain; charset=UTF-8; format=flowed This is an email challenge automatically sent to you by [% conf.app_name %]. Somebody has requested creation of test accounts -for the service provider with entityid [% sp_entityid %] declared in eduGAIN interfederation. You are listed as -contact for this service provider in eduGAIN metadata. +for the service provider with entityid [% sp_entityid %]. This SAML entity is registered with eduGAIN interfederation. You are +identified as a contact for this service provider within eduGAIN metadata. -To complete test accounts activation you should copy and paste the validation token below in the [% conf.app_name %] web form. +To complete these test accounts creation, paste the following validation token on the [% conf.app_name %] web form. Validation token: [% authentication_token %] - +[% conf.app_name %]: [% conf.app_url %] \ No newline at end of file diff --git a/templates/web/content.tt2.html b/templates/web/content.tt2.html index 5036c762897fbd0876eb9944764485266a9643c8..3e9714208657b32603be52996c1fe332c2283b0d 100644 --- a/templates/web/content.tt2.html +++ b/templates/web/content.tt2.html @@ -28,6 +28,13 @@ An error occured [% END %] +[% ELSIF action == 'validate_token' %] + [% TRY %] + [% PROCESS 'templates/web/validate_token.tt2.html' %] + [% CATCH %] + An error occured + [% END %] + [% ELSE %] Error: unknown action diff --git a/templates/web/errors.tt2.html b/templates/web/errors.tt2.html index 5cfe4b42095c55e5f44ad41ac15db046bde72fac..40261757769bde1fe91ea38e3785df1b0fb68124 100644 --- a/templates/web/errors.tt2.html +++ b/templates/web/errors.tt2.html @@ -1,3 +1,4 @@ +[% IF errors %] [% FOREACH err IN errors %] <p class="ui-state-error ui-corner-all" style="margin-top: 20px; padding: 0 .7em;"><span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span> @@ -9,6 +10,15 @@ [% ELSIF err == 'internal' %] internal error; administrators of the federation registry have been notified. + [% ELSIF err == 'wrong_token' %] + the validation token you provided is incorrect or it has already been used + + [% ELSIF err == 'wrong_token_for_sp' %] + the validation token you provided cannot be used to obtain test accounts for this service provider ([% sp_entityid %]) + + [% ELSIF err == 'accounts_creation_failed' %] + failed to create your test accounts + [% ELSIF (matches = err.match('missing_(\w+)')) %] missing parameter '[% matches.0 %]' @@ -19,3 +29,6 @@ </p> <br/> [% END %] <!-- FOREACH --> + + You can report the issue to the administrators (<a href="mailto:[% conf.support_email %]>[% conf.support_email %]</a>). +[% END %] diff --git a/templates/web/generate_token.tt2.html b/templates/web/generate_token.tt2.html index f2d8ae944ccd3e0b155d973ef4d2510fbe2fe905..83581d919625da3e14595d141abb87216c99c59f 100644 --- a/templates/web/generate_token.tt2.html +++ b/templates/web/generate_token.tt2.html @@ -5,7 +5,7 @@ An email challenge including a validation token has been emailed to you at [% em <fieldset> <legend>Provide token</legend> - <label for="authentication_token">PLease provide the secret token here :</label> + <label for="authentication_token">Please provide the secret token here :</label> <input name="authentication_token" value="" id="authentication_token" type="text" class="required"/> diff --git a/templates/web/get_sp_list.tt2.html b/templates/web/get_sp_list.tt2.html index 1821ba8d2334fa467e59c06febbf299c6ca154a5..34f43683d9b0d5843c70404d3c9c878f015dab14 100644 --- a/templates/web/get_sp_list.tt2.html +++ b/templates/web/get_sp_list.tt2.html @@ -26,7 +26,7 @@ jQuery(document).ready(function($){ }); } - // Trigger loading of the 'select_sp' with the selected SP entityid as parameter + // Trigger loading of the 'generate_token' with the selected SP entityid as parameter // Result gets included in the next tab if (currentIndex === 1 && newIndex === 2) { @@ -39,6 +39,19 @@ jQuery(document).ready(function($){ }); } + // Trigger loading of the 'validate_token' with the list of test accounts created + // Result gets included in the next tab + if (currentIndex === 2 && newIndex === 3) + { + form.steps("remove", 3); + form.steps("insert", 3, { + title: "Get test accounts", + contentMode: "async", + contentUrl: "https://dev-edugain.renater.fr/accountmanager?action=validate_token&style=nobanner&sp_entityid="+ + encodeURIComponent($('#sp_entityid').val())+"&authentication_token="+encodeURIComponent($('#authentication_token').val()) + }); + } + // Allways allow previous action even if the current form is not valid! if (currentIndex > newIndex) { @@ -94,7 +107,7 @@ in eduGAIN inter-federation. Note that only a Service Provider administrator can <select id="sp_entityid" name="sp_entityid" class="required"> <option value="">Select your Service Provider below</option> - [% FOREACH entity IN federation_metadata_as_hashref %] + [% FOREACH entity IN federation_metadata_as_hashref.sort('entityid') %] <option value="[% entity.entityid %]">[% IF entity.display_name && entity.display_name.en %][% entity.display_name.en %] - [% END %][% entity.entityid %]</option> [% END %] </select> diff --git a/templates/web/index-nobanner.tt2.html b/templates/web/index-nobanner.tt2.html index 16dd4deecd630c45432d7f04d164ca1d43607b39..76fc3eef4e511e21e77e3754e28e011f6b522d22 100644 --- a/templates/web/index-nobanner.tt2.html +++ b/templates/web/index-nobanner.tt2.html @@ -4,4 +4,10 @@ Content-Type: text/html <?xml version="1.0" encoding="utf-8" ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/x html1/DTD/xhtml1-transitional.dtd"> +[% IF errors %] +<div class="ui-widget"> +[% PROCESS 'templates/web/errors.tt2.html' %] +</div> +[% ELSE %] [% PROCESS 'templates/web/content.tt2.html' %] +[% END %] diff --git a/templates/web/validate_token.tt2.html b/templates/web/validate_token.tt2.html new file mode 100644 index 0000000000000000000000000000000000000000..4d9bf0d9f6a294769c6d3720926108238695e12d --- /dev/null +++ b/templates/web/validate_token.tt2.html @@ -0,0 +1,27 @@ +<h3>Get test accounts</h3> + +<div> +Your identity has been checked successfully. + +Test accounts with different profiles have been created for you, see details below. Note that these accounts will automatically +expire in [% conf.accounts_validity_period %] days. Note also that these test accounts can only be used to login on your SP [% sp_entityid %]. + +<ol> + [% FOREACH test_account IN test_accounts %] + <li>account profile: [% test_account.get('account_profile') %] + <dl> + <dd>user name: user[% test_account.get('id') %]</dd> + <dd>user password: [% test_account.get('user_password') %]</dd> + </dl> + </li> + [% END %] + +</ol> + +You can now use these accounts to login at your federated service. +To do so, you should select "[% conf.app_name %]" in your service discovery service menu. + +You should keep a record of these user names and passwords since we don't provide any mecanism to remind you the credentials. If +you forget the credentials you can however ask for new test accounts via this service. + +Thank you for using the [% conf.app_name %] \ No newline at end of file