Skip to content
Snippets Groups Projects
Commit 916c258e authored by Guillaume ROUSSE's avatar Guillaume ROUSSE
Browse files

drop multipart response, allow distinct accounts download instead

parent b9a67ff9
No related branches found
No related tags found
No related merge requests found
......@@ -30,6 +30,8 @@ CREATE TABLE `services` (
CREATE TABLE `accounts` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`token` varchar(50) NOT NULL,
`password_crypt` varchar(50) NOT NULL,
`password_hash` varchar(50) NOT NULL,
`creation_date` datetime DEFAULT NULL,
`expiration_date` datetime DEFAULT NULL,
......
......@@ -12,7 +12,9 @@ __PACKAGE__->meta->setup(
columns => [
id => { type => 'bigserial', not_null => 1 },
password_hash => { type => 'varchar', length => 50, not_null => 1 },
password_crypt => { type => 'varchar', length => 50, not_null => 1 },
password => { type => 'varchar', length => 50, nonpersistent => 1 },
token => { type => 'varchar', length => 50, not_null => 1 },
creation_date => { type => 'datetime' },
expiration_date => { type => 'datetime' },
profile => { type => 'varchar', length => 100, not_null => 1 },
......
......@@ -6,13 +6,10 @@ use warnings;
use CGI;
use DateTime;
use English qw(-no_match_vars);
use HTTP::AcceptLanguage;
use Template;
use Log::Any::Adapter;
use List::MoreUtils qw(uniq);
use HTTP::AcceptLanguage;
use HTTP::Message;
use HTTP::Response;
use IO::String;
use Text::CSV;
use AccountManager::Account;
......@@ -40,6 +37,7 @@ my %actions = (
select_email => 'req_select_email',
complete_challenge => 'req_complete_challenge',
create_accounts => 'req_create_accounts',
download_accounts => 'req_download_accounts',
);
## New web request
......@@ -109,7 +107,8 @@ sub run {
email => $parameters{email},
style => $parameters{style},
entityid => $parameters{entityid},
token => $parameters{token}
token => $parameters{token},
key => $parameters{key},
};
}
......@@ -158,7 +157,6 @@ sub respond {
binmode(STDOUT, ":utf8");
print $self->{cgi}->header(
-nph => 1,
-type => 'text/html',
-charset => 'utf8'
);
......@@ -413,6 +411,11 @@ sub req_create_accounts {
$self->respond({ errors => [ "missing_token" ] });
}
unless ($self->{in}->{email}) {
$self->{logger}->error("Missing parameter email");
$self->respond({ errors => [ "missing_email" ] });
}
my $token = AccountManager::Token->new(
db => $self->{db},
token => $self->{in}->{token}
......@@ -455,6 +458,24 @@ sub req_create_accounts {
$self->{configuration}->{$entity}->{account_validity_period} ||
$self->{configuration}->{service}->{account_validity_period};
my $download_token = AccountManager::Token->new(
db => $self->{db},
email_address => $self->{in}->{email},
sp_entityid => $self->{in}->{entityid},
creation_date => DateTime->now(),
expiration_date => DateTime->now()->add(hours => $validity_period),
token => AccountManager::Tools::generate_secret(20)
);
unless ($download_token->save()) {
push @{ $self->{out}->{errors} }, "internal";
$self->{logger}->error("Failed to save authentication token");
$self->respond();
}
my $key = AccountManager::Tools::generate_secret(10);
foreach my $profile (split(/, */, $profiles)) {
my $password = AccountManager::Tools::generate_password(10);
my $account = AccountManager::Account->new(
......@@ -463,7 +484,9 @@ sub req_create_accounts {
sp_entityid => $entity,
scope => $self->{configuration}->{idp}->{scope},
password => $password,
password_crypt => AccountManager::Tools::encrypt($password, $key),
password_hash => AccountManager::Tools::sha256_hash($password),
token => $download_token->token(),
creation_date => DateTime->now(),
expiration_date => DateTime->now()->add(days => $validity_period)
);
......@@ -505,55 +528,85 @@ sub req_create_accounts {
$self->{in}->{token}
);
binmode(STDOUT, ":utf8");
$self->respond({
accounts => \@accounts,
entityid => $self->{in}->{entityid},
key => $key,
token => $download_token->token(),
action => 'create_accounts'
});
}
my $response = HTTP::Response->new(
200, 'OK', [ 'Content-Type' => 'multipart/x-mixed-replace' ]
);
sub req_download_accounts {
my ($self) = @_;
$response->protocol('HTTP/1.1');
$response->date(time);
$response->server($ENV{SERVER_SOFTWARE});
unless ($self->{in}->{entityid}) {
push @{ $self->{out}->{errors} }, "missing_entityid";
$self->{logger}->error("Missing parameter entityid");
$self->respond();
}
# HTML page
my $data = {
app => {
name => $self->{configuration}->{app}->{name},
url => $self->{configuration}->{app}->{url},
support_email => $self->{configuration}->{app}->{support_email},
version => $self->{configuration}->{app}->{version},
},
accounts => \@accounts,
accounts_validity_period => $self->{configuration}->{service}->{account_validity_period},
idp_displayname => $self->{configuration}->{idp}->{displayname},
entityid => $self->{in}->{entityid},
action => 'create_accounts'
};
unless ($self->{in}->{token}) {
push @{ $self->{out}->{errors} }, "missing_token";
$self->{logger}->error("Missing parameter token");
$self->respond();
}
my $lang = HTTP::AcceptLanguage->new($ENV{HTTP_ACCEPT_LANGUAGE})->match(qw/en fr/) || 'en';
unless ($self->{in}->{key}) {
push @{ $self->{out}->{errors} }, "missing_key";
$self->{logger}->error("Missing parameter key");
$self->respond();
}
my $tt2 = Template->new({
ENCODING => 'utf8',
INCLUDE_PATH => $self->{configuration}->{_}->{templates_dir} . "/web/$lang"
});
my $token = AccountManager::Token->new(
db => $self->{db},
token => $self->{in}->{token}
);
if (! $token->load(speculative => 1)) {
push @{ $self->{out}->{errors} }, "wrong_token";
$self->{logger}->errorf(
"Non-existing authentication token %s",
$self->{in}->{token},
);
$self->respond();
}
my $page_content;
$tt2->process('index.tt2.html', $data, \$page_content);
if (! $token->sp_entityid() eq $self->{in}->{entityid}) {
push @{ $self->{out}->{errors} }, "wrong_token_for_sp";
$self->{logger}->errorf(
"Authentication token %s cannot be used for SP %s",
$self->{in}->{token},
$self->{in}->{entityid}
);
$self->respond();
}
my $page_response = HTTP::Message->new(
[
'Content-Type' => 'text/html; charset=utf-8',
'Content-Disposition' => 'inline'
# delete the token
unless ($token->delete()) {
$self->{logger}->errorf(
"Failed to delete authentication token %s",
$self->{in}->{token}
);
}
# load accounts from database
my $accounts = AccountManager::Account::Manager->get_accounts(
db => $self->{db},
query => [
token => $self->{in}->{token}
],
$page_content
);
$response->add_part($page_response);
# CSV file
my $csv = Text::CSV->new({ binary => 1, eol => "\r\n", quote_space => 0 });
my $file_content;
my $file_content_io = IO::String->new($file_content);
$csv->print($file_content_io, [ qw/
binmode(STDOUT, ":utf8");
print $self->{cgi}->header(
-type => 'text/csv',
-content_disposition => 'attachment; filename="accounts.csv"'
);
my $csv = Text::CSV->new ({ binary => 1, eol => "\r\n", quote_space => 0 });
$csv->print(\*STDOUT, [ qw/
username
password
profile
......@@ -568,8 +621,13 @@ sub req_create_accounts {
schacHomeOrganizationType
/ ]);
foreach my $account (@accounts) {
$csv->print($file_content_io, [
foreach my $account (@$accounts) {
my $password = AccountManager::Tools::decrypt(
$account->password_crypt(),
$self->{in}->{key}
);
$account->password($password);
$csv->print(\*STDOUT, [
$account->internal_uid(),
$account->password(),
$account->profile(),
......@@ -584,17 +642,6 @@ sub req_create_accounts {
$account->schacHomeOrganizationType(),
]);
}
my $file_response = HTTP::Message->new(
[
'Content-Type' => 'text/csv; charset=utf-8',
'Content-Disposition' => 'attachment; filename="accounts.csv"'
],
$file_content
);
$response->add_part($file_response);
print $response->as_string();
}
## Return the homepage of the service
......
......@@ -7,6 +7,8 @@
do so, select <strong>[% idp_displayname %]</strong> when choosing an identity
provider.</p>
<p>Click <a href="[% conf.app_url %]?action=download_accounts&entityid=[% entityid %]&token=[% token %]&key=[% key %]">here</a> to download the list of those accounts in CSV format.</p>
<div class="accounts_profile">
[% FOREACH account IN accounts %]
<div class="tbl">
......
......@@ -44,7 +44,8 @@ jQuery(document).ready(function($){
if (currentIndex === 2 && newIndex === 3)
{
window.location="[% app.url %]?action=create_accounts&entityid="+
encodeURIComponent($('#entityid').val())+"&token="+encodeURIComponent($('#token').val());
encodeURIComponent($('#entityid').val())+"&token="+encodeURIComponent($('#token').val()) +
"&email="+encodeURIComponent($('#email').val());
}
// Allways allow previous action even if the current form is not valid!
......
......@@ -7,6 +7,8 @@
service fédéré. Pour le faire, sélectionnez <strong>[% idp_displayname
%]</strong> lors du choix du founisseur d'identité à utiliser.</p>
<p>Cliquez <a href="[% conf.app_url %]?action=download_accounts&entityid=[% entityid %]&token=[% token %]&key=[% key %]">ici</a> pour télécharger la liste de ces comptes au format CSV.</p>
<div class="accounts_profile">
[% FOREACH account IN accounts %]
<div class="tbl">
......
......@@ -44,7 +44,8 @@ jQuery(document).ready(function($){
if (currentIndex === 2 && newIndex === 3)
{
window.location="[% app.url %]?action=create_accounts&entityid="+
encodeURIComponent($('#entityid').val())+"&token="+encodeURIComponent($('#token').val());
encodeURIComponent($('#entityid').val())+"&token="+encodeURIComponent($('#token').val()) +
"&email="+encodeURIComponent($('#email').val());
}
// Allways allow previous action even if the current form is not valid!
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment