Newer
Older
package IdPAccountManager::Tools;
use Template;
renater.salaun
committed
# load Template::Stash to make method tables visible
use Template::Stash;
renater.salaun
committed
use Encode;
my %log_levels = ('debug' => 0, 'info' => 1, 'trace' => 1, 'notice' => 2, 'error' => 3);
renater.salaun
committed
INIT {
## a TT2 virtual method to get a variable type
$Template::Stash::LIST_OPS->{isa} = sub {
my $list = shift;
my $type = shift;
return 1 if ($type eq 'ARRAY');
return 0;
};
$Template::Stash::SCALAR_OPS->{isa} = sub {
my $list = shift;
my $type = shift;
return 1 if ($type eq 'SCALAR');
return 0;
};
}
# get SHA256 hash for a string
sub sha256_hash {
my $s = shift;
return &Digest::SHA::sha256_base64($s);
}
# This function generates a random password
sub generate_password{
my $length_of_randomstring=10;# the length of
# the random string to generate
# plusieurs tirages :
# 1-tirage des caractères obligatoires : les mettre dans un tableau
my @uppers=('A'..'N','P'..'Z');
my @lowers=('a'..'k','m'..'z');
renater.salaun
committed
my @punctuation=(':','!','?','&','$','=','-','#');
my @numerics=('0'..'9');
my @rndtab;
push(@rndtab,$uppers[rand @uppers]);
push(@rndtab,$lowers[rand @lowers]);
push(@rndtab,$punctuation[rand @punctuation]);
## Pas de caractères 8bit pour l'antispam
push(@rndtab,$numerics[rand @numerics]);
# 2-tirage des caractères optionnels : les ajouter au tableau
my @chars=('a'..'k','m'..'z','A'..'N','P'..'Z','0'..'9','_','%',';',':','!','?','&','$','*','(',')','{','}','[',']','.','=','-','#');
foreach (6..$length_of_randomstring) {
# rand @chars will generate a random
# number between 0 and scalar @chars
push(@rndtab,$chars[rand @chars]);
}
# 3-ordonnancement de ceux-ci : les retirer aléatoirement du tableau en les concaténant dans une chaîne
my $rndstring='';
my $cpt=1;
while($cpt<=$length_of_randomstring) {
my $indice = rand @rndtab;
$rndstring.=$rndtab[$indice];
splice (@rndtab, $indice, 1);
$cpt+=1;
}
return $rndstring;
}
## Updates simpleSamlPhp authsources.php configuration file
sub update_ssp_authsources {
my $tt2 = Template->new({'INCLUDE_PATH' => $Conf::global{'root_manager_dir'}.':'.$Conf::global{'root_manager_dir'}.'/templates/accountProfiles'});
my %args = ('accounts' => IdPAccountManager::TestAccount::list_test_accounts(),
#chdir $Conf::global{'root_manager_dir'};
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
my $template_file = 'templates/accountProfiles/valid-accounts.php.tt2';
my $output_file = 'conf/valid-accounts.php';
#printf "Trace : in=%s, out=%s\n", $template_file, $output_file;
unless ($tt2->process($template_file, \%args, $output_file)) {
IdPAccountManager::Tools::do_log('error', "Failed to update valid-accounts.php: %s", $tt2->error());
return undef;
}
}
## Dump a variable's content
sub dump_var {
my ($var, $level, $fd) = @_;
if (ref($var)) {
if (ref($var) eq 'ARRAY') {
foreach my $index (0..$#{$var}) {
print $fd "\t"x$level.$index."\n";
&dump_var($var->[$index], $level+1, $fd);
}
}elsif (ref($var) eq 'HASH') {
foreach my $key (sort keys %{$var}) {
print $fd "\t"x$level.'_'.$key.'_'."\n";
&dump_var($var->{$key}, $level+1, $fd);
}
}else {
if (defined $var) {
print $fd "\t"x$level."'$var'"."\n";
}else {
print $fd "\t"x$level."UNDEF\n";
}
}
}else {
if (defined $var) {
print $fd "\t"x$level."'$var'"."\n";
}else {
print $fd "\t"x$level."UNDEF\n";
}
}
}
sub do_log {
my $level = shift;
my $message = shift;
unless ($log_levels{$level} < $log_levels{$Conf::global{'log_level'}}) {
## Determine calling function and parameters
## If in 'err' level, build a stack trace
my $caller_string;
if ($level eq 'error'){
my $go_back = 1;
my @calls;
while (my @call = caller($go_back)) {
unshift @calls, $call[3].'#'.$call[2];
$go_back++;
}
$caller_string = join(' > ',@calls);
}else {
my @call = caller(1);
$caller_string = $call[3].'()';
}
$level = uc($level);
my $ip = $ENV{'REMOTE_HOST'}.'/'.$ENV{'REMOTE_ADDR'} || 'NOIP';
my $date = POSIX::strftime ("%Y:%m:%d %H:%M:%S", localtime(time));
my $user = lc($ENV{'mail'}) || 'NOEMAIL';
open LOG, ">>".$Conf::global{'log_file'}; printf LOG "$date - ($level) - $ip - $user - $caller_string $message\n", @_; close LOG;
}
return 1;
}
## Send a mail notice
## Default is to send email to the manager admins, unless other recipients are specified
## mail_notice(IN)
## IN is a HASH with expected entries :
## template : mail template file
## data : data used by the TT2 parser
sub mail_notice {
my %in = @_;
my $tt2_file = $in{'template'};
my $mail_data = $in{'data'};
$mail_data->{'conf'} ||= \%Conf::global;
my $notice_email = $in{'to'} || $Conf::global{'admin_email'};
$mail_data->{'to'} = $notice_email;
## Protection to prevent notifications during test dev phases
## Notify only admin_email or dev_sp_contact addresses
if ($Conf::global{'dev_no_mail_outside'}) {
my %rcpt = map {$_ => 1} split(/,/, $notice_email);
my %authorized_rcpt = map {$_ => 1} split(/,/, join(',', $Conf::global{'admin_email'}, $Conf::global{'dev_sp_contact'}));
my $change_rcpt = 0;
foreach my $email (keys %rcpt) {
unless ($authorized_rcpt{$email}) {
$change_rcpt = 1;
last
}
}
if ($change_rcpt) {
&do_log('info',"no_mail_outside option set; notification for %s rerouted to admins ; ", $notice_email);
$notice_email = $Conf::global{'admin_email'};
}
}
&do_log('trace', '(template=%s, to=%s)', $in{'template'}, $mail_data->{'to'});
open SENDMAIL, "|/usr/sbin/sendmail -f ".$Conf::global{'notice_from'}." $notice_email";
my $tt2 = Template->new(FILTERS => {qencode => [\&qencode, 0]});
unless ($tt2->process($tt2_file, $mail_data, \*SENDMAIL)) {
renater.salaun
committed
&do_log('error', "Error TT2 : %s", $tt2->error());
}
close SENDMAIL;
}
sub qencode {
my $string = shift;
# We are not able to determine the name of header field, so assume
# longest (maybe) one.
return MIME::EncWords::encode_mimewords(Encode::decode('utf8', $string),
Encoding=>'A',
Charset=> 'utf8',
Field=>"subject");
}
## usefull to pass parameters to TT2
sub encode_utf8 ($) {
my $string = shift||'';
return Encode::encode('utf8', $string);
}
renater.salaun
committed
## Escape characters that may interfer in an XML document
sub escape_xml {
my $s = shift;
$s =~ s/\&/&\;/gm;
$s =~ s/\"/"\;/gm;
$s =~ s/\</<\;/gm;
$s =~ s/\>/>\;/gm;
$s =~ s/\'/'/gm;
return $s;
}
## usefull to pass parameters to TT2
sub escape_quotes {
my $string = shift;
$string =~ s/\'/\\\'/g;
return $string;
}
renater.salaun
committed
## returns an integer (0 or 1), given an input string ('true' or 'false')
sub boolean2integer {
my $boolean = shift;
if ($boolean eq 'true') {
return 1;
}elsif ($boolean eq 'false') {
return 0;
}
return undef;
}
renater.salaun
committed
1; # Magic true value required at end of module
__END__
=head1 NAME
IdPAccountManager::Tools - Set of subroutines usefull for the Test Account manager
=head1 DESCRIPTION
The Test Account manager instanciates test accounts associated to a SAML Identity Provider.
This module gathers a set of usefull subroutines.
=head1 SUBROUTINES/METHODS
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
=over 8
=item C<dump_var ($var, $level, $fd)>
Dumps a complex perl data structure. $var is a reference to the variable to dump. $level should be set to 0 (subroutine called recursively). $fd is the file descriptor for the output (default is STDOUT).
=item C<do_log ($level, $message)>
Write $message to the log file. $level sets the log level (debug, info, trace, notice, error).
=item C<encode_utf8 ($string)>
Return a UTF8 encoded version of $string.
=item C<escape_xml ($string)>
Escape XML chars in $string.
=item C<generate_password>
Returns a random password following some security guidelines.
=item C<mail_notice (ARGS)>
Send a mail notice.
Supported arguments include:
=over 12
=item C<template>
TT2 mail template to parse.
=item C<to>
Destination email address.
=item C<data>
A hashref with parameters used to parse the mail template.
=back
=item C<qencode ($string)>
Retunrs a Q-encoded version of $string.
=item C<sha256_hash ($string)>
Returns a SHA256 hash for $string.
=item C<update_ssp_authsources>
Update simpleSAMLphp authsources.php configuration file with the currently valid test accounts.
=back
=head1 AUTHOR
Olivier Salaün (olivier.salaun@renater.fr)
=head1 LICENSE
Copyright (c) 2015 RENATER on behalf of the GÉANT project