446 lines
14 KiB
Perl
Executable file
446 lines
14 KiB
Perl
Executable file
#!/usr/bin/env perl
|
|
|
|
#-------------------------------------------------------------------
|
|
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
|
#-------------------------------------------------------------------
|
|
# Please read the legal notices (docs/legal.txt) and the license
|
|
# (docs/license.txt) that came with this distribution before using
|
|
# this software.
|
|
#-------------------------------------------------------------------
|
|
# http://www.plainblack.com info@plainblack.com
|
|
#-------------------------------------------------------------------
|
|
|
|
use strict;
|
|
use File::Basename ();
|
|
use File::Spec;
|
|
|
|
my $webguiRoot;
|
|
BEGIN {
|
|
$webguiRoot = File::Spec->rel2abs(File::Spec->catdir(File::Basename::dirname(__FILE__), File::Spec->updir));
|
|
unshift @INC, File::Spec->catdir($webguiRoot, 'lib');
|
|
}
|
|
|
|
use Digest::MD5;
|
|
use Getopt::Long;
|
|
use Pod::Usage;
|
|
use WebGUI::DateTime;
|
|
use WebGUI::Group;
|
|
use WebGUI::Session;
|
|
use WebGUI::User;
|
|
use WebGUI::Utility;
|
|
|
|
$|=1;
|
|
|
|
my $delimiter = "\t";
|
|
my $usersFile;
|
|
my $configFile;
|
|
my $defaultIdentifier = '123qwe';
|
|
my $help;
|
|
my $authMethod = 'WebGUI';
|
|
my $groups;
|
|
my $ldapUrl;
|
|
my $status = 'Active';
|
|
my $expireOffset;
|
|
my $expireUnits = 'seconds';
|
|
my $override;
|
|
my $quiet;
|
|
my $update;
|
|
my $updateAdd;
|
|
my $replaceGroups;
|
|
my $canChangePass;
|
|
|
|
GetOptions(
|
|
'usersFile=s'=>\$usersFile,
|
|
'configFile=s'=>\$configFile,
|
|
'help'=>\$help,
|
|
'authMethod:s'=>\$authMethod,
|
|
'delimiter:s'=>\$delimiter,
|
|
'password|identifier:s'=>\$defaultIdentifier,
|
|
'groups:s'=>\$groups,
|
|
'ldapUrl:s'=>\$ldapUrl,
|
|
'quiet'=>\$quiet,
|
|
'status:s'=>\$status,
|
|
'expireOffset:i'=>\$expireOffset,
|
|
'expireUnits:s'=>\$expireUnits,
|
|
'override'=>\$override,
|
|
'update'=>\$update,
|
|
'updateAdd'=>\$updateAdd,
|
|
'replaceGroups'=>\$replaceGroups,
|
|
'canChangePass'=>\$canChangePass
|
|
);
|
|
|
|
pod2usage( verbose => 2 ) if $help;
|
|
pod2usage() unless ($usersFile && $configFile);
|
|
|
|
if (!($^O =~ /^Win/i) && $> != 0 && !$override) {
|
|
print "You must be the super user to use this utility.\n";
|
|
exit;
|
|
}
|
|
|
|
|
|
|
|
print "Starting up..." unless ($quiet);
|
|
my $session = WebGUI::Session->open($webguiRoot,$configFile);
|
|
$session->user({userId=>3});
|
|
open(FILE,"<".$usersFile) || die("Could not open $usersFile for reading: $!");
|
|
print "OK\n" unless ($quiet);
|
|
|
|
my $lineNumber = 0;
|
|
my @field;
|
|
my @profileFields = $session->db->buildArray("select fieldName from userProfileField");
|
|
while(my $line = <FILE>) {
|
|
$lineNumber++;
|
|
|
|
chomp $line;
|
|
next
|
|
if $line eq '';
|
|
my @row = split($delimiter, $line);
|
|
use Data::Dumper ();
|
|
chomp @row;
|
|
if ($lineNumber == 1) {
|
|
@field = @row;
|
|
next;
|
|
}
|
|
# parse fields
|
|
my %user;
|
|
foreach my $item (@row) {
|
|
$item =~ s/\s+$//;
|
|
}
|
|
@user{@field} = @row;
|
|
if ($user{username} eq "" && $user{firstName} ne "" && $user{lastName} ne "") {
|
|
$user{username} = $user{firstName}.".".$user{lastName};
|
|
}
|
|
elsif ($user{username} eq '') {
|
|
print "Skipping line ${lineNumber}: No username.\n" unless ($quiet);
|
|
next;
|
|
}
|
|
|
|
$user{identifier} ||= $user{password}
|
|
if $user{password};
|
|
$user{ldapUrl} ||= $ldapUrl
|
|
if $ldapUrl;
|
|
$user{authMethod} ||= $authMethod
|
|
if $authMethod;
|
|
$user{groups} ||= $groups
|
|
if $groups;
|
|
$user{status} ||= $status
|
|
if $status;
|
|
$user{expireOffset} ||= $expireOffset
|
|
if $expireOffset;
|
|
$user{expireOffset} = calculateExpireOffset($user{expireOffset},$expireUnits)
|
|
if $user{expireOffset};
|
|
$user{birthdate} = WebGUI::DateTime->new($user{birthdate}." 00:00:00")->epoch()
|
|
if $user{birthdate};
|
|
$user{changePassword} ||= $canChangePass
|
|
if $user{changePassword} == '';
|
|
# process user
|
|
my $u;
|
|
my ($userId) = $session->db->quickArray("select userid from users where username=?",[$user{username}]);
|
|
if (($update || $updateAdd) && $userId) {
|
|
# Allowed to update, and user exists
|
|
print "Updating user '$user{username}'\n" unless ($quiet);
|
|
$u = WebGUI::User->new($session, $userId);
|
|
if ($replaceGroups && $user{groups}) {
|
|
my $groups = $u->getGroups;
|
|
$u->deleteFromGroups(@$groups);
|
|
}
|
|
}
|
|
elsif ($update) {
|
|
# Can only update, user doesn't exist
|
|
print "User '$user{username}' not found. Skipping.\n" unless ($quiet);
|
|
next;
|
|
}
|
|
elsif ($userId) {
|
|
print "User '$user{username}' already exists. Skipping.\n" unless ($quiet);
|
|
next;
|
|
}
|
|
else {
|
|
# Allowed to add, user doesn't exist
|
|
print "Adding user '$user{username}'\n" unless ($quiet);
|
|
$u = WebGUI::User->new($session, "new");
|
|
$user{identifier} ||= $defaultIdentifier
|
|
}
|
|
$user{identifier} = Digest::MD5::md5_base64($user{identifier})
|
|
if ($user{identifier});
|
|
if ($u) {
|
|
$u->username($user{username});
|
|
$u->authMethod($user{authMethod})
|
|
if $user{authMethod};
|
|
$u->status($user{status})
|
|
if $user{status};
|
|
my $class = "WebGUI::Auth::".$authMethod;
|
|
(my $mod = "$class.pm") =~ s{::|'}{/}g;
|
|
if (! eval { require $mod; 1 } ) {
|
|
$session->log->fatal("Authentication module failed to compile: $mod.".$@) if($@);
|
|
exit;
|
|
}
|
|
my $auth = $class->new($session, $authMethod,$u->userId);
|
|
$auth->saveParams($u->userId,"WebGUI",{identifier=>$user{identifier}})
|
|
if $user{identifier};
|
|
$auth->saveParams($u->userId,"LDAP",{ldapUrl=>$user{ldapUrl}})
|
|
if $user{ldapUrl};
|
|
$auth->saveParams($u->userId,"LDAP",{connectDN=>$user{connectDN}})
|
|
if $user{connectDN};
|
|
$auth->saveParams($u->userId,"WebGUI",{changePassword=>$user{changePassword}});
|
|
foreach my $field (keys %user) {
|
|
if (isIn($field, @profileFields)) {
|
|
$u->profileField($field,$user{$field});
|
|
}
|
|
}
|
|
if ($user{groups}) {
|
|
my @groups = split(/,/,$user{groups});
|
|
# Groups that have : in them have unique expiration dates
|
|
$u->addToGroups([grep { !/:/ } @groups],$user{expireOffset});
|
|
for my $groupDef ( grep { /:/ } @groups ) {
|
|
my ( $groupId, $expireDate ) = split /:/, $groupDef, 2;
|
|
|
|
# Calculate expiration offset
|
|
my $dtparse = DateTime::Format::Strptime->new(
|
|
pattern => '%F %T',
|
|
on_error => 'croak',
|
|
);
|
|
|
|
eval {
|
|
my $expireOffset = $dtparse->parse_datetime( $expireDate )->epoch - time;
|
|
$u->addToGroups( [$groupId], $expireOffset );
|
|
};
|
|
if ( $@ ) {
|
|
print "Could not add user $user{username} to group $groupId: $@";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
print "Cleaning up..." unless ($quiet);
|
|
close(FILE);
|
|
$session->var->end;
|
|
$session->close;
|
|
print "OK\n" unless ($quiet);
|
|
|
|
|
|
#-------------------------------------------------
|
|
# calculateExpireOffset(expireOffset,expireUnits)
|
|
# return: offsetInSeconds
|
|
sub calculateExpireOffset {
|
|
my ($offset, $units) = @_;
|
|
return undef if ($offset < 1);
|
|
if ($units eq "epoch") {
|
|
my $seconds = ($offset);
|
|
if ($seconds < 1) {
|
|
return undef;
|
|
}
|
|
else {
|
|
return $seconds;
|
|
}
|
|
}
|
|
if ($units eq "fixed") {
|
|
my $seconds = (($offset - time()));
|
|
if ($seconds < 1) {
|
|
return undef;
|
|
}
|
|
else {
|
|
return int($seconds);
|
|
}
|
|
}
|
|
return $session->datetime->intervalToSeconds($offset, $units)
|
|
}
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
userImport - Bulk load users into WebGUI database
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
userImport --configFile config.conf --usersFile pathname
|
|
[--authMethod method]
|
|
[--canChangePass]
|
|
[--delimiter string]
|
|
[--expireOffset integer [--expireUnits string]]
|
|
[--groups groupid,...]
|
|
[--ldapUrl uri]
|
|
[--password text]
|
|
[--status status]
|
|
[--override]
|
|
[--quiet]
|
|
[--update | --updateAdd]
|
|
[--replaceGroups]
|
|
|
|
userImport --help
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This WebGUI utility script reads user information from a text file
|
|
and loads them into the specified WebGUI database. Default user
|
|
parameters can be specified through command line options, taking
|
|
overriding values from the file.
|
|
|
|
This utility is designed to be run as a superuser on Linux systems,
|
|
since it needs to be able to put files into WebGUI's data directories
|
|
and change ownership of files. If you want to run this utility without
|
|
superuser privileges, use the B<--override> option described below.
|
|
|
|
The user information is given in a simple TAB-delimited text file,
|
|
that describes both the field names and field data for each user. You
|
|
can change de actual delimiter with the B<--delimiter> option (see below).
|
|
|
|
The first line of the file contains the field names whose values are
|
|
going to be loaded. From then on, all non-blank lines in the file must have
|
|
the same number of fields. All-blank lines are ignored. The valid field
|
|
names are:
|
|
|
|
=over
|
|
|
|
=item B<username>
|
|
|
|
=item B<password>
|
|
|
|
=item B<authMethod>
|
|
|
|
=item B<status>
|
|
|
|
=item B<ldapUrl>
|
|
|
|
=item B<connectDN>
|
|
|
|
=item B<groups>
|
|
|
|
=item B<expireOffset>
|
|
|
|
=item Any valid User Profile field name available in WebGUI's database, e.g. B<firstName>, B<lastName>, B<email>, etc.
|
|
|
|
=back
|
|
|
|
If you use the field B<groups>, each following line should contain a comma
|
|
separated list of WebGUI Group Ids; note that this could be a problem
|
|
if you chose to use comma as a delimiter for fields.
|
|
|
|
If no username is specified it will default to B<firstName.lastName>. If
|
|
no B<username> is specified, nor B<firstName> and B<lastName>, then the
|
|
user will B<not> be loaded.
|
|
|
|
If you specify the B<userId> field for import on any record, that B<userId>
|
|
will be used instead of generating a new one automatically. If you do this,
|
|
be careful not to insert duplicates!
|
|
|
|
If you use an invalid field name, its values will be ignored.
|
|
|
|
=over
|
|
|
|
=item B<--configFile config.conf>
|
|
|
|
The WebGUI config file to use. Only the file name needs to be specified,
|
|
since it will be looked up inside WebGUI's configuration directory.
|
|
This parameter is required.
|
|
|
|
=item B<--usersFile pathname>
|
|
|
|
Pathname to the file containing users information for bulk loading.
|
|
|
|
=item B<--authMethod method>
|
|
|
|
Specify the default authentication method to set for each loaded user.
|
|
It can be overridden in the import file for specific users.
|
|
If left unspecified, it defaults to B<WebGUI>.
|
|
|
|
=item B<--canChangePass>
|
|
|
|
Set loaded users to be able to change their passwords. If left
|
|
unspecified, loaded users will B<NOT> be able to change their
|
|
passwords until and administrator grants them the privilege.
|
|
|
|
=item B<--delimiter string>
|
|
|
|
Specify the string delimiting fields in the import file. If left
|
|
unspecified, it defaults to a single TAB (ASCII 9).
|
|
|
|
=item B<--expireOffset integer>
|
|
|
|
Specify the default amount of time before the loaded user will be
|
|
expired from the groups they are added to. The units are specified
|
|
by B<--expireUnits> (see below). It can be overridden in the import
|
|
file for specific users. If left unspecified, it defaults to the
|
|
expire offset set in the group definition within WebGUI.
|
|
|
|
=item B<--expireUnits unidades>
|
|
|
|
Specify the units for B<--expireOffset> (see above). Valid values
|
|
are B<seconds>, B<minutes>, B<hours>, B<days>, B<weeks>, B<months>,
|
|
B<years>, B<epoch>, or B<fixed>. If set to B<epoch> the system will
|
|
assume that the expire offset should be taken as an epoch date
|
|
(absolute number of seconds since January 1, 1970) rather than an
|
|
interval. If set to B<fixed> the system will assume that the
|
|
B<--expireOffset> is a fixed date. If left unspecified, it defaults
|
|
to B<seconds>.
|
|
|
|
=item B<--groups groupid,...>
|
|
|
|
Specify a comma separated list of WebGUI Group Ids that each loaded
|
|
user will be set to. It can be overridden in the import file for
|
|
specific users.
|
|
|
|
You can specify a unique expiration date for a group by adding it
|
|
after the group ID, separated by a colon. The date/time should be in
|
|
"YYYY-MM-DD HH:NN:SS" format.
|
|
|
|
groupId:2000-01-01 01:00:00,groupId2:2001-01-02 02:00:00
|
|
|
|
=item B<--ldapUrl uri>
|
|
|
|
Specify the URI used to connect to the LDAP server for authentication.
|
|
The URI must conform to what L<Net::LDAP> uses for connecting.
|
|
It can be overridden in the import file for specific users.
|
|
|
|
=item B<--password string>
|
|
=item B<--identifier string>
|
|
|
|
Specify the default password to use for loaded users. It can (and should)
|
|
be overridden in the import file for specific users. If left unspecified,
|
|
it defaults to B<123qwe>.
|
|
|
|
=item B<--status status>
|
|
|
|
Specify the default account status for loaded users. Valid values are
|
|
B<Active> and B<Deactivated>. If left unspecified, it defaults to
|
|
B<Active>.
|
|
|
|
=item B<--update>
|
|
|
|
Search WebGUI's database for each user listed in the import file, and
|
|
update its information using the provided fields. Users in the import
|
|
file that are B<not> found in the database are B<ignored>. See
|
|
B<--updateAdd> below if you want to add the extra users.
|
|
|
|
=item B<--updateAdd>
|
|
|
|
Search WebGUI's database for each user listed in the import file, and
|
|
update its information using the provided fields. Users in the import
|
|
file that are B<not> found in the database are B<added>. See
|
|
B<--update> above if you do not want to add the extra users.
|
|
|
|
=item B<--replaceGroups>
|
|
|
|
If the user being updated with B<--update> or B<--updateAdd> already
|
|
belongs to some other groups, remove the user from them.
|
|
|
|
=item B<--override>
|
|
|
|
This flag will allow you to run this utility without being the super user,
|
|
but note that it may not work as intended.
|
|
|
|
=item B<--quiet>
|
|
|
|
Disable all output unless there's an error.
|
|
|
|
=item B<--help>
|
|
|
|
Shows this documentation, then exits.
|
|
|
|
=back
|
|
|
|
=head1 AUTHOR
|
|
|
|
Copyright 2001-2009 Plain Black Corporation.
|
|
|
|
=cut
|