934 lines
25 KiB
Perl
Executable File
934 lines
25 KiB
Perl
Executable File
#!/usr/local/bin/perl
|
|
# save_user.cgi
|
|
# Create, update or delete an LDAP user
|
|
|
|
require './ldap-useradmin-lib.pl';
|
|
use Time::Local;
|
|
&ReadParse();
|
|
$ldap = &ldap_connect();
|
|
$schema = $ldap->schema();
|
|
&lock_user_files();
|
|
if (!$in{'new'}) {
|
|
# Get existing user
|
|
$rv = $ldap->search(base => $in{'dn'},
|
|
scope => 'base',
|
|
filter => &user_filter());
|
|
($uinfo) = $rv->all_entries;
|
|
$uinfo || &error($text{'usave_egone'});
|
|
%ouser = &dn_to_hash($uinfo);
|
|
&can_edit_user(\%ouser) || &error($text{'usave_eedit'});
|
|
}
|
|
else {
|
|
$access{'ucreate'} || &error($text{'usave_ecreate'});
|
|
}
|
|
|
|
if ($in{'mailboxes'}) {
|
|
# Just re-direct to mailboxes page
|
|
&redirect("../mailboxes/list_mail.cgi?user=$ouser{'user'}");
|
|
exit;
|
|
}
|
|
elsif ($in{'switch'}) {
|
|
# Just re-direct to Usermin switch user program
|
|
&redirect("../usermin/switch.cgi?user=$ouser{'user'}");
|
|
exit;
|
|
}
|
|
elsif ($in{'delete'}) {
|
|
# Delete the user .. but ask first!
|
|
&ui_print_header(undef, $text{'udel_title'}, "");
|
|
$home = $uinfo->get_value("homeDirectory");
|
|
$user = $uinfo->get_value("uid");
|
|
if ($in{'confirm'}) {
|
|
# Run the before command
|
|
%uhash = &dn_to_hash($uinfo);
|
|
&set_user_envs(\%uhash, 'DELETE_USER', undef, undef);
|
|
$merr = &making_changes();
|
|
&error(&text('usave_emaking', "<tt>$merr</tt>"))
|
|
if (defined($merr));
|
|
|
|
# Work out old classes
|
|
@classes = $uinfo->get_value("objectClass");
|
|
@cyrus_class_2 = split(' ',$cyrus_class);
|
|
$wascyrus = &indexof($cyrus_class_2[0], @classes) >= 0;
|
|
|
|
# Delete from other modules
|
|
%user = &dn_to_hash($uinfo);
|
|
if ($in{'others'}) {
|
|
print "$text{'udel_other'}<br>\n";
|
|
&useradmin::other_modules("useradmin_delete_user",
|
|
\%user);
|
|
print "$text{'udel_done'}<p>\n";
|
|
}
|
|
|
|
# Delete from any groups
|
|
print "$text{'udel_groups'}<br>\n";
|
|
$base = &get_group_base();
|
|
$rv = $ldap->search(base => $base,
|
|
filter => &group_filter());
|
|
foreach $g ($rv->all_entries) {
|
|
local @mems = $g->get_value("memberUid");
|
|
local $idx = &indexof($user, @mems);
|
|
if ($idx >= 0) {
|
|
# Take out of this group
|
|
splice(@mems, $idx, 1);
|
|
$rv = $ldap->modify($g->dn(), replace =>
|
|
{ 'memberUid' => \@mems });
|
|
if ($rv->code) {
|
|
&error(&text('usave_emodgroup',
|
|
$g->get_value('cn'),
|
|
$rv->error));
|
|
}
|
|
}
|
|
}
|
|
print "$text{'udel_done'}<p>\n";
|
|
|
|
# Delete from the LDAP db
|
|
print "$text{'udel_pass'}<br>\n";
|
|
$rv = $ldap->delete($in{'dn'});
|
|
if ($rv->code) {
|
|
&error(&text('usave_edelete', $rv->error));
|
|
}
|
|
print "$text{'udel_done'}<p>\n";
|
|
|
|
# Delete his addressbook entry
|
|
if ($config{'addressbook'} && $wascyrus) {
|
|
print "$text{'udel_book'}<br>\n";
|
|
$err = &delete_addressbook();
|
|
if ($err) {
|
|
print &text('udel_failed', $err),"<p>\n";
|
|
}
|
|
else {
|
|
print "$text{'udel_done'}<p>\n";
|
|
}
|
|
}
|
|
|
|
# Delete his home directory
|
|
if ($in{'delhome'}) {
|
|
print "$text{'udel_home'}<br>\n";
|
|
$home = $uinfo->get_value("homeDirectory");
|
|
if (-d $home && $home ne "/") {
|
|
local $realhome = &resolve_links($home);
|
|
local $qhome = quotemeta($realhome);
|
|
system("rm -rf $qhome >/dev/null 2>&1");
|
|
unlink($home); # in case of links
|
|
}
|
|
print "$text{'udel_done'}<p>\n";
|
|
|
|
# Delete his IMAP mailbox only if home gets deleted, too
|
|
if ($config{'imap_host'}) {
|
|
print "$text{'udel_imap'}<br>\n";
|
|
$imap = &imap_connect();
|
|
$rv = $imap->delete("user".$config{'imap_foldersep'}.
|
|
$uinfo->get_value("uid"));
|
|
$imap->logout();
|
|
print "$text{'udel_done'}<p>\n";
|
|
}
|
|
}
|
|
|
|
&made_changes();
|
|
|
|
%p = ( %in, %user );
|
|
&webmin_log("delete", "user", $user{'user'}, \%p);
|
|
}
|
|
else {
|
|
# Show confirmation page
|
|
if ($home ne "/" && -d $home) {
|
|
# With option to delete home
|
|
$size = &nice_size(&disk_usage_kb($home)*1024);
|
|
$msg = &text('udel_sure', $user, $home, $size);
|
|
@buts = ( [ undef, $text{'udel_del1'} ],
|
|
[ "delhome", $text{'udel_del2'} ] );
|
|
}
|
|
else {
|
|
# Without home
|
|
$msg = &text('udel_sure2', $user);
|
|
@buts = ( [ undef, $text{'udel_del1'} ] );
|
|
}
|
|
print &ui_confirmation_form(
|
|
"save_user.cgi",
|
|
$msg,
|
|
[ [ "dn", $in{'dn'} ],
|
|
[ "confirm", 1 ],
|
|
[ "delete", 1 ] ],
|
|
\@buts,
|
|
&ui_checkbox("others", 1, $text{'udel_dothers'},
|
|
$mconfig{'default_other'}),
|
|
$user eq 'root' ?
|
|
"<font color=#ff0000>$text{'udel_root'}</font>" : ""
|
|
);
|
|
}
|
|
|
|
$ldap->unbind();
|
|
&ui_print_footer("", $text{'index_return'});
|
|
exit;
|
|
}
|
|
elsif ($in{'raw'}) {
|
|
# Show all LDAP attributes for user
|
|
&redirect("raw.cgi?user=1&dn=".&urlize($in{'dn'}));
|
|
exit;
|
|
}
|
|
else {
|
|
# Validate inputs
|
|
&error_setup($text{'usave_err'});
|
|
$in{'user'} =~ /^[^:\t]+$/ ||
|
|
&error(&text('usave_ebadname', $in{'user'}));
|
|
$in{'user'} =~ s/\r//g;
|
|
$err = &useradmin::check_username_restrictions($in{'user'});
|
|
&error($err) if ($err);
|
|
$in{'real'} || &error($text{'usave_ereal'});
|
|
@users = split(/\n/, $in{'user'});
|
|
$user = $users[0];
|
|
$in{'uid'} =~ /^\-?[0-9]+$/ || &error(&text('usave_euid', $in{'uid'}));
|
|
$uid = $in{'uid'};
|
|
$in{'real'} =~ /^[^:]*$/ || &error(&text('usave_ereal', $in{'real'}));
|
|
$firstname = $in{'firstname'};
|
|
$lastname = $in{'lastname'};
|
|
$real = $in{'real'};
|
|
$shell = $in{'shell'} eq '*' ? $in{'othersh'} : $in{'shell'};
|
|
if ($in{'new'}) {
|
|
&check_user_used($ldap, $user) &&
|
|
&error(&text('usave_einuse', $user));
|
|
}
|
|
|
|
# Check for UID clash
|
|
if ($in{'new'} && !$access{'umultiple'}) {
|
|
&check_uid_used($ldap, $uid) &&
|
|
&error($text{'usave_euidused2'});
|
|
}
|
|
|
|
# Validate IMAP quota
|
|
$quota = undef;
|
|
if ($config{'quota_support'} && !$in{'quota_def'} &&
|
|
defined($in{'quota'})) {
|
|
$in{'quota'} =~ /^\d+$/ || &error($text{'usave_equota'});
|
|
$quota = $in{'quota'};
|
|
}
|
|
|
|
# Validate new group
|
|
if ($in{'gidmode'} == 0) {
|
|
# An existing group
|
|
if ($in{'gid'} =~ /^\d+$/) {
|
|
$gid = $in{'gid'};
|
|
}
|
|
else {
|
|
$gid = &all_getgrnam($in{'gid'});
|
|
defined($gid) ||
|
|
&error(&text('usave_egid', $in{'gid'}));
|
|
}
|
|
$grp = &all_getgrgid($gid);
|
|
}
|
|
else {
|
|
# Creating a new group
|
|
if ($in{'gidmode'} == 2) {
|
|
# Same name as the user
|
|
$grp = $in{'user'};
|
|
}
|
|
else {
|
|
# Group name was entered
|
|
$in{'newgid'} =~ /^[^: \t]+$/ ||
|
|
&error(&text('gsave_ebadname', $in{'newgid'}));
|
|
$grp = $in{'newgid'};
|
|
}
|
|
# Check for a clash
|
|
&check_group_used($ldap, $grp) &&
|
|
&error(&text('usave_einuseg', $grp));
|
|
}
|
|
|
|
# Compute and validate home directory
|
|
if ($access{'autohome'}) {
|
|
if ($in{'new'} || $ouser{'user'} ne $user) {
|
|
$home = &auto_home_dir($access{'home'}, $user, $grp);
|
|
}
|
|
else {
|
|
$home = $ouser{'home'};
|
|
}
|
|
}
|
|
elsif ($mconfig{'home_base'} && $in{'home_base'}) {
|
|
$home = &auto_home_dir($mconfig{'home_base'}, $user, $grp);
|
|
}
|
|
else {
|
|
$home = $in{'home'};
|
|
$home =~ /^\// || &error(&text('usave_ehome', $home));
|
|
}
|
|
if (!$access{'autohome'}) {
|
|
$home =~ /^\// || &error(&text('usave_ehome', $home));
|
|
$al = length($access{'home'});
|
|
if (length($home) < $al ||
|
|
substr($home, 0, $al) ne $access{'home'}) {
|
|
&error(&text('usave_ehomepath', $home));
|
|
}
|
|
}
|
|
|
|
local $pfx = $config{'md5'} == 1 || $config{'md5'} == 3 ? "{md5}" :
|
|
$config{'md5'} == 4 ? "{ssha}" :
|
|
$config{'md5'} == 5 ? "{sha}" :
|
|
$config{'md5'} == 0 ? "{crypt}" : "";
|
|
if ($in{'passmode'} == 0) {
|
|
# Password is blank
|
|
if (!$mconfig{'empty_mode'}) {
|
|
local $err = &useradmin::check_password_restrictions(
|
|
"", $user, $in{'new'} ? 'none' : \%ouser);
|
|
&error($err) if ($err);
|
|
}
|
|
$pass = "";
|
|
}
|
|
elsif ($in{'passmode'} == 1) {
|
|
# Password is locked
|
|
$pass = $mconfig{'lock_string'};
|
|
}
|
|
elsif ($in{'passmode'} == 2) {
|
|
# Specific encrypted password entered, or possibly no change
|
|
$pass = $in{'encpass'};
|
|
$pass = $pfx.$pass if ($pass !~ /^\{[a-z0-9]+\}/i && $pfx);
|
|
}
|
|
elsif ($in{'passmode'} == 3) {
|
|
# Normal password entered - check restrictions
|
|
local $err = &useradmin::check_password_restrictions(
|
|
$in{'pass'}, $user,
|
|
$in{'new'} ? 'none' : \%ouser);
|
|
&error($err) if ($err);
|
|
$pass = $pfx.&encrypt_password($in{'pass'});
|
|
$plainpass = $in{'pass'};
|
|
}
|
|
if ($in{'disable'} && ($in{'passmode'} == 2 || $in{'passmode'} == 3)) {
|
|
$pass = $useradmin::disable_string.$pass;
|
|
}
|
|
|
|
# Build useradmin-style hash of user details
|
|
local %uhash = ( 'user' => $user,
|
|
'uid' => $uid,
|
|
'gid' => $gid,
|
|
'group' => $in{'group'},
|
|
'real' => $real,
|
|
'shell' => $shell,
|
|
'pass' => $pass,
|
|
'plainpass' => $plainpass,
|
|
'home' => $home,
|
|
'firstname' => $firstname,
|
|
'lastname' => $lastname );
|
|
|
|
if ($in{'new'}) {
|
|
defined(&all_getpwnam($user)) &&
|
|
&error(&text('usave_einuse', $user));
|
|
if ($in{'passmode'} == 1 || $in{'passmode'} == 2) {
|
|
if ($in{'cyrus'}) {
|
|
&error($text{'usave_ecyruspass'});
|
|
}
|
|
}
|
|
|
|
# Run the pre-change command
|
|
&set_user_envs(\%uhash, 'CREATE_USER',
|
|
$in{'passmode'} == 3 ? $in{'pass'} : "",
|
|
undef);
|
|
$merr = &making_changes();
|
|
&error(&text('usave_emaking', "<tt>$merr</tt>"))
|
|
if (defined($merr));
|
|
|
|
# Create home dir
|
|
if (!-e $home && $in{'makehome'}) {
|
|
&lock_file($home);
|
|
mkdir($home, oct($mconfig{'homedir_perms'})) ||
|
|
&error(&text('usave_emkdir', $!));
|
|
chmod(oct($mconfig{'homedir_perms'}), $home) ||
|
|
&error(&text('usave_echmod', $!));
|
|
chown($uid, $gid, $home) ||
|
|
&error(&text('usave_echown', $!));
|
|
&unlock_file($home);
|
|
}
|
|
|
|
# Create a new group
|
|
if ($in{'gidmode'}) {
|
|
my $base = &get_group_base();
|
|
my $newdn = "cn=$grp,$base";
|
|
my @classes = ( &def_group_obj_class() );
|
|
push(@classes, split(/\s+/, $config{'gother_class'}));
|
|
$gid = $mconfig{'base_gid'};
|
|
while(&check_gid_used($ldap, $gid)) {
|
|
$gid++;
|
|
}
|
|
$rv = $ldap->add($newdn, attr =>
|
|
[ "cn" => $grp,
|
|
"gidNumber" => $gid,
|
|
@props,
|
|
"objectClass" => \@classes ] );
|
|
if ($rv->code) {
|
|
&error(&text('gsave_eadd', $rv->error));
|
|
}
|
|
}
|
|
|
|
# Get configured properties for new users
|
|
local @props = &split_props($config{'props'}, \%uhash);
|
|
if ($in{'cyrus'}) {
|
|
push(@props, &split_props($config{'imap_props'},
|
|
\%uhash));
|
|
}
|
|
|
|
# Build Samba-related properties
|
|
if ($in{'samba'}) {
|
|
&samba_properties(1, \%uhash, $in{'passmode'},
|
|
$in{'pass'}, $schema, \@props, $ldap);
|
|
}
|
|
|
|
if ($in{'cyrus'}) {
|
|
# Build mail-related properties
|
|
&mail_props();
|
|
}
|
|
|
|
# Add any extra LDAP fields
|
|
&parse_extra_fields($config{'fields'}, \@props, \@rprops,
|
|
$ldap);
|
|
|
|
# Add shadow LDAP fields
|
|
$shadow = &shadow_fields();
|
|
|
|
# Add to the ldap database
|
|
@classes = ( &def_user_obj_class(), "shadowAccount" );
|
|
if ($schema && $schema->objectclass("person") && $config{'person'}) {
|
|
push(@classes, "person");
|
|
}
|
|
|
|
push(@classes, split(/\s+/, $config{'other_class'}));
|
|
push(@classes, $samba_class) if ($in{'samba'});
|
|
push(@classes, split(' ',$cyrus_class)) if ($in{'cyrus'});
|
|
@classes = grep { /\S/ } @classes; # Remove empty
|
|
&name_fields();
|
|
@classes = &uniquelc(@classes);
|
|
$base = &get_user_base();
|
|
$newdn = "uid=$user,$base";
|
|
@allprops = ( "cn" => $real,
|
|
"uid" => \@users,
|
|
"uidNumber" => $uid,
|
|
"loginShell" => $shell,
|
|
"homeDirectory" => $home,
|
|
"gidNumber" => $gid,
|
|
$pass ? ( "userPassword" => $pass ) : ( ),
|
|
"objectClass" => \@classes,
|
|
@props );
|
|
if (&indexoflc("person", @classes) >= 0 &&
|
|
!&in_props(\@allprops, "sn")) {
|
|
# Person needs an 'sn' too
|
|
push(@allprops, "sn", $real);
|
|
}
|
|
$rv = $ldap->add($newdn, attr => \@allprops);
|
|
if ($rv->code) {
|
|
&error(&text('usave_eadd', $rv->error));
|
|
}
|
|
|
|
if ($in{'cyrus'}) {
|
|
if ($config{'addressbook'}) {
|
|
# Create addressbook entry
|
|
&setup_addressbook(\%uhash);
|
|
}
|
|
|
|
# Disconnect to save the changes
|
|
$ldap->unbind();
|
|
undef($ldap);
|
|
|
|
# Create imap account
|
|
&setup_imap(\%uhash, $quota);
|
|
|
|
# Re-connect for later LDAP operations
|
|
$ldap = &ldap_connect();
|
|
}
|
|
|
|
# Copy files into user's directory
|
|
if ($in{'makehome'} && $mconfig{'user_files'}) {
|
|
local $uf = $mconfig{'user_files'};
|
|
local $shell = $user{'shell'}; $shell =~ s/^(.*)\///g;
|
|
$uf =~ s/\$group/$in{'gid'}/g;
|
|
$uf =~ s/\$gid/$user{'gid'}/g;
|
|
$uf =~ s/\$shell/$shell/g;
|
|
&useradmin::copy_skel_files($uf, $home, $uid, $gid);
|
|
}
|
|
}
|
|
else {
|
|
# Modifying a user
|
|
$olduser = $uinfo->get_value('uid');
|
|
if ($olduser ne $user) {
|
|
defined(&all_getpwnam($user)) &&
|
|
&error(&text('usave_einuse', $user));
|
|
}
|
|
|
|
# Work out old settings
|
|
@classes = $uinfo->get_value("objectClass");
|
|
$wassamba = &indexof($samba_class, @classes) >= 0;
|
|
@cyrus_class_2 = split(' ',$cyrus_class);
|
|
$wascyrus = &indexof($cyrus_class_2[0], @classes) >= 0;
|
|
if ($in{'passmode'} == 1 || $in{'passmode'} == 2) {
|
|
if (!$wascyrus && $in{'cyrus'}) {
|
|
&error($text{'usave_ecyruspass'});
|
|
}
|
|
}
|
|
|
|
# Run the pre-change command
|
|
&set_user_envs(\%uhash, 'MODIFY_USER',
|
|
$in{'passmode'} == 3 ? $in{'pass'} : "",
|
|
undef);
|
|
$merr = &making_changes();
|
|
&error(&text('usave_emaking', "<tt>$merr</tt>"))
|
|
if (defined($merr));
|
|
|
|
# Rename home dir, if needed
|
|
$oldhome = $uinfo->get_value("homeDirectory");
|
|
if ($home ne $oldhome && -d $oldhome && !-e $home &&
|
|
$in{'movehome'}) {
|
|
$out = `mv '$oldhome' '$home' 2>&1`;
|
|
if ($?) { &error(&text('usave_emove', $out)); }
|
|
}
|
|
|
|
# Change GID on files if needed
|
|
$oldgid = $uinfo->get_value("gidNumber");
|
|
$olduid = $uinfo->get_value("uidNumber");
|
|
if ($oldgid != $gid && $in{'chgid'}) {
|
|
if ($in{'chgid'} == 1) {
|
|
&useradmin::recursive_change($home, $olduid,
|
|
$oldgid, -1, $gid);
|
|
}
|
|
else {
|
|
&useradmin::recursive_change("/", $olduid,
|
|
$oldgid, -1, $gid);
|
|
}
|
|
}
|
|
|
|
# Change UID on files if needed
|
|
if ($olduid != $uid && $in{'chuid'}) {
|
|
if ($in{'chuid'} == 1) {
|
|
&useradmin::recursive_change($home, $olduid,
|
|
-1, $uid, -1);
|
|
}
|
|
else {
|
|
&useradmin::recursive_change("/", $olduid,
|
|
-1, $uid, -1);
|
|
}
|
|
}
|
|
|
|
# Get properties for modified users
|
|
local @props = &split_props($config{'mod_props'}, \%uhash);
|
|
|
|
# Work out samba-related property changes
|
|
$oldpass = $uinfo->get_value('userPassword');
|
|
if ($in{'samba'}) {
|
|
# Is a samba user .. add or update props
|
|
$passmode = $in{'passmode'};
|
|
if ($passmode == 2 && $wassamba &&
|
|
$in{'encpass'} eq $oldpass) {
|
|
# Not being changed
|
|
$passmode = 4;
|
|
}
|
|
&samba_properties(!$wassamba, \%uhash, $passmode,
|
|
$in{'pass'}, $schema, \@props, $ldap);
|
|
}
|
|
elsif ($wassamba) {
|
|
# Is no longer a samba user .. take away standard
|
|
# samba properties
|
|
&samba_removes(\%uhash, $schema, \@rprops);
|
|
}
|
|
|
|
# Work out imap-related property changes
|
|
if ($in{'cyrus'}) {
|
|
&mail_props();
|
|
}
|
|
if ($in{'cyrus'} && !$wascyrus) {
|
|
# Add any extra properties for IMAP users
|
|
push(@props, &split_props($config{'imap_props'}));
|
|
}
|
|
elsif (!$in{'cyrus'} && $wascyrus) {
|
|
# Take away properties for IMAP users
|
|
push(@rprops, &split_first($config{'imap_props'}));
|
|
&delete_mail_props();
|
|
}
|
|
|
|
# Add or update any extra LDAP fields
|
|
&parse_extra_fields($config{'fields'}, \@props, \@rprops,
|
|
$ldap, $in{'dn'});
|
|
|
|
# Add or update shadow LDAP fields
|
|
$shadow = &shadow_fields();
|
|
|
|
# Update the ldap database
|
|
if ($in{'samba'}) {
|
|
push(@classes, $samba_class);
|
|
}
|
|
else {
|
|
@classes = grep { $_ ne $samba_class } @classes;
|
|
}
|
|
if ($in{'cyrus'}) {
|
|
push(@classes, split(' ',$cyrus_class));
|
|
}
|
|
else {
|
|
@cyrus_class_4 = split(' ',$cyrus_class);
|
|
foreach $one_cyrus_class (@cyrus_class_4) {
|
|
@classes = grep { $_ ne $one_cyrus_class }
|
|
@classes;
|
|
}
|
|
}
|
|
push(@classes, "shadowAccount") if ($shadow);
|
|
&name_fields();
|
|
@classes = &uniquelc(@classes);
|
|
@classes = grep { /\S/ } @classes; # Remove empty
|
|
@rprops = grep { defined($uinfo->get_value($_)) } @rprops;
|
|
|
|
if ($olduser ne $user) {
|
|
# Need to rename the LDAP dn itself, first
|
|
$renaming = 1;
|
|
$base = &get_user_base();
|
|
$newdn = "uid=$user,$base";
|
|
$rv = $ldap->moddn($in{'dn'}, newrdn => "uid=$user");
|
|
if ($rv->code) {
|
|
&error(&text('usave_emoddn', $rv->error));
|
|
}
|
|
}
|
|
else {
|
|
$newdn = $in{'dn'};
|
|
}
|
|
|
|
# Change the user's properties
|
|
%allprops = ( "cn" => $real,
|
|
"uid" => \@users,
|
|
"uidNumber" => $uid,
|
|
"loginShell" => $shell,
|
|
"homeDirectory" => $home,
|
|
"gidNumber" => $gid,
|
|
$pass ? ( "userPassword" => $pass ) : ( ),
|
|
"objectClass" => \@classes,
|
|
@props );
|
|
if (&indexoflc("person", @classes) >= 0 &&
|
|
!$allprops{'sn'}) {
|
|
# Person needs 'sn'
|
|
$allprops{'sn'} = $real;
|
|
}
|
|
if (!$pass) {
|
|
push(@rprops, "userPassword");
|
|
}
|
|
$rv = $ldap->modify($newdn, 'replace' => \%allprops,
|
|
'delete' => \@rprops);
|
|
if ($rv->code) {
|
|
&error(&text('usave_emod', $rv->error));
|
|
}
|
|
|
|
if ($olduser ne $user) {
|
|
# Check if an addressbook dn exists
|
|
local $olda =
|
|
"ou=$olduser, $config{'addressbook'}";
|
|
$rv = $ldap->search(base => $olda,
|
|
scope => 'base',
|
|
filter => '(&(objectClass=organizationalUnit))');
|
|
($oldbook) = $rv->all_entries;
|
|
|
|
if ($oldbook) {
|
|
# Need to rename the addressbook dn
|
|
$rv = $ldap->modify($olda, replace =>
|
|
{ "ou" => $user });
|
|
if ($rv->code) {
|
|
&error(&text('usave_emodbook',
|
|
$rv->error));
|
|
}
|
|
|
|
$rv = $ldap->moddn($olda, newrdn =>
|
|
"ou=$user");
|
|
if ($rv->code) {
|
|
&error(&text('usave_emodbookdn',
|
|
$rv->error));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($in{'cyrus'} && !$wascyrus) {
|
|
# Adding IMAP support
|
|
if ($config{'addressbook'}) {
|
|
# Create addressbook entry
|
|
&setup_addressbook();
|
|
}
|
|
|
|
# Setup the imap account as well
|
|
&setup_imap(\%uhash, $quota);
|
|
}
|
|
elsif (!$in{'cyrus'} && $wascyrus) {
|
|
# Removing IMAP support
|
|
if ($config{'addressbook'}) {
|
|
# Delete addressbook entry
|
|
&delete_addressbook();
|
|
}
|
|
}
|
|
elsif ($in{'cyrus'} && $wascyrus) {
|
|
# Changing IMAP support
|
|
if (!$in{'quota_def'} && $config{'quota_support'}) {
|
|
&set_imap_quota(\%uhash, $in{'quota'});
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($config{'secmode'} != 1) {
|
|
# Update any groups that the user has been added to/removed from
|
|
@sgnames = $config{'secmode'} == 2 ? split(/\s+/, $in{'sgid'})
|
|
: split(/\r?\n/, $in{'sgid'});
|
|
foreach $gname (@sgnames) {
|
|
$ingroup{$gname}++;
|
|
}
|
|
$base = &get_group_base();
|
|
$rv = $ldap->search(base => $base,
|
|
filter => &group_filter());
|
|
foreach $g ($rv->all_entries) {
|
|
local @mems = $g->get_value("memberUid");
|
|
local $gname = $g->get_value("cn");
|
|
local $ldap_group_id = $g->get_value("gidNumber");
|
|
if ($renaming) {
|
|
local $idx = &indexof($olduser, @mems);
|
|
if ($ingroup{$gname} && $idx<0) {
|
|
# Need to add to the group
|
|
push(@mems, $user);
|
|
push(@sgids, $ldap_group_id);
|
|
}
|
|
elsif (!$ingroup{$gname} && $idx>=0) {
|
|
# Need to remove from the group
|
|
splice(@mems, $idx, 1);
|
|
}
|
|
elsif ($idx >= 0) {
|
|
# Need to rename in group
|
|
$mems[$idx] = $user;
|
|
push(@sgids, $ldap_group_id);
|
|
}
|
|
else { next; }
|
|
}
|
|
else {
|
|
local $idx = &indexof($user, @mems);
|
|
if ($ingroup{$gname} && $idx<0) {
|
|
# Need to add to the group
|
|
push(@mems, $user);
|
|
push(@sgids, $ldap_group_id);
|
|
}
|
|
elsif (!$ingroup{$gname} && $idx>=0) {
|
|
# Need to remove from the group
|
|
splice(@mems, $idx, 1);
|
|
}
|
|
elsif ($ingroup{$gname} && $idx >=0) {
|
|
# already in this group
|
|
push(@sgids, $ldap_group_id);
|
|
next;
|
|
}
|
|
else { next; }
|
|
}
|
|
|
|
# Actually change the group
|
|
$rv = $ldap->modify($g->dn(), replace =>
|
|
{ 'memberUid' => \@mems });
|
|
if ($rv->code) {
|
|
&error(&text('usave_emodgroup', $g->get_value('cn'),
|
|
$rv->error));
|
|
}
|
|
}
|
|
}
|
|
|
|
# Get the updated user object
|
|
$rv = $ldap->search(base => $newdn,
|
|
scope => 'base',
|
|
filter => &user_filter());
|
|
($uinfo) = $rv->all_entries;
|
|
%user = &dn_to_hash($uinfo);
|
|
|
|
# Run post-change script
|
|
&set_user_envs(\%user, $in{'new'} ? 'CREATE_USER' : 'MODIFY_USER',
|
|
$in{'passmode'} == 3 ? $in{'pass'} : "", \@sgids);
|
|
&made_changes();
|
|
|
|
# Run other modules' scripts
|
|
if ($in{'others'}) {
|
|
$user{'passmode'} = $in{'passmode'};
|
|
if ($in{'passmode'} == 2 && $user{'pass'} eq $ouser{'pass'}) {
|
|
# not changing password
|
|
$user{'passmode'} = 4;
|
|
}
|
|
$user{'plainpass'} = $in{'pass'} if ($in{'passmode'} == 3);
|
|
$ldap->unbind(); # force commit?
|
|
if (!$in{'new'}) {
|
|
$user{'olduser'} = $ouser{'user'};
|
|
&useradmin::other_modules("useradmin_modify_user",
|
|
\%user, \%ouser);
|
|
}
|
|
else {
|
|
&useradmin::other_modules("useradmin_create_user",
|
|
\%user);
|
|
}
|
|
$ldap = &ldap_connect();
|
|
}
|
|
}
|
|
$ldap->unbind();
|
|
delete($in{'pass'});
|
|
delete($in{'passmode'});
|
|
&unlock_user_files();
|
|
&webmin_log(!$in{'new'} ? 'modify' : 'create', 'user', $user, \%in);
|
|
&redirect($in{'return'} || "");
|
|
|
|
# mail_props()
|
|
# Add properties for mail and aliases
|
|
sub mail_props
|
|
{
|
|
# Do nothing if no domain is set
|
|
return if (!$config{'domain'});
|
|
|
|
# Add surname and first name details
|
|
local ($autofirstname, $autolastname);
|
|
if ($firstname && $lastname) {
|
|
$autofirstname = $firstname;
|
|
$autolastname = $lastname;
|
|
}
|
|
elsif ($in{'real'} =~ /(\S+)\s+(\S+)$/) {
|
|
$autofirstname = lc($1);
|
|
$autolastname = lc($2);
|
|
}
|
|
elsif ($in{'real'} =~ /(\S+)/) {
|
|
$autofirstname = lc($1);
|
|
}
|
|
else {
|
|
$autofirstname = lc($in{'user'});
|
|
}
|
|
if ($autolastname) {
|
|
if (&in_schema($schema, "mail")) {
|
|
if ($config{'mailfmt'} == 0) {
|
|
push(@props, "mail",
|
|
"$autofirstname.$autolastname\@$config{'domain'}")
|
|
}
|
|
else {
|
|
push(@props, "mail",
|
|
"$user\@$config{'domain'}")
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
push(@props, "mail", "$autofirstname\@$config{'domain'}")
|
|
if (&in_schema($schema, "mail"));
|
|
}
|
|
|
|
# Add extra aliases
|
|
local $aattr = $config{'maillocaladdress'} || "alias";
|
|
if (&in_schema($schema, $aattr)) {
|
|
local @alias = split(/\s+/, $in{'alias'});
|
|
if ($in{'alias'}) {
|
|
if (!$config{'alias_same'}) {
|
|
($dup, $dupwhat) = &check_duplicates($ldap, $aattr, \@alias, $in{'dn'});
|
|
$dup && &error(&text('save_ealiasdup', $dupwhat, $dup->dn()));
|
|
}
|
|
push(@props, $aattr, \@alias);
|
|
}
|
|
else {
|
|
push(@rprops, $aattr);
|
|
}
|
|
}
|
|
local $battr = $config{'mailroutingaddress'};
|
|
push(@props, $battr, lc($in{'user'})."\@$config{'imap_host'}")
|
|
if ($battr ne "") && (&in_schema($schema, $battr));
|
|
}
|
|
|
|
# delete_mail_props()
|
|
# Take away any extra properties added by mail_props
|
|
sub delete_mail_props
|
|
{
|
|
local $aattr = $config{'maillocaladdress'} || "alias";
|
|
if (&in_schema($schema, $aattr)) {
|
|
push(@rprops, $aattr);
|
|
}
|
|
local $battr = $config{'mailroutingaddress'};
|
|
if (($battr ne "") && &in_schema($schema, $battr)) {
|
|
push(@rprops, $battr);
|
|
}
|
|
push(@rprops, "mail")
|
|
if (&in_schema($schema, "mail"));
|
|
}
|
|
|
|
sub delete_addressbook
|
|
{
|
|
return &delete_ldap_subtree($ldap, "ou=$user, $config{'addressbook'}");
|
|
}
|
|
|
|
sub name_fields
|
|
{
|
|
if ($config{'given'}) {
|
|
if ($firstname) {
|
|
if (&in_schema($schema, "gn")) {
|
|
push(@props, "gn", $firstname);
|
|
}
|
|
elsif (&in_schema($schema, "givenName")) {
|
|
push(@props, "givenName", $firstname)
|
|
}
|
|
}
|
|
if ($lastname && &in_schema($schema, "sn")) {
|
|
push(@props, "sn", $lastname);
|
|
}
|
|
if ($firstname || $lastname) {
|
|
push(@classes, $config{'given_class'});
|
|
}
|
|
}
|
|
if (&in_schema($schema, "gecos") && $config{'gecos'}) {
|
|
push(@props, "gecos", &remove_accents($in{'real'}));
|
|
}
|
|
}
|
|
|
|
sub shadow_fields
|
|
{
|
|
if (&in_schema($schema, "shadowLastChange")) {
|
|
# Validate shadow-password inputs
|
|
$in{'min'} =~ /^\-?[0-9]*$/ ||
|
|
&error(&text('usave_emin', $in{'min'}));
|
|
if ($in{'min'} ne '') {
|
|
push(@props, "shadowMin", $in{'min'});
|
|
}
|
|
else {
|
|
push(@rprops, "shadowMin");
|
|
}
|
|
$in{'max'} =~ /^\-?[0-9]*$/ ||
|
|
&error(&text('usave_emax', $in{'max'}));
|
|
if ($in{'max'} ne '') {
|
|
push(@props, "shadowMax", $in{'max'});
|
|
}
|
|
else {
|
|
push(@rprops, "shadowMax");
|
|
}
|
|
if ($in{'expired'} ne "" && $in{'expirem'} ne ""
|
|
&& $in{'expirey'} ne "") {
|
|
eval { $expire = timelocal(0, 0, 12,
|
|
$in{'expired'},
|
|
$in{'expirem'}-1,
|
|
$in{'expirey'}-1900); };
|
|
if ($@) { &error($text{'usave_eexpire'}); }
|
|
push(@props, "shadowExpire", int($expire / (60*60*24)));
|
|
}
|
|
else {
|
|
push(@rprops, "shadowExpire");
|
|
}
|
|
$in{'warn'} =~ /^\-?[0-9]*$/ ||
|
|
&error(&text('usave_ewarn', $in{'warn'}));
|
|
if ($in{'warn'} ne '') {
|
|
push(@props, "shadowWarning", $in{'warn'});
|
|
}
|
|
else {
|
|
push(@rprops, "shadowWarning");
|
|
}
|
|
$in{'inactive'} =~ /^\-?[0-9]*$/ ||
|
|
&error(&text('usave_einactive', $in{'inactive'}));
|
|
if ($in{'inactive'} ne '') {
|
|
push(@props, "shadowInactive", $in{'inactive'});
|
|
}
|
|
else {
|
|
push(@rprops, "shadowInactive");
|
|
}
|
|
if ($in{'forcechange'} == 1){
|
|
if ($in{'passmode'} != 1) {
|
|
push(@props, "shadowLastChange", 0);
|
|
}
|
|
} else {
|
|
if ($in{'passmode'} == 3 ||
|
|
$in{'passmode'} == 2 && $pass ne $oldpass) {
|
|
|
|
$daynow = int(time() / (60*60*24));
|
|
push(@props, "shadowLastChange", $daynow);
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|