All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

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);