epita-std
/
ACU
Archived
1
0
Fork 0
This repository has been archived on 2021-10-08. You can view files and clone it, but cannot push or open issues or pull requests.
ACU/utils/lpt

2154 lines
52 KiB
Perl
Executable File

#!/usr/bin/env perl
use v5.10.1;
use strict;
use warnings;
use utf8;
use open IO => ':utf8';
use open ':std';
use Encode qw(decode);
use Digest::SHA;
use Email::MIME;
use File::Find;
use IPC::Cmd qw[run];
use MIME::Base64;
use Net::LDAPS;
use Net::LDAP::Util qw(ldap_error_text);
use Pod::Usage;
use Term::ANSIColor qw(:constants);
use Term::ReadKey;
#use Cwd 'abs_path';
#use File::Basename;
# Avoid installation of liblerdorf on workstations
use lib "/sgoinfre/root/new_intra/";
use ACU::LDAP;
use ACU::Log;
###########################################################
# #
# Global variables #
# #
###########################################################
my $noconfirm = 0;
my $wksHomePrefix = "/home/";
my $nfsHomePrefix = "/srv/nfs/accounts/";
my $shellFalse = "/bin/false";
my $shellValid = "/bin/zsh";
my $colorize = defined($ENV{'ENABLE_COLOR'});
my %dev_quota = ( home => "/dev/mapper/acu-nfs--accounts",
sgoinfre => "/dev/mapper/acu-nfs--sgoinfre" );
my %def_quota = ( block => { home => 2306866, sgoinfre => 5242880 },
file => { home => 50000, sgoinfre => 60000 } );
###########################################################
# #
# Main Program #
# #
###########################################################
my $dbh;
my %cmds =
(
"account" => \&cmd_account,
"group" => \&cmd_group,
"help" => \&cmd_help,
"list" => \&cmd_list,
"role" => \&cmd_role,
"ssh-keys" => \&cmd_ssh_keys,
"strong-auth" => \&cmd_strong_auth,
"sync-quota" => \&cmd_sync_quota,
"system-group"=> \&cmd_systemgrp,
"year" => \&cmd_year,
);
my %cmds_account =
(
"add" => \&cmd_account_add,
"alias" => \&cmd_account_alias,
"close" => \&cmd_account_close,
"cn" => \&cmd_account_cn,
"create" => \&cmd_account_create,
"delete" => \&cmd_account_delete,
"finger" => \&cmd_account_view,
"mail" => \&cmd_account_mail,
"name" => \&cmd_account_cn,
"nopass" => \&cmd_account_nopass,
"password" => \&cmd_account_password,
"passgen" => \&cmd_account_passgen,
"photo" => \&cmd_account_photo,
"quota" => \&cmd_account_quota,
"reopen" => \&cmd_account_reopen,
"rights" => \&cmd_account_rights,
"services" => \&cmd_account_services,
"shell" => \&cmd_account_shell,
"view" => \&cmd_account_view,
"grant-intra" => \&cmd_account_grantintra,
"grant-lab" => \&cmd_account_grantlab,
"grant-mail" => \&cmd_account_grantmail,
);
my %cmds_group =
(
"view" => \&cmd_group_view,
"members" => \&cmd_group_members,
"rights" => \&cmd_group_rights,
"create" => \&cmd_group_create,
"delete" => \&cmd_group_delete
);
my %cmds_list =
(
"accounts" => \&cmd_list_accounts,
"groups" => \&cmd_list_groups,
"roles" => \&cmd_list_roles,
);
my %cmds_strong_auth =
(
"view" => \&cmd_no_strong_auth_view,
"warn" => \&cmd_no_strong_auth_warn,
"close" => \&cmd_no_strong_auth_close,
);
my %cmds_ssh_keys =
(
"view" => \&cmd_ssh_keys_without_passphrase_view,
"warn" => \&cmd_ssh_keys_without_passphrase_warn,
"remove" => \&cmd_ssh_keys_without_passphrase_remove,
);
my %group_types =
(
"intra" => "ou=intra,ou=groups",
"roles" => "ou=roles,ou=groups",
"system" => "ou=system,ou=groups",
);
######################################
# #
# UTILITY FUNCTIONS #
# #
######################################
sub ldap_get_password()
{
my $bindsecret;
if (defined($ENV{'LDAP_PASSWORD'}) && $ENV{'LDAP_PASSWORD'} ne "") {
return $ENV{'LDAP_PASSWORD'};
}
say "To avoid typing password everytime, set LDAP_PASSWORD in your env.";
say "Do not do this in your shell configuration file!";
say "Use a command like:\n";
say ' $ echo -n "LDAP password: "; read -s LDAP_PASSWORD; echo';
say ' $ LDAP_PASSWORD=$LDAP_PASSWORD lpt ...';
say "The last line prevent you from exporting the LDAP password to all commands but lpt!";
say "";
ReadMode("noecho");
print BOLD, "Need LDAP password: ", RESET;
$bindsecret = <STDIN>;
ReadMode("restore");
print "\n";
chomp $bindsecret;
return $bindsecret;
}
$LDAP::binddn = "cn=admin,dc=acu,dc=epita,dc=fr";
$LDAP::secret_search = \&ldap_get_password;
######################################
# #
# ACCOUNT BLOCK #
# #
######################################
sub cmd_account(@)
{
my $login = shift;
if (! $login) {
pod2usage(-verbose => 99,
-sections => [ 'ACCOUNT COMMANDS' ],
-exitval => 1);
}
my $subcmd = shift // "view";
if (! exists $cmds_account{$subcmd}) {
log(USAGE, "Unknown command for account: ". $subcmd);
return 1;
}
return $cmds_account{$subcmd}($login, @_);
}
sub cmd_account_alias($@)
{
return cmd_account_multiple_vieworchange('mailAlias', 'alias', @_);
}
sub cmd_account_close($;@)
{
my $login = shift;
if ($#_ > -1) {
log(USAGE, "<lpt> account <login> close");
return -1;
}
my $ldap;
eval {
$ldap = LDAP::ldap_connect();
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
};
log(ERROR, $@) if ($@);
my $entry = LDAP::get_dn($ldap, $dn, 'objectClass', 'userPassword', 'loginShell');
if (grep { "epitaAccount" } $entry->get_value("objectClass"))
{
log(INFO, "Invalidating password for ", YELLOW, $login, RESET, " ...");
my $passwd = $entry->get_value("userPassword");
$passwd =~ s/^(\{[^\}]+\})/$1!/ if ($passwd !~ /^\{[^\}]+\}!/);
$entry->replace("userPassword" => $passwd);
$entry->update($ldap);
}
$ldap->unbind or die ("couldn't disconnect correctly");
if (grep { "posixAccount" } $entry->get_value("objectClass"))
{
log(DEBUG, "Setting shell for $login ...");
cmd_account_shell($login, "/bin/false");
}
log(DONE, "Done; don't forget to restart nscd on servers and workstations!");
return 0;
}
sub cmd_account_cn($@)
{
return cmd_account_vieworchange('cn', 'name', @_);
}
sub cmd_account_add($@)
{
my $login = shift;
my $passwd_path = shift // "./passwd";
if (! -f $passwd_path)
{
log(USAGE, "lpt account <login> add [./passwd] [nopass|passgen|password]");
return 1;
}
open my $fh, "<", $passwd_path;
my @passwd_cnt = <$fh>;
close($fh);
for my $line (grep { /^$login:x/ } @passwd_cnt)
{
if ($line =~ /^$login:x:([0-9]+):([0-9]+):([^ :]+) ?([^:]*):/)
{
my $uid = $1;
my $gid = $2;
my $firstname = ucfirst $3;
my $lastname = ucfirst $4;
if (! $noconfirm)
{
say "Add user: ", YELLOW, BOLD, "$login", RESET, ":\n\tFirstname: ", BOLD, $firstname, RESET, "\n\tLastname: ", BOLD, $lastname, RESET, "\n\tUID:\t", BOLD, $uid, RESET, "\n\tGroup:\t", BOLD, $gid, RESET;
print "Would you like to add this user? [", GREEN, "y", RESET, "/", RED, "N", RESET, "] ";
my $go = <STDIN>;
chomp $go;
next if ($go ne "y" and $go ne "yes");
}
cmd_account_create($login, $gid, $uid, $firstname, $lastname, @_);
}
}
}
sub cmd_account_create($@)
{
my $login = shift;
if ($#_ < 3) {
log(USAGE, "lpt account <login> create <year> <uid> <prénom> <nom> [nopass|passgen|password]");
return 1;
}
my $group = shift;
log(DEBUG, "Adding dn: uid=$login,ou=$group,ou=users,dc=acu,dc=epita,dc=fr ...");
my $ldap = LDAP::ldap_connect();
# Check if the OU exists
my $oudn = "ou=$group,ou=users";
my $ou = LDAP::get_dn($ldap, $oudn);
if (! $ou)
{
my $mesg = $ldap->add( "$oudn,dc=acu,dc=epita,dc=fr",
attrs => [
objectclass => [ "top", "organizationalUnit" ],
ou => "$group",
]
);
if ($mesg->code == 0) {
log(INFO, "New OU created: $oudn");
} else {
log(WARN, "Unable to add new OU $oudn: ", RESET, $mesg->error);
}
}
my $mesg = $ldap->add( "uid=$login,$oudn,dc=acu,dc=epita,dc=fr",
attrs => [
objectclass => [ "top", "epitaAccount" ],
uidNumber => shift,
cn => ucfirst(shift(@_))." ".ucfirst(shift(@_)),
mail => "$login\@epita.fr",
uid => $login,
]
);
#$ldap->unbind or die ("couldn't disconnect correctly");
if ($mesg->code == 0)
{
log(INFO, "Account added: $login");
my $pass = shift // "nopass";
return cmd_account($login, $pass, @_) if ($pass ne "nopass");
return 0;
}
else {
log(ERROR, "Unable to add: $login: ", RESET, $mesg->error);
}
}
sub cmd_account_delete($@)
{
my $login = shift;
my $ldap = LDAP::ldap_connect();
my $dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
log(DEBUG, "Deleting dn: $dn ...");
if (LDAP::delete_entry($ldap, $dn))
{
log DONE, "Account ", YELLOW, $login, RESET, " successfully deleted.";
return 0;
}
else
{
log ERROR, "Unable to delete account ", YELLOW, $login, RESET, ".";
return 1;
}
}
sub cmd_account_grantintra($@)
{
my $login = shift;
my $ldap = LDAP::ldap_connect();
my $dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
if (LDAP::add_attribute($ldap, $dn, "objectClass", "intraAccount")) {
log(INFO, "$login now grants to use the intranet.");
}
$ldap->unbind or die ("couldn't disconnect correctly");
}
sub cmd_account_grantlab($@)
{
my $login = shift;
my $group = shift // "";
if ($group ne "acu" && $group ne "yaka" && $group ne "ferry")
{
log(USAGE, "lpt account <login> grant-lab <acu|yaka|ferry>");
return 1;
}
my $ldap = LDAP::ldap_connect();
my $dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
my $entry = LDAP::get_dn($ldap, $dn, "objectClass", "mail", "mailAlias", "mailAccountActive", "loginShell", "homeDirectory", "gidNumber");
if (!LDAP::get_attribute($ldap, $dn, "mail")) {
LDAP::add_attribute($ldap, $dn, "mail", "$login\@epita.fr");
}
if ($group eq "acu" || $group eq "yaka")
{
if (! grep { $_ eq "MailAccount" } @{ $entry->get_value("objectClass") })
{
$entry->replace("mailAccountActive" => [ "yes" ]);
my @oc = $entry->get_value("objectClass");
push @oc, "MailAccount";
$entry->replace("objectClass" => \@oc);
my @aliases = $entry->get_value("mailAlias");
push @aliases, "$login\@$group.epita.fr";
$entry->replace("objectClass" => \@aliases);
}
$entry->replace("loginShell" => [ "/bin/zsh" ]) if ($entry->get_value("loginShell"));
$entry->replace("homeDirectory" => [ "/home/201X/$login" ]) if ($entry->get_value("homeDirectory"));
$entry->replace("gidNumber" => [ "4242" ]) if ($entry->get_value("gidNumber"));
}
elsif ($group eq "ferry")
{
$entry->replace("loginShell" => [ "/bin/noexists" ]);
$entry->replace("homeDirectory" => [ "/dev/null" ]);
$entry->replace("gidNumber" => [ "4243" ]);
}
my @oc = $entry->get_value("objectClass");
push @oc, "labAccount";
$entry->replace("objectClass" => \@oc);
my $mesg = $entry->update($ldap) or die $!;
if ($mesg->code != 0) { log(WARN, $mesg->error); return 0; }
log(INFO, "$login now grants to receive e-mail and connect in laboratory.") if ($group eq "acu" || $group eq "yaka");
log(INFO, "$login now grants to connect in laboratory for exam.") if ($group eq "ferry");
$ldap->unbind or die ("couldn't disconnect correctly");
}
sub cmd_account_grantmail($)
{
my $login = shift;
my $ldap = LDAP::ldap_connect();
my $dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
my $entry = LDAP::get_dn($ldap, $dn, "mailAccountActive", "objectClass");
my @oc = $entry->get_value("objectClass");
push @oc, "MailAccount";
$entry->replace("objectClass" => \@oc);
$entry->replace("mailAccountActive" => [ "yes" ]);
my $mesg = $entry->update($ldap) or die $!;
if ($mesg->code != 0) { log(WARN, $mesg->error); return 0; }
else { log(INFO, "$login now grants to receive e-mail. Remember to add some aliases!"); }
$ldap->unbind or die ("couldn't disconnect correctly");
}
sub cmd_account_mail(@)
{
return cmd_account_vieworchange('mail', 'mail', @_);
}
sub cmd_account_nopass($@)
{
my $login = shift;
my $ldap;
eval {
$ldap = LDAP::ldap_connect();
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
};
log(ERROR, $@) if ($@);
my @pass = LDAP::get_attribute($ldap, $dn, 'userPassword');
if (@pass == 1 && $pass[0] eq "{crypt}!toto")
{
$ldap->unbind;
log(WARN, "Password already empty");
return 2;
}
else
{
if (!$noconfirm)
{
print STDERR "Are you sure you want to reset password for ", YELLOW, $login, RESET, "? [", GREEN, "y", RESET, "/", RED, "N", RESET, "] ";
my $go = <STDIN>;
chomp $go;
if ($go ne "y" and $go ne "yes")
{
log(DEBUG, "y response expected to continue, leaving.");
log(WARN, "Password unchanged for $login.");
return 2;
}
}
if (LDAP::update_attribute($ldap, $dn, 'userPassword', "{crypt}!toto"))
{
log(DONE, YELLOW, $login, RESET, " have no more password.");
}
}
$ldap->unbind or die ("couldn't disconnect correctly");
return 0;
}
sub cmd_account_passgen($@)
{
my $login = shift;
my $nb_char = shift // 10;
if ($nb_char < 10) {
log(USAGE, "lpt account <login> passgen [nb_char>=10]");
return 1;
}
if (!$noconfirm)
{
print STDERR "Are you sure you want to change password for ", YELLOW, $login, RESET, "? [", GREEN, "y", RESET, "/", RED, "N", RESET, "] ";
my $go = <STDIN>;
chomp $go;
if ($go ne "y" and $go ne "yes")
{
log(DEBUG, "y response expected to continue, leaving.");
log(WARN, "Password unchanged for $login.");
return 2;
}
}
log(DEBUG, "Generating a $nb_char chars password...");
my $pass = "";
open (my $fh, "pwgen -s -n -c -y -1 $nb_char 1 |");
$pass = <$fh>;
close($fh);
chomp($pass);
log(DEBUG, "Setting $pass password to ", YELLOW, $login, RESET, "...");
if (cmd_account_password($login, $pass)) {
return 3;
}
else {
say "$login:$pass";
return 0;
}
}
sub cmd_account_password($@)
{
my $login = shift;
if ($#_ > 0) {
log(USAGE, "lpt account <login> password [new_password]");
return 1;
}
my $pass = shift;
if (! $pass)
{
say STDERR "Changing password for ", YELLOW, $login, RESET, ".";
ReadMode("noecho");
print STDERR "New password: "; my $pass1 = <STDIN>;
print STDERR "\nRetype new password: "; my $pass2 = <STDIN>;
ReadMode("restore");
print STDERR "\n";
log(DEBUG, "Read passwords: $pass1 and $pass2");
$pass1 eq $pass2 || log(ERROR, "Passwords did not match.");
$pass = $pass1;
}
chomp($pass);
log(FATAL, "Empty password refused.") if ($pass eq "");
my $salt = join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64, rand 64, rand 64];
my $ctx = Digest::SHA->new(1);
$ctx->add($pass);
$ctx->add($salt);
my $enc_password = "{SSHA}" . encode_base64($ctx->digest . $salt ,'');
my $ldap;
eval {
$ldap = LDAP::ldap_connect();
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
};
log(ERROR, $@) if ($@);
return !LDAP::update_attribute($ldap, $dn, 'userPassword', $enc_password);
}
sub cmd_account_photo($@)
{
return cmd_account_vieworchange('photoURI', 'photo', @_);
}
sub cmd_account_reopen(@)
{
my $login = shift;
if ($#_ != -1) {
log(USAGE, "<lpt> account <login> reopen");
return 1;
}
my $ldap;
eval {
$ldap = LDAP::ldap_connect();
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
};
log(ERROR, $@) if ($@);
my $entry = LDAP::get_dn($ldap, $dn, 'objectClass', 'cn', 'userPassword', 'loginShell');
if (grep { "epitaAccount" } $entry->get_value("objectClass"))
{
# update password
my $passwd = $entry->get_value("userPassword");
if ($passwd =~ /^\{[^\}]+\}!/)
{
log(INFO, "Restoring password for ", YELLOW, $login, RESET, " ...");
$passwd =~ s/^(\{[^\}]+\})!/$1/;
LDAP::update_attribute($ldap, "userPassword", $passwd);
}
}
$ldap->unbind or die ("couldn't disconnect correctly");
if (grep { "posixAccount" } $entry->get_value("objectClass"))
{
log(DEBUG, "Setting shell for $login ...");
cmd_account_shell($login, $shellValid);
}
log(DONE, "Done; don't forget to restart nscd on servers and workstations!");
return 0;
}
sub cmd_account_rights($@)
{
return cmd_account_multiple_vieworchange("intraRight", "right", @_);
}
sub cmd_account_services($@)
{
return cmd_account_multiple_vieworchange("labService", "laboratory_service", @_);
}
sub cmd_account_shell($@)
{
return cmd_account_vieworchange("loginShell", "shell", @_);
}
sub cmd_account_multiple_vieworchange($$$@)
{
my $type = shift;
my $typeName = shift;
my $login = shift;
my $action = shift // "list";
my $change = shift;
if (($action ne "list" and $action ne "add" and $action ne "del" and $action ne "flush") or (!$change and $action ne "list" and $action ne "flush")) {
log(USAGE, "lpt account <login> $typeName [list|add|del|flush] [string]");
return 1;
}
my $ldap;
eval {
$ldap = LDAP::ldap_connect() if ($action ne "list");
$ldap = LDAP::ldap_connect_anon() if ($action eq "list");
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
};
log(ERROR, $@) if ($@);
my @attr = LDAP::get_attribute($ldap, $dn, $type);
if ($action eq "add")
{
log(INFO, "Adding ", BOLD, YELLOW, $change, RESET, " as ".$typeName."s for ", YELLOW, $login, RESET, " ...");
if (LDAP::add_attribute($ldap, $dn, $type, $change)) {
log(DONE, "Done!");
}
}
elsif ($action eq "del")
{
log(INFO, "Deleting ", BOLD, YELLOW, $change, RESET, " as ".$typeName."s for ", YELLOW, $login, RESET, " ...");
if (LDAP::delete_attribute($ldap, $dn, $type, $change)) {
log(DONE, "Done!");
}
}
elsif ($action eq "flush")
{
log(DONE, YELLOW, $login, RESET, " have no more $typeName.") if LDAP::flush_attribute($ldap, $dn, $type);
}
else
{
if (@attr)
{
log(INFO, BOLD, YELLOW, $login, RESET, "'s ".$typeName."s are:");
for my $val (@attr) {
say " - ", BOLD, $val, RESET;
}
}
else {
log(INFO, YELLOW, $login, RESET, " have no $typeName.");
}
}
$ldap->unbind or die ("couldn't disconnect correctly");
return 0;
}
sub cmd_account_vieworchange($$@)
{
my $type = shift;
my $typeName = shift;
my $login = shift;
if ($#_ > 0) {
log(USAGE, "lpt account <login> $typeName [new_$typeName]");
return 1;
}
my $change = shift;
my $ldap;
eval {
$ldap = LDAP::ldap_connect() if ($change);
$ldap = LDAP::ldap_connect_anon() if (!$change);
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
};
log(ERROR, $@) if ($@);
my $attr = LDAP::get_attribute($ldap, $dn, $type);
if ($change)
{
log(INFO, "Setting $typeName to ", YELLOW, BOLD, $change, RESET " for ", YELLOW, $login, " ...");
LDAP::update_attribute($ldap, $dn, $type, $change);
log(DONE, "Done!");
}
elsif ($attr) {
log(INFO, YELLOW, $login, RESET, "'s $typeName is ", BOLD, YELLOW, $attr, RESET, ".");
}
else {
log(INFO, YELLOW, $login, RESET, "'s has no $typeName.");
}
$ldap->unbind or die ("couldn't disconnect correctly");
return 0;
}
sub cmd_account_view($@)
{
my $login = shift;
my $ldap = LDAP::ldap_connect_anon();
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
};
log(ERROR, $@) if ($@);
my @classes = LDAP::get_attribute($ldap, $dn, 'objectClass');
log(DEBUG, "objectClasses: ", join(', ', @classes));
my @attrs;
if ($#_ >= 0) {
push @attrs, @_;
}
else
{
push @attrs, 'uid', 'cn', 'mail', 'uidNumber' if (grep { "epitaAccount" } @classes);
push @attrs, 'gecos', 'loginShell', 'homeDirectory', 'gidNumber' if (grep { "posixAccount" } @classes);
push @attrs, 'labService', 'quotaHomeBlock', 'quotaHomeFile', 'quotaSgoinfreBlock', 'quotaSgoinfreFile' if (grep { "labAccount" } @classes);
push @attrs, 'intraRight' if (grep { "intraAccount" } @classes);
push @attrs, 'mailAlias' if (grep { "MailAccount" } @classes);
}
log(DEBUG, "attrs to get: " . join(', ', @attrs));
my @res = LDAP::get_dn($ldap, $dn, @attrs);
my $nb = 0;
for my $entry (@res)
{
say "==" if ($nb > 0);
say BOLD, YELLOW, "dn: ", RESET, YELLOW, $entry->dn, RESET;
for my $attr (@attrs)
{
if ($#attrs < 3)
{
for my $entry ($entry->get_value($attr)) {
say CYAN, "$attr: ", RESET, $entry;
}
}
else {
say CYAN, "$attr: ", RESET, join(', ', $entry->get_value($attr));
}
}
$nb++;
}
say "\n$nb users displayed" if ($nb > 1);
$ldap->unbind or die ("couldn't disconnect correctly");
return 0;
}
######################################
# #
# GROUP BLOCKS #
# #
######################################
sub cmd_group(@)
{
return cmd_groups($group_types{intra}, @_);
}
sub cmd_role(@)
{
return cmd_groups($group_types{roles}, @_);
}
sub cmd_systemgrp(@)
{
return cmd_groups($group_types{system}, @_);
}
sub cmd_groups($@)
{
my $ou = shift;
my $gname = shift;
if ($gname && $gname =~ /^(2[0-9]{3})$/)
{
$ou = "ou=$1,$ou";
$gname = shift;
}
if (! $gname) {
pod2usage(-verbose => 99,
-sections => [ 'GROUP COMMANDS' ],
-exitval => 1);
}
my $subcmd = shift // "view";
if (! exists $cmds_group{$subcmd}) {
log(USAGE, "Unknown command for group: ". $subcmd);
return 1;
}
return $cmds_group{$subcmd}($ou, $gname, @_);
}
sub cmd_group_multiple_vieworchange
{
my $type = shift;
my $typeName = shift;
my $ou = shift;
my $gname = shift;
my $action = shift // "list";
my $change = shift;
if (($action ne "list" and $action ne "add" and $action ne "del" and $action ne "flush") or (!$change and $action ne "list" and $action ne "flush")) {
log(USAGE, "lpt group [year] <group-name> $typeName [list|add|del|flush] [string]");
return 1;
}
my $ldap;
eval {
$ldap = LDAP::ldap_connect() if ($action ne "list");
$ldap = LDAP::ldap_connect_anon() if ($action eq "list");
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, $ou, "cn=$gname");
};
log(ERROR, $@) if ($@);
my @attr = LDAP::get_attribute($ldap, $dn, $type);
if ($action eq "add")
{
log(INFO, "Adding ", BOLD, YELLOW, $change, RESET, " as ", $typeName, "s for ", YELLOW, $gname, RESET, " ...");
if (LDAP::add_attribute($ldap, $dn, $type, $change)) {
log(DONE, "Done!");
}
}
elsif ($action eq "del")
{
log(INFO, "Deleting ", BOLD, YELLOW, $change, RESET, " as ".$typeName."s for ", YELLOW, $gname, RESET, " ...");
if (LDAP::delete_attribute($ldap, $dn, $type, $change)) {
log(DONE, "Done!");
}
}
elsif ($action eq "flush")
{
log(DONE, YELLOW, $gname, RESET, " have no more $typeName.") if LDAP::flush_attribute($ldap, $dn, $type);
}
else
{
if (@attr)
{
log(INFO, BOLD, YELLOW, $gname, RESET, "'s ".$typeName."s are:");
for my $val (@attr) {
say " - $val";
}
}
else {
log(INFO, YELLOW, $gname, RESET, " have no $typeName.");
}
}
$ldap->unbind or die ("couldn't disconnect correctly");
return 0;
}
sub cmd_group_vieworchange
{
my $type = shift;
my $typeName = shift;
my $ou = shift;
my $gname = shift;
if ($#_ > 0) {
log(USAGE, "<lpt> group <group-name> $typeName [new_string]");
return 1;
}
my $change = shift;
my $ldap;
eval {
$ldap = LDAP::ldap_connect() if ($change);
$ldap = LDAP::ldap_connect_anon() if (!$change);
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, $ou, "cn=$gname");
};
log(ERROR, $@) if ($@);
my $attr = LDAP::get_attribute($ldap, $dn, $type);
if ($change)
{
log(INFO, "Setting $typeName to ", YELLOW, BOLD, $change, RESET " for ", YELLOW, $gname, " ...");
LDAP::update_attribute($ldap, $dn, $type, $change);
log(DONE, "Done!");
}
elsif ($attr) {
log(INFO, YELLOW, $gname, RESET, "'s $typeName is ", BOLD, YELLOW, $attr, RESET, ".");
}
else {
log(INFO, YELLOW, $gname, RESET, "'s has no $typeName.");
}
$ldap->unbind or die ("couldn't disconnect correctly");
return 0;
}
sub cmd_group_view
{
my $ou = shift;
my $gname = shift;
my $ldap = LDAP::ldap_connect_anon();
my $dn;
eval {
$dn = LDAP::search_dn($ldap, $ou, "cn=$gname");
};
log(ERROR, $@) if ($@);
my @classes = LDAP::get_attribute($ldap, $dn, 'objectClass');
log(DEBUG, "objectClasses: ", join(', ', @classes));
my @attrs;
if ($#_ >= 0) {
push @attrs, @_;
}
else
{
push @attrs, 'intraRight' if (grep { "intraGroup" } @classes);
push @attrs, 'cn', 'memberUid' if (grep { "posixGroup" } @classes);
}
log(DEBUG, "attrs to get: " . join(', ', @attrs));
my @res = LDAP::get_dn($ldap, $dn, @attrs);
my $nb = 0;
for my $entry (@res)
{
say "==" if ($nb > 0);
say BOLD, YELLOW, "dn: ", RESET, YELLOW, $entry->dn, RESET;
for my $attr (@attrs)
{
if ($#attrs < 3)
{
for my $entry ($entry->get_value($attr)) {
say CYAN, "$attr: ", RESET , $entry;
}
}
else {
say CYAN, "$attr: ", RESET , join(', ', $entry->get_value($attr));
}
}
$nb++;
}
say "\n$nb groups displayed" if ($nb > 1);
$ldap->unbind or die ("couldn't disconnect correctly");
return 0;
}
sub cmd_group_members($@)
{
return cmd_group_multiple_vieworchange('memberUid', 'member', @_);
}
sub cmd_group_rights($@)
{
return cmd_group_multiple_vieworchange('intraRight', 'right', @_);
}
sub cmd_group_create
{
my $ou = shift;
my $gname = shift;
log(DEBUG, "Adding dn: cn=$gname,ou=intra,ou=groups,dc=acu,dc=epita,dc=fr ...");
my $dn = "cn=$gname,$ou";
my $class;
$class = "intraGroup" if ($ou ne $group_types{system});
$class = "posixGroup" if ($ou eq $group_types{system});
my $ldap;
eval {
$ldap = LDAP::ldap_connect();
};
log(ERROR, $@) if ($@);
my $mesg = $ldap->add( $dn . ",dc=acu,dc=epita,dc=fr",
attrs => [
objectclass => [ "top", $class ],
cn => $gname,
]
);
$ldap->unbind or die ("couldn't disconnect correctly");
if ($mesg->code == 0)
{
log(DONE, "Group added: ", YELLOW, $gname, RESET);
return 0;
}
else {
log(ERROR, "Unable to add: $gname: ", RESET, $mesg->error);
}
}
sub cmd_group_delete(@)
{
my $ou = shift;
my $gname = shift;
my $dn = "cn=$gname,$ou";
log(DEBUG, "Deleting dn: $dn ...");
my $ldap = LDAP::ldap_connect();
if (LDAP::delete_entry($ldap, $dn))
{
log DONE, "Group ", YELLOW, $gname, RESET, " successfully deleted.";
return 0;
}
else
{
log ERROR, "Unable to delete group ", YELLOW, $gname, RESET, ".";
return 1;
}
}
######################################
# #
# LIST BLOCK #
# #
######################################
sub cmd_list(@)
{
my $subcmd = shift;
if (! $subcmd) {
pod2usage(-verbose => 99,
-sections => [ 'LIST COMMANDS' ] );
}
elsif (! exists $cmds_list{$subcmd}) {
log(USAGE, "Unknown command for list: ". $subcmd);
return 1;
}
return $cmds_list{$subcmd}(@_);
}
sub cmd_list_accounts(@)
{
my $ou = "ou=users";
my $action = shift // "all";
if ($action =~ /^2[0-9{3}]$/)
{
$ou = "ou=$action,$ou";
$action = shift // "all";
}
my $ldap;
eval {
$ldap = LDAP::ldap_connect_anon() if ($action eq "services");
$ldap = LDAP::ldap_connect() if ($action ne "services");
};
log(ERROR, $@) if ($@);
if ($action eq "services")
{
my $service = shift // "*";
my @entries = LDAP::search_dns($ldap,
$ou,
"&(labService=$service)(|(objectClass=posixAccount)(objectClass=epitaAccount))",
'uid',
'labService');
if ($#entries < 0) {
log(WARN, "No account found!");
}
else
{
for my $entry (@entries) {
say YELLOW, $entry->get_value("uid"), "\t", RESET, join(", ", $entry->get_value("labService"));
}
}
}
else
{
my $filter;
if ($action eq "open") {
$filter = "&(!(loginShell=$shellFalse))(|(objectClass=posixAccount)(objectClass=epitaAccount))";
}
elsif ($action eq "close") {
$filter = "&(!(loginShell=$shellFalse))(|(objectClass=posixAccount)(objectClass=epitaAccount))";
}
elsif ($action eq "posix") {
$filter = "objectClass=posixAccount";
}
elsif ($action eq "intra") {
$filter = "objectClass=intraAccount";
}
elsif ($action eq "all") {
$filter = "|(objectClass=posixAccount)(objectClass=epitaAccount)";
}
my @entries = LDAP::search_dns($ldap,
$ou,
$filter,
'userPassword',
'loginShell');
if ($#entries < 0) {
log(WARN, "No account found");
}
else
{
for my $entry (@entries)
{
my $closed = 0;
$closed++ if (!$entry->get_value("userPassword") || $entry->get_value("userPassword") =~ /^\{[^\}]\}!/);
$closed++ if (!$entry->get_value("loginShell") || $entry->get_value("loginShell") eq $shellFalse);
if ($closed == 0) {
print GREEN, "Opened:\t", RESET;
} elsif ($closed == 2) {
print RED, "Closed:\t", RESET;
} else {
print YELLOW, "Partially closed:\t", RESET;
}
say $entry->dn;
}
}
}
$ldap->unbind or die ("couldn't disconnect correctly");
return 0;
}
######################################
# #
# YEAR BLOCK #
# #
######################################
sub cmd_year(@)
{
my $year = shift;
if ($year)
{
if ($year =~ /^[0-9]{4}$/)
{
say BOLD, MAGENTA, ">>>", RESET, " Changing current year to: ", YELLOW, BOLD, $year, RESET;
log (DONE, "Done!") if (LDAP::update_attribute(undef, LDAP::YEAR_DN, "year", $year))
}
else {
say BOLD, RED, ">>>", WHITE, " $year is not a valid year.", RESET;
return 1;
}
}
else {
say BOLD, BLUE, ">>>", RESET, " Current year: ", YELLOW, BOLD, LDAP::get_year(), RESET;
}
return 0
}
######################################
# #
# QUOTA COMMAND #
# #
######################################
sub cmd_account_quota($@)
{
my $login = shift;
my $action = shift // "view";
if ($action eq "view") {
cmd_account_quota_view($login, @_);
}
elsif ($action eq "sync")
{
if (! -d $nfsHomePrefix)
{
log(FATAL, "Quota sychronization can only be performed on the NFS server.");
return 1;
}
cmd_account_quota_sync($login, 0);
}
else {
cmd_account_quota_set($login, $action, @_);
}
}
sub cmd_account_quota_view($@)
{
my $login = shift;
my $ldap;
eval {
$ldap = LDAP::ldap_connect_anon();
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
};
log(ERROR, $@) if ($@);
my $entry = LDAP::get_dn($ldap, $dn, 'quotaHomeBlock', 'quotaHomeFile', 'quotaSgoinfreBlock', 'quotaSgoinfreFile');
say BOLD, YELLOW, "dn: ", RESET, YELLOW, $entry->dn, ":", RESET;
say " - ", BLUE, "Home blocks:\t\t", RESET, ($entry->get_value("quotaHomeBlock") or "(standard)");
say " - ", BLUE, "Home files:\t\t", RESET, ($entry->get_value("quotaHomeFile") or "(standard)");
say " - ", BLUE, "Sgoinfre blocks:\t", RESET, ($entry->get_value("quotaSgoinfreBlock") or "(standard)");
say " - ", BLUE, "Sgoinfre files:\t", RESET, ($entry->get_value("quotaSgoinfreFile") or "(standard)");
$ldap->unbind or die ("couldn't disconnect correctly");
}
sub cmd_account_quota_set($@)
{
my $login = shift;
if ($#_ < 2 || $#_ > 2)
{
log(USAGE, "<lpt> account <login> quota <volume> <type> <value>");
say " With:\n\tvolume := home | sgoinfre\n\ttype := file | block\n\tvalue := [+-]?[0-9]+[TGMk]?";
return 1;
}
my $volume = shift;
my $type = shift;
my $value = shift;
# check args
log(ERROR, "Volume must be home or sgoinfre; given: $volume") if (!($volume eq "home" || $volume eq "sgoinfre"));
log(ERROR, "Type must be file or block; given: $type") if (!($type eq "file" || $type eq "block"));
# generate quotaName
my $quotaName = "quota";
$quotaName .= "Home" if ($volume eq "home");
$quotaName .= "Sgoinfre" if ($volume eq "sgoinfre");
$quotaName .= "File" if ($type eq "file");
$quotaName .= "Block" if ($type eq "block");
my $ldap;
eval {
$ldap = LDAP::ldap_connect() if ($value);
$ldap = LDAP::ldap_connect_anon() if (!$value);
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "uid=$login");
};
log(ERROR, $@) if ($@);
my $entry = LDAP::get_dn($ldap, $dn, $quotaName);
my $old_value = $entry->get_value($quotaName) // $def_quota{$type}{$volume};
if (!$value)
{
say YELLOW, "dn: ", $entry->dn, RESET;
say BLUE, $quotaName, ": ", RESET, $old_value;
return 0;
}
my $nb;
if ($value =~ '([0-9]+)([MKGTmkgt]?)')
{
$nb = $1;
$nb *= 1024 if ($2 eq "K" or $2 eq "k");
$nb *= 1048576 if ($2 eq "M" or $2 eq "m");
$nb *= 1073741824 if ($2 eq "G" or $2 eq "g");
$nb *= 1099511627776 if ($2 eq "T" or $2 eq "t");
}
if ($value =~ '^\+([0-9]+)([MKGTmkgt]?)$')
{
$value = $old_value + $nb;
}
elsif ($value =~ '^-([0-9]+)([MKGTmkgt]?)$')
{
$value = $old_value - $nb;
}
elsif ($value !~ /^[0-9]+[MKGTmkgt]?$/) {
log(ERROR, "Value must be an integer or +i or -i");
}
else {
$value = $nb;
}
log(INFO, "Changing quota of $quotaName of $login to $value...");
if (LDAP::update_attribute($ldap, $dn, $quotaName, $value)) {
log(DONE, "Done!");
}
$ldap->unbind;
}
sub cmd_account_quota_sync($;$)
{
my $login = shift;
my $nosync = shift;
my $ldap;
eval {
$ldap = LDAP::ldap_connect_anon();
};
log(ERROR, $@) if ($@);
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "(&(uid=$login)(objectClass=labAccount))");
};
log(ERROR, $@) if ($@);
my $entry = LDAP::get_dn($ldap, $dn,
'uid', 'uidNumber',
'quotaHomeBlock', 'quotaHomeFile',
'quotaSgoinfreBlock', 'quotaSgoinfreFile');
my $quotaHomeBlock = $entry->get_value("quotaHomeBlock") // $def_quota{block}{home};
my $quotaHomeFile = $entry->get_value("quotaHomeFile") // $def_quota{file}{home};
my $quotaSgoinfreBlock = $entry->get_value("quotaSgoinfreBlock") // $def_quota{block}{sgoinfre};
my $quotaSgoinfreFile = $entry->get_value("quotaSgoinfreFile") // $def_quota{file}{sgoinfre};
require Quota;
if (Quota::setqlim($dev_quota{home}, $entry->get_value("uidNumber"), int(0.9 * $quotaHomeBlock), $quotaHomeBlock, int(0.9 * $quotaHomeFile), $quotaHomeFile, 1, 0) == 0 and
Quota::setqlim($dev_quota{sgoinfre}, $entry->get_value("uidNumber"), int(0.9 * $quotaSgoinfreBlock), $quotaSgoinfreBlock, int(0.9 * $quotaSgoinfreFile), $quotaSgoinfreFile, 1, 0) == 0) {
log(DONE, YELLOW, $login, RESET, "'s quota synchronized!");
}
else {
log(ERROR, "An error occurs during quota synchronization: ", Quota::strerr());
return 2;
}
$ldap->unbind or die ("couldn't disconnect correctly");
if (!$nosync)
{
Quota::sync($dev_quota{home});
Quota::sync($dev_quota{sgoinfre});
}
return 0;
}
sub cmd_sync_quota(@)
{
require Quota;
# Set root quota
Quota::setqlim($dev_quota{home}, 0, 0, 0, 0, 0, 1, 0);
Quota::setqlim($dev_quota{sgoinfre}, 0, 0, 0, 0, 0, 1, 0);
my $ldap;
eval {
$ldap = LDAP::ldap_connect_anon();
};
log(ERROR, $@) if ($@);
my @entries = LDAP::search_dns($ldap, "ou=users", "(objectClass=labAccount)", "uid");
$ldap->unbind or die ("couldn't disconnect correctly");
for my $entry (@entries) {
cmd_account_quota_sync($entry->get_value("uid"), 1);
}
Quota::sync($dev_quota{home});
Quota::sync($dev_quota{sgoinfre});
}
######################################
# #
# STRONG_AUTH COMMAND #
# #
######################################
sub cmd_strong_auth(@)
{
my $subcmd = shift // "view";
if (! exists $cmds_strong_auth{$subcmd}) {
log(USAGE, "Unknown command for strong_auth: ". $subcmd);
return 1;
}
return $cmds_strong_auth{$subcmd}(@_);
}
sub get_no_strong_auth_user()
{
my @faulty_users;
my $ldap;
eval {
$ldap = LDAP::ldap_connect_anon();
};
log(ERROR, $@) if ($@);
my @entries = LDAP::search_dns($ldap, "ou=users", "&(&(objectClass=labAccount)(!(homeDirectory=/dev/null)))(!(loginShell=/bin/false))",
'uid', 'cn', 'mailAlias', 'homeDirectory', 'labService');
foreach my $entry (@entries)
{
my $home = $entry->get_value("homeDirectory");
$home =~ s#^$wksHomePrefix#$nfsHomePrefix#;
my $token = $home . "/.google_authenticator";
my $login = $entry->get_value("uid");
push @faulty_users, $entry if (! -f $token || -s $token < 90);
}
$ldap->unbind or die ("couldn't disconnect correctly");
return @faulty_users;
}
sub cmd_no_strong_auth_view(@)
{
for my $entry (get_no_strong_auth_user())
{
print $entry->get_value("uid");
print " ", GREEN, "ACK", RESET if (grep { $_ eq "no-strong-auth" } $entry->get_value('labService'));
print "\n";
}
}
sub cmd_no_strong_auth_warn(@)
{
require Email::Sender::Simple;
Email::Sender::Simple->import(qw(sendmail));
for my $entry (get_no_strong_auth_user())
{
next if (grep { $_ eq "no-strong-auth" } $entry->get_value('labService'));
say $entry->get_value("uid");
my $body = "Bonjour ".decode('UTF-8', $entry->get_value("cn"), Encode::FB_CROAK).",
Vous n'avez pas activé l'authentification forte pour SSH.
Pour connaître la marche à suivre pour l'activer, consultez :
https://www.acu.epita.fr/wiki/index.php?title=Ssh_double_factor_auth
Merci de rectifier la situation au plus vite ou votre compte sera mis
en suspens.
Cordialement,
P.-S. : Ce message est généré automatiquement, les roots sont en copie.
Pour toute demande, merci de faire un ticket à admin\@acu.epita.fr
--
Les roots ACU";
my $mail = Email::MIME->create(
header_str => [
From => "Roots assistants <admin\@acu.epita.fr>",
To => $entry->get_value("mailAlias"),
Cc => 'Roots assistants <root@acu.epita.fr>',
Subject => "[PILA][AUTH-FORTE] Authentification forte SSH non active"
],
attributes => {
encoding => 'quoted-printable',
charset => 'utf-8',
format => 'flowed',
},
body_str => $body,
);
sendmail($mail);
}
}
sub cmd_no_strong_auth_close(@)
{
require Email::Sender::Simple;
Email::Sender::Simple->import(qw(sendmail));
for my $entry (get_no_strong_auth_user())
{
next if (grep { $_ eq "no-strong-auth" } $entry->get_value('labService'));
say $entry->get_value("uid");
cmd_account_close($entry->get_value("uid"));
my $body = "Bonjour ".decode('UTF-8', $entry->get_value("cn"), Encode::FB_CROAK).",
Après plusieurs relances de notre part, vous n'avez toujours pas activé
l'authentification forte pour SSH. Votre compte a donc été suspendu.
Nous vous invitons à passer au laboratoire pour faire réactiver votre
compte.
Cordialement,
--
Les roots ACU";
# create the message
my $mail = Email::MIME->create(
header_str => [
From => "Roots assistants <admin\@acu.epita.fr>",
To => $entry->get_value("mailAlias"),
Cc => 'Roots assistants <root@acu.epita.fr>',
Subject => "[PILA][ACCES] Compte suspendu"
],
attributes => {
encoding => 'quoted-printable',
charset => 'utf-8',
format => 'flowed',
},
body_str => $body,
);
sendmail($mail);
}
}
######################################
# #
# SSH_KEYS COMMAND #
# #
######################################
sub cmd_ssh_keys(@)
{
my $subcmd = shift // "view";
if (! exists $cmds_ssh_keys{$subcmd}) {
log(USAGE, "Unknown command for ssh_keys: ". $subcmd);
return 1;
}
return $cmds_ssh_keys{$subcmd}(@_);
}
sub get_ssh_keys_unprotected()
{
my %keys_unprotected = qw();
my $ldap;
eval {
$ldap = LDAP::ldap_connect_anon();
};
log(ERROR, $@) if ($@);
my @entries = LDAP::search_dns($ldap, "ou=users", "&(objectClass=posixAccount)(!(homeDirectory=/dev/null))",
'uid', 'cn', 'homeDirectory');
foreach my $entry (@entries)
{
my $home = $entry->get_value("homeDirectory");
$home =~ s#^$wksHomePrefix#$nfsHomePrefix#;
my $sshDir = $home . "/.ssh";
my $login = $entry->get_value("uid");
if (-d $sshDir)
{
my $process_file = sub() {
my $file = $_;
if (-f $file)
{
open my $fh, '<', $file or die $!;
my @lines = <$fh>;
close $fh;
if ( grep { chomp; $_ =~ /PRIVATE KEY/ } @lines )
{
if (! grep { chomp; $_ =~ /ENCRYPTED/ } @lines )
{
if (!exists $keys_unprotected{$login}) {
$keys_unprotected{$login} = [$file];
} else {
push(@{$keys_unprotected{$login}}, $file);
}
}
}
}
};
find({ wanted => \&$process_file, no_chdir => 1 }, $sshDir);
}
}
$ldap->unbind or die ("couldn't disconnect correctly");
return %keys_unprotected;
}
sub cmd_ssh_keys_without_passphrase_generic(@)
{
my $func = shift;
my %keys_unprotected = get_ssh_keys_unprotected();
my $ldap;
eval {
$ldap = LDAP::ldap_connect_anon();
};
log(ERROR, $@) if ($@);
foreach my $login (keys %keys_unprotected)
{
my $dn;
eval {
$dn = LDAP::search_dn($ldap, "ou=users", "(uid=$login)");
};
log(ERROR, $@) if ($@);
my $entry = LDAP::get_dn($ldap, $dn, 'uid', 'cn', 'mailAlias');
# Apply func
&$func($entry, \@{$keys_unprotected{$login}});
}
$ldap->unbind or die ("couldn't disconnect correctly");
}
# list unprotected keys
sub cmd_ssh_keys_without_passphrase_view(@)
{
my $process = sub() {
my $entry = shift;
my $keys = shift;
# Display
say $entry->get_value("cn"), ":";
for my $key (@$keys) {
say " * $key";
}
print "\n";
};
cmd_ssh_keys_without_passphrase_generic(\&$process);
}
# warn about unprotected keys
sub cmd_ssh_keys_without_passphrase_warn(@)
{
require Email::Sender::Simple;
Email::Sender::Simple->import(qw(sendmail));
my $process = sub() {
my $entry = shift;
my $keys = shift;
# Display
say $entry->get_value("uid");
my $body = "Bonjour ".decode('UTF-8', $entry->get_value("cn"), Encode::FB_CROAK).",
Un outil automatique a découvert une clef sans passphrase sur votre compte
du laboratoire. Il est impératif de mettre une passphrase chiffrant votre
clef pour des raisons de sécurité.
Les clefs non protégées sont les suivantes :\n";
foreach my $key (@$keys)
{
$key =~ s#^$nfsHomePrefix#$wksHomePrefix#;
$body .= " - $key\n";
}
$body .= "\nPour mettre une passphrase :
\$ ssh-keygen -p -f CHEMIN_VERS_LA_CLE_PRIVEE
Merci de rectifier la situation au plus vite ou votre clé sera supprimée et
votre compte sera mis en suspens.
Cordialement,
PS: Ce message est généré automatiquement, les roots sont en copie.
Pour toute demande, merci de faire un ticket à admin\@acu.epita.fr
--
Les roots ACU";
# create the message
my $mail = Email::MIME->create(
header_str => [
From => "Roots assistants <admin\@acu.epita.fr>",
To => $entry->get_value("mailAlias"),
Cc => 'Roots assistants <root@acu.epita.fr>',
Subject => "[PILA][SSH-KEY] Clef SSH non protégée"
],
attributes => {
encoding => 'quoted-printable',
charset => 'utf-8',
format => 'flowed',
},
body_str => $body,
);
sendmail($mail);
};
cmd_ssh_keys_without_passphrase_generic(\&$process);
}
# remove unprotected keys
sub cmd_ssh_keys_without_passphrase_remove(@)
{
require Email::Sender::Simple;
Email::Sender::Simple->import(qw(sendmail));
my $process = sub() {
my $entry = shift;
my $keys = shift;
# Display
say $entry->get_value("uid");
# create the message
my $body = "Bonjour ".decode('UTF-8', $entry->get_value("cn"), Encode::FB_CROAK).",
Un outil automatique a découvert une clef sans passphrase sur votre
compte du laboratoire.
N'ayant pas corrigé votre situation après plusieurs relances, nous avons
désactivé votre compte et supprimé le(s) clef(s) incriminées.
Pour information, voici l'empreinte de chacune des clefs supprimée :\n";
foreach my $key (@$keys)
{
open (FNGR, "ssh-keygen -l -f '$key' | cut -d ' ' -f 2 |");
my $fingerprint = <FNGR>;
chomp $fingerprint;
close (FNGR);
unlink($key);
$key =~ s#^$nfsHomePrefix#$wksHomePrefix#;
$body .= " - $key: $fingerprint\n";
}
$body .= "\n
Contacter les roots pour faire reouvrir votre compte.
Cordialement,
PS: Ce message est généré automatiquement, les roots sont en copie.
Pour toute demande, merci de faire un ticket à admin\@acu.epita.fr
--
Les roots ACU";
my $mail = Email::MIME->create(
header_str => [
From => "Roots assistants <admin\@acu.epita.fr>",
To => $entry->get_value("mailAlias"),
Cc => 'Roots assistants <root@acu.epita.fr>',
Subject => "[PILA][SSH-KEY] Clef SSH non protégée supprimée"
],
attributes => {
encoding => 'quoted-printable',
charset => 'utf-8',
format => 'flowed',
},
body_str => $body,
);
sendmail($mail);
};
cmd_ssh_keys_without_passphrase_generic(\&$process);
}
######################################
# #
# MAIN CORE #
# #
######################################
sub cmd_help
{
pod2usage(-exitval => 1, -verbose => 2);
}
if ($#ARGV == -1) {
cmd_help();
exit(1);
}
my $cmd = shift;
if ($cmd eq "-v" or $cmd eq "--verbose" or $cmd eq "--debug") {
$ACU::Log::display_level = 8;
$cmd = shift;
}
elsif ($cmd eq "-q" or $cmd eq "--quiet") {
$ACU::Log::display_level = 6;
$cmd = shift;
}
elsif ($cmd eq "-y" or $cmd eq "--yes") {
$noconfirm = 1;
$cmd = shift;
}
$ACU::Log::fatal_error = 1;
$ACU::Log::fatal_warn = 0;
if (! exists $cmds{$cmd})
{
say BOLD, "Usage: ", RESET, "$0 ", GREEN, "command", RESET, " <arguments>";
log(ERROR, "Uknown command : $cmd");
}
exit ($cmds{$cmd}(@ARGV));
__END__
=head1 NAME
lpt - Lab Power Tool
=head1 SYNOPSIS
B<lpt> I<command> [arguments]
I<command> can be:
B<lpt> I<account> <login> [arguments]
Manage the account <login>.
B<lpt> I<group> [year] <group-name> [arguments]
Manage the intranet group <group-name> for the current or given year.
B<lpt> I<help>
Display this screen.
B<lpt> I<role> [year] <role-name> [arguments]
Manage the intranet role <role-name> for the current or given year.
B<lpt ssh-keys> [view|warn|remove]
Search for users with SSH keys without passphrase. Warn the users and
remove them if requested.
B<lpt strong-auth> [view|warn|close]
Search for users without strong authentication. Warn the users and
close its account if requested.
B<lpt sync-quota>
Sync the quota of all users.
B<lpt> I<system-group> <group-name> [arguments]
Manage the system group <group-name>.
B<lpt> I<year> [year]
Display or set the current year.
=head1 ACCOUNT COMMANDS
B<lpt account> <login> [I<view> [I<attribute> [I<attribute> [...]]]]
Display information about <login>.
<login> can be a globbing string.
If <attribute> are given, display only those attributes.
B<lpt account> <login> I<add> [./passwd] [nopass|password|passgen]
This is used to create a new Epita account, base for intra and/or lab account.
This will use the passwd file given in argument to import information about the login.
B<lpt account> <login> I<create> <promo> <uid> <Prenom> <Nom> [nopass|password|passgen]
This is used to create a new Epita account, base for intra and/or lab account.
Promo for professor are professors, other people are guests.
B<lpt account> <login> I<grant-intra>
Give rights to the user to access the intranet.
B<lpt account> <login> I<grant-lab> <acu | yaka | ferry>
Give rights to the user to access intern systems of the laboratory (SSH, Unix, ...)
If ferry is given, open an account for exam only, with restricted rights.
B<lpt account> <login> I<grant-mail>
Give rights to the user to receive e-mails.
B<lpt account> <login> I<alias> [list|add|del|flush] [string]
This is used to manage e-mail aliases.
B<lpt account> <login> I<close>
This is used to close an existing account.
B<lpt account> <login> I<delete>
This is used to delete an existing account.
NEVER DELETE AN ACCOUNT, close it instead.
B<lpt account> <login> I<mail> [new-mail]
This is used to display, or change if [new-mail] is given, the account contact adress.
B<lpt account> <login> I<name> [new-name]
This is used to display, or change if [new-name] is given, the account common name.
B<lpt account> <login> I<reopen>
This is used to reopen a previously closed account.
B<lpt account> <login> I<shell> <shell path>
This is used to change default shell for an existing accout.
B<lpt account> <login> I<nopass>
This is used to erase the user password.
B<lpt account> <login> I<passgen> [nb_char]
This is used to set user password. Generated by pwgen.
nb_char must be at least egal to 10.
B<lpt account> <login> I<password> [password]
This is used to set user password. Interactively asked if not given.
B<lpt account> I<mail> <login> [new]
This is used to get user email (to which are forwarded its emails) if
'new' is empty, and to change it if the 'new' adress is given.
B<lpt account> <login> I<services> [list|add|del|flush] [string]
Manage services associated to the <login>.
B<lpt account> <login> I<rights> [list|add|del|flush] [string]
Manage rights associated to the <login>.
=head1 GROUP COMMANDS
B<lpt group|role|system-group> [I<year>] <group-name> [I<view> [I<attribute> [I<attribute> [...]]]]
This is used to view general informations on the group-name. If attributes are given, display only those one.
B<lpt group|role> I<year> <group-name> I<create>
This is used to create a new intra group into the OU <year>.
B<lpt system-group> <group-name> I<create>
This is used to create a new POSIX group.
B<lpt group|role|system-group> [I<year>] <group-name> I<members> [list|add|del|flush] [string]
This is used to manage group members.
B<lpt group|role> [I<year>] <group-name> I<rights> [list|add|del|flush] [string]
This is used to manage rights on the group.
B<lpt group|role|system-group> [I<year>] <group-name> I<delete>
This is used to delete a group.
=head1 LIST COMMANDS
B<lpt> I<list> accounts [year] <open | close | services | posix | intra> [service]
List accounts: with access to the PILA, without, with access to services, with a POSIX account, with an intra accout.
=head1 QUOTA COMMANDS
B<lpt quota> I<show> <login>
Display the quota of everyone or someone.
B<lpt quota> I<set> <login> <volume> <type> <value>
Set the quota of someone. Volume is home/sgoinfre and type is
block/file.
=head1 DESCRIPTION
B<lpt> is a tool developed to replace old perl scripts used to manage accounts, and some other stuff.
The goal was to give an unique tool with meaningful commands to perform usual operations. lpt is born from ipt.
=head1 AUTHORS
Project started by : Adnan Aita <I<ski@epita.fr>>, root@acu 2006
Modified by Laroche Emeric <I<laroch_e@epita.fr>>, root@acu 2007
Modified by Sterckeman Julien <I<sterck_j@epita.fr>>, root@acu 2008
Modified by Sebastien Luttringer <I<seblu@epita.fr>>, root@acu 2008
Modified by Vincent Nguyen <I<nguyen_v@epita.fr>>, root@acu 2010
Modified by JB et Antoine <I<root@acu.epita.fr>>, root@acu 2012
Modified by megra <I<j@marguerie.org>>, root@acu 2013 : added tons of features :)
Strongly modified by nemunaire <I<nemunaire@nemunai.re>>, root@acu 2014, introducing Lab 2.0!
=head1 VERSION
This is B<lpt> version 2.0.
=head1 BUGS
No bug, just features.
=cut