manage_profile.php 17.6 KB
Newer Older
1 2
<?php
#
3
# Copyright (c) 2000-2017 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
# 
# {{{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");
26
include_once("webtask.php");
27 28
chdir("apt");
include("quickvm_sup.php");
29 30
include_once("profile_defs.php");
include_once("instance_defs.php");
31 32
# Must be after quickvm_sup.php since it changes the auth domain.
include_once("../session.php");
33
$page_title = "Manage Profile";
34
$notifyupdate = 0;
35
$notifyclone = 0;
36 37 38 39

#
# Get current user.
#
40
RedirectSecure();
41 42
$this_user = CheckLoginOrRedirect();
$this_idx  = $this_user->uid_idx();
43 44 45
if ((!isset($action) || $action == "create") && NOPROJECTMEMBERSHIP()) {
    return NoProjectMembershipError($this_user);
}
46 47 48 49 50

#
# Verify page arguments.
#
$optargs = OptionalPageArguments("create",      PAGEARG_STRING,
51
				 "action",      PAGEARG_STRING,
52
				 "uuid",        PAGEARG_STRING,
53
                                 "fromexp",     PAGEARG_STRING,
54
				 "copyuuid",    PAGEARG_STRING,
55
				 "snapuuid",    PAGEARG_STRING,
56
				 "snapnode_id", PAGEARG_NODEID,
57
				 "finished",    PAGEARG_BOOLEAN,
58 59 60 61 62
				 "formfields",  PAGEARG_ARRAY);

#
# Spit the form
#
63
function SPITFORM($formfields, $errors)
64
{
65
    global $this_user, $projlist, $action, $profile, $DEFAULT_AGGREGATE;
66
    global $notifyupdate, $notifyclone, $copyuuid, $snapuuid, $snapnode_id;
67
    global $ISCLOUD, $fromexp;
Leigh B Stoller's avatar
Leigh B Stoller committed
68
    global $version_array, $WITHPUBLISHING;
69 70
    $viewing    = 0;
    $candelete  = 0;
71
    $nodelete   = 0;
72 73
    $canmodify  = 0;
    $canpublish = 0;
Leigh B Stoller's avatar
Leigh B Stoller committed
74
    $history    = 0;
75
    $activity   = 0;
76
    $ispp       = 0;
77
    $isadmin    = (ISADMIN() ? 1 : 0);
Leigh B Stoller's avatar
Leigh B Stoller committed
78
    $isstud     = (STUDLY() ? 1 : 0);
79
    $canrepo    = (ISADMIN() || STUDLY() ? 1 : 0);
80
    $multisite  = ($ISCLOUD ? 1 : 0);
81
    $cloning    = 0;
82
    $copying    = 0;
83
    $disabled   = 0;
84 85
    $version_uuid = "null";
    $profile_uuid = "null";
86
    $this_version = "null";
87 88
    $latest_uuid    = "null";
    $latest_version = "null";
89

90
    if ($action == "edit") {
91
	$button_label = "Save";
92
	$viewing      = 1;
93 94
	$version_uuid = "'" . $profile->uuid() . "'";
	$profile_uuid = "'" . $profile->profile_uuid() . "'";
95
	$candelete    = ($profile->CanDelete($this_user) ? 1 : 0);
96
	$nodelete     = ($profile->isLocked() ? 1 : 0);
97 98 99
	$history      = ($profile->HasHistory() ? 1 : 0);
	$canmodify    = ($profile->CanModify() ? 1 : 0);
	$canpublish   = ($profile->CanPublish() ? 1 : 0);
100
	$activity     = ($profile->HasActivity() ? 1 : 0);
101
	$ispp         = ($profile->isParameterized() ? 1 : 0);
102
        $disabled     = ($profile->isDisabled() ? 1 : 0);
103
        $this_version = $profile->version();
104 105 106 107 108 109
	if ($canmodify) {
	    $title    = "Modify Profile";
	}
	else {
	    $title    = "View Profile";
	}
110 111 112
        $latest_profile = Profile::Lookup($profile->profile_uuid());
        $latest_uuid    = "'" . $latest_profile->uuid() . "'";
        $latest_version = $latest_profile->version();
113 114
    }
    else  {
115 116 117
        # New page action is now create, not copy or clone.
        if ($action == "copy" || $action == "clone") {
            if ($action == "clone") {
118
                $cloning = 1;
119
            }
120 121 122
            else {
                $copying = 1;
            }
123 124
	    $action = "create";
        }
125 126 127
	$button_label = "Create";
	$title        = "Create Profile";
    }
128 129 130

    SPITHEADER(1);

131
    echo "<div id='ppviewmodal_div'></div>\n";
132
    # Place to hang the toplevel template.
133 134 135 136 137 138 139 140 141
    echo "<div id='page-body'></div>\n";

    # Place to hang the genilib-editor template.
    echo "<div id='genilib-editor-body'></div>\n";

    # These two modals live outside so that genilib-editor can
    # use them as well.
    echo "<div id='waitwait_div'></div>
          <div id='oops_div'></div>";
142

143 144 145 146 147 148 149
    # I think this will take care of XSS prevention?
    echo "<script type='text/plain' id='form-json'>\n";
    echo htmlentities(json_encode($formfields)) . "\n";
    echo "</script>\n";
    echo "<script type='text/plain' id='error-json'>\n";
    echo htmlentities(json_encode($errors));
    echo "</script>\n";
150

151 152 153 154 155
    # Needed for genilib-editor
    echo "<script src='https://cdn.jsdelivr.net/ace/1.2.3/noconflict/ace.js'></script>\n";
    echo "<script src='https://cdn.jsdelivr.net/ace/1.2.3/noconflict/keybinding-vim.js'></script>\n";
    echo "<script src='https://cdn.jsdelivr.net/ace/1.2.3/noconflict/keybinding-emacs.js'></script>\n";

156 157 158 159
    # Pass project list through. Need to convert to list without groups.
    # When editing, pass through a single value. The template treats a
    # a single value as a read-only field.
    $plist = array();
Leigh B Stoller's avatar
Leigh B Stoller committed
160
    if ($viewing) {
161
	$plist[] = $formfields["profile_pid"];
162 163
    }
    else {
164
	while (list($project) = each($projlist)) {
165
	    $plist[] = $project;
166
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
167
    }
168 169 170
    echo "<script type='text/plain' id='projects-json'>\n";
    echo htmlentities(json_encode($plist));
    echo "</script>\n";
Leigh B Stoller's avatar
Leigh B Stoller committed
171 172 173 174 175 176

    if ($viewing) {
        echo "<script type='text/plain' id='versions-json'>\n";
        echo json_encode($version_array);
        echo "</script>\n";
    }
177
    
178
    echo "<link rel='stylesheet'
179
            href='css/jquery-ui-1.10.4.custom.min.css'>\n";
180
    echo "<link rel='stylesheet'
181
            href='css/jquery.appendGrid-1.3.1.min.css'>\n";
182
    # For progress bubbles in the imaging modal.
183
    echo "<link rel='stylesheet' href='css/progress.css'>\n";
184
    echo "<link rel='stylesheet' href='css/codemirror.css'>\n";
185 186
    echo "<link rel='stylesheet' href='css/genilib-editor.css'>\n";

187
    SpitAggregateStatus();
188
    echo "<script type='text/javascript'>\n";
189
    echo "    window.VIEWING  = $viewing;\n";
190 191
    echo "    window.VERSION_UUID = $version_uuid;\n";
    echo "    window.PROFILE_UUID = $profile_uuid;\n";
192 193
    echo "    window.LATEST_UUID = $latest_uuid;\n";
    echo "    window.LATEST_VERSION = $latest_version;\n";
194
    echo "    window.THIS_VERSION = $this_version;\n";
195
    echo "    window.UPDATED  = $notifyupdate;\n";
196
    echo "    window.SNAPPING = $notifyclone;\n";
197 198
    echo "    window.AJAXURL  = 'server-ajax.php';\n";
    echo "    window.ACTION   = '$action';\n";
199
    echo "    window.CANDELETE= $candelete;\n";
200
    echo "    window.NODELETE = $nodelete;\n";
201 202
    echo "    window.CANMODIFY= $canmodify;\n";
    echo "    window.CANPUBLISH= $canpublish;\n";
203
    echo "    window.DISABLED= $disabled;\n";
204
    echo "    window.ISADMIN  = $isadmin;\n";
Leigh B Stoller's avatar
Leigh B Stoller committed
205
    echo "    window.ISSTUD  = $isstud;\n";
206
    echo "    window.MULTISITE  = $multisite;\n";
207
    echo "    window.HISTORY  = $history;\n";
208
    echo "    window.CLONING  = $cloning;\n";
209
    echo "    window.COPYING  = $copying;\n";
210
    echo "    window.ACTIVITY = $activity;\n";
211 212
    echo "    window.TITLE    = '$title';\n";
    echo "    window.BUTTONLABEL = '$button_label';\n";
213
    echo "    window.ISPPPROFILE = $ispp;\n";
Leigh B Stoller's avatar
Leigh B Stoller committed
214
    echo "    window.WITHPUBLISHING = $WITHPUBLISHING;\n";
215 216 217 218
    if (isset($copyuuid)) {
	echo "    window.COPYUUID = '$copyuuid';\n";
    }
    elseif (isset($snapuuid)) {
219
	echo "    window.SNAPUUID = '$snapuuid';\n";
220 221 222
        if (isset($snapnode_id)) {
            echo "    window.SNAPNODE_ID = '$snapnode_id';\n";
        }
223
    }
224 225 226
    if (isset($fromexp)) {
	echo "    window.EXPUUID = '$fromexp';\n";
    }
227
    echo "    window.CANREPO = $canrepo;\n";
228
    echo "</script>\n";
229 230
    echo "<script src='js/lib/jquery-ui.js'></script>\n";
    echo "<script src='js/lib/jquery.appendGrid-1.3.1.min.js'></script>\n";
231
    echo "<script src='js/lib/codemirror-min.js'></script>\n";
232 233 234 235 236 237 238 239 240 241

    REQUIRE_UNDERSCORE();
    REQUIRE_SUP();
    REQUIRE_FILESIZE();
    REQUIRE_JACKS_EDITOR();
    REQUIRE_IMAGE();
    REQUIRE_MOMENT();
    REQUIRE_APTFORMS();
    REQUIRE_FILESTYLE();
    REQUIRE_MARKED();
242
    REQUIRE_GENILIB_EDITOR();
243
    AddLibrary("js/gitrepo.js");
244
    SPITREQUIRE("js/manage_profile.js");
245

246
    AddTemplateList(array('manage-profile', 'waitwait-modal', 'renderer-modal', 'showtopo-modal', 'oops-modal', 'rspectextview-modal', 'publish-modal', 'share-modal', 'gitrepo-picker', 'profile-list-modal', 'confirm-delete-profile', "copy-repobased-profile"));
247 248
    SPITFOOTER();
}
249 250 251 252

#
# See what projects the user can do this in.
#
253
$projlist = $this_user->ProjectAccessList($TB_PROJECT_CREATEEXPT);
254

Leigh B Stoller's avatar
Leigh B Stoller committed
255
if (isset($action) && ($action == "edit" || $action == "copy")) {
256
    if (!isset($uuid)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
257
	SPITUSERERROR("Must provide uuid!");
258 259 260 261 262 263 264 265 266
    }
    else {
	$profile = Profile::Lookup($uuid);
	if (!$profile) {
	    SPITUSERERROR("No such profile!");
	}
	else if ($profile->locked()) {
	    SPITUSERERROR("Profile is currently locked!");
	}
267
	else if ($profile->deleted()) {
268
	    SPITUSERERROR("Profile has been deleted!");
269
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
270 271 272 273 274 275
	if ($action == "edit") {
	    if ($this_idx != $profile->creator_idx() && !ISADMIN()) {
		SPITUSERERROR("Not enough permission!");
	    }
	}
	elseif (!$profile->CanView($this_user) && !ISADMIN()) {
276 277
	    SPITUSERERROR("Not enough permission!");
	}
278 279 280 281 282 283 284
        #
        # Spit out the version history.
        #
        $version_array  = array();
        $profileid      = $profile->profileid();

        $query_result =
285 286
            DBQueryFatal("select v.*,DATE(v.created) as created, ".
                         "    vp.uuid as parent_uuid ".
287
                         "  from apt_profile_versions as v ".
288 289 290 291
                         "left join apt_profile_versions as vp on ".
                         "     v.parent_profileid is not null and ".
                         "     vp.profileid=v.parent_profileid and ".
                         "     vp.version=v.parent_version ".
292 293
                         "where v.profileid='$profileid' ".
                         "order by v.version asc");
294 295 296

        while ($row = mysql_fetch_array($query_result)) {
            $uuid    = $row["uuid"];
297
            $puuid   = $row["parent_uuid"];
298 299 300
            $version = $row["version"];
            $pversion= $row["parent_version"];
            $created = $row["created"];
301
            $deleted = (isset($row["deleted"]) ? 1 : 0);
302
            $published = $row["published"];
303
            $repourl = $row["repourl"];
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
            $rspec   = $row["rspec"];
            $desc    = '';
            $obj     = array();

            if (!$published) {
                $published = " ";
            }
            else {
                $published = date("Y-m-d", strtotime($published));
            }
            $parsed_xml = simplexml_load_string($rspec);
            if ($parsed_xml &&
                $parsed_xml->rspec_tour &&
                $parsed_xml->rspec_tour->description) {
                $desc = (string) $parsed_xml->rspec_tour->description;
            }
            $obj["uuid"]    = $uuid;
            $obj["version"] = $version;
            $obj["description"] = $desc;
            $obj["created"]     = $created;
324
            $obj["deleted"]     = $deleted;
325
            $obj["published"]   = $published;
326
            $obj["parent_uuid"] = $puuid;
327
            $obj["parent_version"] = $pversion;
328
            $version_array[]  = $obj;
329
        }
330 331 332
    }
}

333 334 335
# We use a session.
session_start();

336
if (! isset($create)) {
337 338
    $errors   = array();
    $defaults = array();
339 340 341 342 343

    # Default action is create.
    if (! isset($action) || $action == "") {
	$action = "create";
    }
344

345
    if (! (isset($projlist) && count($projlist))) {
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
	SPITUSERERROR("You do not appear to be a member of any projects in ".
                      "which you have permission to create new profiles");
    }
    if (isset($snapuuid)) {
        if (!IsValidUUID($snapuuid)) {
            SPITUSERERROR("Not a valid UUID for clone");
        }
        else {
            $instance = Instance::Lookup($snapuuid);
            if (!$instance) {
                SPITUSERERROR("No such instance to clone!");
            }
            else if ($this_idx != $instance->creator_idx() && !ISADMIN()) {
                SPITUSERERROR("Not enough permission!");
            }
            else if ($instance->status() != "ready") {
                SPITUSERERROR("Instance is busy, cannot clone it. " .
                              "Please try again later.");
            }
        }
366
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
367 368 369
    if ($action == "edit" || $action == "clone" || $action == "copy") {
	if ($action == "clone" || $action == "copy") {
	    if ($action == "clone") {
370 371
		if (! isset($instance)) {
		    SPITUSERERROR("No experiment specified for clone!");
Leigh B Stoller's avatar
Leigh B Stoller committed
372 373 374 375 376 377 378 379 380
		}
		$profile = Profile::Lookup($instance->profile_id(),
					   $instance->profile_version());
		if (!$profile) {
		    SPITUSERERROR("Cannot load profile!");
		}
		if (!$profile->CanView($this_user)) {
		    SPITUSERERROR("Not allowed to access this profile!");
		}
381
	    }
382 383 384 385
            elseif ($action == "copy") {
                # Pass this along through the new create page.
                $copyuuid = $profile->uuid();
            }
Leigh B Stoller's avatar
Leigh B Stoller committed
386
	    $defaults["profile_who"]   = "private";
387 388 389
	    if ($profile->rspec() && $profile->rspec() != "") {
                $defaults["profile_rspec"]  = $profile->rspec();
            }
390 391 392
	    if ($profile->script() && $profile->script() != "") {
		$defaults["profile_script"] = $profile->script();
	    }
Leigh B Stoller's avatar
Leigh B Stoller committed
393 394
            $defaults["portal_converted"]
                = ($profile->portal_converted() == 1 ? "yes" : "no");
395
            
396 397 398 399 400 401
            # Default the project if in only one project.
	    if (count($projlist) == 1) {
		list($project) = each($projlist);
		reset($projlist);
		$defaults["profile_pid"] = $project;
	    }
402 403 404 405 406 407 408
            elseif (array_key_exists($profile->pid(), $projlist)) {
                #
                # Default to same project as the original, *if* the user
                # is a member of that project. Convenient.
                #
		$defaults["profile_pid"] = $profile->pid();
            }
409 410
	}
	else {
411 412 413
	    $defaults["profile_pid"]         = $profile->pid();
	    $defaults["profile_name"]        = $profile->name();
	    $defaults["profile_version"]     = $profile->version();
414 415 416
	    if ($profile->rspec() && $profile->rspec() != "") {
                $defaults["profile_rspec"] = $profile->rspec();
            }
417 418 419
	    if ($profile->script() && $profile->script() != "") {
		$defaults["profile_script"] = $profile->script();
	    }
Leigh B Stoller's avatar
Leigh B Stoller committed
420 421
            $defaults["portal_converted"]
                = ($profile->portal_converted() == 1 ? "yes" : "no");
422 423 424 425
	    if ($profile->repourl() && $profile->repourl() != "") {
		$defaults["profile_repourl"]  = $profile->repourl();
                # Need this so JS code knows when HEAD changes.
		$defaults["profile_repohash"]  = $profile->repohash();
426 427 428
		$defaults["profile_repopushurl"]
                    = "https://www.emulab.net:51369/githook/" .
                    $profile->repokey();
429
	    }
430
	    $defaults["profile_creator"]     = $profile->creator();
431
	    $defaults["profile_updater"]     = $profile->updater();
432 433
	    $defaults["profile_created"]     =
		DateStringGMT($profile->created());
434
	    $defaults["profile_published"]   =
435 436
		($profile->published() ?
		 DateStringGMT($profile->published()) : "");
437 438
	    $defaults["profile_version_url"] = $profile->URL();
	    $defaults["profile_profile_url"] = $profile->ProfileURL();
439 440 441 442 443
	    $defaults["profile_listed"]      =
		($profile->listed() ? "checked" : "");
	    $defaults["profile_who"] =
		($profile->shared() ? "shared" : 
		 ($profile->ispublic() ? "public" : "private"));
444 445
	    $defaults["profile_topdog"]      =
		($profile->topdog() ? "checked" : "");
446 447
	    $defaults["profile_disabled"]      =
		($profile->isDisabled() ? "checked" : "");
448 449
	    $defaults["profile_nodelete"]      =
		($profile->isLocked() ? "checked" : "");
450 451 452 453 454

	    # Warm fuzzy message.
	    if (isset($_SESSION["notifyupdate"])) {
		$notifyupdate = 1;
		unset($_SESSION["notifyupdate"]);
455 456
		session_destroy();
		session_commit();
Leigh B Stoller's avatar
Leigh B Stoller committed
457
	    }
458
	}
459 460 461 462 463 464 465 466 467 468
        #
        # See if we have a task running in the background
        # for this profile. At the moment it can only be a
        # clone task. If there is one, we have to tell
        # the js code to show the status of the clone.
        #
        $webtask = $profile->webtask();
        if ($webtask->TaskValue("cloning")) {
            $notifyclone = 1;
        }
469
    }
470 471 472
    else {
	# Default the project if in only one project.
	if (count($projlist) == 1) {
473 474 475
	    list($project) = each($projlist);
	    reset($projlist);
	    $defaults["profile_pid"] = $project;
476
	}
477 478 479 480 481 482 483 484
        elseif (isset($instance) &&
                array_key_exists($instance->pid(), $projlist)) {
            #
            # Default to same project as the original, *if* the user
            # is a member of that project. Convenient.
            #
            $defaults["profile_pid"] = $instance->pid();
        }
Leigh B Stoller's avatar
Leigh B Stoller committed
485
	$defaults["profile_who"]   = "private";
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500

        #
        # If coming from a classic emulab experiment, then do permission checks
        # and then use the NS file for the script. Also set the project.
        #
        if (isset($fromexp) && $fromexp != "") {
            $experiment = Experiment::LookupByUUID($fromexp);
            if (!$experiment) {
                SPITUSERERROR("No such classic emulab experiment!");
            }
            if (!$experiment->AccessCheck($this_user, $TB_EXPT_MODIFY)) {
                SPITUSERERROR("Not enough permission to create a profile from ".
                              "this classic emulab experiment");
            }
	    $defaults["profile_pid"] = $experiment->pid();
501
	    $defaults["profile_name"] = $experiment->eid();
502
        }
503
    }
504 505 506 507
    SPITFORM($defaults, $errors);
    return;
}

508
?>