mercoledì 26 marzo 2008

Creazione automatica di account NIS da un file CSV

Quello presentato di seguito è uno script Perl utilizzato per la generazione automatica di account per gli studenti su un sistema Linux+NIS. L'idea è quella di partire da un file CSV contentente diverse informazioni (nome utente, nome account, informazioni circa l'anno di studio, ecc.).

L'idea alla base dello script è quella di costruire una collezione di hash, uno per ogni utente letto dal file CSV, nel quale vengono memorizzate le informazioni realmente necessarie alla creazione del relativo account (come ad esempio username, password, shell, etc.). Il file viene letto una riga alla volta dalla funzione parseFile, la quale passa il controllo a parseLine che costruisce l'hash dai valori di una singola linea; l'hash viene poi aggiunto ad un array che contiene la lista di tutti gli hash estratti dal file sorgente. Durante la creazione di un hash utente viene anche generata una password casuale (funzione generatePassword), che verrà poi salvata in chiaro in un file di log.

Una volta ottenuti tutti gli hash per tutti gli utenti, si passa il controllo alla funzione addUser che si occupa di lanciare i comandi Unix per la creazione e il setup dell'account utente sulla macchina locale. Si noti che è possibile creare più directory home per ogni utente, come specificato nell'array @homeDirs; questo nel caso serva esportare (tramite NFS) diversi file system a seconda di differenti architetture client.

Una volta terminato il ciclo di aggiunta di ogni utente, vengono lanciati i comandi per la ripopolazione del NIS e l'aggiornamento dei client collegati al server stesso.
#!/usr/bin/perl



# esempio di una riga del file di account
#Luca;Ferrari;20202;599;II;luca.ferrari37094@unimo.it;37094;si;0;37094;U4L5R;;;;;;;;;Sì;C;Windows



# * Copyright (C) Luca Ferrari 2003-2008
# *
# * This program is free software: you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation, either version 3 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program. If not, see .





# se definito questo flag lo script non esegue i comandi, li simula solo
$DEBUG_MODE="true";


# separatore di campo nel file degli account
$FIELD_SEPARATOR=';';
# il carattere da anteporre ad ogni username ricavato dal file
$USERNAME_START="n";
# gruppo di default
$DEFAULT_GROUP="users";
# lunghezza di default delle eventuali password generate automaticamente
$DEFAULT_PASSWORD_LENGTH=8;


# array con le home da creare per ogni utente
@homeDirs = ("/export/Linux-i686/home/",
"/export/Linux-sparc64/home/",
"/export/SunOS-sun4u/home/",
"/export/SunOS5.8-sun4u/home/",
"/export/shared/home/",
);


# indicatori dei vari campi (loro posizioni) nel file di account
$ACCOUNT_POSITION=6;
$PASSWORD_POSITION=10; # impostare ad un valore negativo per avere la generazione automatica delle password
$NAME_POSITION=1;
$SURNAME_POSITION=0;
$OTHER_INFO_POSITION=5;


# chiavi di ongi singolo hash di informazioni
$USERNAME_KEY='username';
$PASSWOD_KEY='password';
$INFO_KEY='info';
$CYPHERED_PASSWORD_KEY='password_cifrata';
$GROUP_KEY='gruppo';



# file di log
$_REPORT = "accounts.log.txt";



# Funzione per fare il log di una stringa
sub history($)
{
my ($_stringa) = @_;

# se il log non e' inizializzato lo apro ora
if( not defined($__log__) ){
open(LF,">$_REPORT") || die("\nNon posso aprire il file di log $_REPORT:\n$!\n\n");
$__log__="true";
}

print LF $_stringa,"\n";
}








# Funzione per fare il parsing di tutto il file
# La funzione accetta il nome del file di cui fare il parsing e ritorna
# un vettore con dei riferimenti ad ogni hash di account.
sub parseFile
{
# prelevo i parametri
my ($_file) = @_;
my $_success=0;
my $currentHash;
my @accounts;

if( not defined($_file) || length($_file)==0 || $_file eq "" ){
return undef();
}

# apertura del file in lettura
open(FILE,"<".$_file) || die("\nNon riesco ad aprire $_file:\n$!");

# leggo ogni riga e la passo alla funzione di parsing
while( $line = ){

$currentHash = parseLine($line);

# inserisco l'account nell'array di account
if( defined($currentHash) ){
$accounts[++$#accounts] = $currentHash;
undef($currentHash);
}
}


return @accounts;

}






# Funzione per fare il parse di una singola linea di un file di account.
# Argoemnti: la linea di input.
# La funzione restituisce un riferimento ad un hash con i dati dell'account.
sub parseLine($)
{
# prelevo i parametri
my ($_line) = @_;

# controllo
if( not defined($_line) || length($_line) == 0 || $_line eq "" ){
return undef();
}

# faccio il parsing della linea
my @_parts = split(/$FIELD_SEPARATOR/,$_line);


if( not defined(@_parts) || $#_parts<0 ){
return undef();
}

# creo l'hash
my $_hash = {
$USERNAME_KEY => $USERNAME_START.$_parts[$ACCOUNT_POSITION],
$PASSWORD_KEY => $_parts[$PASSWORD_POSITION],
$INFO_KEY => "$_parts[$SURNAME_POSITION] $_parts[$NAME_POSITION] - $_parts[$OTHER_INFO_POSITION]",
};


# ATTENZIONE: se la password e' nulla oppure non e' definito il campo PASSWORD_POSITION genero io una
# password.
if( (not defined($PASSWORD_POSITION)) || $PASSWORD_POSITION<0>{$PASSWORD_KEY}) ||
length($_hash->{$PASSWORD_KEY}) == 0 || $_hash->{$PASSWORD_KEY} eq "" ){
my $_psw = generatePassword($DEFAULT_PASSWORD_LENGTH);
$_hash->{$PASSWORD_KEY} = $_psw;
}


# Pulisco ogni campo da spazi bianchi all'inizio e alla fine.
foreach $_i ( keys(%$_hash) ){
$_tmp = $_hash->{$_i};
$_tmp =~ s/^\s+|\s+$//g;
$_tmp =~ s/\'//g;
$_hash->{$_i} = $_tmp;
}


# restituisco l'hash (il suo riferimento)
return $_hash;
}







# FUnzione per generare una password.
# La funzione si appoggia a rand e map per generare una password totalmente casuale.
sub generatePassword($)
{
my ($_size) = @_;
my $_password;
# lista dei caratteri validi nelle password
my @chars = ('a'..'z','A'..'Z',0..9,'_','+','-','(',')' );

for($_i=0;$_i<=$_size;$_i++){
$_password .= join('',$chars[rand($#chars)]);
}

return $_password;
}











# Funzione di debug per stampare il contenuto di uno degli hash utente
sub dumphash($)
{
my ($h) = @_;

print "Hash costuito:\n";
print $h->{$USERNAME_KEY},"<",length($h->{$USERNAME_KEY}),"> - ",$h->{$PASSWORD_KEY}," - ",$h->{$INFO_KEY};
print " - ",$h->{$CYPHERED_PASSWORD_KEY};
print "\n----------------------------------------------\n";
}




# Funzione per aggiungere un singolo utente dato il suo hash di account.
# La funzione ritorna undef in caso di errore.
sub addUser($)
{
my ($hash) = @_;

if( not defined($hash) || not defined($hash->{$USERNAME_KEY}) || not defined($hash->{$PASSWORD_KEY}) ){
return undef();
}

# devo ottenere la password cifrata
my $cyphered = crypt($hash->{$PASSWORD_KEY},1);
$hash->{$CYPHERED_PASSWORD_KEY} = $cyphered;


# primo passo: invocazione di useradd
# ATTENZIONE: se non e' definito un gruppo uso quello di default
if( not defined($has->{$GROUP_KEY}) ){ $hash->{$GROUP_KEY} = $DEFAULT_GROUP; }
$useradd = " useradd -g ".$hash->{$GROUP_KEY}. " -s /bin/bash -p ". $hash->{$CYPHERED_PASSWORD_KEY}. " ";
$useradd .= " -c \"".$hash->{$INFO_KEY}."\" ".$hash->{$USERNAME_KEY}." ";

# secondo passo: creazione delle home
$createhome="";

foreach $__home (@homeDirs){
if( not -d $__home ){
if( mkdir($__home) ){
history("Creata directory $__home");
}
}
$currentHome = $__home;
$currentHome .= $hash->{$USERNAME_KEY};

$createhome .= " mkdir $currentHome; ";
if( defined($hash->{$GROUP_KEY}) ){
$createhome .= " chown ".$hash->{$USERNAME_KEY}.".".$hash->{$GROUP_KEY}." $currentHome; ";
}else{
$createhome .= " chown ".$hash->{$USERNAME_KEY}." $currentHome; ";
}
$createhome .= " chmod 755 $currentHome; ";
}






# log dei comandi
history("-------------- UTENTE ".$hash->{$USERNAME_KEY}."----------------------------");
history("password in chiaro => ".$hash->{$PASSWORD_KEY});
history($useradd);
history($createhome);
history("------------------------------------------------------------");


# esecuzione dei comandi
if(defined($DEBUG_MODE) ){ return "true"; }
return (`$useradd` && `$createhome`);
}


# Funzione per la ricostruzione della mappa del nis.
sub reNIS
{
my $nis = "cd /var/yp; ";
$nis .= "make";

history(">>>>>>>>>>>>>>>>>>>> RICOSTRUZIONE DEL NIS <<<<<<<<<<<<<<<<<<<<<<<");
history($nis);

if(defined($DEBUG_MODE) ){ return "true"; }
return `$nis`;
}
















#############################################################
#############################################################
#############################################################
#############################################################
#############################################################

# controllo parametri
if($#ARGV<0){
print "Utilizzo:\n";
print "$0 \n";
print "oppure:\n";
print "$0 username password_in_chiaro gruppo info";
exit(1);
}
else{

print "\nEsecuzione di $0....log impostato su $_REPORT\n";


if($#ARGV==0){
# un solo parametro: deve essere il file degli account
$ACCOUNT_FILE=$ARGV[0];
chomp($ACCOUNT_FILE);

# faccio il parsing del file
print "\nparsing del file di account $ACCOUNT_FILE...";
if( @acc = parseFile($ACCOUNT_FILE) ){
print "\nRilevati $#acc account da inserire, procedo...";

foreach $u (@acc){
# $u => riferimento all'hash dell'account
print "\nAggiunta dell'utente <",$u->{$USERNAME_KEY},">, [",$u->{$INFO_KEY} ,"]....";
if( addUser($u) ){
print "fatto!";
}
else{
print "NON riuscito!\n";
}
}

}
}
elsif($#ARGV==3){
# inserimento di un solo account
$_usr = $ARGV[0];
chomp($_usr);
$_pwd = $ARGV[1];
chomp($_pwd);
$_grp = $ARGV[2];
chomp($_grp);
$_info = $ARGV[3];
chomp($_info);

$_crypt = crypt($_pwd,1);

$singleAccount = {
$USERNAME_KEY => $_usr,
$PASSWORD_KEY => $_pwd,
$GROUP_KEY => $_grp,
$INFO_KEY => $_info,
$CYPHERED_KEY => $_crypt,
};

# aggiungo l'utente
print "\nAggiunta dell'utente <",$singleAccount->{$USERNAME_KEY},">...\n";
if( addUser($singleAccount) ){
print "fatto!";
}
else{
print "NON riuscito";
}
}


# ora che ho aggiunto gli utenti ripopolo il nis
print "\n\nRipopolazione del NIS...";
reNIS();
print "fatto\n\n";

}

Nessun commento: