diff --git a/backend/GNUmakefile.in b/backend/GNUmakefile.in index 3697ae7c36d1f9724281333218b7140ef8f4ab56..4aac3824ddea13b6ca0ce72ebe436abfbefe3750 100644 --- a/backend/GNUmakefile.in +++ b/backend/GNUmakefile.in @@ -12,8 +12,8 @@ UNIFIED = @UNIFIED_BOSS_AND_OPS@ include $(OBJDIR)/Makeconf -BIN_SCRIPTS = moduserinfo -WEB_BIN_SCRIPTS = webmoduserinfo +BIN_SCRIPTS = moduserinfo newgroup +WEB_BIN_SCRIPTS = webmoduserinfo webnewgroup WEB_SBIN_SCRIPTS= LIBEXEC_SCRIPTS = $(WEB_BIN_SCRIPTS) $(WEB_SBIN_SCRIPTS) diff --git a/backend/newgroup.in b/backend/newgroup.in new file mode 100644 index 0000000000000000000000000000000000000000..0668c3da0856c16ec189778cd90ead390ce820da --- /dev/null +++ b/backend/newgroup.in @@ -0,0 +1,375 @@ +#!/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; + +# +# Back-end script to create a Project Group. +# +sub usage() +{ + print("Usage: newgroup [-v] <xmlfile>\n"); + exit(-1); +} +my $optlist = "dv"; +my $debug = 0; +my $verify = 0; # Check data and return status only. + +# +# Configure variables +# +my $TB = "@prefix@"; +my $TBOPS = "@TBOPSEMAIL@"; +my $TBAUDIT = "@TBAUDITEMAIL@"; +my $MKGROUP = "$TB/sbin/mkgroup"; +my $MODGROUPS = "$TB/sbin/modgroups"; + +# +# 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; +use Project; +use Group; + +# Protos +sub fatal($); +sub UserError(;$); +sub escapeshellarg($); + +# +# 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{"v"})) { + $verify = 1; +} +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 the +# current user context is "implied" (see tbauth.php3). +# +my $this_user; + +if (getpwuid($UID) ne "nobody") { + $this_user = User->ThisUser(); + + if (! defined($this_user)) { + fatal("You ($UID) do not exist!"); + } + # You don't need admin privileges to create a Project Group. +} +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"); + } + + # The web interface (and in the future the xmlrpc interface) sets this. + $this_user = User->ImpliedUser(); + + if (! defined($this_user)) { + fatal("Cannot determine implied user!"); + } +} + +# +# These are the fields that we allow to come in from the XMLfile. +# +my $SLOT_OPTIONAL = 0x1; # The field is not required. +my $SLOT_REQUIRED = 0x2; # The field is required and must be non-null. +my $SLOT_ADMINONLY = 0x4; # Only admins can set this field. +# +# XXX We should encode all of this in the DB so that we can generate the +# forms on the fly, as well as this checking code. +# +my %xmlfields = + # XML Field Name DB slot name Flags Default + ("project" => ["project", $SLOT_REQUIRED], + "group_id" => ["group_id", $SLOT_REQUIRED], + "group_leader" => ["group_leader", $SLOT_REQUIRED], + "group_description"=> ["group_description",$SLOT_OPTIONAL, ""]); + +# +# Must wrap the parser in eval since it exits on error. +# +my $xmlparse = eval { XMLin($xmlfile, + VarAttr => 'name', + ContentKey => '-content', + SuppressEmpty => undef); }; +fatal($@) + if ($@); + +# +# Process and dump the errors (formatted for the web interface). +# We should probably XML format the errors instead but not sure I want +# to go there yet. +# +my %errors = (); + +# +# Make sure all the required arguments were provided. +# +foreach my $key (keys(%xmlfields)) { + my (undef, $required, undef) = @{$xmlfields{$key}}; + + $errors{$key} = "Required value not provided" + if ($required & $SLOT_REQUIRED && + ! exists($xmlparse->{'attribute'}->{"$key"})); +} +UserError() + if (keys(%errors)); + +# +# We build up an array of arguments to pass to Group->Create() as we check +# the attributes. +# +my %newgroup_args = (); + +foreach my $key (keys(%{ $xmlparse->{'attribute'} })) { + my $value = $xmlparse->{'attribute'}->{"$key"}->{'value'}; + + if ($debug) { + print STDERR "User attribute: '$key' -> '$value'\n"; + } + + $errors{$key} = "Unknown attribute" + if (!exists($xmlfields{$key})); + + my ($dbslot, $required, $default) = @{$xmlfields{$key}}; + + if ($required & $SLOT_REQUIRED) { + # A slot that must be provided, so do not allow a null value. + if (!defined($value)) { + $errors{$key} = "Must provide a non-null value"; + next; + } + } + if ($required & $SLOT_OPTIONAL) { + # Optional slot. If value is null skip it. Might not be the correct + # thing to do all the time? + if (!defined($value)) { + next + if (!defined($default)); + $value = $default; + } + } + if ($required & $SLOT_ADMINONLY) { + # Admin implies optional, but thats probably not correct approach. + $errors{$key} = "Administrators only" + if (! $this_user->IsAdmin()); + } + + # Now check that the value is legal. + if (! TBcheck_dbslot($value, "groups", $dbslot, TBDB_CHECKDBSLOT_ERROR)) { + $errors{$key} = TBFieldErrorString(); + next; + } + + $newgroup_args{$dbslot} = $value; +} +UserError() + if (keys(%errors)); + +# +# Now do special checks. +# +my $project = Project->Lookup($newgroup_args{"project"}); +if (!defined($project)) { + UserError("Project: No such project"); +} +if (!$project->AccessCheck($this_user, TB_PROJECT_MAKEOSID())) { + UserError("Project: Not enough permission"); +} + +# Need these below +my $group_id = $newgroup_args{"group_id"}; +my $group_leader = $newgroup_args{"group_leader"}; +my $descr = $newgroup_args{"group_description"}; +my $group_pid = $project->pid(); + +# +# Certain of these values must be escaped or otherwise sanitized. +# +$descr = escapeshellarg($descr); + +# +# Verify permission. +# +if (!$project->AccessCheck($this_user, TB_PROJECT_MAKEGROUP())) { + UserError("Access: Not group_root in project $group_pid"); +} + +# Need the user object for creating the group. +my $leader = User->Lookup($group_leader); +if (! $leader) { + UserError("GroupLeader: User '$group_leader' is an unknown user"); +} + +# +# Verify leader. Any user can lead a group, but they must be a member of +# the project, and we have to avoid an ISADMIN() check in AccessCheck(). +# +my $proj_leader = $project->GetLeader(); +if (!$leader->SameUser($proj_leader) || + $leader->status() eq USERSTATUS_UNAPPROVED() || + !$project->AccessCheck($leader, TB_PROJECT_LEADGROUP())) { + UserError("GroupLeader: $group_leader does not have enough permission ". + "to lead a group in project $group_pid!"); +} + +# +# Make sure the GID is not already there. +# +my $oldgroup = Group->LookupByPidGid($group_pid, $group_id); +if ($oldgroup) { + UserError("GroupId: The group $group_id already exists! ". + "Please select another."); +} + +# +# The unix group name must be globally unique. Form a name and check it. +# Subgroup names have a project-name prefix, and a numeric suffix if needed. +# +my $unix_gname = substr($group_pid, 0, 3) . "-" . $group_id; +my $maxtries = 99; +my $count = 0; +my $TBDB_UNIXGLEN = 16; # XXX Where should this be? +while ($count < $maxtries) { + if (length($unix_gname) > $TBDB_UNIXGLEN) { + UserError("GroupId: Unix group name $unix_gname is too long!"); + } + + my $query_result = + DBQueryFatal("select gid from groups where unix_name='$unix_gname'"); + if (!$query_result->numrows) { + last; + } + + $count++; + $unix_gname = substr($group_pid, 0, 3) . "-" . + substr($group_id, 0, length($group_id) - 2) . "$count"; +} +if ($count == $maxtries) { + UserError("GroupId: Could not form a unique Unix group name!"); +} + +exit(0) + if ($verify); + +# +# Now safe to create a Project Group. +# + +# Put it in the DB. (This is used by Project->Create too.) +my $new_group = Group->Create($project, $group_id, + $leader, $descr, $unix_gname); +fatal("Could not create new Group!") + if (!defined($new_group)); + +my $group_idx = $new_group->gid_idx(); + +# +# Run the script to make the group directory, set the perms, etc. +# +my $cmd = "mkgroup $group_idx"; +##print $cmd; +my $cmd_out = `$cmd`; +UserError("Error: " . $cmd_out) + if ($?); + +# +# Now add the group leader to the group. +# +my $safe_id = escapeshellarg($group_id); +my $cmd = "webmodgroups -a $group_pid:$safe_id:group_root $group_leader", +##print $cmd; +$cmd_out = `$cmd`; +UserError("Error: " . $cmd_out) + if ($?); + +# The web interface requires this line to be printed. +print "GROUP $group_id/$group_idx has been created\n"; +exit(0); + +sub fatal($) +{ + my ($mesg) = @_; + + print STDERR "*** $0:\n". + " $mesg\n"; + # Exit with negative status so web interface treats it as system error. + exit(-1); +} + +sub UserError(;$) +{ + my ($mesg) = @_; + + if (keys(%errors)) { + foreach my $key (keys(%errors)) { + my $val = $errors{$key}; + print "${key}: $val\n"; + } + } + print "$mesg\n" + if (defined($mesg)); + + # Exit with positive status so web interface treats it as user error. + exit(1); +} + +sub escapeshellarg($) +{ + my ($str) = @_; + + $str =~ s/[^[:alnum:]]/\\$&/g; + return $str; +} diff --git a/configure b/configure index e061fdb31c5a7a452d90f8eca70140295959b61d..3e044219b566b96727fe6061aa65bfb5f38cd9ba 100755 --- a/configure +++ b/configure @@ -2427,7 +2427,7 @@ outfiles="$outfiles Makeconf GNUmakefile \ account/GNUmakefile account/tbacct \ account/addpubkey account/addsfskey account/genpubkeys \ account/quotamail account/mkusercert account/newproj account/newuser \ - backend/GNUmakefile backend/moduserinfo \ + backend/GNUmakefile backend/moduserinfo backend/newgroup \ tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \ tbsetup/spewrpmtar tbsetup/gentopofile tbsetup/power_sgmote.pm \ tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \ diff --git a/configure.in b/configure.in index ef103b351745c46794ea7d1e1a77980571f30550..2fac1dc383c25d065f0b9c98f7a46eec58d88074 100755 --- a/configure.in +++ b/configure.in @@ -807,7 +807,7 @@ outfiles="$outfiles Makeconf GNUmakefile \ account/GNUmakefile account/tbacct \ account/addpubkey account/addsfskey account/genpubkeys \ account/quotamail account/mkusercert account/newproj account/newuser \ - backend/GNUmakefile backend/moduserinfo \ + backend/GNUmakefile backend/moduserinfo backend/newgroup \ tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \ tbsetup/spewrpmtar tbsetup/gentopofile tbsetup/power_sgmote.pm \ tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \ diff --git a/db/Group.pm.in b/db/Group.pm.in index f76fddbbaf2fc2c6fcd9fbab971328fc943ff14e..2969835aabe73b8eb4e174676807ed99bb55619b 100644 --- a/db/Group.pm.in +++ b/db/Group.pm.in @@ -436,6 +436,10 @@ sub AccessCheck($$$) return 0 if (! ref($self)); + my $pid = $self->pid(); + my $gid = $self->gid(); + my $uid = $user->uid(); + if ($access_type < TB_PROJECT_MIN || $access_type > TB_PROJECT_MAX) { print "*** Invalid access type: $access_type!\n"; @@ -448,17 +452,83 @@ sub AccessCheck($$$) if ($access_type == TB_PROJECT_READINFO) { $mintrust = PROJMEMBERTRUST_USER; } - elsif ($access_type == TB_PROJECT_CREATEEXPT || - $access_type == TB_PROJECT_MAKEOSID) { + elsif ($access_type == TB_PROJECT_MAKEGROUP || + $access_type == TB_PROJECT_DELGROUP) { + $mintrust = PROJMEMBERTRUST_GROUPROOT; + } + elsif ($access_type == TB_PROJECT_LEADGROUP) { + # + # Allow mere user (in default group) to lead a subgroup. + # + $mintrust = PROJMEMBERTRUST_USER; + } + elsif ($access_type == TB_PROJECT_MAKEOSID || + $access_type == TB_PROJECT_MAKEIMAGEID || + $access_type == TB_PROJECT_CREATEEXPT) { $mintrust = PROJMEMBERTRUST_LOCALROOT; } - elsif ($access_type == TB_PROJECT_DELUSER) { - $mintrust = PROJMEMBERTRUST_PROJROOT; + elsif ($access_type == TB_PROJECT_ADDUSER || + $access_type == TB_PROJECT_EDITGROUP) { + # + # If user is project_root or group_root in default group, + # allow them to add/edit/remove users in any group. + # + if (TBMinTrust(TBGrpTrust($uid, $pid, $pid), + PROJMEMBERTRUST_GROUPROOT)) { + return 1; + } + # + # Otherwise, editing a group requires group_root + # in that group. + # + $mintrust = PROJMEMBERTRUST_GROUPROOT; } - elsif ($access_type == TB_PROJECT_MAKEGROUP || - $access_type == TB_PROJECT_DELGROUP) { + elsif ($access_type == TB_PROJECT_BESTOWGROUPROOT) { + # + # If user is project_root, + # allow them to bestow group_root in any group. + # + if (TBMinTrust(TBGrpTrust($uid, $pid, $pid), + PROJMEMBERTRUST_PROJROOT)) { + return 1; + } + + if ($gid == $pid) { + # + # Only project_root can bestow group_root in default group, + # and we already established that they are not project_root, + # so fail. + # + return 0; + } + else { + # + # Non-default group. + # group_root in default group may bestow group_root. + # + if (TBMinTrust(TBGrpTrust($uid, $pid, $pid), + PROJMEMBERTRUST_GROUPROOT)) { + return 1; + } + + # + # group_root in the group in question may also bestow + # group_root. + # + $mintrust = PROJMEMBERTRUST_GROUPROOT; + } + } + elsif ($access_type == TB_PROJECT_GROUPGRABUSERS) { + # + # Only project_root or group_root in default group + # may grab (involuntarily add) users into groups. + # + $gid = $pid; $mintrust = PROJMEMBERTRUST_GROUPROOT; } + elsif ($access_type == TB_PROJECT_DELUSER) { + $mintrust = PROJMEMBERTRUST_PROJROOT; + } else { print "*** Invalid access type: $access_type!\n"; return 0; @@ -785,7 +855,6 @@ sub LeaderMailList($) # # Return list of members in this group, by specific trust. # - sub MemberList($$;$$) { my ($self, $prval, $flags, $desired_trust) = @_; diff --git a/sql/database-fill.sql b/sql/database-fill.sql index fa21f4e6ef30fc5ab28074303399dc858de02b10..020f361184102ef5e31d20d6bcdd357acbd6c19f 100644 --- a/sql/database-fill.sql +++ b/sql/database-fill.sql @@ -565,7 +565,13 @@ REPLACE INTO table_regex VALUES ('experiments','wa_plr_solverweight','float','re REPLACE INTO table_regex VALUES ('experiments','cpu_usage','int','redirect','default:tinyint',0,5,NULL); REPLACE INTO table_regex VALUES ('experiments','mem_usage','int','redirect','default:tinyint',0,5,NULL); REPLACE INTO table_regex VALUES ('experiments','sync_server','text','redirect','virt_nodes:vname',0,0,NULL); + +REPLACE INTO table_regex VALUES ('groups','project','text','redirect','projects:pid',0,0,NULL); REPLACE INTO table_regex VALUES ('groups','gid','text','regex','^[a-zA-Z][-\\w]+$',2,12,NULL); +REPLACE INTO table_regex VALUES ('groups','group_id','text','redirect','groups:gid',2,12,NULL); +REPLACE INTO table_regex VALUES ('groups','group_leader','text','redirect','users:uid',2,8,NULL); +REPLACE INTO table_regex VALUES ('groups','group_description','text','redirect','default:tinytext',0,256,NULL); + REPLACE INTO table_regex VALUES ('nodes','node_id','text','regex','^[-\\w]+$',1,12,NULL); REPLACE INTO table_regex VALUES ('nseconfigs','pid','text','redirect','projects:pid',0,0,NULL); REPLACE INTO table_regex VALUES ('nseconfigs','eid','text','redirect','experiments:eid',0,0,NULL); diff --git a/www/group_defs.php b/www/group_defs.php index afff99800592c062cbdd7c0546c35572583f0157..6053a0320135cdc9ef4ae288de5ea3c3de7ceb6c 100644 --- a/www/group_defs.php +++ b/www/group_defs.php @@ -93,6 +93,94 @@ class Group return 0; } + # + # Class function to create a new Project Group. + # + function Create($project, $uid, $args, &$errors) { + global $suexec_output, $suexec_output_array; + + # + # Generate a temporary file and write in the XML goo. + # + $xmlname = tempnam("/tmp", "newgroup"); + if (! $xmlname) { + TBERROR("Could not create temporary filename", 0); + $errors[] = "Transient error; please try again later."; + return null; + } + if (! ($fp = fopen($xmlname, "w"))) { + TBERROR("Could not open temp file $xmlname", 0); + $errors[] = "Transient error; please try again later."; + return null; + } + + # Add these. Maybe caller should do this? + $args["project"] = $project->pid(); + + fwrite($fp, "<group>\n"); + foreach ($args as $name => $value) { + fwrite($fp, "<attribute name=\"$name\">"); + fwrite($fp, " <value>" . htmlspecialchars($value) . "</value>"); + fwrite($fp, "</attribute>\n"); + } + fwrite($fp, "</group>\n"); + fclose($fp); + chmod($xmlname, 0666); + + # Note: running as the user for mkgroup and modgroups underneath. + $unix_gid = $project->unix_gid(); + $retval = SUEXEC($uid, $unix_gid, "webnewgroup $xmlname", + SUEXEC_ACTION_IGNORE); + + if ($retval) { + if ($retval < 0) { + $errors[] = "Transient error; please try again later."; + SUEXECERROR(SUEXEC_ACTION_CONTINUE); + } + else { + # unlink($xmlname); + if (count($suexec_output_array)) { + for ($i = 0; $i < count($suexec_output_array); $i++) { + $line = $suexec_output_array[$i]; + if (preg_match("/^([-\w]+):\s*(.*)$/", + $line, $matches)) { + $errors[$matches[1]] = $matches[2]; + } + else + $errors[] = $line; + } + } + else + $errors[] = "Transient error; please try again later."; + } + return null; + } + + # + # Parse the last line of output. Ick. + # + unset($matches); + + if (!preg_match("/^GROUP\s+([^\/]+)\/(\d+)\s+/", + $suexec_output_array[count($suexec_output_array)-1], + $matches)) { + $errors[] = "Transient error; please try again later."; + SUEXECERROR(SUEXEC_ACTION_CONTINUE); + return null; + } + $group = $matches[2]; + $newgroup = Group::Lookup($group); + if (! $newgroup) { + $errors[] = "Transient error; please try again later."; + TBERROR("Could not lookup new group $group", 0); + return null; + } + # Unlink this here, so that the file is left behind in case of error. + # We can then create the group by hand from the xmlfile, if desired. + unlink($xmlname); + return $newgroup; + } + # # Load the project for a group lazily. # diff --git a/www/newgroup.php3 b/www/newgroup.php3 index fa98826f364a41b29f76fe95bce1081f89b9f044..35b1f9b99e12fb28d4586f47a6855b1cb7ebc5af 100644 --- a/www/newgroup.php3 +++ b/www/newgroup.php3 @@ -19,125 +19,209 @@ $uid = $this_user->uid(); $isadmin = ISADMIN(); # -# Verify page arguments -# -$reqargs = RequiredPageArguments("project", PAGEARG_PROJECT, - "group_id", PAGEARG_STRING, - "group_description", PAGEARG_ANYTHING, - "group_leader", PAGEARG_STRING); - -# Need these below -$group_pid = $project->pid(); -$unix_gid = $project->unix_gid(); -$safe_id = escapeshellarg($group_id); - -# -# Check ID for sillyness. -# -if ($group_id == "") { - USERERROR("Must provide a group name!", 1); -} -elseif (! TBvalid_gid($group_id)) { - USERERROR("Invalid group name: " . TBFieldErrorString(), 1); +# Verify page arguments. +# +$optargs = OptionalPageArguments("project", PAGEARG_PROJECT, + "submit", PAGEARG_STRING, + "formfields", PAGEARG_ARRAY); +if (!isset($project)) { + # + # See what projects the uid can do this in. + # + $projlist = $this_user->ProjectAccessList($TB_PROJECT_MAKEGROUP); + + if (! count($projlist)) { + USERERROR("You do not appear to be a member of any Projects in which ". + "you have permission to create new groups.", 1); + } } -if ($group_leader == "") { - USERERROR("Must provide a group leader!", 1); +else { + # + # Verify permission for specific project. + # + $pid = $project->pid(); + + if (!$project->AccessCheck($this_user, $TB_PROJECT_MAKEGROUP)) { + USERERROR("You do not have permission to create groups in ". + "project $pid!", 1); + } } # -# Certain of these values must be escaped or otherwise sanitized. +# Spit the form out using the array of data. # -$group_description = addslashes($group_description); +function SPITFORM($formfields, $errors) +{ + global $project, $pid, $projlist; + global $TBDB_GIDLEN, $TBDB_UIDLEN; + + if ($errors) { + echo "<table class=nogrid + align=center border=0 cellpadding=6 cellspacing=0> + <tr> + <th align=center colspan=2> + <font size=+1 color=red> + Oops, please fix the following errors! + </font> + </td> + </tr>\n"; + + while (list ($name, $message) = each ($errors)) { + echo "<tr> + <td align=right> + <font color=red>$name: </font></td> + <td align=left> + <font color=red>$message</font></td> + </tr>\n"; + } + echo "</table><br>\n"; + } -# -# Verify permission. -# -if (!$project->AccessCheck($this_user, $TB_PROJECT_MAKEGROUP)) { - USERERROR("You do not have permission to create groups in project ". - "$group_pid!", 1); -} + echo "<br> + <table align=center border=1> + <tr> + <td align=center colspan=2> + <em>(Fields marked with * are required)</em> + </td> + </tr>\n"; + + if (isset($project)) { + $url = CreateURL("newgroup", $project); + echo "<form action='$url' method=post> + <tr> + <td>* Project:</td> + <td class=left> + <input name=project type=readonly value='$pid'> + </td> + </tr>\n"; + } + else { + $url = CreateURL("newgroup"); + echo "<form action='$url' method=post> + <tr> + <td>*Select Project:</td>"; + echo " <td><select name=project>"; + + while (list($proj) = each($projlist)) { + echo "<option value='$proj'>$proj </option>\n"; + } + + echo " </select>"; + echo " </td> + </tr>\n"; + } -# Need the user object for creating the group. -if (! ($leader = User::Lookup($group_leader))) { - USERERROR("User '$group_leader' is an unknown user!", 1); + echo "<tr> + <td>*Group Name (no blanks, lowercase):</td> + <td class=left> + <input type=text + name=\"formfields[group_id]\" + value=\"" . $formfields["group_id"] . "\" + size=$TBDB_GIDLEN + maxlength=$TBDB_GIDLEN> + </td> + </tr>\n"; + + echo "<tr> + <td>*Group Description:</td> + <td class=left> + <input type=text size=50 + name=\"formfields[group_description]\" + value=\"" . $formfields["group_description"] . "\"> + </td> + </tr>\n"; + + echo "<tr> + <td>*Group Leader (Emulab userid):</td> + <td class=left> + <input type=text + name=\"formfields[group_leader]\" + value=\"" . $formfields["group_leader"] . "\" + size=$TBDB_UIDLEN maxlength=$TBDB_UIDLEN> + </td> + </tr>\n"; + + echo "<tr> + <td align=center colspan=2> + <b><input type=submit name=submit value=Submit></b> + </td> + </tr>\n"; + + echo "</form> + </table>\n"; + + echo "<br><center> + Important <a href=docwrapper.php3?docname=groups.html#SECURITY'> + security issues</a> are discussed in the + <a href='docwrapper.php3?docname=groups.html'>Groups Tutorial</a>. + </center>\n"; } # -# Verify leader. Any user can lead a group, but they must be a member of -# the project, and we have to avoid an ISADMIN() check in AccessCheck(). +# Accumulate error reports for the user, e.g. +# $errors["Key"] = "Msg"; +# Required page args may need to be checked early. +$errors = array(); + +# +# On first load, display a virgin form and exit. # -if (!$project->IsMember($leader, $isapproved) || - !$project->AccessCheck($leader, $TB_PROJECT_LEADGROUP)) { - USERERROR("$group_leader does not have enough permission to lead a group ". - "in project $group_pid!", 1); +if (!isset($submit)) { + $defaults = array(); + $defaults["group_id"] = ""; + $defaults["group_description"] = ""; + $defaults["group_leader"] = $uid; + + SPITFORM($defaults, $errors); + PAGEFOOTER(); + return; } # -# Make sure the GID is not already there. -# -if (($oldgroup = Group::LookupByPidGid($group_pid, $group_id))) { - USERERROR("The group $group_id already exists! Please select another.", 1); +# If any errors, respit the form with the current values and the +# error messages displayed. Iterate until happy. +# +if (count($errors)) { + SPITFORM($formfields, $errors); + PAGEFOOTER(); + return; } # -# The unix group name must be globally unique. Form a name and check it. +# Build up argument array to pass along. # -$unix_gname = substr($group_pid, 0, 3) . "-" . $group_id; -$maxtries = 99; -$count = 0; - -while ($count < $maxtries) { - if (strlen($unix_gname) > $TBDB_UNIXGLEN) { - TBERROR("Unix group name $unix_gname is too long!", 1); - } - - $query_result = - DBQueryFatal("select gid from groups where unix_name='$unix_gname'"); - - if (mysql_num_rows($query_result) == 0) { - break; - } - $count++; +$args = array(); - $unix_gname = substr($group_pid, 0, 3) . "-" . - substr($group_id, 0, strlen($group_id) - 2) . "$count"; +if (isset($formfields["project"]) && + $formfields["project"] != "none" && $formfields["project"] != "") { + $args["project"] = $formfields["project"]; } -if ($count == $maxtries) { - TBERROR("Could not form a unique Unix group name!", 1); +if (isset($formfields["group_id"]) && $formfields["group_id"] != "") { + $args["group_id"] = $formfields["group_id"]; } - -# -# Create the new group and set up the initial membership for the leader. -# -# Note, if the project leader wants to be in the subgroup, he/she has to -# add themself via the edit page. -# -if (! ($newgroup = Group::NewGroup($project, $group_id, $leader, - $group_description, $unix_gname))) { - TBERROR("Could not create new group $group_pid/$group_id!", 1); +if (isset($formfields["group_description"]) && + $formfields["group_description"] != "") { + $args["group_description"] = $formfields["group_description"]; +} +if (isset($formfields["group_leader"]) && $formfields["group_leader"] != "") { + $args["group_leader"] = $formfields["group_leader"]; } -$gid_idx = $newgroup->gid_idx(); -STARTBUSY("Creating project group $group_id."); +$group_id = $formfields["group_id"]; +###STARTBUSY("Creating project group $group_id."); +echo "<br>Creating project group $group_id.<br>\n"; -# -# Run the script. This will make the group directory, set the perms, etc. -# -SUEXEC($uid, $unix_gid, - "webmkgroup $gid_idx", SUEXEC_ACTION_DIE); - -# -# Now add the group leader to the group. -# -SUEXEC($uid, $unix_gid, - "webmodgroups -a $group_pid:$safe_id:group_root $group_leader", - SUEXEC_ACTION_DIE); +if (! ($newgroup = Group::Create($project, $uid, $args, $errors))) { + # Always respit the form so that the form fields are not lost. + # I just hate it when that happens so lets not be guilty of it ourselves. + SPITFORM($formfields, $errors); + PAGEFOOTER(); + return; +} -STOPBUSY(); +###STOPBUSY(); -# -# Redirect back to project page. -# +echo "<center><h3>Done!</h3></center>\n"; PAGEREPLACE(CreateURL("showgroup", $newgroup)); # diff --git a/www/newgroup_form.php3 b/www/newgroup_form.php3 deleted file mode 100644 index 88e7087ba48fcf54c54d888c735fcefe39006730..0000000000000000000000000000000000000000 --- a/www/newgroup_form.php3 +++ /dev/null @@ -1,120 +0,0 @@ -<?php -# -# EMULAB-COPYRIGHT -# Copyright (c) 2000-2007 University of Utah and the Flux Group. -# All rights reserved. -# -include("defs.php3"); - -# -# Standard Testbed Header -# -PAGEHEADER("Create a Project Group"); - -# -# Only known and logged in users. -# -$this_user = CheckLoginOrDie(); -$uid = $this_user->uid(); -$isadmin = ISADMIN(); - -# -# Verify page arguments. -# -$optargs = OptionalPageArguments("project", PAGEARG_PROJECT); - -if (!isset($project)) { - # - # See what projects the uid can do this in. - # - $projlist = $this_user->ProjectAccessList($TB_PROJECT_MAKEGROUP); - - if (! count($projlist)) { - USERERROR("You do not appear to be a member of any Projects in which ". - "you have permission to create new groups.", 1); - } -} -else { - # - # Verify permission for specific project. - # - $pid = $project->pid(); - - if (!$project->AccessCheck($this_user, $TB_PROJECT_MAKEGROUP)) { - USERERROR("You do not have permission to create groups in ". - "project $pid!", 1); - } -} - - -echo "<form action=newgroup.php3 method=post> - <table align=center border=1> - <tr> - <td align=center colspan=2> - <em>(Fields marked with * are required)</em> - </td> - </tr>\n"; - -if (isset($project)) { - echo "<tr> - <td>* Project:</td> - <td class=left> - <input name=project type=readonly value='$pid'> - </td> - </tr>\n"; -} -else { - echo "<tr> - <td>*Select Project:</td>"; - echo " <td><select name=project>"; - - while (list($project) = each($projlist)) { - echo "<option value='$project'>$project </option>\n"; - } - - echo " </select>"; - echo " </td> - </tr>\n"; -} - -echo "<tr> - <td>*Group Name (no blanks, lowercase):</td> - <td class=left> - <input name=group_id type=text size=$TBDB_GIDLEN - maxlength=$TBDB_GIDLEN> - </td> - </tr>\n"; - -echo "<tr> - <td>*Group Description:</td> - <td class=left> - <input name=group_description type=text size=50> - </td> - </tr>\n"; - -echo "<tr> - <td>*Group Leader (Emulab userid):</td> - <td class=left> - <input name=group_leader type=text value='$uid' - size=$TBDB_UIDLEN maxlength=$TBDB_UIDLEN> - </td> - </tr>\n"; - -echo "<tr> - <td align=center colspan=2> - <b><input type=submit value=Submit></b></td> - </tr> - </form> - </table>\n"; - -echo "<br><center> - Important <a href=docwrapper.php3?docname=groups.html#SECURITY'> - security issues</a> are discussed in the - <a href='docwrapper.php3?docname=groups.html'>Groups Tutorial</a>. - </center>\n"; - -# -# Standard Testbed Footer -# -PAGEFOOTER(); -?> diff --git a/www/showproject.php3 b/www/showproject.php3 index 0186e5f6a7f794508d3b947184154da4394c4f6f..a9b3d7258041876a26da9d9317d517355aa5f85d 100644 --- a/www/showproject.php3 +++ b/www/showproject.php3 @@ -45,7 +45,7 @@ if (! $project->AccessCheck($this_user, $TB_PROJECT_READINFO)) { SUBPAGESTART(); SUBMENUSTART("Project Options"); WRITESUBMENUBUTTON("Create Subgroup", - "newgroup_form.php3?pid=$pid"); + "newgroup.php3?pid=$pid"); WRITESUBMENUBUTTON("Edit User Privs", "editgroup_form.php3?pid=$pid&gid=$pid"); WRITESUBMENUBUTTON("Remove Users",