diff --git a/tbsetup/mkproj.in b/tbsetup/mkproj.in index 4dbce9a4bcfcf30fc51d850bf08482e707db47df..14c349caebee3737be1ddc2b51102e53cb6ddd65 100755 --- a/tbsetup/mkproj.in +++ b/tbsetup/mkproj.in @@ -1,48 +1,64 @@ #!/usr/bin/perl -wT - # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2006 University of Utah and the Flux Group. +# Copyright (c) 2000-2007 University of Utah and the Flux Group. # All rights reserved. # - use English; - -# -# Make a project directory hierarchy. Must be called as tbroot. -# Creates a directory rooted /proj/pid. The directory is setuid -# to the project leader, and setgid to the project gid. We get -# this info from the database. -# -# usage: mkproj <pid> -# - +use Getopt::Std; + +# +# Perform project approval. Does a lot of stuff, see below! +# +sub usage() +{ + print(STDERR + "Usage: mkproj [-s] [-h leader_uid] [-m <message> | -f <file>] ". + "<pid>\n". + "switches and arguments:\n". + "-s - silent; do not send approval email to leader\n". + "-h <uid> - switch project leader to specified uid\n". + "-m <text> - Include text in approval email message\n". + "-f <file> - Include text from file in approval email message\n". + "<pid> - project to approve.\n"); + exit(-1); +} +my $optlist = "qsh:m:f:"; +my $quiet = 0; +my $silent = 0; +my $leader_uid; +my $message; +my $mfilename; +my $pid; + +# Protos sub fatal($); # # Configure variables # -my $TB = "@prefix@"; -my $TBOPS = "@TBOPSEMAIL@"; -my $MKGROUP = "$TB/sbin/mkgroup"; -my $MODGROUPS= "$TB/sbin/modgroups"; -my $MKACCT = "$TB/sbin/tbacct add"; -my $CVSBIN = "/usr/bin/cvs"; -my $CHOWN = "/usr/sbin/chown"; -my $GRANTTYPE= "$TB/sbin/grantnodetype -d"; +my $TB = "@prefix@"; +my $TBOPS = "@TBOPSEMAIL@"; +my $TBAPPROVAL = "@TBAPPROVALEMAIL@"; +my $TBBASE = "@TBBASE@"; +my $MKGROUP = "$TB/sbin/mkgroup"; +my $MODGROUPS = "$TB/sbin/modgroups"; +my $MKACCT = "$TB/sbin/tbacct add"; +my $CVSBIN = "/usr/bin/cvs"; +my $CHOWN = "/usr/sbin/chown"; +my $GRANTTYPE = "$TB/sbin/grantnodetype -d"; my $WIKISUPPORT = @WIKISUPPORT@; my $BUGDBSUPPORT = @BUGDBSUPPORT@; my $OPSDBSUPPORT = @OPSDBSUPPORT@; my $CVSSUPPORT = @CVSSUPPORT@; my $MAILMANSUPPORT= @MAILMANSUPPORT@; -my $ADDWIKIPROJ = "$TB/sbin/addwikiproj"; -my $ADDBUGDBPROJ= "$TB/sbin/addbugdbproj"; -my $ADDMMLIST = "$TB/sbin/addmmlist"; -my $OPSDBCONTROL= "$TB/sbin/opsdb_control"; +my $ADDWIKIPROJ = "$TB/sbin/addwikiproj"; +my $ADDBUGDBPROJ = "$TB/sbin/addbugdbproj"; +my $ADDMMLIST = "$TB/sbin/addmmlist"; +my $OPSDBCONTROL = "$TB/sbin/opsdb_control"; my @DIRLIST = ("exp", "images", "logs", "deltas", "tarfiles", "rpms", "groups", "tiplogs", "images/sigs", "templates"); -my $projhead; # # Untaint the path @@ -62,6 +78,8 @@ use lib "@prefix@/lib"; use libaudit; use libdb; use libtestbed; +use User; +use Project; my $PROJROOT = PROJROOT(); my $GRPROOT = GROUPROOT(); @@ -73,8 +91,12 @@ my $SCRATCHROOT = SCRATCHROOT(); my $TFTPDIR = "/tftpboot/$PROJROOT"; my $CVSREPOS = "$PROJROOT/cvsrepos"; +# Locals +my $leader; +my $projhead; + # -# We don't want to run this script unless its the real version. +# We do not want to run this script unless its the real version. # if ($EUID != 0) { die("*** $0:\n". @@ -93,36 +115,53 @@ if ($UID == 0) { # # Check args. # -if ($#ARGV < 0) { - die("Usage: mkprojdir <pid>\n"); +my %options = (); +if (! getopts($optlist, \%options)) { + usage(); +} +if (defined($options{"q"})) { + $quiet = 1; } -my $pid = $ARGV[0]; +if (defined($options{"s"})) { + $silent = 1; +} +if (defined($options{"m"})) { + $message = $options{"m"}; +} +if (defined($options{"f"})) { + $mfilename = $options{"f"}; + fatal("$mfilename does not exist!") + if (! -e $mfilename); +} +if (defined($options{"h"})) { + $leader_uid = $options{"h"}; +} +usage() + if (! @ARGV); + +$pid = $ARGV[0]; # # Untaint the argument. # -if ($pid =~ /^([-\@\w.]+)$/) { +if ($pid =~ /^([-\w]+)$/) { $pid = $1; } else { die("Invalid pid '$pid' contains illegal characters.\n"); } -# -# Figure out who called us. Only with admin status in the DB can run -# this script. -# -if (!TBAdmin($UID)) { - die("*** $0:\n". - " You must be a TB administrator to run this script!\n"); +# Map invoking user to object. +my $this_user = User->ThisUser(); +if (! defined($this_user)) { + fatal("You ($UID) do not exist!"); } # -# We need the project leader name. +# Figure out who called us. Must have admin status to do this. # -if (! ($projhead = ProjLeader($pid))) { - die("*** $0:\n". - " Could not get project leader for project $pid!\n"); +if (!TBAdmin()) { + fatal("You must be a TB administrator to run this script!"); } # @@ -135,6 +174,74 @@ if (AuditStart(0)) { exit(0); } +# +# Map project name to object. +# +my $target_project = Project->Lookup($pid); +if (! defined($target_project)) { + fatal("Could not map project $pid to its object!"); +} + +# +# The welcome message ... +# +if (defined($mfilename)) { + open(MFILE, $mfilename) or + fatal("Could not open $mfilename"); + + $message = ""; + while (<MFILE>) { + $message .= $_; + } + close(MFILE); +} + +# +# If a leader uid was provided on the command line, we are changing the +# leader. Note that this is allowed *only* for projects that have not +# been approved yet. +# +if (defined($leader_uid)) { + $leader = User->Lookup($leader_uid); + if (!defined($leader)) { + fatal("Could not map user $leader_uid to its object!"); + } + # See if already did this; is so skip the following checks. + my $curleader = $target_project->GetLeader(); + if (!defined($curleader)) { + fatal("Could not map current leader of project $pid to its object!"); + } + if (! $curleader->SameUser($leader)) { + fatal("Not allowed to change the leader of an approved project!") + if ($target_project->approved()); + + # Update the project structure with the new leader. We are going + # to set the approved bit below, so this is the last chance to do + # this until we have code in place to change it later. + $target_project->ChangeLeader($leader) == 0 or + fatal("Could not change project leader for $pid to $leader_uid!"); + } +} +else { + $leader = $target_project->GetLeader(); + if (!defined($leader)) { + fatal("Could not map current leader of project $pid to its object!"); + } +} +# Avoid taint check problem. +$leader_uid = $leader->uid(); + +# Approve the project; we are committed to the leader. +$target_project->SetApproved(1) == 0 or + fatal("Could not set the approval bit on project $target_project!"); + +# +# Leader needs to have his approved bit set. Eventually this should be done +# in mkaccount when that code moves from the web interface. +# +$leader->SetStatus(USERSTATUS_ACTIVE()) == 0 or + fatal("Could not change $leader to active!"); + # # Before we can proceed, we need to create the project (unix) group # and then create an account for the project leader. We pass this off @@ -163,19 +270,19 @@ if ($MAILMANSUPPORT) { fatal("$ADDMMLIST -a ${pid}-users failed!"); } -system("$MKACCT $projhead") == 0 or - fatal("$MKACCT $projhead failed!"); +system("$MKACCT $leader_uid") == 0 or + fatal("$MKACCT $leader_uid failed!"); -system("$MODGROUPS -a $pid:$pid:project_root $projhead") == 0 or - fatal("$MODGROUPS -a $pid:$pid:project_root $projhead failed!"); +system("$MODGROUPS -a $pid:$pid:project_root $leader_uid") == 0 or + fatal("$MODGROUPS -a $pid:$pid:project_root $leader_uid failed!"); $EUID = 0; # # This acts as check (and we need the numeric uid) in case mkacct failed! # -my (undef,undef,$uid) = getpwnam($projhead) - or fatal("$projhead not in passwd file"); +my (undef,undef,$uid) = getpwnam($leader_uid) + or fatal("$leader_uid not in passwd file"); my (undef,undef,$gid) = getgrnam($pid) or fatal("$pid not in group file"); @@ -354,6 +461,28 @@ if ($query_result->num_rows) { } } +# Send email, unless silent option given. +if (!$silent) { + my $leader_name = $leader->name(); + my $leader_email = $leader->email(); + + SENDMAIL("$leader_name <$leader_email>", + "Project '$pid' Approval", + "\n". + "This message is to notify you that your project '$pid'\n". + "has been approved. We recommend that you save this link so that\n". + "you can send it to people you wish to have join your project.\n". + "Otherwise, tell them to go to ${TBBASE} and join it.\n". + "\n". + " ${TBBASE}/joinproject.php3?target_pid=$pid\n". + (defined($message) ? "\n${message}\n" : "") . + "\n". + "Thanks,\n". + "Testbed Operations\n", + "$TBAPPROVAL", + "Bcc: $TBAPPROVAL"); +} + print "Project Creation Completed!\n"; exit(0); diff --git a/www/approveproject.php3 b/www/approveproject.php3 index 48edeca0e8f0e04dd8b0f89115f7254004c1a6a0..cae6f0604ac849aca5b6b33f381242fb859ab09b 100644 --- a/www/approveproject.php3 +++ b/www/approveproject.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group. +# Copyright (c) 2000-2007 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -49,7 +49,7 @@ $headuid = $this_project->head_uid(); # If the user wanted to change the head uid, do that now (we change both # the head_uid and the leader of the default project) # -if (isset($head_uid) && $head_uid != "") { +if ($approval == "approve" && isset($head_uid) && $head_uid != "") { if (! ($newleader = User::Lookup($head_uid))) { TBERROR("Unknown user $head_uid", 1); } @@ -78,6 +78,7 @@ if (!isset($user_interface) || $curstatus = $leader->status(); $headuid_email = $leader->email(); $headname = $leader->name(); +#$headidx = $leader->uid_idx(); #echo "Status = $curstatus, Email = $headuid_email<br>\n"; # @@ -175,28 +176,15 @@ elseif ((strcmp($approval, "deny") == 0) || </h3>\n"; } elseif (strcmp($approval, "approve") == 0) { - - # - # Change the status if necessary. This only happens for new users - # being approved in their first project. After this, the status is - # going to be "active", and we just leave it that way. - # - if (strcmp($curstatus, "active")) { - if (strcmp($curstatus, "unapproved") == 0) { - $newstatus = "active"; - } - else { - TBERROR("Invalid $headuid status $curstatus in ". - "approveproject.php3", 1); - } - $leader->SetUserInterface($user_interface); - $leader->SetStatus($newstatus); + $optargs = ""; + + # Sanity check the leader status. + if ($curstatus != TBDB_USERSTATUS_ACTIVE && + $curstatus != TBDB_USERSTATUS_UNAPPROVED) { + TBERROR("Invalid $headuid status $curstatus", 1); } - - # - # Set the project "approved" field to true. - # - $this_project->SetApproved(1); + # Why is this here? + $leader->SetUserInterface($user_interface); # # XXX @@ -218,41 +206,43 @@ elseif (strcmp($approval, "approve") == 0) { $this_project->SetRemoteOK($foo); } + unset($tmpfname); + if (isset($message)) { + $tmpfname = tempnam("/tmp", "approveproj"); + $fp = fopen($tmpfname, "w"); + fwrite($fp, $message); + fclose($fp); + + $optargs = " -f " . escapeshellarg($tmpfname); + } + # # Invoke the script. This does it all. If it fails, we will find out # about it. # - echo "<br> - Project '$pid' is being created!<br><br> - This will take a minute or two. <b>Please</b> do not click the Stop - button during this time. If you do not receive notification within - a reasonable amount of time, please contact $TBMAILADDR.\n"; - flush(); + STARTBUSY("Project '$pid' is being created"); + + $retval = SUEXEC($uid, $TBADMINGROUP, "webmkproj $optargs $pid", + SUEXEC_ACTION_IGNORE); - SUEXEC($uid, $TBADMINGROUP, "webmkproj $pid", SUEXEC_ACTION_DIE); + CLEARBUSY(); - TBMAIL("$headname '$headuid' <$headuid_email>", - "Project '$pid' Approval", - "\n". - "This message is to notify you that your project '$pid'\n". - "has been approved. We recommend that you save this link so that\n". - "you can send it to people you wish to have join your project.\n". - "Otherwise, tell them to go to ${TBBASE} and join it.\n". - "\n". - " ${TBBASE}/joinproject.php3?target_pid=$pid\n". - "\n". - "$message\n". - "\n". - "Thanks,\n". - "Testbed Operations\n", - "From: $TBMAIL_APPROVAL\n". - "Bcc: $TBMAIL_APPROVAL\n". - "Errors-To: $TBMAIL_WWW"); + if (isset($tmpfname)) { + unlink($tmpfname); + } + if ($retval) { + # Lets tack the message onto the output so we have a record. + if (isset($message)) { + $suexec_output .= "\n\n*** Saved approval message text:\n\n"; + $suexec_output .= $message; + } + SUEXECERROR(SUEXEC_ACTION_DIE); + return; + } if (!$FirstInitState) { - echo "<p><b> - Project $pid (User: $headuid) has been approved. - </b>\n"; + sleep(1); + PAGEREPLACE(CreateURL("showproject", $this_project)); } else { echo "<br><br><font size=+1>\n";