diff --git a/Makeconf.in b/Makeconf.in index 3eb468f5ff3e212f13aa0d4b798e0a3235c5567c..1219243d9c1de781ceb012cb36e337ff090ae170 100644 --- a/Makeconf.in +++ b/Makeconf.in @@ -1,6 +1,6 @@ # # 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. # # Most of the configure-substitution magic is done here. @@ -37,6 +37,7 @@ TBAUDITEMAIL = @TBAUDITEMAIL@ TBACTIVEARCHIVE = @TBACTIVEARCHIVE@ TBUSERSARCHIVE = @TBUSERSARCHIVE@ TBERRORSEMAIL = @TBERRORSEMAIL@ +TBAPPROVALEMAIL = @TBAPPROVALEMAIL@ BOSSNODE = @BOSSNODE@ USERNODE = @USERNODE@ FSNODE = @FSNODE@ diff --git a/account/GNUmakefile.in b/account/GNUmakefile.in index 8d675becc5fba56ae6fa65ed36364a0af1798654..2b31de4d4fb06dd5d95140883da9ec90b2804648 100644 --- a/account/GNUmakefile.in +++ b/account/GNUmakefile.in @@ -12,8 +12,10 @@ UNIFIED = @UNIFIED_BOSS_AND_OPS@ include $(OBJDIR)/Makeconf -SBIN_STUFF = tbacct addsfskey addpubkey mkusercert quotamail genpubkeys -LIBEXEC_STUFF = webtbacct webaddsfskey webaddpubkey webmkusercert +SBIN_STUFF = tbacct addsfskey addpubkey mkusercert quotamail genpubkeys \ + newuser newproj +LIBEXEC_STUFF = webtbacct webaddsfskey webaddpubkey webmkusercert \ + webnewuser webnewproj # These scripts installed setuid, with sudo. SETUID_BIN_SCRIPTS = diff --git a/account/addpubkey.in b/account/addpubkey.in index f2f47288f6aceb10063cf9683a448f2bc57ddd08..9d5dcc6470f5f5c3a0a637d558e23ea432dd8c6e 100644 --- a/account/addpubkey.in +++ b/account/addpubkey.in @@ -171,22 +171,25 @@ if (defined($user)) { $user_email = $target_user->email(); $user_dbid = $target_user->dbid(); $user_uid = $target_user->uid(); + $USERUID = $user_uid; } # -# If invoked as "nobody" its for a user with no actual account. +# If invoked as "nobody" we came from the web interface. We have to have +# a credential in the environment, unless its just a key verification +# operation, which anyone can do. # if (getpwuid($UID) eq "nobody") { - if ($initmode || $genmode) { - fatal("Bad usage as 'nobody'"); + $this_user = User->ImpliedUser(); + + if (($initmode || $genmode || !$verify) && !defined($this_user)) { + fatal("Bad usage from web interface"); } $nobody = 1; } else { - $USERUID = getpwnam($user); - - # Map invoking user to object. - $this_user = User->LookupByUnixId($UID); + # From the command line; map invoking user to object. + $this_user = User->ThisUser(); if (! defined($this_user)) { fatal("You ($UID) do not exist!"); @@ -194,16 +197,17 @@ else { } # -# Initmode or genmode, do it and exit. +# Initmode or genmode, do it and exit. Eventually get rid of the switch +# to the target user. # if ($initmode) { # Drop root privs, switch to target user. - $EUID = $USERUID; + $EUID = $UID = $USERUID; exit InitUser(); } if ($genmode) { # Drop root privs, switch to target user. - $EUID = $USERUID; + $EUID = $UID = $USERUID; exit GenerateKeyFile(); } @@ -235,10 +239,12 @@ else { # if (!$verify) { # If its the user himself, then we can generate a new authkeys file. - if (!$nobody && !TBAdmin() && !$target_user->SameUser($this_user)) { + if (!$target_user->SameUser($this_user) && !TBAdmin()) { fatal("You are not allowed to set pubkeys for $target_user\n"); } if (-d "$HOMEDIR/$user_uid/.ssh") { + # Drop root privs, switch to target user. + $EUID = $UID = $USERUID; $genmode = 1; } @@ -250,11 +256,6 @@ if (!$verify) { AuditStart(0); } -# Drop root privs, switching to user. -if (!$nobody) { - $EUID = $USERUID; -} - # # Grab the first line of the file. Parse it to see if its in the # format we like (openssh), either protocol 1 or 2. diff --git a/account/newproj.in b/account/newproj.in new file mode 100644 index 0000000000000000000000000000000000000000..68a8b659ec39246c954a16666ec1a4947f305cbe --- /dev/null +++ b/account/newproj.in @@ -0,0 +1,306 @@ +#!/usr/bin/perl -wT +# +# EMULAB-COPYRIGHT +# Copyright (c) 2000-2007 University of Utah and the Flux Group. +# All rights reserved. +# +use English; +use strict; +use Getopt::Std; +use XML::Simple; +use Data::Dumper; + +# +# Create a new user from a XML description. +# +sub usage() +{ + print("Usage: newproj <xmlfile>\n"); + print(" newproj -m <pid_idx>\n"); + exit(-1); +} +my $optlist = "dm:"; +my $debug = 1; +my $resend; + +# +# Configure variables +# +my $TB = "@prefix@"; +my $TBOPS = "@TBOPSEMAIL@"; +my $TBAPPROVAL = "@TBAPPROVALEMAIL@"; +my $TBAUDIT = "@TBAUDITEMAIL@"; +my $TBBASE = "@TBBASE@"; +my $TBWWW = "@TBWWW@"; + +# +# This script is setuid, so please do not run it as root. Hard to track +# what has happened. +# +if ($EUID == 0) { + die("*** $0:\n". + " Please do not run this as root!\n"); +} + +# +# Untaint the path +# +$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/usr/bin:/usr/sbin"; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +# +# Turn off line buffering on output +# +$| = 1; + +# +# Load the Testbed support stuff. +# +use lib "@prefix@/lib"; +use libdb; +use libtestbed; +use Project; +use User; + +# Protos +sub fatal($); +sub UserError($); + +# Locals +my $SAVEUID = $UID; +my $xmlfile; + +# +# Parse command arguments. Once we return from getopts, all that should be +# left are the required arguments. +# +my %options = (); +if (! getopts($optlist, \%options)) { + usage(); +} +if (defined($options{"d"})) { + $debug = 1; +} +if (defined($options{"m"})) { + $resend = $options{"m"}; + + if ($resend =~ /^(\d*)$/) { + $resend = $1; + } + else { + fatal("Bad characters in -m option: $resend"); + } +} +else { + usage() + if (@ARGV != 1); + + $xmlfile = shift(@ARGV); +} + +# +# Map invoking user to object. +# If invoked as "nobody" we are coming from the web interface and there +# is no current user context. +# +my $this_user; + +if (getpwuid($UID) ne "nobody") { + $this_user = User->ThisUser(); + + if (! defined($this_user)) { + fatal("You ($UID) do not exist!"); + } + fatal("You must have admin privledges to create new projects!") + if (!TBAdmin()); +} + +# +# Resend email message and exit. +# +if (defined($resend)) { + my $project = Project->Lookup($resend); + fatal("Could not map project $resend to object!") + if (!defined($project)); + + exit($project->SendNewProjectEmail()); +} + +# +# Check the filename when invoked from the web interface; must be a +# file in /tmp. +# +if (! defined($this_user)) { + if ($xmlfile =~ /^([-\w\.\/]+)$/) { + $xmlfile = $1; + } + else { + fatal("Bad data in pathname: $xmlfile"); + } + + # Use realpath to resolve any symlinks. + my $translated = `realpath $xmlfile`; + if ($translated =~ /^(\/tmp\/[-\w\.\/]+)$/) { + $xmlfile = $1; + } + else { + fatal("Bad data in translated pathname: $xmlfile"); + } +} + +# +# These are the fields that we allow to come in from the XMLfile. +# +my %required = ("name" => "pid", + "leader" => "head_uid", + "short description" => "name", + "URL" => "URL", + "funders" => "funders", + "long description" => "why", + "public" => "public", + "num_pcs" => "num_pcs", + "linkedtous" => "linked_to_us"); + +my %optional = ("members" => "num_members", + "ron" => "num_ron", + "plab" => "num_pcplab", + "whynotpublic" => "public_whynot", + "user_interface" => "default_user_interface"); + +# +# This script is not audited cause we want the output to be sent back +# to the web interface. Thats okay since we send email from the script +# anyway. +# + +# +# Must wrap the parser in eval since it exits on error. +# +my $xmlparse = eval { XMLin($xmlfile, + VarAttr => 'name', + ContentKey => '-content', + SuppressEmpty => undef); }; +fatal($@) + if ($@); + + +# +# Make sure all the required arguments were provided. +# +foreach my $key (keys(%required)) { + + fatal("Missing required attribute '$key'") + if (! exists($xmlparse->{'attribute'}->{"$key"})); +} + +# +# We build up an array of arguments to pass to User->Create() as we check +# the attributes. +# +my %newproj_args = (); + +foreach my $key (keys(%{ $xmlparse->{'attribute'} })) { + my $value = $xmlparse->{'attribute'}->{"$key"}->{'value'}; + + if ($debug) { + print STDERR "Project attribute: '$key' -> '$value'\n"; + } + + my $dbslot; + + # Must be in the allowed lists above, with exceptions handled below + if (exists($required{$key})) { + $dbslot = $required{$key}; + next + if (!defined($dbslot)); + fatal("Null value for required field $key") + if (!defined($value)); + } + elsif (exists($optional{$key})) { + $dbslot = $optional{$key}; + next + if (!defined($dbslot) || !defined($value)); + } + else { + fatal("Invalid attribute in XML: '$key' -> '$value'\n"); + } + + # Now check that the value is legal. + if (! TBcheck_dbslot($value, "projects", $dbslot, + TBDB_CHECKDBSLOT_WARN|TBDB_CHECKDBSLOT_ERROR)) { + UserError("Illegal data: $key - $value"); + } + + # + # Do a taint check to avoid warnings, since the values are abviously okay. + # + if ($value =~ /^(.*)$/) { + $value = $1; + } + $newproj_args{$dbslot} = $value; +} + +# +# Now do special checks. +# +UserError("Project already exists; pick another name!") + if (Project->Lookup($newproj_args{'pid'})); + +my $leader = User->Lookup($newproj_args{'head_uid'}); +UserError("Project leader does not exist!") + if (!defined($leader)); + +# +# Now safe to create the project. Move the pid out of the argument array +# since its actually an argument to the Create() routine. Ditto for the +# project leader. +# +my $new_pid = $newproj_args{'pid'}; +delete($newproj_args{'pid'}); +delete($newproj_args{'head_uid'}); + +my $newproj = Project->Create($new_pid, $leader, \%newproj_args); +if (!defined($newproj)) { + fatal("Could not create new project!"); +} +my $new_idx = $newproj->pid_idx(); + +# +# See if we are in an initial Emulab setup. If so, no email sent. +# +my $firstinitstate; +TBGetSiteVar("general/firstinit/state", \$firstinitstate); + +# +# Send the email notification. +# +$newproj->SendNewProjectEmail($firstinitstate eq "createproject"); + +# The web interface requires this line to be printed! +print "User $new_pid/$new_idx has been created\n"; +exit(0); + +sub fatal($) { + my($mesg) = $_[0]; + + print STDERR "*** $0:\n". + " $mesg\n"; + exit(-1); +} +sub UserError($) { + my($mesg) = $_[0]; + + print $mesg; + exit(1); +} + +sub escapeshellarg($) +{ + my ($str) = @_; + + $str =~ s/(')/'\\''/g; + return $str; +} + + + diff --git a/account/newuser.in b/account/newuser.in new file mode 100644 index 0000000000000000000000000000000000000000..f8ed0f3a1c88b12a4c7a77d0e3759781c52332a0 --- /dev/null +++ b/account/newuser.in @@ -0,0 +1,449 @@ +#!/usr/bin/perl -wT +# +# EMULAB-COPYRIGHT +# Copyright (c) 2000-2007 University of Utah and the Flux Group. +# All rights reserved. +# +use English; +use strict; +use Getopt::Std; +use XML::Simple; +use Data::Dumper; + +# +# Create a new user from a XML description. +# +sub usage() +{ + print("Usage: newuser -t <type> <xmlfile>\n"); + exit(-1); +} +my $optlist = "dt:"; +my $debug = 1; +my $type = ""; + +# +# Configure variables +# +my $TB = "@prefix@"; +my $TBOPS = "@TBOPSEMAIL@"; +my $TBAPPROVAL = "@TBAPPROVALEMAIL@"; +my $TBAUDIT = "@TBAUDITEMAIL@"; +my $TBBASE = "@TBBASE@"; +my $TBWWW = "@TBWWW@"; +my $WIKISUPPORT = @WIKISUPPORT@; +my $TBADMINGROUP= "@TBADMINGROUP@"; +my $checkpass = "$TB/libexec/checkpass"; +my $addpubkey = "$TB/sbin/addpubkey"; + +# +# This script is setuid, so please do not run it as root. Hard to track +# what has happened. +# +if ($EUID == 0) { + die("*** $0:\n". + " Please do not run this as root!\n"); +} + +# +# Untaint the path +# +$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/usr/bin:/usr/sbin"; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +# +# Turn off line buffering on output +# +$| = 1; + +# +# Load the Testbed support stuff. +# +use lib "@prefix@/lib"; +use libdb; +use libtestbed; +use User; + +# Protos +sub fatal($); +sub UserError($); +sub escapeshellarg($); + +# Locals +my $SAVEUID = $UID; +my $keyfile; + +# +# Parse command arguments. Once we return from getopts, all that should be +# left are the required arguments. +# +my %options = (); +if (! getopts($optlist, \%options)) { + usage(); +} +if (defined($options{"d"})) { + $debug = 1; +} +if (defined($options{"t"})) { + $type = $options{"t"}; +} +if (@ARGV != 1) { + usage(); +} +my $xmlfile = shift(@ARGV); + +# +# Map invoking user to object. +# If invoked as "nobody" we are coming from the web interface and there +# is no current user context. +# +my $this_user; + +if (getpwuid($UID) ne "nobody") { + $this_user = User->ThisUser(); + + if (! defined($this_user)) { + fatal("You ($UID) do not exist!"); + } + fatal("You must have admin privledges to create new users") + if (!TBAdmin()); +} +else { + # + # Check the filename when invoked from the web interface; must be a + # file in /tmp. + # + if ($xmlfile =~ /^([-\w\.\/]+)$/) { + $xmlfile = $1; + } + else { + fatal("Bad data in pathname: $xmlfile"); + } + + # Use realpath to resolve any symlinks. + my $translated = `realpath $xmlfile`; + if ($translated =~ /^(\/tmp\/[-\w\.\/]+)$/) { + $xmlfile = $1; + } + else { + fatal("Bad data in translated pathname: $xmlfile"); + } +} + +# +# These are the fields that we allow to come in from the XMLfile. +# +my %required = ("name" => "usr_name", + "email" => "usr_email", + "address" => "usr_addr", + "city" => "usr_city", + "state" => "usr_state", + "zip" => "usr_zip", + "country" => "usr_country", + "phone" => "usr_phone", + "title" => "usr_title", + "affiliation" => "usr_affil", + "password" => undef, + "wikiname" => "wikiname"); + +my %optional = ("login" => "uid", + "address2" => "usr_addr2", + "URL" => "usr_URL", + "shell" => "usr_shell", + "pubkey" => undef); + +# +# Must wrap the parser in eval since it exits on error. +# +my $xmlparse = eval { XMLin($xmlfile, + VarAttr => 'name', + ContentKey => '-content', + SuppressEmpty => undef); }; +fatal($@) + if ($@); + + +# +# Make sure all the required arguments were provided. +# +foreach my $key (keys(%required)) { + + fatal("Missing required attribute '$key'") + if (! exists($xmlparse->{'attribute'}->{"$key"})); +} + +# +# We build up an array of arguments to pass to User->Create() as we check +# the attributes. +# +my %newuser_args = (); + +foreach my $key (keys(%{ $xmlparse->{'attribute'} })) { + my $value = $xmlparse->{'attribute'}->{"$key"}->{'value'}; + + if ($debug) { + print STDERR "User attribute: '$key' -> '$value'\n"; + } + + my $dbslot; + + # Must be in the allowed lists above, with exceptions handled below + if (exists($required{$key})) { + $dbslot = $required{$key}; + next + if (!defined($dbslot)); + fatal("Null value for required field $key") + if (!defined($value)); + } + elsif (exists($optional{$key})) { + $dbslot = $optional{$key}; + next + if (!defined($dbslot) || !defined($value)); + } + else { + fatal("Invalid attribute in XML: '$key' -> '$value'\n"); + } + + # Now check that the value is legal. + if (! TBcheck_dbslot($value, "users", $dbslot, + TBDB_CHECKDBSLOT_WARN|TBDB_CHECKDBSLOT_ERROR)) { + UserError("Illegal data: $key - $value"); + } + + # + # Do a taint check to avoid warnings, since the values are abviously okay. + # + if ($value =~ /^(.*)$/) { + $value = $1; + } + $newuser_args{$dbslot} = $value; +} + +# +# Now do special checks. +# + +# +# If user selected his own uid, must be unique. +# +if (exists($newuser_args{'uid'})) { + UserError("User already exists; pick another login name!") + if (User->Lookup($newuser_args{'uid'})); + + UserError("Reserved user name; pick another login name!") + if (getpwnam($newuser_args{'uid'})); +} + +# +# User name must be at least two tokens. +# +my @foo = split(/\s+/, $newuser_args{'usr_name'}); + +UserError("User name must be more then a single token!") + if (@foo < 2); + +# +# Wikiname must be unique. +# +if ($WIKISUPPORT) { + UserError("Wikiname already in use; please pick another!") + if (User->LookupByWikiName($newuser_args{'wikiname'})); +} + +# +# And the email address has to be unique. +# +UserError("Email address already in use; please pick another!") + if (User->LookupByEmail($newuser_args{'usr_email'})); + +# +# Check the password. +# +my $pswd = $xmlparse->{'attribute'}->{'password'}->{'value'}; + +my $checkpass_args = escapeshellarg($pswd); +$checkpass_args .= " " . + (exists($newuser_args{'uid'}) ? $newuser_args{'uid'} : "ignored"); +$checkpass_args .= escapeshellarg($newuser_args{'usr_name'} . ":" . + $newuser_args{'usr_email'}); + +my $pwokay = `$checkpass $checkpass_args`; +if ($?) { + chomp($pwokay); + + if (! ($pwokay =~ /^ok$/)) { + UserError("$pwokay"); + } + fatal("Checkpass failed with $?"); +} +$newuser_args{'usr_pswd'} = crypt($pswd, "\$1\$" . substr(time(), 0, 8)); + +# +# Do a check on the pubkey if supplied. The safest thing to do is generate +# a temporary file and pass that to addpubkey to check. +# +if (exists($xmlparse->{'attribute'}->{'pubkey'})) { + $keyfile = TBMakeTempFile("addpubkey"); + fatal("Could not create tempfile") + if ($?); + + open(KEY, ">> $keyfile") or + fatal("Could not open $keyfile"); + print KEY $xmlparse->{'attribute'}->{'pubkey'}->{'value'}; + close($keyfile); + + my $result = `$addpubkey -n -f $keyfile`; + chomp($result); + UserError("Could not parse public key") + if ($?); +} + +# +# Now safe to create the user. Move the uid out of the argument array +# since its actually an argument to the Create() routine. +# +my $new_uid; + +if (exists($newuser_args{'uid'})) { + $new_uid = $newuser_args{'uid'}; + delete($newuser_args{'uid'}); +} + +# +# The type modifier comes in on the command line since this is available +# only from the web interface or locally. The usual case is to create a +# normal user. +# +my $flags = 0; +if ($type eq "webonly") { + $flags = $User::NEWUSER_FLAGS_WEBONLY; +} +elsif ($type eq "wikionly") { + $flags = $User::NEWUSER_FLAGS_WIKIONLY; +} +elsif ($type eq "leader") { + $flags = $User::NEWUSER_FLAGS_PROJLEADER; +} +my $newuser = User->Create($new_uid, $flags, \%newuser_args); +if (!defined($newuser)) { + fatal("Could not create new user!"); +} + +# +# See if we are in an initial Emulab setup. If so, no email sent. +# +my $firstinitstate; +if (TBGetSiteVar("general/firstinit/state", \$firstinitstate)) { + # + # The first user gets admin status and some extra groups, etc. + # + DBQueryFatal("update users set ". + " admin=1,status='". $User::USERSTATUS_UNAPPROVED . "' " . + "where uid='$new_uid'"); + + DBQueryFatal("insert into unixgroup_membership set ". + "uid='$new_uid', gid='wheel'"); + + DBQueryFatal("insert into unixgroup_membership set ". + "uid='$new_uid', gid='$TBADMINGROUP'"); + + exit(0) + if ($firstinitstate eq "createproject"); +} + +# +# Send the email notification. +# +my $key = $newuser->verify_key(); +my $usr_uid = $newuser->uid(); +my $usr_name = $newuser->name(); +my $usr_email = $newuser->email(); + +SENDMAIL("$usr_name '$usr_uid' <$usr_email>", + "Your New User Key", + "\n". + "Dear $usr_name ($usr_uid):\n\n". + "This is your account verification key: $key\n\n". + "Please use this link to verify your user account:\n". + "\n". + " ${TBBASE}/login.php3?vuid=$usr_uid&key=$key\n". + "\n". + ($type eq "wikionly" ? + "Once you have verified your account, you will be able to access\n". + "the Wiki. You MUST verify your account first!" + : + ($type eq "webonly" ? + "Once you have verified your account, Testbed Operations will be\n". + "able to approve you. You MUST verify your account first!" + : + ($type eq "leader" ? + "You will then be verified as a user. When you have been both\n". + "verified and approved by Testbed Operations, you will be marked\n". + "as an active user and granted full access to your account.\n". + "You MUST verify your account before your project can be approved!\n" + : + "Once you have verified your account, the project leader will\n". + "be able to approve you.\n\n". + "You MUST verify your account before the project leader can ". + "approve you\n". + "After project approval, you will be marked as an active user,\n". + "and will be granted full access to your user account."))) . + "\n\n". + "Thanks,\n". + "Testbed Operations\n", + "$TBAPPROVAL", + "Bcc: $TBAUDIT"); + +# +# Do we have a keyfile? If so, rerun addpubkey for real now that the +# user is created and email is sent. +# +my $user_uid = $newuser->uid(); +my $user_idx = $newuser->uid_idx(); + +if (defined($keyfile)) { + # Set the implied user for addpubkey. + $ENV{'HTTP_INVOKING_USER'} = $user_idx; + my $result = `$addpubkey -u $user_uid -f $keyfile`; + chomp($result); + fatal("Could not parse public key: $result") + if ($?); + unlink($keyfile) + if (! $debug); + +} + +# The web interface requires this line to be printed! +print "User $user_uid/$user_idx has been created\n"; +exit(0); + +sub fatal($) { + my($mesg) = $_[0]; + + unlink($keyfile) + if (defined($keyfile) && !$debug); + + print STDERR "*** $0:\n". + " $mesg\n"; + exit(-1); +} +sub UserError($) { + my($mesg) = $_[0]; + + unlink($keyfile) + if (defined($keyfile) && !$debug); + + print $mesg; + exit(1); +} + +sub escapeshellarg($) +{ + my ($str) = @_; + + $str =~ s/(')/'\\''/g; + return $str; +} + + + diff --git a/account/tbacct.in b/account/tbacct.in index 535bb243db0fd23a6c8a0bd80848d65124197fd3..18867c07f939eb8d77d76aa166c8718a2befde96 100644 --- a/account/tbacct.in +++ b/account/tbacct.in @@ -24,7 +24,8 @@ use Getopt::Std; sub usage() { print("Usage: tbacct [-f] [-b] ". - "<add|del|mod|passwd|wpasswd|email|freeze|thaw> <user> [args]\n"); + "<add|del|mod|passwd|wpasswd|email|freeze|thaw|verify> ". + "<user> [args]\n"); exit(-1); } my $optlist = "fb"; @@ -135,6 +136,7 @@ sub UpdateWindowsPassword(); sub UpdateUser(;$); sub FreezeUser(); sub ThawUser(); +sub VerifyUser(); sub UpdateEmail(); sub CheckDotFiles(); sub GenerateSFSKey(); @@ -181,7 +183,7 @@ if ($user =~ /^([-\w]+)$/i) { else { die("Tainted argument: $user\n"); } -if ($cmd =~ /^(add|del|mod|freeze|passwd|wpasswd|thaw|email)$/) { +if ($cmd =~ /^(add|del|mod|freeze|passwd|wpasswd|thaw|email|verify)$/) { $cmd = $1; } else { @@ -211,7 +213,7 @@ if (getpwuid($UID) eq "nobody") { $this_user = $target_user; } else { - $this_user = User->LookupByUnixId($UID); + $this_user = User->ThisUser(); if (! defined($this_user)) { fatal("You ($UID) do not exist!"); @@ -302,6 +304,10 @@ SWITCH: for ($cmd) { ThawUser(); last SWITCH; }; + /^verify$/ && do { + VerifyUser(); + last SWITCH; + }; } # Always do this! @@ -867,6 +873,34 @@ sub ThawUser() return UpdateUser(0); } +# +# Verify a user. Converts status and sends email +# +sub VerifyUser() +{ + # + # Only admin people can do this unless its the user himself. + # + if (! $target_user->SameUser($this_user) && ! TBAdmin()) { + fatal("You do not have permission to verify user $user."); + } + + if ($target_user->status() ne USERSTATUS_NEWUSER) { + fatal("$user is not a newuser! Cannot verify the account!"); + } + + my $newstatus = ($target_user->wikionly() ? + USERSTATUS_ACTIVE() : USERSTATUS_UNAPPROVED()); + + $target_user->SetStatus($newstatus) == 0 or + fatal("Could not set user status to '$newstatus' for $target_user"); + + $target_user->SendVerifiedEmail() == 0 or + fatal("Could not send verified email for $target_user"); + + return 0; +} + # # Check dot files. We do this over and over ... # diff --git a/db/User.pm.in b/db/User.pm.in index 6db265932b59c2bdd73f706c0f2f8ab12a4070af..d2ee7bbc8aa280e9b9b703761c1c8002727a62fa 100644 --- a/db/User.pm.in +++ b/db/User.pm.in @@ -618,9 +618,9 @@ sub SetPassword($$$) } # -# User approved; find users groups and send email. +# User verified; find users groups and send email. # -sub SendApprovalEmail($) +sub SendVerifiedEmail($) { my ($self) = @_; diff --git a/sql/database-fill.sql b/sql/database-fill.sql index 779cbb59a191386a7203d1ca6c5502a5933746a4..b0bb8c726024d42716f49ef81d68c75f6dd3bb9e 100644 --- a/sql/database-fill.sql +++ b/sql/database-fill.sql @@ -573,7 +573,15 @@ REPLACE INTO table_regex VALUES ('nseconfigs','vname','text','redirect','virt_no REPLACE INTO table_regex VALUES ('nseconfigs','nseconfig','text','regex','^[\\040-\\176\\012\\011\\015]*$',0,16777215,NULL); REPLACE INTO table_regex VALUES ('os_info','osname','text','regex','^[-\\w\\.+]+$',2,20,NULL); REPLACE INTO table_regex VALUES ('projects','newpid','text','regex','^[a-zA-Z][-a-zA-Z0-9]+$',2,12,NULL); +REPLACE INTO table_regex VALUES ('projects','head_uid','text','redirect','users:uid',0,0,NULL); +REPLACE INTO table_regex VALUES ('projects','name','text','redirect','default:tinytext',0,256,NULL); +REPLACE INTO table_regex VALUES ('projects','funders','text','redirect','default:tinytext',0,256,NULL); +REPLACE INTO table_regex VALUES ('projects','public','int','redirect','default:tinyint',0,1,NULL); +REPLACE INTO table_regex VALUES ('projects','linked_to_us','int','redirect','default:tinyint',0,1,NULL); +REPLACE INTO table_regex VALUES ('projects','public_whynot','text','redirect','default:tinytext',0,256,NULL); +REPLACE INTO table_regex VALUES ('projects','default_user_interface','text','regex','^(emulab|plab)$',2,12,NULL); REPLACE INTO table_regex VALUES ('projects','pid','text','regex','^[-\\w]+$',2,12,NULL); +REPLACE INTO table_regex VALUES ('projects','URL','text','redirect','default:tinytext',0,0,NULL); REPLACE INTO table_regex VALUES ('reserved','vname','text','redirect','virt_nodes:vname',1,32,NULL); REPLACE INTO table_regex VALUES ('users','uid','text','regex','^[a-zA-Z][\\w]+$',2,8,NULL); REPLACE INTO table_regex VALUES ('users','usr_phone','text','regex','^[-\\d\\(\\)\\+\\.x ]+$',7,64,NULL); diff --git a/www/defs.php3.in b/www/defs.php3.in index 2eb167313d2141524070dbc27be3c039b2ee39ca..eaf2b6adef66b596763077ce7fb249bf3bed94fa 100644 --- a/www/defs.php3.in +++ b/www/defs.php3.in @@ -340,20 +340,16 @@ function SUEXEC($uid, $gid, $cmdandargs, $action) { return $suexec_retval; } -function ADDPUBKEY($uid, $cmdandargs) { +# +# We invoke addpubkey as user nobody all the time. The implied user is passed +# along in an HTTP_ variable (see tbauth). This avoids a bunch of confusion +# that results from new users who do not have a context yet. +# +function ADDPUBKEY($cmdandargs) { global $TBSUEXEC_PATH; - # - # Complication. User might not have an actual account if setting or - # changing his own pubkeys. webonly, unapproved, and unverified users - # can still muck with their personal info. So, just invoke as user - # nobody. We will get audit email in case we need to track what has - # happened. - # - if (!$uid || !HASREALACCOUNT($uid)) { - $uid = "nobody"; - } - return SUEXEC($uid, "nobody", $cmdandargs, 0); + return SUEXEC("nobody", "nobody", "webaddpubkey $cmdandargs", + SUEXEC_ACTION_CONTINUE); } # diff --git a/www/deletepubkey.php3 b/www/deletepubkey.php3 index d31ae28c24501c6541d9c4afbf2af597b79095b8..676d763b5e0c79bf146342d92ae6789679454541 100644 --- a/www/deletepubkey.php3 +++ b/www/deletepubkey.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2003, 2006 University of Utah and the Flux Group. +# Copyright (c) 2000-2003, 2006, 2007 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -139,7 +139,7 @@ DBQueryFatal("delete from user_pubkeys ". # will complain and die! # if (HASREALACCOUNT($target_uid)) { - ADDPUBKEY($uid, "webaddpubkey -w $target_uid"); + ADDPUBKEY("-w $target_uid"); } header("Location: " . CreateURL("showpubkeys", $target_user)); diff --git a/www/joinproject.php3 b/www/joinproject.php3 index 469ee4873200af94f0278e83c3e292d24121b1ad..e09dee65a0334858159fc6c82d7c099147dcc675 100644 --- a/www/joinproject.php3 +++ b/www/joinproject.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group. +# Copyright (c) 2000-2003, 2005, 2006, 2007 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -676,73 +676,6 @@ if (count($errors)) { return; } -# Okay, do pubkey checks. -if (!$returning && !$forwikionly) { - # - # Pub key provided in form (paste in). - # - if (isset($formfields[usr_key]) && - strcmp($formfields[usr_key], "")) { - # - # This is passed off to the shell, so taint check it. - # - if (! preg_match("/^[-\w\s\.\@\+\/\=]*$/", $formfields[usr_key])) { - $errors["PubKey"] = "Invalid characters"; - } - else { - # - # Replace any embedded newlines first. - # - $formfields[usr_key] = - ereg_replace("[\n]", "", $formfields[usr_key]); - $usr_key = $formfields[usr_key]; - - # - # Verify key format. - # - if (ADDPUBKEY(null, "webaddpubkey -n -k '$usr_key' ")) { - $errors["Pubkey Format"] = - "Could not be parsed. Is it a public key?"; - } - else { - $addpubkeyargs = "-k '$usr_key' "; - } - } - } - - # - # If usr provided a file for the key, it overrides the paste in text. - # - if (isset($_FILES['usr_keyfile']) && - $_FILES['usr_keyfile']['name'] != "" && - $_FILES['usr_keyfile']['name'] != "none") { - - $localfile = $_FILES['usr_keyfile']['tmp_name']; - - if (! stat($localfile)) { - $errors["PubKey File"] = "No such file"; - } - # Taint check shell arguments always! - elseif (! preg_match("/^[-\w\.\/]*$/", $localfile)) { - $errors["PubKey File"] = "Invalid characters"; - } - else { - chmod($localfile, 0644); - - # - # Verify key format. - # - if (ADDPUBKEY(null, "webaddpubkey -n $localfile ")) { - $errors["Pubkey Format"] = - "Could not be parsed. Is it a public key?"; - } - else { - $addpubkeyargs = "$localfile"; - } - } - } -} - # # Need the user, project and group objects for the rest of this. # @@ -769,84 +702,57 @@ if (count($errors)) { } # -# Create a new user. +# Create a new user. We do this by creating a little XML file to pass to +# the newuser script. # if (! $returning) { - # - # Certain of these values must be escaped or otherwise sanitized. - # - $joining_uid = ($USERSELECTUIDS ? $formfields[joining_uid] : null); - $usr_name = addslashes($formfields[usr_name]); - $usr_email = $formfields[usr_email]; - $password1 = $formfields[password1]; - $password2 = $formfields[password2]; - $wikiname = ($WIKISUPPORT ? $formfields[wikiname] : ""); - - if (!$forwikionly) { - $usr_affil = addslashes($formfields[usr_affil]); - $usr_title = addslashes($formfields[usr_title]); - $usr_addr = addslashes($formfields[usr_addr]); - $usr_city = addslashes($formfields[usr_city]); - $usr_state = addslashes($formfields[usr_state]); - $usr_zip = addslashes($formfields[usr_zip]); - $usr_country = addslashes($formfields[usr_country]); - $usr_phone = $formfields[usr_phone]; + $args = array(); + $args["name"] = $formfields[usr_name]; + $args["email"] = $formfields[usr_email]; + $args["address"] = $formfields[usr_addr]; + $args["address2"] = $formfields[usr_addr2]; + $args["city"] = $formfields[usr_city]; + $args["state"] = $formfields[usr_state]; + $args["zip"] = $formfields[usr_zip]; + $args["country"] = $formfields[usr_country]; + $args["phone"] = $formfields[usr_phone]; + $args["shell"] = 'tcsh'; + $args["title"] = $formfields[usr_title]; + $args["affiliation"] = $formfields[usr_affil]; + $args["password"] = $formfields[password1]; + $args["wikiname"] = ($WIKISUPPORT ? $formfields[wikiname] : ""); + + if (isset($formfields[usr_URL]) && + $formfields[usr_URL] != $HTTPTAG && $formfields[usr_URL] != "") { + $args["URL"] = $formfields[usr_URL]; } - else { - $usr_affil = ""; - $usr_title = ""; - $usr_addr = ""; - $usr_city = ""; - $usr_state = ""; - $usr_zip = ""; - $usr_country = ""; - $usr_phone = ""; + if ($USERSELECTUIDS) { + $args["login"] = $formfields[joining_uid]; } - if (! isset($formfields[usr_URL]) || - strcmp($formfields[usr_URL], "") == 0 || - strcmp($formfields[usr_URL], $HTTPTAG) == 0) { - $usr_URL = ""; - } - else { - $usr_URL = addslashes($formfields[usr_URL]); - } + # Backend verifies pubkey and returns error. + if (!$forwikionly) { + if (isset($_FILES['usr_keyfile']) && + $_FILES['usr_keyfile']['name'] != "" && + $_FILES['usr_keyfile']['name'] != "none") { - if (! isset($formfields[usr_addr2])) { - $usr_addr2 = ""; - } - else { - $usr_addr2 = addslashes($formfields[usr_addr2]); + $localfile = $_FILES['usr_keyfile']['tmp_name']; + $args["pubkey"] = file_get_contents($localfile); + } + elseif (isset($formfields['usr_key']) && + $formfields['usr_key'] != "") { + $args["pubkey"] = $formfields[usr_key]; + } } - - $args = array(); - $args["usr_expires"] = date("Y:m:d", time() + (86400 * 120)); - $args["usr_name"] = $usr_name; - $args["usr_email"] = $usr_email; - $args["usr_addr"] = $usr_addr; - $args["usr_addr2"] = $usr_addr2; - $args["usr_city"] = $usr_city; - $args["usr_state"] = $usr_state; - $args["usr_zip"] = $usr_zip; - $args["usr_country"] = $usr_country; - $args["usr_URL"] = $usr_URL; - $args["usr_phone"] = $usr_phone; - $args["usr_shell"] = 'tcsh'; - $args["usr_title"] = $usr_title; - $args["usr_affil"] = $usr_affil; - $args["usr_pswd"] = crypt("$password1"); - $args["wikiname"] = $wikiname; - - if (! ($user = User::NewUser($joining_uid, - ($forwikionly ? TBDB_NEWACCOUNT_WIKIONLY : 0), - $args))) { - TBERROR("Could not create new user '$usr_email'!", 1); + if (! ($user = User::NewNewUser(($forwikionly ? TBDB_NEWACCOUNT_WIKIONLY : 0), + $args, + $error)) != 0) { + $errors["Error Creating User"] = $error; + SPITFORM($formfields, $returning, $errors); + PAGEFOOTER(); + return; } $joining_uid = $user->uid(); - - if (!$forwikionly && isset($addpubkeyargs)) { - ADDPUBKEY($joining_uid, "webaddpubkey -u $joining_uid $addpubkeyargs"); - } } # diff --git a/www/newproject.php3 b/www/newproject.php3 index 11605f10f11ecc3dcc09d429aae65f1578981d3b..8234fcf115d38a7c22adb215e83153dbca346836 100755 --- a/www/newproject.php3 +++ b/www/newproject.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group. +# Copyright (c) 2000-2003, 2005, 2006, 2007 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -869,74 +869,6 @@ if (count($errors)) { return; } -# Okay, do pubkey checks. -if (!$returning) { - # - # Pub Key. - # - if (isset($formfields[usr_key]) && - strcmp($formfields[usr_key], "")) { - # - # This is passed off to the shell, so taint check it. - # - if (! preg_match("/^[-\w\s\.\@\+\/\=]*$/", $formfields[usr_key])) { - $errors["PubKey"] = "Invalid characters"; - } - else { - # - # Replace any embedded newlines first. - # - $formfields[usr_key] = - ereg_replace("[\n]", "", $formfields[usr_key]); - $usr_key = $formfields[usr_key]; - - # - # Verify key format. - # - if (ADDPUBKEY(null, "webaddpubkey -n -k '$usr_key' ")) { - $errors["Pubkey Format"] = - "Could not be parsed. Is it a public key?"; - } - else { - $addpubkeyargs = "-k '$usr_key' "; - } - } - } - - # - # If usr provided a file for the key, it overrides the paste in text. - # - if (isset($_FILES['usr_keyfile']) && - $_FILES['usr_keyfile']['name'] != "" && - $_FILES['usr_keyfile']['name'] != "none") { - - $localfile = $_FILES['usr_keyfile']['tmp_name']; - - if (! stat($localfile)) { - $errors["PubKey File"] = "No such file"; - } - # Taint check shell arguments always! - elseif (! preg_match("/^[-\w\.\/]*$/", $localfile)) { - $errors["PubKey File"] = "Invalid characters"; - } - else { - chmod($localfile, 0644); - - # - # Verify key format. - # - if (ADDPUBKEY(null, "webaddpubkey -n $localfile ")) { - $errors["Pubkey Format"] = - "Could not be parsed. Is it a public key?"; - } - else { - $addpubkeyargs = "$localfile"; - } - - } - } -} - # Done with sanity checks! if (count($errors)) { SPITFORM($formfields, $returning, $errors); @@ -948,235 +880,105 @@ if (count($errors)) { # Certain of these values must be escaped or otherwise sanitized. # if (!$returning) { - $proj_head_uid = (($USERSELECTUIDS || - $FirstInitState == "createproject") ? - $formfields[proj_head_uid] : null); - $usr_title = addslashes($formfields[usr_title]); - $usr_name = addslashes($formfields[usr_name]); - $usr_affil = addslashes($formfields[usr_affil]); - $usr_email = $formfields[usr_email]; - $usr_addr = addslashes($formfields[usr_addr]); - $usr_city = addslashes($formfields[usr_city]); - $usr_state = addslashes($formfields[usr_state]); - $usr_zip = addslashes($formfields[usr_zip]); - $usr_country = addslashes($formfields[usr_country]); - $usr_phone = $formfields[usr_phone]; - $password1 = $formfields[password1]; - $password2 = $formfields[password2]; - $wikiname = ($WIKISUPPORT ? $formfields[wikiname] : ""); - $usr_returning = "No"; - - if (! isset($formfields[usr_URL]) || - strcmp($formfields[usr_URL], "") == 0 || - strcmp($formfields[usr_URL], $HTTPTAG) == 0) { - $usr_URL = ""; + $args = array(); + $args["name"] = $formfields[usr_name]; + $args["email"] = $formfields[usr_email]; + $args["address"] = $formfields[usr_addr]; + $args["address2"] = $formfields[usr_addr2]; + $args["city"] = $formfields[usr_city]; + $args["state"] = $formfields[usr_state]; + $args["zip"] = $formfields[usr_zip]; + $args["country"] = $formfields[usr_country]; + $args["phone"] = $formfields[usr_phone]; + $args["shell"] = 'tcsh'; + $args["title"] = $formfields[usr_title]; + $args["affiliation"] = $formfields[usr_affil]; + $args["password"] = $formfields[password1]; + $args["wikiname"] = ($WIKISUPPORT ? $formfields[wikiname] : ""); + + if (isset($formfields[usr_URL]) && + $formfields[usr_URL] != $HTTPTAG && $formfields[usr_URL] != "") { + $args["URL"] = $formfields[usr_URL]; } - else { - $usr_URL = addslashes($formfields[usr_URL]); + if ($USERSELECTUIDS || $FirstInitState == "createproject") { + $args["login"] = $formfields[proj_head_uid]; } - - if (! isset($formfields[usr_addr2])) { - $usr_addr2 = ""; + + # Backend verifies pubkey and returns error. + if (isset($_FILES['usr_keyfile']) && + $_FILES['usr_keyfile']['name'] != "" && + $_FILES['usr_keyfile']['name'] != "none") { + + $localfile = $_FILES['usr_keyfile']['tmp_name']; + $args["pubkey"] = file_get_contents($localfile); } - else { - $usr_addr2 = addslashes($formfields[usr_addr2]); + elseif (isset($formfields['usr_key']) && + $formfields['usr_key'] != "") { + $args["pubkey"] = $formfields[usr_key]; } - $args = array(); - $args["usr_expires"] = $proj_expires; - $args["usr_name"] = $usr_name; - $args["usr_email"] = $usr_email; - $args["usr_addr"] = $usr_addr; - $args["usr_addr2"] = $usr_addr2; - $args["usr_city"] = $usr_city; - $args["usr_state"] = $usr_state; - $args["usr_zip"] = $usr_zip; - $args["usr_country"] = $usr_country; - $args["usr_URL"] = $usr_URL; - $args["usr_phone"] = $usr_phone; - $args["usr_shell"] = 'tcsh'; - $args["usr_title"] = $usr_title; - $args["usr_affil"] = $usr_affil; - $args["usr_pswd"] = crypt("$password1"); - $args["wikiname"] = $wikiname; - - if (! ($leader = User::NewUser($proj_head_uid, - TBDB_NEWACCOUNT_PROJLEADER, $args))) { - TBERROR("Could not create new user '$usr_email'!", 1); + if (! ($leader = User::NewNewUser(TBDB_NEWACCOUNT_PROJLEADER, + $args, + $error)) != 0) { + $errors["Error Creating User"] = $error; + SPITFORM($formfields, $returning, $errors); + PAGEFOOTER(); + return; } # If null; used below $proj_head_uid = $leader->uid(); - - if (isset($addpubkeyargs)) { - ADDPUBKEY($proj_head_uid, - "webaddpubkey -u $proj_head_uid $addpubkeyargs"); - } } else { $leader = $this_user; - $usr_title = $leader->title(); - $usr_name = $leader->name(); - $usr_affil = $leader->affil(); - $usr_email = $leader->email(); - $usr_addr = $leader->addr(); - $usr_addr2 = $leader->addr2(); - $usr_city = $leader->city(); - $usr_state = $leader->state(); - $usr_zip = $leader->zip(); - $usr_country = $leader->country(); - $usr_phone = $leader->phone(); - $usr_URL = $leader->URL(); - $wikiname = $leader->wikiname(); - $usr_returning = "Yes"; } -# And the project details. -$pid = $formfields[pid]; -$proj_name = addslashes($formfields[proj_name]); -$proj_URL = addslashes($formfields[proj_URL]); -$proj_funders = addslashes($formfields[proj_funders]); -$proj_whynotpublic = addslashes($formfields[proj_whynotpublic]); -$proj_members = $formfields[proj_members]; -$proj_pcs = $formfields[proj_pcs]; -$proj_why = addslashes($formfields[proj_why]); -$proj_expires = date("Y:m:d", time() + (86400 * 120)); +# +# Now for the new Project +# +$args = array(); +$args["name"] = $formfields["pid"]; +$args["short description"] = $formfields["proj_name"]; +$args["URL"] = $formfields["proj_URL"]; +$args["members"] = $formfields["proj_members"]; +$args["num_pcs"] = $formfields["proj_pcs"]; +$args["long description"] = $formfields["proj_why"]; +$args["funders"] = $formfields["proj_funders"]; +$args["whynotpublic"] = $formfields["proj_whynotpublic"]; if (!isset($formfields[proj_public]) || - strcmp($formfields[proj_public], "checked")) { - $proj_public = "No"; - $public = 0; + $formfields[proj_public] != "checked") { + $args["public"] = 0; } else { - $proj_public = "Yes"; - $public = 1; + $args["public"] = 1; } if (!isset($formfields[proj_linked]) || - strcmp($formfields[proj_linked], "checked")) { - $proj_linked = "No"; - $linked = 0; + $formfields[proj_linked] != "checked") { + $args["linkedtous"] = 0; } else { - $proj_linked = "Yes"; - $linked = 1; + $args["linkedtous"] = 1; } if (isset($formfields[proj_plabpcs]) && $formfields[proj_plabpcs] == "checked") { - $proj_plabpcs = "Yes"; - $plabpcs = 1; -} -else { - $proj_plabpcs = "No"; - $plabpcs = 0; + $args["plab"] = 1; } if (isset($formfields[proj_ronpcs]) && $formfields[proj_ronpcs] == "checked") { - $proj_ronpcs = "Yes"; - $ronpcs = 1; -} -else { - $proj_ronpcs = "No"; - $ronpcs = 0; + $args["ron"] = 1; } - -# -# Now for the new Project -# -$args = array(); -$args["expires"] = $proj_expires; -$args["name"] = $proj_name; -$args["URL"] = $proj_URL; -$args["num_members"] = $proj_members; -$args["num_pcs"] = $proj_pcs; -$args["why"] = $proj_why; -$args["funders"] = $proj_funders; -$args["num_pcplab"] = $plabpcs; -$args["num_ron"] = $ronpcs; -$args["public"] = $public; -$args["public_whynot"] = $proj_whynotpublic; -$args["linked_to_us"] = $linked; - -if (! ($project = Project::NewProject($pid, $leader, $args))) { - TBERROR("Could not create new project '$pid'!", 1); +if (! ($project = Project::NewNewProject($leader, $args, $error))) { + $errors["Error Creating Project"] = $error; + SPITFORM($formfields, $returning, $errors); + PAGEFOOTER(); + return; } # -# If a new user, do not send the full blown message until verified. -# -if ($returning || $FirstInitState) { - $unix_gid = $project->unix_gid(); - $unix_name = $project->unix_name(); - - # - # The mail message to the approval list. - # - TBMAIL($TBMAIL_APPROVAL, - "New Project '$pid' ($proj_head_uid)", - "'$usr_name' wants to start project '$pid'.\n". - "\n". - "Name: $usr_name ($proj_head_uid)\n". - "Returning User?: $usr_returning\n". - "Email: $usr_email\n". - "User URL: $usr_URL\n". - "Project: $proj_name\n". - "Expires: $proj_expires\n". - "Project URL: $proj_URL\n". - "Public URL: $proj_public\n". - "Why Not Public: $proj_whynotpublic\n". - "Link to Us?: $proj_linked\n". - "Funders: $proj_funders\n". - "Job Title: $usr_title\n". - "Affiliation: $usr_affil\n". - "Address 1: $usr_addr\n". - "Address 2: $usr_addr2\n". - "City: $usr_city\n". - "State: $usr_state\n". - "ZIP/Postal Code: $usr_zip\n". - "Country: $usr_country\n". - "Phone: $usr_phone\n". - "Members: $proj_members\n". - "PCs: $proj_pcs\n". - "Planetlab PCs: $proj_plabpcs\n". - "RON PCs: $proj_ronpcs\n". - "Unix GID: $unix_name ($unix_gid)\n". - "Reasons:\n$proj_why\n\n". - "Please review the application and when you have made a \n". - "decision, go to $TBWWW and\n". - "select the 'Project Approval' page.\n\n". - "They are expecting a result within 72 hours.\n", - "From: $usr_name '$proj_head_uid' <$usr_email>\n". - "Reply-To: $TBMAIL_APPROVAL\n". - "Errors-To: $TBMAIL_WWW"); -} -else { - TBMAIL($TBMAIL_APPROVAL, - "New Project '$pid' ($proj_head_uid)", - "'$usr_name' wants to start project '$pid'.\n". - "\n". - "Name: $usr_name ($proj_head_uid)\n". - "Email: $usr_email\n". - "Returning User?: No\n". - "\n". - "No action is necessary until the user has verified the account.\n", - "From: $usr_name '$proj_head_uid' <$usr_email>\n". - "Reply-To: $TBMAIL_APPROVAL\n". - "Errors-To: $TBMAIL_WWW"); -} - +# Need to do some extra work for the first project; eventually move to backend +# if ($FirstInitState) { - # - # The first user gets admin status and some extra groups, etc. - # - DBQueryFatal("update users set ". - " admin=1,status='". TBDB_USERSTATUS_UNAPPROVED . "' " . - "where uid='$proj_head_uid'"); - - DBQueryFatal("insert into unixgroup_membership set ". - "uid='$proj_head_uid', gid='wheel'"); - - DBQueryFatal("insert into unixgroup_membership set ". - "uid='$proj_head_uid', gid='$TBADMINGROUP'"); - Group::Initialize($proj_head_uid, $pid); # diff --git a/www/project_defs.php b/www/project_defs.php index d84ccd0225a60e6e622995257713c4aebb50a6a0..f259d31c16d1ebd60fe9f991e68af7d376851a07 100644 --- a/www/project_defs.php +++ b/www/project_defs.php @@ -207,6 +207,76 @@ class Project return $newproject; } + function NewNewProject($leader, $args, &$error) { + global $suexec_output, $suexec_output_array; + + # + # Generate a temporary file and write in the XML goo. + # + $xmlname = tempnam("/tmp", "newproj"); + if (! $xmlname) { + TBERROR("Could not create temporary filename", 0); + $error = "Transient error; please try again later."; + return null; + } + if (! ($fp = fopen($xmlname, "w"))) { + TBERROR("Could not open temp file $xmlname", 0); + $error = "Transient error; please try again later."; + return null; + } + + # Need to say who is going to be leading this project. + $args["leader"] = $leader->uid(); + + fwrite($fp, "<project>\n"); + foreach ($args as $name => $value) { + fwrite($fp, "<attribute name=\"$name\">"); + fwrite($fp, " <value>" . htmlspecialchars($value) . "</value>"); + fwrite($fp, "</attribute>\n"); + } + fwrite($fp, "</project>\n"); + fclose($fp); + chmod($xmlname, 0666); + + $retval = SUEXEC("nobody", "nobody", "webnewproj $xmlname", + SUEXEC_ACTION_IGNORE); + + if ($retval) { + if ($retval < 0) { + $error = "Transient error; please try again later."; + SUEXECERROR(SUEXEC_ACTION_CONTINUE); + } + else { + $error = $suexec_output; + } + return null; + } + + # + # Parse the last line of output. Ick. + # + unset($matches); + + if (!preg_match("/^User\s+(\w+)\/(\d+)\s+/", + $suexec_output_array[count($suexec_output_array)-1], + $matches)) { + $error = "Transient error; please try again later."; + SUEXECERROR(SUEXEC_ACTION_CONTINUE); + return null; + } + $pid_idx = $matches[2]; + $newproj = Project::Lookup($pid_idx); + if (! $newproj) { + $error = "Transient error; please try again later."; + TBERROR("Could not lookup new project $pid_idx", 0); + return null; + } + # Unlink this here, so that the file is left behind in case of error. + # We can then create the project by hand from the xmlfile, if desired. + unlink($xmlname); + return $newproj; + } + # # Access Check, which for now uses the global function to avoid duplication # until all code is changed. diff --git a/www/showpubkeys.php3 b/www/showpubkeys.php3 index 51cc369b741212b00e84f0bd287b7b56a594fabf..5cd9257342ba0216ea7dc950204ca8c7addb0e9b 100644 --- a/www/showpubkeys.php3 +++ b/www/showpubkeys.php3 @@ -1,7 +1,7 @@ <?php # # EMULAB-COPYRIGHT -# Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group. +# Copyright (c) 2000-2003, 2005, 2006, 2007 University of Utah and the Flux Group. # All rights reserved. # include("defs.php3"); @@ -324,7 +324,7 @@ if (count($errors)) { # Okay, first run the script in verify mode to see if the key is # parsable. If it is, then do it for real. # -if (ADDPUBKEY($uid, "webaddpubkey -n -u $target_uid $addpubkeyargs")) { +if (ADDPUBKEY("-n -u $target_uid $addpubkeyargs")) { $errors["Pubkey Format"] = "Could not be parsed. Is it a public key?"; SPITFORM($formfields, $errors); PAGEFOOTER(); @@ -333,7 +333,7 @@ if (ADDPUBKEY($uid, "webaddpubkey -n -u $target_uid $addpubkeyargs")) { # # Insert key, update authkeys files and nodes if appropriate. # -ADDPUBKEY($uid, "webaddpubkey -u $target_uid $addpubkeyargs"); +ADDPUBKEY("-u $target_uid $addpubkeyargs"); # # Redirect back, avoiding a POST in the history. diff --git a/www/user_defs.php b/www/user_defs.php index 5ede5dea96337a745bdc1d8ea11dd37a4c393919..4c3354e2be302dd29f5d5cf2c3ebafa507311f77 100644 --- a/www/user_defs.php +++ b/www/user_defs.php @@ -427,6 +427,81 @@ class User return $newuser; } + function NewNewUser($flags, $args, &$error) { + global $suexec_output, $suexec_output_array; + $typearg = ""; + + if ($flags & TBDB_NEWACCOUNT_PROJLEADER) + $typearg = "-t leader"; + elseif ($flags & TBDB_NEWACCOUNT_WIKIONLY) + $typearg = "-t wikionly"; + elseif ($flags & TBDB_NEWACCOUNT_WEBONLY) + $typearg = "-t webonly"; + + # + # Generate a temporary file and write in the XML goo. + # + $xmlname = tempnam("/tmp", "newuser"); + if (! $xmlname) { + TBERROR("Could not create temporary filename", 0); + $error = "Transient error; please try again later."; + return null; + } + if (! ($fp = fopen($xmlname, "w"))) { + TBERROR("Could not open temp file $xmlname", 0); + $error = "Transient error; please try again later."; + return null; + } + + fwrite($fp, "<user>\n"); + foreach ($args as $name => $value) { + fwrite($fp, "<attribute name=\"$name\">"); + fwrite($fp, " <value>" . htmlspecialchars($value) . "</value>"); + fwrite($fp, "</attribute>\n"); + } + fwrite($fp, "</user>\n"); + fclose($fp); + chmod($xmlname, 0666); + + $retval = SUEXEC("nobody", "nobody", "webnewuser $typearg $xmlname", + SUEXEC_ACTION_IGNORE); + + if ($retval) { + if ($retval < 0) { + $error = "Transient error; please try again later."; + SUEXECERROR(SUEXEC_ACTION_CONTINUE); + } + else { + $error = $suexec_output; + } + return null; + } + + # + # Parse the last line of output. Ick. + # + unset($matches); + + if (!preg_match("/^User\s+(\w+)\/(\d+)\s+/", + $suexec_output_array[count($suexec_output_array)-1], + $matches)) { + $error = "Transient error; please try again later."; + SUEXECERROR(SUEXEC_ACTION_CONTINUE); + return null; + } + $uid_idx = $matches[2]; + $newuser = User::Lookup($uid_idx); + if (! $newuser) { + $error = "Transient error; please try again later."; + TBERROR("Could not lookup new user $uid_idx", 0); + return null; + } + # Unlink this here, so that the file is left behind in case of error. + # We can then create the user by hand from the xmlfile, if desired. + unlink($xmlname); + return $newuser; + } + # # Delete a user, but JUST from the users table. # diff --git a/www/verifyusr.php3 b/www/verifyusr.php3 index 134e01327b754f6891b099ff35bcc438cc7b4162..156647d6ea0555aefba7f769f6dd4a167e20d896 100644 --- a/www/verifyusr.php3 +++ b/www/verifyusr.php3 @@ -49,185 +49,19 @@ if ($key != $this_user->verify_key()) { "Please enter the correct key.", 1); } -function INFORMLEADERS($this_user) { - global $TBWWW, $TBMAIL_APPROVAL, $TBMAIL_AUDIT, $TBMAIL_WWW; +if ($status == TBDB_USERSTATUS_NEWUSER) { + STARTBUSY("Verifying $uid"); - # - # Grab user info. - # - $usr_email = $this_user->email(); - $usr_URL = $this_user->URL(); - $usr_addr = $this_user->addr(); - $usr_addr2 = $this_user->addr2(); - $usr_city = $this_user->city(); - $usr_state = $this_user->state(); - $usr_zip = $this_user->zip(); - $usr_country = $this_user->country(); - $usr_name = $this_user->name(); - $usr_phone = $this_user->phone(); - $usr_title = $this_user->title(); - $usr_affil = $this_user->affil(); - $uid_idx = $this_user->uid_idx(); - $uid = $this_user->uid(); - - # - # Get the list of all project/groups this users has tried to join - # but whose membership messages where delayed until the user verified - # himself. - # - $group_result = - DBQueryFatal("select gid_idx from group_membership ". - "where uid_idx='$uid_idx' and trust='none'"); - - while ($row = mysql_fetch_array($group_result)) { - $gid_idx = $row["gid_idx"]; - - if (! ($group = Group::Lookup($gid_idx))) { - TBERROR("Could not find group record for $gid_idx", 1); - } - $project = $group->Project(); - $projleader = $project->GetLeader(); - $groupleader = $group->GetLeader(); - $pid = $project->pid(); - $gid = $group->gid(); - - if ($this_user->SameUser($projleader)) { - # - # Project leader verifying himself. - # - $proj_name = $project->name(); - $proj_URL = $project->URL(); - $proj_funders = $project->funders(); - $proj_public = ($project->public() ? "Yes" : "No"); - $proj_linked = ($project->linked_to_us() ? "Yes" : "No"); - $proj_whynotpublic = $project->public_whynot(); - $proj_members = $project->num_members(); - $proj_pcs = $project->num_pcs(); - $proj_plabpcs = $project->num_pcplab(); - $proj_ronpcs = $project->num_ron(); - $proj_why = $project->why(); - $unix_gid = $group->unix_gid(); - $unix_name = $group->unix_name(); - - TBMAIL($TBMAIL_APPROVAL, - "New Project '$pid' ($uid)", - "'$usr_name' wants to start project '$pid'.\n". - "\n". - "Name: $usr_name ($uid)\n". - "Returning User?: No\n". - "Email: $usr_email\n". - "User URL: $usr_URL\n". - "Project: $proj_name\n". - "Project URL: $proj_URL\n". - "Public URL: $proj_public\n". - "Why Not Public: $proj_whynotpublic\n". - "Linked to Us?: $proj_linked\n". - "Funders: $proj_funders\n". - "Job Title: $usr_title\n". - "Affiliation: $usr_affil\n". - "Address 1: $usr_addr\n". - "Address 2: $usr_addr2\n". - "City: $usr_city\n". - "State: $usr_state\n". - "ZIP/Postal Code: $usr_zip\n". - "Country: $usr_country\n". - "Phone: $usr_phone\n". - "Members: $proj_members\n". - "PCs: $proj_pcs\n". - "Planetlab PCs: $proj_plabpcs\n". - "RON PCs: $proj_ronpcs\n". - "Unix GID: $unix_name ($unix_gid)\n". - "Reasons:\n$proj_why\n\n". - "Please review the application and when you have made \n". - "a decision, go to $TBWWW and\n". - "select the 'Project Approval' page.\n\n". - "They are expecting a result within 72 hours.\n", - "From: $usr_name '$uid' <$usr_email>\n". - "Reply-To: $TBMAIL_APPROVAL\n". - "Errors-To: $TBMAIL_WWW"); - } - else { - $leader_name = $groupleader->name(); - $leader_email = $groupleader->email(); - $leader_uid = $groupleader->uid(); - $allleaders = $group->LeaderMailList(); - - TBMAIL("$leader_name '$leader_uid' <$leader_email>", - "$uid $pid Project Join Request", - "$usr_name is trying to join your group $gid in project $pid.". - "\n". - "\n". - "Contact Info:\n". - "Name: $usr_name\n". - "Emulab ID: $uid\n". - "Email: $usr_email\n". - "User URL: $usr_URL\n". - "Job Title: $usr_title\n". - "Affiliation: $usr_affil\n". - "Address 1: $usr_addr\n". - "Address 2: $usr_addr2\n". - "City: $usr_city\n". - "State: $usr_state\n". - "ZIP/Postal Code: $usr_zip\n". - "Country: $usr_country\n". - "Phone: $usr_phone\n". - "\n". - "Please return to $TBWWW,\n". - "log in, select the 'New User Approval' page, and enter\n". - "your decision regarding $usr_name's membership in your\n". - "project.\n\n". - "Thanks,\n". - "Testbed Operations\n", - "From: $TBMAIL_APPROVAL\n". - "Cc: $allleaders\n". - "Bcc: $TBMAIL_AUDIT\n". - "Errors-To: $TBMAIL_WWW"); - } - } -} - -if (strcmp($status, TBDB_USERSTATUS_UNVERIFIED) == 0) { - $this_user->SetStatus(TBDB_USERSTATUS_ACTIVE); + SUEXEC("nobody", "nobody", "webtbacct verify $uid", SUEXEC_ACTION_DIE); - TBMAIL($TBMAIL_AUDIT, - "User '$uid' has been verified", - "\n". - "User '$uid' has been verified.\n". - "Status has been changed from 'unverified' to 'active'\n". - "\n". - "Testbed Operations\n", - "From: $TBMAIL_OPS\n". - "Errors-To: $TBMAIL_WWW"); - - echo "<p>". - "Because your membership has already been approved, ". - "you are now an active user of emulab. ". - "Click on the 'Home' link at your left, and any options ". - "that are now available to you will appear.\n"; -} -elseif (strcmp($status, TBDB_USERSTATUS_NEWUSER) == 0) { - $newstatus = ($wikionly ? - TBDB_USERSTATUS_ACTIVE : TBDB_USERSTATUS_UNAPPROVED); - - $this_user->SetStatus($newstatus); - - TBMAIL($TBMAIL_AUDIT, - "User '$uid' has been verified", - "\n". - "User '$uid' has been verified.\n". - "Status has been changed from 'newuser' to '$newstatus'\n". - "\n". - "Testbed Operations\n", - "From: $TBMAIL_OPS\n". - "Errors-To: $TBMAIL_WWW"); - if ($wikionly) { # # For wikionly accounts, build the account now. # Just builds the wiki account of course (nothing else). # - SUEXEC("nobody", $TBADMINGROUP, "webtbacct add $uid", - SUEXEC_ACTION_DIE); + SUEXEC("nobody", "nobody", "webtbacct add $uid", SUEXEC_ACTION_DIE); + + STOPBUSY(); # # The backend sets the actual WikiName @@ -235,13 +69,14 @@ elseif (strcmp($status, TBDB_USERSTATUS_NEWUSER) == 0) { $this_user->Refresh(); $wikiname = $this_user->wikiname(); - echo "You have been verified. You may now access the Wiki at<br>". + echo "<br>". + "You have been verified. You may now access the Wiki at<br>". "<a href='$WIKIURL/$wikiname'>$WIKIURL/$wikiname</a>\n"; } else { - INFORMLEADERS($this_user); + STOPBUSY(); - echo "<p>". + echo "<br>". "You have now been verified. However, your application ". "has not yet been approved. You will receive ". "email when that has been done.\n"; diff --git a/www/widearea_register.php b/www/widearea_register.php index 81d23a88b506ad18f1c251fceb541a89749ccf03..151e364c72e47ed8c90cca45f21b5a707d507f17 100644 --- a/www/widearea_register.php +++ b/www/widearea_register.php @@ -717,7 +717,7 @@ if (!$returning) { # # Verify key format. # - if (ADDPUBKEY(null, "webaddpubkey -n -k '$usr_key' ")) { + if (ADDPUBKEY("-n -k '$usr_key' ")) { $errors["Pubkey Format"] = "Could not be parsed. Is it a public key?"; } @@ -749,7 +749,7 @@ if (!$returning) { # # Verify key format. # - if (ADDPUBKEY(null, "webaddpubkey -n $localfile ")) { + if (ADDPUBKEY("-n $localfile ")) { $errors["Pubkey Format"] = "Could not be parsed. Is it a public key?"; } @@ -867,7 +867,7 @@ if (!$returning) { $usr_dbid = $user->dbid(); if (isset($addpubkeyargs)) { - ADDPUBKEY($usr_uid, "webaddpubkey -u $usr_uid $addpubkeyargs"); + ADDPUBKEY("-u $usr_uid $addpubkeyargs"); } } else {