448 lines
16 KiB
Plaintext
448 lines
16 KiB
Plaintext
|
#!/usr/bin/perl -w
|
||
|
#==============================================================================
|
||
|
# lat-users
|
||
|
# =========
|
||
|
# 0.9.0 (2004-09-08)
|
||
|
# (c)2003-2004 Altiplano bvba
|
||
|
#==============================================================================
|
||
|
package esmith;
|
||
|
use strict;
|
||
|
use esmith::db;
|
||
|
use esmith::util;
|
||
|
use Getopt::Long;
|
||
|
use Pod::Usage;
|
||
|
my %conf;
|
||
|
tie %conf, 'esmith::config';
|
||
|
my %accounts;
|
||
|
tie %accounts, 'esmith::config', '/home/e-smith/db/accounts';
|
||
|
my ($Hlp, $Cml, $Frc, $Inp, $Pwf);
|
||
|
my $Add =0;
|
||
|
my $Del =0;
|
||
|
my $Kaa =0;
|
||
|
my $passwlist="./passwords.new";
|
||
|
|
||
|
#==============================================================================
|
||
|
# Main
|
||
|
#==============================================================================
|
||
|
# Analyze commandline options
|
||
|
GetOptions ("help" => \$Hlp,
|
||
|
"add" => \$Add,
|
||
|
"delete" => \$Del,
|
||
|
"force" => \$Frc,
|
||
|
"nickname" => \$Kaa,
|
||
|
"passwords" => \$Pwf,
|
||
|
"command-line=s" => \$Cml,
|
||
|
"input-file=s" => \$Inp);
|
||
|
|
||
|
if ( $Hlp ) { &PrintPod(9); exit; }
|
||
|
|
||
|
# We need one argument or the other, but not both
|
||
|
if ((($Cml && $Inp) || (! $Cml && ! $Inp)) ||
|
||
|
($Add + $Del != 1))
|
||
|
{ &PrintPod(1); exit; }
|
||
|
|
||
|
my @records;
|
||
|
if ($Inp) {
|
||
|
open(LIST,"< $Inp") || die "Can't find $Inp.\n";
|
||
|
@records = grep(!/(^\s*#)|(^\s*$)/,<LIST>);
|
||
|
close(LIST); }
|
||
|
elsif ($Cml) { @records=($Cml); }
|
||
|
else { &PrintPod(1); exit; }
|
||
|
|
||
|
# Add accounts
|
||
|
if ($Add) {
|
||
|
# Process each user
|
||
|
foreach my $record (@records)
|
||
|
{
|
||
|
my @fields=split(/\|/,$record);
|
||
|
for (my $cnt=0; $cnt <= $#fields; ++$cnt) { for ($fields[$cnt]) { s/^\s+//; s/\s+$//; }}
|
||
|
my $username = $fields[0];
|
||
|
|
||
|
if ( @fields >= 1) {
|
||
|
if ( &TestName($username))
|
||
|
{
|
||
|
my $uid;
|
||
|
if ($fields[11]) { $uid = $fields[11]; }
|
||
|
else { $uid = &FindUid;}
|
||
|
if (&TestUid($uid)) {
|
||
|
my %user = ("FirstName", $fields[1],
|
||
|
"LastName", $fields[2],
|
||
|
"PasswordSet", "no",
|
||
|
"Uid",$uid,
|
||
|
"Dept", $fields[4],
|
||
|
"Company", $fields[5],
|
||
|
"Shell", "/usr/bin/rssh",
|
||
|
"VPNClientAccess","no",
|
||
|
"Street", $fields[6],
|
||
|
"City", $fields[7],
|
||
|
"Phone", $fields[8],
|
||
|
"EmailForward", $fields[9],
|
||
|
"ForwardAddress", $fields[10]);
|
||
|
if ( ! $user{'FirstName'} ) { $user{"FirstName"} = $username; }
|
||
|
if ( ! $user{'LastName' } ) { $user{"LastName"} = $username; }
|
||
|
if ( ! $user{'EmailForward'} ) { $user{"EmailForward"} = "local"; }
|
||
|
if ( ! $user{'EmailForward'} =~/^(local)$|^(forward)$|^(both)$/)
|
||
|
{ $user{"EmailForward"} = "local"; }
|
||
|
|
||
|
# Create or update account
|
||
|
if ( ! db_get(\%accounts, $username)) {
|
||
|
print "Creating user account for $username (Uid:$uid).\n";
|
||
|
db_set(\%accounts, $username, 'user', \%user);
|
||
|
system("/sbin/e-smith/signal-event", "user-create", $username) == 0
|
||
|
or die ("An error occurred while creating account '$username'.\n");
|
||
|
}
|
||
|
else {
|
||
|
print "Updating user account $username.\n";
|
||
|
foreach my $value (keys %user) {
|
||
|
if ( ! $user{$value} ) { $user{$value} = "" }
|
||
|
if ($user{$value} eq "" )
|
||
|
{ $user{$value} = db_get_prop(\%accounts, $username, $value) }
|
||
|
$user{"PasswordSet"} = db_get_prop(\%accounts, $username, "PasswordSet");
|
||
|
$user{"Uid"} = db_get_prop(\%accounts, $username, "Uid");
|
||
|
}
|
||
|
|
||
|
db_set(\%accounts, $username, 'user', \%user);
|
||
|
system("/sbin/e-smith/signal-event", "user-modify", $username) == 0
|
||
|
or die ("An error occurred while modifying account '$username'.\n");
|
||
|
}
|
||
|
|
||
|
# Set password
|
||
|
if ((! $fields[3]) && ($Pwf)) { $fields[3] = &RandomPassword($username);}
|
||
|
if ($fields[3]) {
|
||
|
esmith::util::setUserPassword($username, $fields[3]);
|
||
|
db_set_prop(\%accounts, $username, 'PasswordSet', 'yes');
|
||
|
}
|
||
|
# Should we assign the user to a group?
|
||
|
if ( @fields > 11) {
|
||
|
for (my $cntgrps=12; $cntgrps < @fields; $cntgrps++) {
|
||
|
system("/usr/sbin/lat-groups -a -c='$fields[$cntgrps]|$fields[$cntgrps]||$username'");
|
||
|
}
|
||
|
}
|
||
|
if ($Kaa) {
|
||
|
|
||
|
print "creating SME default pseudonyms\a\n";
|
||
|
$user{"FirstName"} = lc($user{"FirstName"}) ;
|
||
|
$user{"LastName"} = lc($user{"LastName"});
|
||
|
system("/usr/sbin/lat-pseudonyms -a -c='$username|".$user{"FirstName"}.".".$user{"LastName"}."|".$user{"FirstName"}."_".$user{"LastName"}."'");
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else { print "User '$username' is not a correct username.\a\n"; }
|
||
|
}
|
||
|
else { print "Please provide at least an account name and the first and last name of the user.\n\a";}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Delete accounts
|
||
|
if ($Del) {
|
||
|
&ExpandWildCard; # Check for wildcards and expand if necessary
|
||
|
|
||
|
foreach my $record (@records)
|
||
|
{
|
||
|
my @fields=split(/\|/,$record);
|
||
|
for (my $cnt=0; $cnt <= $#fields; ++$cnt) { for ($fields[$cnt]) { s/^\s+//; s/\s+$//; }}
|
||
|
my $username = $fields[0];
|
||
|
|
||
|
if ((db_get(\%accounts, $username)) && (db_get_type(\%accounts, $username) eq "user")) {
|
||
|
my $yn = 'yes';
|
||
|
my $userdir = `cat /etc/passwd|grep "^$username:"|cut -d":" -f6|tr -d '\n'`;
|
||
|
#print "user folder: '$userdir'\n";
|
||
|
if ( $userdir eq "/home/e-smith/files/users/$username" )
|
||
|
{
|
||
|
if (! $Frc) {
|
||
|
print "Do you want to delete user '$username'?\n";
|
||
|
print "All files belonging to this user account will be deleted! [yes/NO/all] ";
|
||
|
$yn = <STDIN>;
|
||
|
if ($yn =~ /^a/i) { $Frc = -1; $yn="yes"; }
|
||
|
}
|
||
|
if ($yn =~ /^y/i) {
|
||
|
print "Deleting user account '$username'.\n";
|
||
|
db_delete(\%accounts, $username);
|
||
|
system("/sbin/e-smith/signal-event", "user-delete", $username);
|
||
|
}
|
||
|
}
|
||
|
else { print "'$username' is not a regular SME user with its home in /home/e-smith/files/users/, can not remove it\n\a";}
|
||
|
}
|
||
|
else { print "Can't find user '$username'.\n\a";}
|
||
|
}
|
||
|
}
|
||
|
#==============================================================================
|
||
|
# Subroutines
|
||
|
#==============================================================================
|
||
|
# Find a unused Uid/Gid
|
||
|
sub FindUid {
|
||
|
open(ACC,"/home/e-smith/db/accounts") || die "Can't find /home/e-smith/db/accounts";
|
||
|
my @recs = grep(/Uid\|\d*/,<ACC>);
|
||
|
close(ACC);
|
||
|
open(PWD,"/etc/passwd") || die "Can't find /etc/passwd";
|
||
|
my @pwds = grep(/\:\d*\:/,<PWD>);
|
||
|
close(PWD);
|
||
|
open(GRP,"/etc/group") || die "Can't find /etc/group";
|
||
|
my @grps = grep(/\:\d*\:/,<GRP>);
|
||
|
close(GRP);
|
||
|
|
||
|
my $newuid=db_get(\%conf, 'MinUid');
|
||
|
do { ++$newuid;} until ((! grep(/Uid\|$newuid\D/,@recs)) &&
|
||
|
(! grep(/\:$newuid\:/,@pwds)) &&
|
||
|
(! grep(/\:$newuid\:/,@grps)));
|
||
|
return $newuid;
|
||
|
}
|
||
|
#==============================================================================
|
||
|
# Test the uid/gid for availability and legality
|
||
|
sub TestUid {
|
||
|
open(ACC,"/home/e-smith/db/accounts") || die "Can't find /home/e-smith/db/accounts";
|
||
|
my @recs = grep(/Uid\|\d*/,<ACC>);
|
||
|
close(ACC);
|
||
|
open(PWD,"/etc/passwd") || die "Can't find /etc/passwd";
|
||
|
my @pwds = grep(/\:\d*\:/,<PWD>);
|
||
|
close(PWD);
|
||
|
open(GRP,"/etc/group") || die "Can't find /etc/group";
|
||
|
my @grps = grep(/\:\d*\:/,<GRP>);
|
||
|
close(GRP);
|
||
|
|
||
|
if (! ($_[0] =~ /^\d*$/)) {
|
||
|
print "Error: A user ID should contain only numbers.\a\n";
|
||
|
return 0;
|
||
|
}
|
||
|
elsif ( $_[0] < db_get(\%conf, 'MinUid')) {
|
||
|
print "Error: The user ID should be greater or equal to ".&FindUid."\a\n";
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
elsif ((grep(/Uid\|$_[0]\D/,@recs)) or
|
||
|
(grep(/\:$_[0]\:/,@pwds)) or
|
||
|
(grep(/\:$_[0]\:/,@grps))) {
|
||
|
print "Error: The uid/gid '$_[0]' is already in use\a\n";
|
||
|
return 0;
|
||
|
}
|
||
|
else { return 1 };
|
||
|
}
|
||
|
#==============================================================================
|
||
|
# Test name for illegal characters and length
|
||
|
sub TestName {
|
||
|
if ( ! $_[0] =~ /^[a-z][a-z\-\d]*$/ ) {
|
||
|
print "The name '$_[0]' contains illegal characters.\n";
|
||
|
print "User names should contain only lower-case letters, ";
|
||
|
print "numbers, hyphens or periods\n";
|
||
|
print "and should start with a lower-case letter.\n\a";
|
||
|
return 0;
|
||
|
}
|
||
|
if ( length($_[0]) > 31 ) {
|
||
|
print "The name '$_[0]' is too long. The maximum is 31 characters.\n";
|
||
|
return 0;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
#==============================================================================
|
||
|
# Create a random password
|
||
|
sub RandomPassword {
|
||
|
my $password;
|
||
|
my $_rand;
|
||
|
my $password_length = 8;
|
||
|
|
||
|
my @chars = split(" ",
|
||
|
"a b c d e f g h i j k l m n o p q r s t u v w x y z
|
||
|
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
|
||
|
0 1 2 3 4 5 6 7 8 9
|
||
|
, . ; - & ! ?");
|
||
|
|
||
|
srand;
|
||
|
$_rand = int(rand 62);
|
||
|
for (my $i=0; $i <= $password_length-1 ;$i++) {
|
||
|
$password .= $chars[$_rand];
|
||
|
$_rand = int(rand 69);
|
||
|
}
|
||
|
|
||
|
open(PWD, ">> $passwlist") or die "Can't create or open $passwlist.\n";
|
||
|
print PWD "Username: $_[0]\nPassword: $password\n\n";
|
||
|
close(PWD);
|
||
|
|
||
|
return $password;
|
||
|
}
|
||
|
#==============================================================================
|
||
|
# Test for wildcards in the username. If any wildecards are found, the array
|
||
|
# @records is expanded with the user names that meet the conditions.
|
||
|
sub ExpandWildCard {
|
||
|
my $ctrec = 0;
|
||
|
foreach my $record (@records)
|
||
|
{
|
||
|
my @fld=split(/\|/,$record);
|
||
|
for (my $cnt=0; $cnt <= $#fld; ++$cnt) { for ($fld[$cnt]) { s/^\s+//; s/\s+$//; }}
|
||
|
|
||
|
if ($fld[0] =~ /\*|\?/) { # Does it contain the wildcards?
|
||
|
$fld[0] =~ s/\*/\.\*/g; # Replace * with .* to allow for grep.
|
||
|
$fld[0] =~ s/\?/\./g; # Replace ? with . to allow for grep.
|
||
|
|
||
|
open USRS, "</home/e-smith/db/accounts" or die "Can't open /home/e-smith/db/accounts: $!";
|
||
|
my @match = grep /^$fld[0]\=user\|/i, <USRS>;
|
||
|
close(USRS);
|
||
|
|
||
|
my $cu = 0;
|
||
|
foreach my $tst (@match) {
|
||
|
$tst =~ /\=/; $tst = $`;
|
||
|
for (my $cnt=1; $cnt <= $#fld; ++$cnt) { $tst = $tst." | ".$fld[$cnt]; };
|
||
|
if ($cu == 0 ) {
|
||
|
$records[$ctrec] = $tst;
|
||
|
$cu =1;
|
||
|
}
|
||
|
else {
|
||
|
push(@records, $tst);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
++$ctrec;
|
||
|
}
|
||
|
}
|
||
|
#==============================================================================
|
||
|
# Print the pod text as a help screen
|
||
|
sub PrintPod {
|
||
|
my ($verbose, $message) = @_;
|
||
|
pod2usage(-verbose => $verbose, -message => $message, -exitval => 64);
|
||
|
}
|
||
|
|
||
|
#==============================================================================
|
||
|
|
||
|
=pod
|
||
|
|
||
|
=head1 NAME
|
||
|
|
||
|
B<lat-users> - The lazy administrator's tool to add user accounts
|
||
|
|
||
|
=head1 DESCRIPTION
|
||
|
|
||
|
Creates or deletes user accounts on Mitel's SME servers (5.x/6.x).
|
||
|
This tool is functionally equivalent to the 'User accounts' option in the server-manager, but can be run from the command line or called from an other script.
|
||
|
It allows you, for example, to create a large number of accounts in a batch process, or delete accounts on a remote machine via an ssh console.
|
||
|
|
||
|
See F</usr/doc/lazy-admin-tools/example.users> for the format of the input file.
|
||
|
|
||
|
=head1 SYNOPSIS
|
||
|
|
||
|
B<lat-tools> -a [-p] -c "user | first | last | password | department | company | street | city | tel | forward | email | uid | group1 [| group..]"
|
||
|
|
||
|
B<lat-users> -a [-p] -i /path/to/users.list
|
||
|
|
||
|
B<lat-users> -d [-f] -c "user"
|
||
|
|
||
|
B<lat-users> -d [-f] -i /path/to/users.list
|
||
|
|
||
|
=head1 OPTIONS
|
||
|
|
||
|
The following options are supported:
|
||
|
|
||
|
=over 4
|
||
|
|
||
|
=item B<-a>, B<--add>
|
||
|
|
||
|
Add a user account to the server.
|
||
|
|
||
|
=item
|
||
|
B<-c "Arguments">, B<--command-line="Arguments">
|
||
|
|
||
|
Take arguments from the command line.
|
||
|
See the 'Arguments' section below for the various arguments that are accepted.
|
||
|
|
||
|
=item B<-d>, B<--delete>
|
||
|
|
||
|
Delete a user account from the server. Wildcards (* and ?) are accepted.
|
||
|
|
||
|
=item B<-f>, B<--force>
|
||
|
|
||
|
Don't prompt before deleting.
|
||
|
|
||
|
=item B<-h>, B<--help>
|
||
|
|
||
|
Extended help for this tool
|
||
|
|
||
|
=item B<-i=FILE>, B<--input-file=FILE>
|
||
|
|
||
|
Use the information from FILE to create or delete the user accounts.
|
||
|
See F</usr/doc/lazy-admin-tools> for an example of an input file.
|
||
|
|
||
|
=item B<-p>, B<--passwords>
|
||
|
|
||
|
Generate random passwords for the users and write them to F<./passwords.new>. If a password was supplied, this option will be ignored.
|
||
|
|
||
|
=item B<-n>, B<--nickname>
|
||
|
|
||
|
Generate standard pseudonyms for the user: firstname_lastname and firstname.lastname.
|
||
|
|
||
|
=back
|
||
|
|
||
|
=head2 Arguments:
|
||
|
|
||
|
user* - Must contain only lower-case letters, numbers,
|
||
|
hyphens, periods and underscores, and should start
|
||
|
with a lower-case letter. Wildcards (* and ?) can
|
||
|
only be used to delete users.
|
||
|
first* - First name
|
||
|
last* - Last name
|
||
|
password - Password for the user (in clear-text!)
|
||
|
department - Department
|
||
|
company - Company
|
||
|
street - Street name and number
|
||
|
city - Zip & City
|
||
|
tel - Telephone number
|
||
|
forward - E-mail delivery: 'local', 'forward' or 'both'
|
||
|
email - Forwarding e-mail adres
|
||
|
uid - User ID. If omitted, a suitable uid will be generated.
|
||
|
group(s) - Group name(s) to which the user should be added. If the
|
||
|
group doesn't exist, it will be created.
|
||
|
|
||
|
* mandatory field
|
||
|
|
||
|
=head1 EXAMPLES
|
||
|
|
||
|
B<lat-users -a -c "harry | Harry | Potter | Quidditch">
|
||
|
|
||
|
Creates user 'harry' from the command line, with password 'Quidditch'.
|
||
|
|
||
|
B<lat-users -a -i /root/users.list>
|
||
|
|
||
|
Uses the arguments specified in F</root/users.list> to create user accounts.
|
||
|
Please refer to F</usr/doc/lazy-admin-tools/example.users> for an example of an input file.
|
||
|
|
||
|
B<lat-users -d -f -c "user*">
|
||
|
|
||
|
Deletes all user accounts that start with 'user'. All users and their files will be deleted without prompting (-f).
|
||
|
|
||
|
B<lat-users -a -p -i /root/users.list>
|
||
|
|
||
|
Creates user accounts as defined in F</root/users.list> and generates a random password for each user.
|
||
|
The names and passwords are written to F<./passwords.new>.
|
||
|
|
||
|
B<lat-users -a -c "ron | Ron | Weasley ||||||||| 6005">
|
||
|
|
||
|
Creates user 'ron' with user ID 6005. All other fields (company, departments, etc.) are left empty.
|
||
|
|
||
|
B<lat-users -a -c "ron | Ron | Weasley |||||||||| quiddich | dada ">
|
||
|
|
||
|
Creates user 'ron' and assigns him to groups quiddich and dada. If any of these groups doesn't exist, it will be created.
|
||
|
|
||
|
B<lat-users -a -n -c "ron | Ron | Weasley |||||||||| quiddich | dada ">
|
||
|
|
||
|
Creates user 'ron' and assigns him to groups quiddich and dada. If any of these groups doesn't exist, it will be created.
|
||
|
Also create pseudonyms ron_weasley and ron.weasley associated with user ron if available, else produces an alert.
|
||
|
|
||
|
=head1 SEE ALSO
|
||
|
|
||
|
lat-group(8), lat-pseudonyms(8), lat-ibays(8), lat-quota(8), lat-domains(8), lat-hosts(8), lat-procmail(8), lat-pptp(8), lat-dump(8)
|
||
|
|
||
|
=head1 VERSION
|
||
|
|
||
|
Version 0.9.0 (2004-09-08). The latest version is hosted at B<http://www.contribs.org/contribs/mblotwijk/>
|
||
|
|
||
|
=head1 COPYRIGHT
|
||
|
|
||
|
(c)2003-2004, Altiplano bvba (B<http://www.altiplano.be>). Released under the terms of the GNU license.
|
||
|
|
||
|
|
||
|
=head1 BUGS
|
||
|
|
||
|
Please report bugs to <Bugs@Altiplano.Be>
|
||
|
|
||
|
=cut
|
||
|
|
||
|
#==============================================================================
|