Commit 8fffc2f3 authored by Russ Fish's avatar Russ Fish

Move editgroup page form logic to a backend Perl script.

     www/editgroup.php3 - The reworked PHP page.
     www/editgroup_form.php3 - Removed, form merged into editgroup.php3 .
     www/showgroup.php3 - Link to editgroup.php3, rather than editgroup_form.php3 .
     www/group_defs.php - Add an Image::EditGroup class method
                            bridging to the script via XML.
     backend/{editgroup,GNUmakefile}.in configure configure.in - New backend script.
     db/Group.pm.in - Add an EditGroup worker class method for script arg checking.
                      Also the missing NonMemberList and CheckTrustConsistency methods,
                      and a GETTRUST flag to MemberList, as in the PHP version.
     db/User.pm.in - Add the missing but tiny {Set,Get}TempData methods.
     sql/database-fill.sql - Add gid_idx to the table_regex 'groups' checking patterns.
parent fba8af4a
......@@ -13,9 +13,9 @@ UNIFIED = @UNIFIED_BOSS_AND_OPS@
include $(OBJDIR)/Makeconf
BIN_SCRIPTS = moduserinfo newgroup newmmlist editexp editimageid \
editnodetype editsitevars newimageid
editnodetype editsitevars newimageid editgroup
WEB_BIN_SCRIPTS = webmoduserinfo webnewgroup webnewmmlist webeditimageid \
webeditnodetype webeditsitevars webnewimageid
webeditnodetype webeditsitevars webnewimageid webeditgroup
WEB_SBIN_SCRIPTS=
LIBEXEC_SCRIPTS = $(WEB_BIN_SCRIPTS) $(WEB_SBIN_SCRIPTS)
......
#!/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 edit group membership.
#
sub usage()
{
print("Usage: editgroup [-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@";
#
# 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 Group;
# Protos
sub fatal($);
sub UserError(;$);
#
# 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 edit group membership.
}
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
("group" => ["gid_idx", $SLOT_REQUIRED]);
# And also trust args, see below.
#
# 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.
#
my $key;
foreach $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->EditGroup() as we check
# the attributes.
#
my %editgroup_args = ();
foreach $key (keys(%{ $xmlparse->{'attribute'} })) {
my $value = $xmlparse->{'attribute'}->{"$key"}->{'value'};
if ($debug) {
print STDERR "User attribute: '$key' -> '$value'\n";
}
# XXX Special for trust args. Either:
# (change|add)_xxx=permit or
# Uxxx$$trust=(user|(local|group)_root)
# where xxx is the uid_idx of a user.
if (($key =~ /^(change|add)_[0-9]+$/ &&
$value eq "permit") ||
($key =~ /^U[0-9]+\$\$trust$/ &&
$value =~ /^(user|(local|group)_root)$/)) {
if ($debug) {
print STDERR "Trust: '$key' -> '$value'\n";
}
$editgroup_args{$key} = $value;
next;
}
$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;
}
$editgroup_args{$dbslot} = $value;
}
UserError()
if (keys(%errors));
#
# Now do special checks.
#
my $group = Group->Lookup($editgroup_args{"gid_idx"});
if (!defined($group)) {
UserError("Group: No such group");
}
if (!$group->AccessCheck($this_user, TB_PROJECT_EDITGROUP())) {
UserError("Group: Not enough permission");
}
# More checking is done in the first pass of Group->EditGroup().
exit(0)
if ($verify);
#
# Now safe to edit group membership.
#
# We pass the group along as an argument to EditGroup(), so remove it from
# the argument array.
#
delete($editgroup_args{"gid_idx"});
my $usrerr;
my $editgroup_val = Group->EditGroup($group, $this_user,
\%editgroup_args, \$usrerr);
UserError($usrerr)
if (defined($usrerr));
fatal("Could not create new Group!")
if (!defined($editgroup_val));
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);
}
......@@ -2429,7 +2429,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
account/quotamail account/mkusercert account/newproj account/newuser \
backend/GNUmakefile backend/moduserinfo backend/newgroup \
backend/newmmlist backend/editexp backend/editimageid backend/editnodetype \
backend/editsitevars backend/newimageid \
backend/editsitevars backend/newimageid backend/editgroup \
tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \
tbsetup/spewrpmtar tbsetup/gentopofile tbsetup/power_sgmote.pm \
tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \
......
......@@ -809,7 +809,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
account/quotamail account/mkusercert account/newproj account/newuser \
backend/GNUmakefile backend/moduserinfo backend/newgroup \
backend/newmmlist backend/editexp backend/editimageid backend/editnodetype \
backend/editsitevars backend/newimageid \
backend/editsitevars backend/newimageid backend/editgroup \
tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \
tbsetup/spewrpmtar tbsetup/gentopofile tbsetup/power_sgmote.pm \
tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \
......
This diff is collapsed.
......@@ -183,6 +183,21 @@ sub wikiname($) { return field($_[0], "wikiname"); }
sub wikionly($) { return field($_[0], "wikionly"); }
sub mailman_password($) { return field($_[0], "mailman_password"); }
# Temporary data storage ... useful.
sub GetTempData($$) { return field($_[0], "tempdata"); }
sub SetTempData($$) {
my ($self, $value) = @_;
# Must be a real reference.
return -1
if (! ref($self));
$self->{'USER'}->{"tempdata"} = $value;
return 0;
}
#
# Lookup user given a plain uid. For backwards compat.
#
......
......@@ -566,6 +566,7 @@ REPLACE INTO table_regex VALUES ('experiments','sync_server','text','redirect','
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','gid_idx','text','regex','^[\\d]+$',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);
......
This diff is collapsed.
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2003, 2005, 2006, 2007 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
#
# Standard Testbed Header
#
PAGEHEADER("Edit Group Membership");
#
# Only known and logged in users.
#
$this_user = CheckLoginOrDie();
$uid = $this_user->uid();
$isadmin = ISADMIN();
#
# Verify page arguments.
#
$reqargs = RequiredPageArguments("group", PAGEARG_GROUP);
# Need these below;
$pid = $group->pid();
$gid = $group->gid();
#
# The default group membership cannot be changed, but the trust levels can.
#
$defaultgroup = $group->IsProjectGroup();
#
# Verify permission.
#
if (! $group->AccessCheck($this_user, $TB_PROJECT_EDITGROUP)) {
USERERROR("You do not have permission to edit group $gid in ".
"project $pid!", 1);
}
#
# See if user is allowed to add non-members to group.
#
$grabusers = 0;
if ($group->AccessCheck($this_user, $TB_PROJECT_GROUPGRABUSERS)) {
$grabusers = 1;
}
#
# See if user is allowed to bestow group_root upon members of group.
#
$bestowgrouproot = 0;
if ($group->AccessCheck($this_user, $TB_PROJECT_BESTOWGROUPROOT)) {
$bestowgrouproot = 1;
}
#
# Grab the user list for the group. Provide a button selection of people
# that can be removed. The group leader cannot be removed!
# Do not include members that have not been approved
# to main group either! This will force them to go through the approval
# page first.
#
$curmembers =& $group->MemberList();
#
# Grab the user list from the project. These are the people who can be
# added. Do not include people in the above list, obviously! Do not
# include members that have not been approved to main group either! This
# will force them to go through the approval page first.
#
$nonmembers = $group->NonMemberList();
#
# We do not allow the actual group info to be edited. Just the membership.
#
$group->Show();
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";
if (count($curmembers) ||
($grabusers && count($nonmembers))) {
$url = CreateURL("editgroup", $group);
echo "<br>
<form action='$url' method=post>
<table align=center border=1>\n";
}
if (count($curmembers)) {
if ($defaultgroup) {
echo "<tr><td align=center colspan=2 nowrap=1>
<br>
<font size=+1><b>Edit Trust Level</b></font>
<br>
You may edit trust level in the default group,<br>
but you are not allowed to remove members.
</td></tr>\n";
}
else {
echo "<tr><td align=center colspan=2 nowrap=1>
<br>
<font size=+1><b>Remove/Edit Group Members.</b></font>
<br>
Deselect the ones you would like to remove,<br>
or edit their trust value.
</td></tr>\n";
}
foreach ($curmembers as $target_user) {
$target_uid = $target_user->uid();
$target_idx = $target_user->uid_idx();
$trust = $target_user->GetTempData();
$showurl = CreateURL("showuser", $target_user);
if ($defaultgroup) {
echo "<tr>
<td>
<input type=hidden name='change_$target_idx'
value=permit>
<A href='$showurl'>$target_uid &nbsp</A>
</td>\n";
}
else {
echo "<tr>
<td>
<input checked type=checkbox value=permit
name='change_$target_idx'>
<A href='$showurl'>$target_uid &nbsp</A>
</td>\n";
}
echo " <td align=center>
<select name='U${target_idx}\$\$trust'>\n";
#
# We want to have the current trust value selected in the menu.
#
if ($group->CheckTrustConsistency($target_user,
TBDB_TRUSTSTRING_USER, 0)) {
echo "<option value='user' " .
((strcmp($trust, "user") == 0) ? "selected" : "") .
">User </option>\n";
}
if ($group->CheckTrustConsistency($target_user,
TBDB_TRUSTSTRING_LOCALROOT, 0)) {
echo "<option value='local_root' " .
((strcmp($trust, "local_root") == 0) ? "selected" : "") .
">Local Root </option>\n";
#
# If group_root is already selected, or we have permission to set
# it, show it. Otherwise do not.
#
if (strcmp($trust, "group_root") == 0 || $bestowgrouproot) {
echo "<option value='group_root' " .
((strcmp($trust, "group_root") == 0) ? "selected" : "") .
">Group Root </option>\n";
}
}
echo " </select>
</td>\n";
}
echo "</tr>\n";
reset($curmembers);
}
if ($grabusers && count($nonmembers)) {
echo "<tr><td align=center colspan=2 nowrap=1>
<br>
<font size=+1><b>Add Group Members</b></font>[<b>1</b>].
<br>
Select the ones you would like to add.<br>
Be sure to select the appropriate trust level.
</td></tr>\n";
foreach ($nonmembers as $target_user) {
$target_uid = $target_user->uid();
$target_idx = $target_user->uid_idx();
$trust = $target_user->GetTempData();
$showurl = CreateURL("showuser", $target_user);
echo "<tr>
<td>
<input type=checkbox value=permit name='add_$target_idx'>
<A href='$showurl'>$target_uid &nbsp</A>
</td>\n";
echo " <td align=center>
<select name='U${target_idx}\$\$trust'>\n";
if ($group->CheckTrustConsistency($target_user,
TBDB_TRUSTSTRING_USER, 0)) {
echo "<option value='user' " .
((strcmp($trust, "user") == 0) ? "selected" : "") .
">User</option>\n";
}
if ($group->CheckTrustConsistency($target_user,
TBDB_TRUSTSTRING_LOCALROOT, 0)) {
echo "<option value='local_root' " .
((strcmp($trust, "local_root") == 0) ? "selected" : "") .
">Local Root</option>\n";
if ($bestowgrouproot) {
echo "<option value='group_root' " .
((strcmp($trust, "group_root") == 0) ? "selected" : "") .
">Group Root</option>\n";
}
}
echo " </select>
</td>\n";
}
echo "</tr>\n";
reset($nonmembers);
}
if (count($curmembers) ||
($grabusers && count($nonmembers))) {
echo "<tr>
<td align=center colspan=2>
<b><input type=submit value=Submit></b>
</td>
</tr>\n";
echo "</table>
</form>\n";
}
else {
echo "<br><center>
<em>There are no project members who are eligible to be added
or removed from this group[<b>1</b>].</em>
</center>\n";
}
echo "<h4><blockquote><blockquote><blockquote>
<ol>
<li> Only members who have already been approved to the main
project will be listed. If a project member is missing, please
go to <a href=approveuser_form.php3>New User Approval</a>
and approve the user to the main project group. Then you can
reload this page and add those members to other groups in your
project.\n";
echo "</ol>
</blockquote></blockquote></blockquote>
</h4>\n";
#
# Standard Testbed Footer
#
PAGEFOOTER();
?>
......@@ -181,6 +181,77 @@ class Group
return $newgroup;
}
#
# Class function to edit group membership.
#
function EditGroup($group, $uid, $args, &$errors) {
global $suexec_output, $suexec_output_array;
#
# Generate a temporary file and write in the XML goo.
#
$xmlname = tempnam("/tmp", "editgroup");
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["group"] = $group->gid_idx();
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);
# Grab the unix GID for running scripts.
$unix_gid = $group->unix_gid();
$retval = SUEXEC($uid, $unix_gid, "webeditgroup $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;
}
# There are no return value(s) to parse at the end of the output.
# Unlink this here, so that the file is left behind in case of error.
# We can then edit the group by hand from the xmlfile, if desired.
unlink($xmlname);
return true;
}
#
# Load the project for a group lazily.
#
......
......@@ -67,7 +67,7 @@ if ($showmenu) {
if ($group->AccessCheck($this_user, $TB_PROJECT_EDITGROUP)) {
WRITESUBMENUBUTTON("Edit this Group",
"editgroup_form.php3?pid=$pid&gid=$gid");
"editgroup.php3?pid=$pid&gid=$gid");
}
#
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment