Commit 664108af authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

Subgroups! What more needs to be said?

parent 1a348241
......@@ -89,7 +89,7 @@ my $UPDATEGENIUSER= "$TB/sbin/protogeni/updategeniuser";
my $STITCHER = "$TB/gcf/src/stitcher.py";
my $OPENSSL = "/usr/bin/openssl";
my $MANAGEINSTANCE= "$TB/bin/manage_instance";
my $DEFAULT_URN = "urn:publicid:IDN+${OURDOMAIN}+authority+cm";
my $DEFAULT_URN = "urn:publicid:IDN+$OURDOMAIN+authority+cm";
my $GUEST_URN = "urn:publicid:IDN+apt.emulab.net+authority+cm";
my $default_aggregate_urn = $DEFAULT_URN;
......@@ -100,7 +100,7 @@ delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# Turn off line buffering on output
#
$| = 1;
$| = 1;
# Load the Testbed support stuff.
use lib "@prefix@/lib";
......@@ -258,7 +258,7 @@ foreach my $key ("username", "email", "profile", "portal") {
# Gather up args and sanity check.
#
my ($value, $user_urn, $user_uid, $user_hrn, $user_email, $project, $pid,
$sshkey, $profile, $profileid, $version, $rspecstr, $errmsg,
$gid, $group, $sshkey, $profile, $profileid, $version, $rspecstr, $errmsg,
$userslice_id, $portal);
# This is used internally to determine which portal was used.
......@@ -561,6 +561,21 @@ if ($localuser) {
}
$pid = $project->pid();
# Option subgroup.
if (exists($xmlparse->{'attribute'}->{"gid"}) &&
$xmlparse->{'attribute'}->{"gid"}->{"value"} ne "" &&
$xmlparse->{'attribute'}->{"gid"}->{"value"} ne $pid) {
my $val = $xmlparse->{'attribute'}->{"gid"}->{"value"};
$group = $project->LookupGroup($val);
if (!defined($group)) {
fatal("Group $val does not exist in project $pid");
}
}
else {
$group = $project->GetProjectGroup();
}
$gid = $group->gid();
# Use of the Image Tracker is a Portal directive at the moment.
$usetracker = 1
if (GetSiteVar("protogeni/use_imagetracker") &&
......@@ -579,6 +594,7 @@ elsif (!$localuser) {
# Guest users get a holding project.
$pid = $APT_HOLDINGPROJECT;
$project = Project->Lookup($pid);
$group = $project->GetProjectGroup();
if (!defined($project)) {
fatal("Project $pid does not exist");
}
......@@ -613,7 +629,9 @@ if (defined($profile)) {
my $safe_uid = $user_uid; $safe_uid =~ s/_/-/;
my $slice_id = (defined($userslice_id) ? $userslice_id :
$safe_uid . "-QV" . TBGetUniqueIndex('next_quickvm', 1));
my $slice_urn = GeniHRN::Generate("${OURDOMAIN}:${pid}", "slice", $slice_id);
my $slice_auth = ($pid eq $gid ? $pid : "${pid}:${gid}");
my $slice_urn = GeniHRN::Generate("${OURDOMAIN}:${slice_auth}",
"slice", $slice_id);
my $slice_hrn = "${PGENIDOMAIN}.${pid}.${slice_id}";
my $SERVER_NAME = (exists($ENV{"SERVER_NAME"}) ? $ENV{"SERVER_NAME"} : "");
......@@ -630,7 +648,7 @@ if (GeniSlice->Lookup($slice_hrn) || GeniSlice->Lookup($slice_urn)) {
fatal("Could not form a unique slice name");
}
}
#
# Generate a certificate for this new slice.
#
......@@ -667,7 +685,7 @@ my $slice_uuid = $slice->uuid();
# nodes, hence the alternate CA, and the XMLRPC server will not allow
# this certificate to do anything, except at the portal RPC server.
#
my $alt_urn = GeniHRN::Generate("aptlab.net:${pid}", "slice", $slice_id);
my $alt_urn = GeniHRN::Generate("aptlab.net:${slice_auth}", "slice", $slice_id);
my $alt_hrn = "aptlab.${pid}.${slice_id}";
my $alt_url = "$PROTOGENI_URL/portal";
......@@ -740,6 +758,8 @@ my $blob = {'uuid' => $quickvm_uuid,
if (defined($project)) {
$blob->{"pid"} = $project->pid();
$blob->{"pid_idx"} = $project->pid_idx();
$blob->{"gid"} = $group->gid();
$blob->{"gid_idx"} = $group->gid_idx();
}
$errmsg = undef;
$instance = APT_Instance->Create($blob, \$errmsg);
......
......@@ -39,6 +39,11 @@ function ClassicExperimentList($which, $target, $state = "active")
$target_idx = $target->uid_idx();
$whereclause = "where e.swapper_idx='$target_idx'";
}
elseif ($which == "group") {
$target_pid = $target->pid();
$target_gid = $target->gid();
$whereclause = "where e.pid='$target_pid' and e.gid='$target_gid'";
}
else {
$target_pid = $target->pid();
$whereclause = "where e.pid='$target_pid'";
......@@ -103,6 +108,11 @@ function ExperimentList($which, $target)
$target_uuid = $target->uuid();
$whereclause = "where a.creator_uuid='$target_uuid'";
}
elseif ($which == "group") {
$target_pid = $target->pid();
$target_gid = $target->gid();
$whereclause = "where a.pid='$target_pid' and a.gid='$target_gid'";
}
else {
$target_pid = $target->pid();
$whereclause = "where a.pid='$target_pid'";
......@@ -218,6 +228,11 @@ function ProfileList($which, $target)
$target_idx = $target->uid_idx();
$whereclause = "where v.creator_idx='$target_idx'";
}
elseif ($which == "group") {
$target_pid = $target->pid();
$target_gid = $target->gid();
$whereclause = "where v.pid='$target_pid' and v.gid='$target_gid'";
}
else {
$target_idx = $target->pid_idx();
$whereclause = "where v.pid_idx='$target_idx'";
......
<?php
#
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
chdir("..");
include("defs.php3");
chdir("apt");
include("quickvm_sup.php");
# Must be after quickvm_sup.php since it changes the auth domain.
$page_title = "Create Group";
#
# Get current user.
#
RedirectSecure();
$this_user = CheckLoginOrRedirect();
$this_idx = $this_user->uid_idx();
$this_uid = $this_user->uid();
$isadmin = (ISADMIN() ? 1 : 0);
#
# Verify page arguments.
#
$reqargs = RequiredPageArguments("project", PAGEARG_PROJECT);
$optargs = OptionalPageArguments("leader", PAGEARG_USER);
if (!$project->AccessCheck($this_user, $TB_PROJECT_MAKEGROUP)) {
SPITUSERERROR("You do not have permission to create groups in ".
"project " . $project->pid());
}
if (isset($leader)) {
$isapproved = 0;
if (! ($project->IsMember($leader, $isapproved) && $isapproved)) {
SPITUSERERROR($leader->uid(). " is not a member of project " .
$project->uid());
}
}
SPITHEADER(1);
# Place to hang the toplevel template.
echo "<div id='main-body'></div>\n";
# Initial form contents.
$formfields = array();
$formfields["project"] = $project->pid();
$formfields["group_id"] = "";
$formfields["group_leader"] = (isset($leader) ? $leader->uid() : $this_uid);
$formfields["group_description"] = "";
echo "<script type='text/plain' id='form-json'>\n";
echo htmlentities(json_encode($formfields)) . "\n";
echo "</script>\n";
echo "<script type='text/javascript'>\n";
echo " window.ISADMIN = $isadmin;\n";
echo "</script>\n";
SPITREQUIRE("create-group");
SPITFOOTER();
?>
<?php
#
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
chdir("..");
chdir("apt");
include_once("profile_defs.php");
include_once("instance_defs.php");
include_once("ajax-routines.ajax");
# We set this in CheckPageArgs
$target_group = null;
#
# Need to check the permission, since we allow admins to mess with
# other accounts.
#
function CheckPageArgs()
{
global $this_user, $target_group;
global $ajax_args;
global $TB_PROJECT_READINFO;
if (!isset($ajax_args["pid"])) {
SPITAJAX_ERROR(-1, "Missing target pid");
return -1;
}
if (!isset($ajax_args["gid"])) {
SPITAJAX_ERROR(-1, "Missing target gid");
return -1;
}
$pid = $ajax_args["pid"];
$gid = $ajax_args["gid"];
if (!TBvalid_pid($pid)) {
SPITAJAX_ERROR(-1, "Invalid target pid");
return -1;
}
if (!TBvalid_pid($gid)) {
SPITAJAX_ERROR(-1, "Invalid target gid");
return -1;
}
$target_group = Group::LookupByPidGid($pid, $gid);
if (!$target_group) {
sleep(2);
SPITAJAX_ERROR(-1, "Unknown target group");
return -1;
}
if (!ISADMIN() && !ISFOREIGN_ADMIN() &&
!$target_group->AccessCheck($this_user, $TB_PROJECT_READINFO)) {
SPITAJAX_ERROR(-1, "Not enough permission");
return -1;
}
return 0;
}
#
# Server side of creating a new group
#
function Do_CreateGroup()
{
global $this_user;
global $ajax_args;
global $DBFieldErrstr;
global $TB_PROJECT_MAKEGROUP;
$this_idx = $this_user->uid_idx();
$this_uid = $this_user->uid();
# Allow for form precheck only. So JS code knows it will be fast.
$checkonly = isset($ajax_args["checkonly"]) && $ajax_args["checkonly"];
if (!isset($ajax_args["formfields"])) {
SPITAJAX_ERROR(1, "Missing formfields");
return;
}
$formfields = $ajax_args["formfields"];
$errors = array();
$required = array("project", "group_leader", "group_id",
"group_description");
foreach ($required as $field) {
if (!isset($formfields[$field]) || $formfields[$field] == "") {
$errors[$field] = "Missing field";
}
}
if (count($errors)) {
SPITAJAX_ERROR(2, $errors);
return;
}
# Project
if (!TBvalid_pid($formfields["project"])) {
$errors["project"] = $DBFieldErrstr;
}
$project = Project::LookupByPid($formfields["project"]);
if (!$project) {
$errors["project"] = "No such project";
}
elseif (!ISADMIN() &&
!$project->AccessCheck($this_user, $TB_PROJECT_MAKEGROUP)) {
$errors["project"] = "No permission to create groups";
}
$pid = $project->pid();
# Group leader.
if (!TBvalid_uid($formfields["group_leader"])) {
$errors["group_leader"] = $DBFieldErrstr;
}
else {
$leader = User::Lookup($formfields["group_leader"]);
if (!$leader) {
$errors["group_leader"] = "No such leader";
}
elseif (! ($project->IsMember($leader, $isapproved) && $isapproved)) {
$errors["group_leader"] = "Leader is not a member of project";
}
}
# Group Name.
if (!TBvalid_gid($formfields["group_id"])) {
$errors["group_id"] = $DBFieldErrstr;
}
else {
$group = Group::LookupByPidGid($pid, $formfields["group_id"]);
if ($group) {
$errors["group_id"] = "Already in use";
}
}
# Description.
if (!TBvalid_description($formfields["group_description"])) {
$errors["group_description"] = $DBFieldErrstr;
}
if (count($errors)) {
SPITAJAX_ERROR(2, $errors);
return;
}
if ($checkonly) {
SPITAJAX_RESPONSE(0);
return;
}
#
# Build up argument array to pass along.
#
$args = array();
$args["project"] = $project->pid_idx();
$args["group_id"] = $formfields["group_id"];
$args["group_description"] = $formfields["group_description"];
$args["group_leader"] = $leader->uid();
if (! ($newgroup = Group::Create($project, $this_uid, $args, $errors))) {
SPITAJAX_ERROR(2, $errors);
return;
}
$gid_idx = $newgroup->gid_idx();
SPITAJAX_RESPONSE("show-group.php?group=$gid_idx");
}
function Do_DeleteGroup()
{
global $this_user, $target_group;
global $TB_PROJECT_DELGROUP;
global $suexec_output, $suexec_output_array;
if (CheckPageArgs()) {
return;
}
if (! $target_group->AccessCheck($this_user, $TB_PROJECT_DELGROUP)) {
SPITAJAX_ERROR(-1, "You are not allowed to delete this group");
return -1;
}
# Check to see if there are any active experiments. Abort if there are.
if ($target_group->ExperimentList(0)) {
SPITAJAX_ERROR(-1, "There are active experiments in the group");
return;
}
$uid = $this_user->uid();
$project = $target_group->Project();
$unix_gid = $project->unix_gid();
$pid = $project->pid();
$gid_idx = $target_group->gid_idx();
if (SUEXEC($uid, $unix_gid,
"webrmgroup $gid_idx", SUEXEC_ACTION_CONTINUE)) {
SPITAJAX_ERROR(-1, "Internal error deleting group");
return;
}
SPITAJAX_RESPONSE("show-project.php?project=$pid");
}
function Do_ExperimentList()
{
global $this_user, $target_group;
if (CheckPageArgs()) {
return;
}
$results = ExperimentList("group", $target_group);
SPITAJAX_RESPONSE($results);
}
function Do_ClassicExperimentList()
{
global $this_user, $target_group;
if (CheckPageArgs()) {
return;
}
$results = ClassicExperimentList("group", $target_group);
SPITAJAX_RESPONSE($results);
}
function Do_ClassicProfileList()
{
global $this_user, $target_group;
if (CheckPageArgs()) {
return;
}
$results = ClassicExperimentList("group", $target_group, "inactive");
SPITAJAX_RESPONSE($results);
}
function Do_ProfileList()
{
global $this_user, $target_group;
if (CheckPageArgs()) {
return;
}
$results = ProfileList("group", $target_group);
SPITAJAX_RESPONSE($results);
}
function Do_MemberList()
{
global $this_user, $target_group;
global $newTrustMap;
if (CheckPageArgs()) {
return;
}
$members = array();
$nonmembers = array();
# We want *all* members including leader and unapproved.
$memberlist = $target_group->MemberList(0, 1);
$nonmemberlist = $target_group->NonMemberList();
$leader = $target_group->GetLeader();
foreach ($memberlist as $user) {
$membership = $target_group->MemberShipInfo($user);
$blob = array();
$blob["uid"] = $user->uid();
$blob["name"] = $user->name();
$blob["email"] = $user->email();
$blob["joined"] = DateStringGMT($user->created());
#
# Need to tag the group leader explicitly.
#
if ($user->SameUser($leader)) {
$blob["trust"] = "leader";
}
else {
$blob["trust"] = $newTrustMap[$membership["trust"]];
}
$blob["approved"] = ($target_group->UserTrust($user) ==
TBDB_TRUSTSTRING_NONE ? 0 : 1);
$members[$user->uid()] = $blob;
}
foreach ($nonmemberlist as $user) {
$blob = array();
$blob["uid"] = $user->uid();
$blob["name"] = $user->name();
$blob["email"] = $user->email();
$nonmembers[$user->uid()] = $blob;
}
$results = array("members" => $members,
"nonmembers" => $nonmembers);
SPITAJAX_RESPONSE($results);
}
function Do_GroupProfile()
{
global $this_user, $target_group;
if (CheckPageArgs()) {
return;
}
$results = array();
$leader = $target_group->GetLeader();
$results["pid"] = $target_group->pid();
$results["pid_idx"] = $target_group->pid_idx();
$results["gid"] = $target_group->gid();
$results["gid_idx"] = $target_group->gid_idx();
$results["leader"] = $target_group->leader();
$results["leader_idx"] = $target_group->leader_idx();
$results["leader_name"] = $leader->name();
$results["created"] = DateStringGMT($target_group->created());
$results["description"] = $target_group->description();
SPITAJAX_RESPONSE($results);
}
function Do_EditMembership()
{
global $this_user, $target_group, $ajax_args;
global $newTrustMap, $TB_PROJECT_ADDUSER;
if (CheckPageArgs()) {
return;
}
$pid = $target_group->pid();
$gid = $target_group->gid();
$isproject = ($pid == $gid ? 1 : 0);
if (!$target_group->AccessCheck($this_user, $TB_PROJECT_ADDUSER)) {
SPITAJAX_ERROR(-1, "You are not allowed to manage group membership");
return -1;
}
if (!isset($ajax_args["action"])) {
SPITAJAX_ERROR(-1, "Missing action");
return -1;
}
$action = $ajax_args["action"];
if (! ($action == "approve" || $action == "add" ||
$action == "deny" || $action == "remove")) {
SPITAJAX_ERROR(-1, "Invalid action");
return -1;
}
if ($isproject && $action != "remove") {
SPITAJAX_ERROR(-1, "Invalid action for project default group");
return -1;
}
if (!isset($ajax_args["users"])) {
SPITAJAX_ERROR(-1, "Missing target user list");
return -1;
}
if (!is_array($ajax_args["users"])) {
SPITAJAX_ERROR(-1, "Target user list is not an array");
return -1;
}
$users = $ajax_args["users"];
foreach ($users as $uid => $trust) {
if (!TBvalid_uid($uid)) {
SPITAJAX_ERROR(-1, "Invalid target user uid: $uid");
return -1;
}
$target_user = User::LookupByUid($uid);
if (!$target_user) {
SPITAJAX_ERROR(-1, "Unknown target user uid: $uid");
return -1;
}
#
# User may not be heading up active experiments in the group.
#
$explist = $target_user->ExperimentList(1, $target_group);
if (count($explist)) {
SPITAJAX_ERROR(-1, "User is still heading up experiments: $uid");
return -1;
}
# Check trust values.
if (!array_key_exists($trust, $newTrustMap)) {
SPITAJAX_ERROR(-1, "Invalid privilege: $trust");
return -1;
}
}
reset($users);
foreach ($users as $uid => $trust) {
$trust = $newTrustMap[$trust];
$target_user = User::LookupByUid($uid);
if (!$target_user) {
SPITAJAX_ERROR(-1, "Unknown target user uid: $uid");
return -1;
}
if ($isproject) {
$retval = SUEXEC($this_user->uid(), $pid,
"webrmuser -p $pid $uid",
SUEXEC_ACTION_CONTINUE);
}
else {
$option = ($action ==