From 0de7b5e81411b4c55e4027564506f2af7e379120 Mon Sep 17 00:00:00 2001
From: "renater.salaun" <renater.salaun@047e039d-479c-447e-8a29-aa6bf4a09bab>
Date: Wed, 10 Sep 2014 07:47:22 +0000
Subject: [PATCH] Rewriting TestAccount.pm code to better articulate
 IdPAccountManager::Data::Testaccount objects with  objects

git-svn-id: https://svn.geant.net/GEANT/edugain_testidp_account_manager/trunk@6 047e039d-479c-447e-8a29-aa6bf4a09bab
---
 bin/account-manager-client.pl                 |  14 +-
 bin/expire-test-accounts.pl                   |   8 +-
 lib/IdPAccountManager/TestAccount.pm          | 114 +++++++++-----
 lib/IdPAccountManager/Tools.pm                | 145 ++++++++++++++++++
 templates/accountProfiles/student1.tt2        |  10 +-
 .../accountProfiles/valid-accounts.php.tt2    |   2 +-
 6 files changed, 241 insertions(+), 52 deletions(-)
 create mode 100644 lib/IdPAccountManager/Tools.pm

diff --git a/bin/account-manager-client.pl b/bin/account-manager-client.pl
index eee0ffc..845ea10 100755
--- a/bin/account-manager-client.pl
+++ b/bin/account-manager-client.pl
@@ -34,10 +34,16 @@ if ($options{'create_test_account'}) {
     my $test_account = new IdPAccountManager::TestAccount(account_profile => $options{'account_profile'},
                                                           sp_entityid => $options{'sp_entityid'});
     unless (defined $test_account) {
-        die "Failed to create test account";
+         IdPAccountManager::Tools::do_log('error',"Failed to create test account");
+         exit -1;
     }
     
-    printf "Account created:\n\tuserid: user%d\n\tpassword: %s\n", $test_account->id, $test_account->user_password;
+    unless ($test_account->save()) {
+        IdPAccountManager::Tools::do_log('error',"Failed to create test account");
+        exit -1;
+    }
+    
+    printf "Account created:\n\tuserid: user%d\n\tpassword: %s\n", $test_account->get('id'), $test_account->get('user_password');
     
 }elsif ($options{'list_test_accounts'}) {
     
@@ -54,8 +60,6 @@ if ($options{'create_test_account'}) {
     my $all = IdPAccountManager::TestAccount::list_test_accounts(%args);
     
     foreach my $test_account (@$all) {
-        printf "Account ID=%s; password=%s; sp_entityid=%s; account_profile=%s; creation_date=%s; expiration_date=%s\n",
-            $test_account->id, $test_account->user_password, $test_account->sp_entityid, $test_account->account_profile,
-            &POSIX::strftime('%Y:%m:%d', localtime($test_account->creation_date)), &POSIX::strftime('%Y:%m:%d', localtime($test_account->expiration_date));
+        $test_account->print();
     }
 }
diff --git a/bin/expire-test-accounts.pl b/bin/expire-test-accounts.pl
index 24ed1b7..070f7c7 100755
--- a/bin/expire-test-accounts.pl
+++ b/bin/expire-test-accounts.pl
@@ -26,9 +26,7 @@ if ($options {'list_only'}) {
     my $all = IdPAccountManager::TestAccount::list_test_accounts('query' => ['expiration_date' => {lt => time}]);
     
     foreach my $test_account (@$all) {
-        printf "Account ID=%s; password=%s; sp_entityid=%s; account_profile=%s; creation_date=%s; expiration_date=%s\n",
-            $test_account->id, $test_account->user_password, $test_account->sp_entityid, $test_account->account_profile,
-            &POSIX::strftime('%Y:%m:%d', localtime($test_account->creation_date)), &POSIX::strftime('%Y:%m:%d', localtime($test_account->expiration_date));
+        $test_account->print();
     }
     
 }else {
@@ -37,9 +35,7 @@ if ($options {'list_only'}) {
     
     printf "Removing expired test accounts...\n";
     foreach my $test_account (@$all) {
-        printf "\tAccount ID=%s; password=%s; sp_entityid=%s; account_profile=%s; creation_date=%s; expiration_date=%s\n",
-            $test_account->id, $test_account->user_password, $test_account->sp_entityid, $test_account->account_profile,
-            &POSIX::strftime('%Y:%m:%d', localtime($test_account->creation_date)), &POSIX::strftime('%Y:%m:%d', localtime($test_account->expiration_date));
+        $test_account->print();
         $test_account->delete || die;
     }
     printf "%d accounts removed\n", $#{$all}+1;
diff --git a/lib/IdPAccountManager/TestAccount.pm b/lib/IdPAccountManager/TestAccount.pm
index b55321b..da9ef0e 100644
--- a/lib/IdPAccountManager/TestAccount.pm
+++ b/lib/IdPAccountManager/TestAccount.pm
@@ -1,48 +1,87 @@
 package IdPAccountManager::TestAccount;
 
+use strict;
+
 use IdPAccountManager::Data::Testaccount;
 use IdPAccountManager::Data::Testaccount::Manager;
 
 use IdPAccountManager::Tools;
 use IdPAccountManager::Conf;
 
-use Moose;
-use Moose::Util::TypeConstraints;
-
-subtype 'entityid',
-    as 'Str',
-    where { /^(urn:|http(s)?\:\/\/)/ },
-    message { "$_ is not a valide entityid"};
-
-has 'account_profile' => (is => 'rw',
-                          isa => 'Str',
-                          required => 1);
-has 'sp_entityid' => (is => 'rw',
-                      isa => 'entityid',
-                      required => 1,
-                      );
-has 'id' => (is => 'ro',
-             isa => 'Int',
-             required => 0,
-             );
-has 'user_password' => (is => 'ro',
-                        isa => 'Str',
-                        required => 0,
-             );
-
-sub BUILD {
+require Exporter;
+my @ISA = qw(Exporter);
+my @EXPORT = qw();
+
+use Carp;
+
+INIT {
+  ## Set error mode  to non fatal
+  IdPAccountManager::Data::Testaccount::Manager->error_mode('return');  
+ }
+
+sub new {
+    my ($pkg) = shift;
+    my %args = @_;
+
+    my $self = {};
+
+    ## Bless Provider object
+    bless $self, $pkg;
+    
+    ## Object may be created either with a hashref as argument or an IdPAccountManager::Data::Testaccount object
+    ## Second case is usefull when fetching a set of IdPAccountManager::Data::Testaccount via IdPAccountManager::Data::Testaccount::Manager
+    if (ref($_[0]) eq 'IdPAccountManager::Data::Testaccount') {
+        $self->{'persistent'} = $_[0];
+    }else {
+        $self->{'persistent'} = IdPAccountManager::Data::Testaccount->new(%args);
+    }
     
+    return $self;
+}
+
+sub get {
     my $self = shift;
-    my $args = shift;
+    my $attribute_name = shift;
     
-    my $testaccount_db = IdPAccountManager::Data::Testaccount->new('account_profile' => $args->{'account_profile'},
-                                                                   'sp_entityid' => $args->{'sp_entityid'},
-                                                                   'user_password' => &IdPAccountManager::Tools::generate_password(),
-                                                                   'creation_date' => time,
-                                                                   'expiration_date' => time + ($IdPAccountManager::Conf::global{'accounts_validity_period'} * 3600 * 24));
-    $testaccount_db->save();
+    return $self->{'persistent'}->$attribute_name;
+}
+
+sub save {
+    my $self = shift;
+    
+    ## If no id is defined, it is a new account
+    unless (defined $self->{'persistent'}->id) {
+        $self->{'persistent'}->creation_date(time);
+        $self->{'persistent'}->expiration_date(time + ($IdPAccountManager::Conf::global{'accounts_validity_period'} * 3600 * 24));
+        $self->{'persistent'}->user_password(&IdPAccountManager::Tools::generate_password());
+    }
+    
+    unless ($self->{'persistent'}->save()) {
+        IdPAccountManager::Tools::do_log('error', "Failed to save Test Account in DB");
+        return undef;
+    }
+}
+
+## Delete a test account
+sub delete {
+    my $self = shift;
     
-    ## TODO : update IdP conf file; given the account profile
+    unless ($self->{'persistent'}->delete()) {
+        IdPAccountManager::Tools::do_log('error', "Failed to delete a test account in DB");
+        return undef;
+    }
+}
+
+## Print the content of a test account
+sub print {
+    my $self = shift;
+    my $fd = shift || \*STDOUT;
+    
+    printf $fd "Account ID=%s; password=%s; sp_entityid=%s; account_profile=%s; creation_date=%s; expiration_date=%s\n",
+            $self->get('id'), $self->get('user_password'), $self->get('sp_entityid'), $self->get('account_profile'),
+            &POSIX::strftime('%Y:%m:%d', localtime($self->get('creation_date'))), &POSIX::strftime('%Y:%m:%d', localtime($self->get('expiration_date')));
+
+    return 1.
 }
 
 ## list all test accounts
@@ -50,9 +89,14 @@ sub BUILD {
 sub list_test_accounts {
     my %args = @_;
 
-    my $all = IdPAccountManager::Data::Testaccount::Manager->get_testaccounts(%args);
+    my $persistent_accounts= IdPAccountManager::Data::Testaccount::Manager->get_testaccounts(%args);
+    my $accounts;
+    foreach my $persistent_account (@{$persistent_accounts}) {
+        my $account = new IdPAccountManager::TestAccount($persistent_account);
+        push @$accounts, $account;
+    }
     
-    return $all;
+    return $accounts;
 }
 
 #before 'new' => sub { print "about to call new\n"; };
diff --git a/lib/IdPAccountManager/Tools.pm b/lib/IdPAccountManager/Tools.pm
new file mode 100644
index 0000000..e23e165
--- /dev/null
+++ b/lib/IdPAccountManager/Tools.pm
@@ -0,0 +1,145 @@
+package IdPAccountManager::Tools;
+
+use Template;
+
+my %log_levels = ('debug' => 0, 'info' => 1, 'trace' => 1, 'notice' => 2, 'error' => 3);
+
+
+# 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');
+        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' => $IdPAccountManager::Conf::global{'root_manager_dir'}.':'.$IdPAccountManager::Conf::global{'root_manager_dir'}.'/templates/accountProfiles'});
+    my %args = ('accounts' => IdPAccountManager::TestAccount::list_test_accounts(),
+                'conf' => \%IdPAccountManager::Conf::global);
+    
+    #chdir $IdPAccountManager::Conf::global{'root_manager_dir'};
+    
+    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{$IdPAccountManager::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 TMP, ">>".$IdPAccountManager::Conf::global{'log_file'}; printf TMP "$date - ($level) - $ip - $user - $caller_string $message\n", @_; close TMP;       
+    }
+    return 1;
+}
+
+1; # Magic true value required at end of module
+__END__
+
+=head1 NAME
+
+IdPAccountManager::Tools - Set of subroutines usefull for the Account manager
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 SUBROUTINES/METHODS
+
+=head1 AUTHOR
+
+Olivier Salaün (olivier.salaun@renater.fr)
diff --git a/templates/accountProfiles/student1.tt2 b/templates/accountProfiles/student1.tt2
index 64095dd..d4aba8c 100644
--- a/templates/accountProfiles/student1.tt2
+++ b/templates/accountProfiles/student1.tt2
@@ -1,13 +1,13 @@
-'user[% account.id %]:[% account.user_password %]' => array(
-	'uid' => 'user[% account.id %]',
+'user[% account.id %]:[% account.get('user_password') %]' => array(
+	'uid' => 'user[% account.get('id') %]',
 	'eduPersonAffiliation' => array('member', 'student'),
 	'eduPersonScopedAffiliation' => array('member@[% conf.idp_scope %]', 'student@[% conf.idp_scope %]'),
         'displayName' => 'John Kleinman',
         'cn' => 'John Kleinman',
         'mail' => 'john.kleinman@[% conf.idp_scope %]',
-        'eduPersonPrincipalName' =>'[% account.id %]@[% conf.idp_scope %]',
-        'eduPersonTargetedID' =>'[% conf.idp_entityid %]![% account.sp_entityid %]!X622UR2A7PG1uVhATobBOrMz+Ys=',
+        '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.sp_entityid %]',
+	'associatedSP' => '[% account.get('sp_entityid') %]',
 ),
diff --git a/templates/accountProfiles/valid-accounts.php.tt2 b/templates/accountProfiles/valid-accounts.php.tt2
index 2f6a363..ecb525f 100644
--- a/templates/accountProfiles/valid-accounts.php.tt2
+++ b/templates/accountProfiles/valid-accounts.php.tt2
@@ -4,6 +4,6 @@ $validTestAccounts = array (
     'exampleauth:UserPass',
 
 [% FOREACH account IN accounts %]
-  [% INCLUDE "${account.account_profile}.tt2" %]
+  [% INCLUDE "${account.get('account_profile')}.tt2" %]
 [% END %]
 );
-- 
GitLab