Commit bf77e242 authored by Leigh Stoller's avatar Leigh Stoller

Various fixes to deactivate/reactivate code, mostly to deal with not

wanting to call setgroups cause it is so slow. also refactor the code to
chown/chgrp user dot files so we can call it from reactivate.

Refactor the code that bumps user/project activity and calls exports
setup so that we can call it from reactivate.

When deleting a ZFS home/proj directory, do the ZFS rename and then
set the mountpoint=none, no need to have it mounted.
parent 9c3a44cb
......@@ -134,6 +134,7 @@ sub CreateSSHKey();
sub ReactivateUser();
sub DeactivateUser();
sub fatal($);
sub SetDotGroup($$);
sub ZFSexists($);
sub MakeDir($$);
sub WhackDir($$);
......@@ -338,30 +339,10 @@ sub ModifyUser()
if (@ARGV > 0) {
$grouplist = "-G '" . join(',', @ARGV) . "'";
}
if (mysystem("$USERMOD $user -g $pgroup $grouplist")) {
fatal("Could not modify user $user to add groups!\n");
}
#
# Make sure the users dot files and other critical files/dirs
# are in the correct group. I looked at the source code to
# chown, and it does not do anything to files that are already
# set correctly. Thank you chown.
#
my @dots = (".login", ".profile", ".cshrc", ".ssl", ".ssh");
my $homedir = USERROOT() . "/$user";
mysystem("$CHOWN $user:$pgroup $homedir") == 0
or fatal("Could not chown home dir to $user:$pgroup");
foreach my $dot (@dots) {
if (-e "$homedir/$dot") {
mysystem("$CHOWN -R $user:$pgroup $homedir/$dot") == 0
or fatal("Could not chown $homedir/$dot to $user:$pgroup");
}
}
SetDotGroup($user, $pgroup);
return 0;
}
......@@ -412,6 +393,16 @@ sub ReactivateUser()
if (mysystem("$USERMOD $user -d $hdir")) {
fatal("Could not set home directory back to $hdir");
}
#
# Make dot files are in the right group, since while the user was
# inactive, we skipped doing that cause the home dir was unmounted
# (ZFS). Need the current default group for that.
#
my (undef,undef,undef,$gid) = getpwnam($user);
if (!defined($gid)) {
fatal("Could not get gid for $user");
}
SetDotGroup($user,$gid);
return 0;
}
......@@ -750,6 +741,36 @@ sub CreateSSHKey()
return 0;
}
#
# Make sure the users dot files and other critical files/dirs
# are in the correct group. I looked at the source code to
# chown, and it does not do anything to files that are already
# set correctly. Thank you chown.
#
sub SetDotGroup($$)
{
my ($user, $gid) = @_;
my @dots = (".login", ".profile", ".cshrc", ".ssl", ".ssh");
my $homedir = USERROOT() . "/$user";
if (! -e $homedir) {
print STDERR "$homedir does not exist, skipping dots chown\n";
return 0;
}
print "Changing dot files group for $user to $gid\n";
mysystem("$CHOWN $user:$gid $homedir") == 0
or fatal("Could not chown home dir to $user:$gid");
foreach my $dot (@dots) {
if (-e "$homedir/$dot") {
mysystem("$CHOWN -R $user:$gid $homedir/$dot") == 0
or fatal("Could not chown $homedir/$dot to $user:$gid");
}
}
}
#
# Check for ZFS existence.
#
......@@ -883,7 +904,15 @@ sub WhackDir($$)
# Since we reuse uid/gids let's make the dir root/0700
$path = "$fs/$dir$suffix";
if (!chown(0, 0, $path) || !chmod(0700, $path)) {
print STDERR "WARNING: could not chown/chmod '$path'!\n";
print STDERR "WARNING: could not chown/chmod '$path': $!\n";
}
#
# And then unmount, we do not need it around. The empty subdir
# will still be there, so we have some clue ... maybe we should
# drop a file into the empty dir?
#
if ($zfsfs && mysystem("$ZFS set mountpoint=none $npath")) {
print STDERR "Could not set ZFS dir $npath to mountpoint=none\n";
}
}
#
......
......@@ -1151,10 +1151,12 @@ sub ReactivateUser()
#
if ($status ne USERSTATUS_ACTIVE) {
fatal("$user is not active! Cannot reactivate the account!")
if (!$update);
if (! ($update || $force));
if ($update) {
$target_user->SetStatus(USERSTATUS_ACTIVE());
$status = USERSTATUS_ACTIVE();
}
}
$UID = 0;
if ($CONTROL ne $BOSSNODE) {
print "Reactivating user $user ($user_number) on $CONTROL\n";
......
......@@ -50,8 +50,12 @@ my $TBAUDIT = "@TBAUDITEMAIL@";
my $TBBASE = "@TBBASE@";
my $TBWWW = "@TBWWW@";
my $WIKISUPPORT = @WIKISUPPORT@;
my $WITHZFS = @WITHZFS@;
my $WITHAMD = @WITHAMD@;
my $ZFS_NOEXPORT= @ZFS_NOEXPORT@;
my $MAILMANSUPPORT = @MAILMANSUPPORT@;
my $ADDPROJADMINLIST = "$TB/sbin/addprojadminlist";
my $EXPORTS_SETUP = "$TB/sbin/exports_setup";
# Cache of instances to avoid regenerating them.
my %projects = ();
......@@ -862,5 +866,51 @@ sub PeerExports($$)
return $self->{'GROUP'}->PeerExports($pref);
}
#
# Do an exports setup if needed (ZFS). See exports_setup, when ZFS is on
# we do not export all projects, only recently active ones.
#
sub UpdateExports($)
{
my ($self) = @_;
my $pid_idx = $self->pid_idx();
return 0
if (! ($WITHZFS && ($ZFS_NOEXPORT || !$WITHAMD)));
my $query_result =
DBQueryWarn("select UNIX_TIMESTAMP(last_activity) from project_stats ".
"where pid_idx='$pid_idx'");
# Hmm.
return 0
if (!$query_result->numrows);
my ($lastactivity) = $query_result->fetchrow_array();
if (!defined($lastactivity) ||
time() - $lastactivity > (24 * 3600)) {
# Update last_activity first so exports_setup will do something.
DBQueryWarn("update project_stats set last_activity=now() ".
"where pid_idx='$pid_idx'")
or return -1;
if ($ZFS_NOEXPORT) {
mysystem($EXPORTS_SETUP);
}
elsif (!$WITHAMD) {
mysystem($EXPORTS_SETUP . " -B");
}
# failed, reset the timestamp
if ($? && defined($lastactivity)) {
DBQueryWarn("update project_stats set ".
" last_activity=FROM_UNIXTIME($lastactivity) ".
"where pid_idx='$pid_idx'");
return -1;
}
}
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -40,7 +40,7 @@ use overload ('""' => 'Stringify');
use vars qw($NEWUSER_FLAGS_PROJLEADER $NEWUSER_FLAGS_WIKIONLY
$NEWUSER_FLAGS_WEBONLY $NEWUSER_FLAGS_ARCHIVED
$NEWUSER_FLAGS_NOUUID $NEWUSER_FLAGS_NONLOCAL
$USERSTATUS_ACTIVE $USERSTATUS_FROZEN
$USERSTATUS_ACTIVE $USERSTATUS_FROZEN $USERSTATUS_INACTIVE
$USERSTATUS_UNAPPROVED $USERSTATUS_UNVERIFIED
$USERSTATUS_NEWUSER $USERSTATUS_ARCHIVED $USERSTATUS_NONLOCAL
@EXPORT_OK);
......@@ -53,6 +53,9 @@ my $TBAUDIT = "@TBAUDITEMAIL@";
my $TBBASE = "@TBBASE@";
my $TBWWW = "@TBWWW@";
my $WIKISUPPORT = @WIKISUPPORT@;
my $WITHZFS = @WITHZFS@;
my $ZFS_NOEXPORT = @ZFS_NOEXPORT@;
my $WITHAMD = @WITHAMD@;
my $BOSSNODE = "@BOSSNODE@";
my $CONTROL = "@USERNODE@";
my $OURDOMAIN = "@OURDOMAIN@";
......@@ -61,6 +64,7 @@ my $MIN_UNIX_GID = @MIN_UNIX_GID@;
my $tbacct = "$TB/sbin/tbacct";
my $MKUSERCERT = "$TB/sbin/mkusercert";
my $EXPIRE_PASSWORDS = @EXPIRE_PASSWORDS@;
my $EXPORTS_SETUP = "$TB/sbin/exports_setup";
# Create() flags.
$NEWUSER_FLAGS_PROJLEADER = 0x01;
......@@ -78,12 +82,13 @@ $USERSTATUS_UNVERIFIED = "unverified";
$USERSTATUS_NEWUSER = "newuser";
$USERSTATUS_ARCHIVED = "archived";
$USERSTATUS_NONLOCAL = "nonlocal";
$USERSTATUS_INACTIVE = "inactive";
# Why, why, why?
@EXPORT_OK = qw($NEWUSER_FLAGS_PROJLEADER $NEWUSER_FLAGS_WIKIONLY
$NEWUSER_FLAGS_WEBONLY $NEWUSER_FLAGS_ARCHIVED
$NEWUSER_FLAGS_NOUUID
$USERSTATUS_ACTIVE $USERSTATUS_FROZEN
$USERSTATUS_ACTIVE $USERSTATUS_FROZEN $USERSTATUS_INACTIVE
$USERSTATUS_UNAPPROVED $USERSTATUS_UNVERIFIED
$USERSTATUS_NEWUSER $USERSTATUS_ARCHIVED $USERSTATUS_NONLOCAL);
......@@ -2210,6 +2215,53 @@ sub GetStoredCredential($)
return ($cred, $cert);
}
#
# Do an exports setup if needed (ZFS). See exports_setup, when ZFS is on
# we do not export all users, only recently active ones.
#
sub UpdateExports($)
{
my ($self) = @_;
my $uid_idx = $self->uid_idx();
return 0
if (! ($WITHZFS && ($ZFS_NOEXPORT || !$WITHAMD)));
my $query_result =
DBQueryWarn("select UNIX_TIMESTAMP(weblogin_last) from users as u ".
"left join user_stats as s on s.uid_idx=u.uid_idx ".
"where u.uid_idx='$uid_idx'");
# Hmm.
return 0
if (!$query_result->numrows);
my ($lastlogin) = $query_result->fetchrow_array();
if (!defined($lastlogin) ||
time() - $lastlogin > (24 * 3600)) {
# Update weblogin_last first so exports_setup will do something.
DBQueryWarn("update user_stats set ".
" weblogin_last=now() ".
"where uid_idx='$uid_idx'")
or return -1;
if ($ZFS_NOEXPORT) {
mysystem($EXPORTS_SETUP);
}
elsif (!$WITHAMD) {
mysystem($EXPORTS_SETUP . " -B");
}
# failed, reset the timestamp
if ($? && defined($lastlogin)) {
DBQueryWarn("update user_stats set ".
" weblogin_last=FROM_UNIXTIME($lastlogin) ".
"where uid_idx='$uid_idx'");
return -1;
}
}
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -817,6 +817,7 @@ sub admin($) { return $_[0]->{'USER'}->admin(); }
sub BumpActivity($) { return $_[0]->{'USER'}->BumpActivity(); }
sub DefaultProject($) { return $_[0]->{'USER'}->DefaultProject(); }
sub FlipTo($$) { return $_[0]->{'USER'}->FlipTo($_[1]); }
sub UpdateExports($) { return $_[0]->{'USER'}->UpdateExports(); }
# Need to construct this since not in User structure.
sub hrn($) { return "${PGENIDOMAIN}." . $_[0]->uid(); }
......
......@@ -112,6 +112,7 @@ sub FlipToUser($$;$)
$default_gid = $unix_gid;
$glist = "$unix_gid $unix_gid";
}
$GID = $default_gid;
$EGID = $glist;
$EUID = $UID = $unix_uid;
......@@ -564,27 +565,10 @@ sub GetHoldingProject($$)
$ENV{'EMULAB_REAL_USER'} = $creator->emulab_user()->uid_idx();
}
done:
if ($WITHZFS) {
if ($ZFS_NOEXPORT) {
#
# Have to force the new directories to be exported.
# See ZFS code in exports_setup
#
$project->BumpActivity();
system($EXPORTSSETUP) == 0 or
fatal("$EXPORTSSETUP failed");
} elsif (!$WITHAMD) {
#
# If we are using autofs, we need to recreate the local autofs
# mapfile so it includes the newly created directories. Otherwise,
# accesses to those new directories will not force a mount and
# the waitForMount() calls below will always fail.
#
$project->BumpActivity();
system("$EXPORTSSETUP -B") == 0 or
fatal("$EXPORTSSETUP -B failed");
}
if ($creator->IsLocal()) {
$creator->UpdateExports();
}
$project->UpdateExports();
return $group;
}
......
......@@ -56,7 +56,7 @@ my $PGENISUPPORT = @PROTOGENI_SUPPORT@;
my $PORTAL_ENABLE = @PORTAL_ENABLE@;
my $PORTAL_PRIMARY = @PORTAL_ISPRIMARY@;
my $MODGROUPS = "$TB/sbin/modgroups";
my $DELACCT = "$TB/sbin/tbacct del";
my $TBACCT = "$TB/sbin/tbacct";
my $POSTCRL = "$TB/sbin/protogeni/postcrl";
# Locals
......@@ -136,6 +136,7 @@ if (! defined($target_user)) {
}
my $target_dbid = $target_user->dbid();
my $target_uid = $target_user->uid();
my $reactivate = $target_user->status() eq $User::USERSTATUS_INACTIVE;
# Map invoking user to object.
my $this_user = User->ThisUser();
......@@ -359,8 +360,15 @@ $target_user->Purge() == 0
if (! $nuke) {
$EUID = $UID;
system("$DELACCT $target_uid") == 0 or
fatal("$DELACCT $target_uid failed!");
#
# Before we can do anything, we have to reactivate.
#
if ($reactivate &&
system("$TBACCT -f reactivate $target_uid")) {
fatal("$TBACCT -f reactivate $target_uid failed!");
}
system("$TBACCT del $target_uid") == 0 or
fatal("$TBACCT del $target_uid failed!");
$EUID = 0;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment