instantiate.php 31.7 KB
Newer Older
Leigh B Stoller's avatar
Leigh B Stoller committed
1 2
<?php
#
Leigh B Stoller's avatar
Leigh B Stoller committed
3
# Copyright (c) 2000-2015 University of Utah and the Flux Group.
Leigh B Stoller's avatar
Leigh B Stoller committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
# 
# {{{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");
include_once("osinfo_defs.php");
include_once("geni_defs.php");
Leigh B Stoller's avatar
Leigh B Stoller committed
28
include_once("webtask.php");
29
chdir("apt");
Leigh B Stoller's avatar
Leigh B Stoller committed
30
include("quickvm_sup.php");
31 32
include_once("instance_defs.php");
include_once("profile_defs.php");
Robert Ricci's avatar
Robert Ricci committed
33
$page_title = "Instantiate a Profile";
Leigh B Stoller's avatar
Leigh B Stoller committed
34
$dblink = GetDBLink("sa");
Leigh B Stoller's avatar
Leigh B Stoller committed
35

36
#
37 38
# Get current user but make sure coming in on SSL. Guest users allowed
# via APT Portal.
39 40 41
#
RedirectSecure();
$this_user = CheckLogin($check_status);
42
if (isset($this_user)) {
43
    CheckLoginOrDie(CHECKLOGIN_NONLOCAL|CHECKLOGIN_WEBONLY);
44 45
}
elseif ($ISCLOUD) {
46
    RedirectLoginPage();
47
}
48

Leigh B Stoller's avatar
Leigh B Stoller committed
49 50 51
#
# Verify page arguments.
#
52 53
$optargs = OptionalPageArguments("create",        PAGEARG_STRING,
				 "profile",       PAGEARG_STRING,
54
				 "version",       PAGEARG_INTEGER,
55 56 57
				 "stuffing",      PAGEARG_STRING,
				 "verify",        PAGEARG_STRING,
				 "project",       PAGEARG_PROJECT,
58
				 "asguest",       PAGEARG_BOOLEAN,
59
				 "default",       PAGEARG_STRING,
60
				 "formfields",    PAGEARG_ARRAY);
61

62
if ($ISAPT && !$this_user) {
63 64 65 66 67 68 69 70 71 72 73 74 75
    #
    # If user appears to have an account, go to login page.
    # Continue as guest on that page.
    #
    if (REMEMBERED_ID()) {
	if (isset($asguest) && $asguest) {
	    # User clicked on continue as guest. If we do not delete the
	    # cookie, then user will go through the same loop next time
            # they click the Home button, since that points here. So delete
	    # the UID cookie. Not sure I like this.
	    ClearRememberedID();
	}
	else {
Leigh B Stoller's avatar
Leigh B Stoller committed
76 77
            header("Location: login.php?from=instantiate&referrer=".
                   urlencode($_SERVER['REQUEST_URI']));
78 79 80
	}
    }
}
81 82
if ($this_user) {
    $projlist = $this_user->ProjectAccessList($TB_PROJECT_CREATEEXPT);
83 84 85 86 87 88
    if (count($projlist) == 0) {
	SPITUSERERROR("You do not belong to any projects with permission to ".
                      "create new experiments. Please contact your project ".
                      "leader to grant you the neccessary privilege.");
	exit();
    }
89
}
90
if ($ISCLOUD) {
91 92
    $profile_default     = "ARM64OpenStack";
    $profile_default_pid = "emulab-ops";
93 94 95 96 97
}
else {
    $profile_default     = "OneVM";
    $profile_default_pid = $TBOPSPID;
}
98 99
$profile_array  = array();
$am_array       = Instance::DefaultAggregateList();
Leigh B Stoller's avatar
Leigh B Stoller committed
100

101 102 103 104
#
# if using the super secret URL, make sure the profile exists, and
# add to the array now since it might not be public or belong to the user.
#
105
if (isset($profile)) {
106 107
    #
    # Guest users must use the uuid, but logged in users may use the
108
    # internal index. But, we have to support simple the URL too, which
109
    # is /p/project/profilename, but only for public profiles.
110
    #
111
    if (isset($project) && isset($profile)) {
112
	$obj = Profile::LookupByName($project, $profile, $version);
113 114 115 116 117
    }
    elseif ($this_user || IsValidUUID($profile)) {
	$obj = Profile::Lookup($profile);
    }
    else {
118 119 120 121 122 123 124 125
	SPITUSERERROR("Illegal profile for guest user: $profile");
	exit();
    }
    if (! $obj) {
	SPITUSERERROR("No such profile: $profile");
	exit();
    }
    if (IsValidUUID($profile)) {
126 127 128 129 130 131 132 133 134 135 136 137
	#
	# If uuid was to profile, then find the most recently published
	# version and instantiate that, since what we have is the most
	# recent version, but might not be published.
	#
	if ($profile == $obj->profile_uuid() && !$obj->published()) {
	    $obj = $obj->LookupMostRecentPublished();
	    if (! $obj) {
		SPITUSERERROR("No published version for profile");
		exit();
	    }
	}
138 139 140
        $profile = $obj;
	$profile_array[$profile->uuid()] = $profile->name();
	$profilename = $profile->name();
141 142
    }
    else {
143 144 145 146 147 148 149 150 151 152 153 154 155
	#
	# If no version provided, then find the most recently published
	# version and instantiate that, since what we have is the most
	# recent version, but might not be published.
	#
	if (!isset($version) && !$obj->published()) {
	    $obj = $obj->LookupMostRecentPublished();
	    if (! $obj) {
		SPITUSERERROR("No published version for profile");
		exit();
	    }
	}
	 
156
	#
Leigh B Stoller's avatar
Leigh B Stoller committed
157
	# Must be public or pass the permission test for the user.
158 159
	#
	if (! ($obj->ispublic() ||
160
	       (isset($this_user) && $obj->CanInstantiate($this_user)))) {
161 162 163
	    SPITUSERERROR("No permission to use profile: $profile");
	    exit();
	}
164 165 166
	$profile = $obj;
	$profile_array[$profile->uuid()] = $profile->name();
	$profilename = $profile->name();
167
    }
168
}
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
else {
    #
    # Find all the public and user profiles. We use the UUID instead of
    # indicies cause we do not want to leak internal DB state to guest
    # users. Need to decide on what clause to use, depending on whether
    # a guest user or not.
    #
    $joinclause   = "";
    $whereclause  = "";
    if (!isset($this_user)) {
	$whereclause = "p.public=1";
    }
    else {
	$this_idx = $this_user->uid_idx();
	$joinclause =
	    "left join group_membership as g on ".
	    "     g.uid_idx='$this_idx' and ".
	    "     g.pid_idx=v.pid_idx and g.pid_idx=g.gid_idx";
	$whereclause =
	    "p.public=1 or p.shared=1 or v.creator_idx='$this_idx' or ".
	    "g.uid_idx is not null ";
190
    }
191 192 193 194 195 196 197

    $query_result =
	DBQueryFatal("select p.*,v.* from apt_profiles as p ".
		     "left join apt_profile_versions as v on ".
		     "     v.profileid=p.profileid and ".
		     "     v.version=p.version ".
		     "$joinclause ".
198 199
		     "where locked is null and ($whereclause) ".
		     "order by p.topdog desc");
200 201
    while ($row = mysql_fetch_array($query_result)) {
	$profile_array[$row["uuid"]] = $row["name"];
202 203 204 205 206 207 208
        if (isset($default)) {
            if ($default == $row["uuid"]) {
                $profile_default = $row["uuid"];
            }
        }
        elseif ($row["pid"] == $profile_default_pid &&
                $row["name"] == $profile_default) {
209
	    $profile_default = $row["uuid"];
210
	}
211
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
212
}
Leigh B Stoller's avatar
Leigh B Stoller committed
213

214
function SPITFORM($formfields, $newuser, $errors)
Leigh B Stoller's avatar
Leigh B Stoller committed
215
{
216
    global $TBBASE, $APTMAIL, $ISCLOUD;
217
    global $profile_array, $this_user, $profilename, $profile, $am_array;
218
    global $projlist;
219 220 221
    $amlist     = array();
    $showabout  = ($ISCLOUD || !$this_user ? 1 : 0);
    $registered = (isset($this_user) ? "true" : "false");
Leigh B Stoller's avatar
Leigh B Stoller committed
222 223
    $webonly    = (isset($this_user) &&
                   $this_user->webonly() ? "true" : "false");
224 225
    $nopprspec  = (!isset($this_user) || $this_user->IsNonLocal() ?
                   "true" : "false");
226 227 228 229 230 231 232 233 234
    $portal     = "";
    # Gack.
    if (isset($this_user) && $this_user->IsNonLocal()) {
        if (preg_match("/^[^+]*\+([^+]+)\+([^+]+)\+(.+)$/",
                       $this_user->nonlocal_id(), $matches) &&
            $matches[1] == "ch.geni.net") {
            $portal = "https://portal.geni.net/";
        }
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
235

236 237 238
    # XSS prevention.
    while (list ($key, $val) = each ($formfields)) {
	$formfields[$key] = CleanString($val);
Leigh B Stoller's avatar
Leigh B Stoller committed
239
    }
240
    # XSS prevention.
Leigh B Stoller's avatar
Leigh B Stoller committed
241
    if ($errors) {
242
	while (list ($key, $val) = each ($errors)) {
243
	    # Skip internal error, we want the html in those errors
244
	    # and we know it is safe.
245 246 247
	    if ($key == "error") {
		continue;
	    }
248
	    $errors[$key] = CleanString($val);
Leigh B Stoller's avatar
Leigh B Stoller committed
249 250
	}
    }
251 252 253 254 255 256 257 258 259

    $formatter = function($field, $html) use ($errors) {
	$class = "form-group";
	if ($errors && array_key_exists($field, $errors)) {
	    $class .= " has-error";
	}
	echo "<div class='$class'>\n";
	echo "     $html\n";
	if ($errors && array_key_exists($field, $errors)) {
260
	    echo "<label class='control-label' for='$field'>" .
261 262 263 264 265
		$errors[$field] . "</label>\n";
	}
	echo "</div>\n";
    };

Leigh B Stoller's avatar
Leigh B Stoller committed
266
    SPITHEADER(1);
Leigh B Stoller's avatar
Leigh B Stoller committed
267

268
    echo "<div id='ppviewmodal_div'></div>\n";
269
    echo "<div class='row'>
Leigh B Stoller's avatar
Leigh B Stoller committed
270
          <div class='col-lg-6  col-lg-offset-3
271
                      col-md-6  col-md-offset-3
Leigh B Stoller's avatar
Leigh B Stoller committed
272 273
                      col-sm-8  col-sm-offset-2
                      col-xs-12 col-xs-offset-0'>\n";
Jonathon Duerig's avatar
Jonathon Duerig committed
274

275 276
    # Placeholder for the "about" panel, which is now a template file.
    echo "<div id='about_div'></div>\n";
Jonathon Duerig's avatar
Jonathon Duerig committed
277

278
    echo "<form id='quickvm_form' role='form'
279
            enctype='multipart/form-data'
280
            method='post' action='instantiate.php'>\n";
281 282 283 284 285 286 287 288
    if (!$this_user) {
	echo "<div class='panel panel-default'>
                <div class='panel-heading'>
                   <h3 class='panel-title'>\n";
    }
    else {
	echo "<h3 style='margin-top: 0px;'>";
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
289
    echo "<center>Start Experiment";
290 291 292
    if (isset($profilename)) {
        echo " using profile &quot;$profilename&quot";
    }
293 294 295 296 297
    echo "</center></h3>\n";
    if (!$this_user) {
	echo "   </div>
	      <div class='panel-body'>\n";
    }
298 299 300 301 302
    
    #
    # If linked to a specific profile, description goes here
    #
    if ($profile) {
303 304
	$cluster = ($ISCLOUD ? "Cloudlab" : "APT");
	
305 306 307 308
	if (!$this_user) {
	    echo "  <p>Fill out the form below to run an experiment ".
		"using this profile:</p>\n";
	}
309
        # Note: Following line is also duplicated below
310 311 312
        echo "  <blockquote><p><span id='selected_profile_description'></span></p></blockquote>\n";
        echo "  <p>When you click the &quot;Create&quot; button, the virtual or
                   physical machines described in the profile will be booted
313
                   on ${cluster}'s hardware<p>\n";
314 315
    }

316 317 318 319 320 321
    echo "   <fieldset>\n";

    #
    # Look for non-specific error.
    #
    if ($errors && array_key_exists("error", $errors)) {
322 323
	echo "<font color=red><center>" . $errors["error"] .
	    "</center></font><br>";
324
    }
325 326 327 328

    #
    # Ask for user information
    #
329 330 331 332 333 334 335 336 337 338 339 340
    if (!isset($this_user)) {
	$formatter("username", 
		  "<input name=\"formfields[username]\"
		          value='" . $formfields["username"] . "'
                          class='form-control'
                          placeholder='Pick a user name'
                          autofocus type='text'>");
   
	$formatter("email", 
		  "<input name=\"formfields[email]\"
                          type='text'
                          value='" . $formfields["email"] . "'
341
                          class='form-control'
342
                          placeholder='Your email address' type='text'>");
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
    }
    # We put the ssh stuff in two different places, so make it a function.
    $spitsshkeystuff = function() use ($formfields, $formatter) {
	if ($formfields["sshkey"] == "") {
	    $title_text = "<span class='text-warning'>
                    No SSH key, browser shell only!<span>";
	    $expand_text = "Add Key";
	}
	else {
	    $title_text = "<span class='text-info'>
                    Your SSH key</span>";
	    $expand_text = "Update";
	}
        echo "<div class='form-group row' style='margin-bottom: 0px;'>";
        echo "  <div class='col-md-12'>
                  <div class='panel panel-default'>\n";
        echo "      <div class='panel-heading'>$title_text
                     <a class='pull-right'
                        data-toggle='collapse' href='#mysshkey'>
                      $expand_text</a>\n";
        echo "      </div>\n";
	echo "      <div id='mysshkey' class='panel-collapse collapse'>\n";
        echo "       <div class='panel-body'>";
	
367 368
	$formatter("keyfile",
		   "<span class='help-block'>
369 370 371
                     Upload a file or paste it in the text box. This will ".
		   "allow you to login using your favorite ssh client. Without ".
		   "a SSH key, you will be limited to using a shell window in ".
372 373 374
		   "your browser. If you already see a key here, you can ".
		   "change it and we will remember your new key for next time. ".
                   "Don't know how to generate your SSH key? ".
375
		   "See <a href='https://help.github.com/articles/generating-ssh-keys'>this tutorial.</a></span>".
376 377
		   "<input type=file name='keyfile'>");

378 379
	$formatter("sshkey", 
		  "<textarea name=\"formfields[sshkey]\" 
380
                             placeholder='Paste in your ssh public key.'
381 382 383
                             class='form-control'
                             rows=4 cols=45>" . $formfields["sshkey"] .
                  "</textarea>");
384 385 386 387 388 389 390 391 392

        echo "       </div>";
        echo "       <div class='clearfix'></div>";
        echo "      </div>";
        echo "    </div>";
        echo "</div></div>"; # End of panel/row.
    };
    if (!isset($this_user)) {
	$spitsshkeystuff();
393
    }
394 395 396 397 398 399

    #
    # Only print profile selection box if we weren't linked to a specific
    # profile
    #
    if (!isset($profile)) {
400
        echo "<div class='form-group row' style='margin-bottom: 0px;'>";
401 402
        echo "<input id='selected_profile' type='hidden' 
                       name='formfields[profile]'/>";
403
        echo "<div class='col-md-12'><div class='panel panel-default'>\n";
404
        echo "<div class='panel-heading'>
Robert Ricci's avatar
Robert Ricci committed
405
                  <span class='panel-title'><strong>Selected Profile:</strong> 
406
                  <span id='selected_profile_text'>
Robert Ricci's avatar
Robert Ricci committed
407
                  </span></span>\n";
408 409 410 411 412 413
        if ($errors && array_key_exists("profile", $errors)) {
            echo "<label class='control-label' for='inputError'>" .
                $errors["profile"] .
                " </label>\n";
        }
        echo " </div>\n";
414
        # Note: Following line is also duplicated above
415 416 417 418
        echo "<div class='panel-body'>";
        echo "  <div id='selected_profile_description'></div>\n";
        echo "</div>";
        echo "<div class='panel-footer'>";
419
        if (isset($this_user) && !$this_user->webonly()) {
420
            echo "<button class='btn btn-default btn-sm pull-left' 
421
                         type='button' id='profile_copy_button'
Leigh B Stoller's avatar
Leigh B Stoller committed
422 423 424 425 426 427 428 429 430 431
                         style='margin-right: 10px;'
		    data-toggle='popover'
		    data-delay='{hide:1500, show:500}'
		    data-html='true'
		    data-content='When you copy a profile
		    you are creating a new profile that
		    uses the same source code and metadata (description,
		    instructions) as the original profile, but without
		    creating a new disk image. Instead, the new profile uses
		    whatever images the original profile uses.'>Copy Profile
432
                  </button>";
433 434 435 436
            echo "<button class='btn btn-default btn-sm pull-left'
                          type='button' id='profile_show_button'>
                    Show Profile
                  </button>";
437 438
        }
        echo "<button id='profile' class='btn btn-primary btn-sm pull-right' 
439
                         type='button' name='profile_button'>
440 441 442 443
                    Change Profile
                  </button>";
        echo "<div class='clearfix'></div>";
        echo "</div>";
444
        echo "</div></div></div>"; # End of panel/row.
445
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
446
    else {
447 448 449 450 451 452
	echo "<input id='selected_profile' type='hidden'
                     name='formfields[profile]'
                     value='" . $formfields["profile"] . "'>\n";

	# Send the original argument for the initial array stuff above.
        # Needs more work.
453 454
        $thisuuid = $profile->uuid();
	echo "<input type='hidden' name='profile' value='$thisuuid'>\n";
Leigh B Stoller's avatar
Leigh B Stoller committed
455
    }
456
    if (isset($this_user) && !$this_user->webonly()) {
457 458
        echo "<div class='panel panel-info'>\n";
        echo "  <div class='panel-body bg-info' style='padding: 5px;'>\n";
459 460 461 462 463 464
        #
        # Local users, show a link to the ssh keys page.
        # Nonlocal users, remind them ssh keys go into their portal.
        #
        if ($this_user->IsNonLocal()) {
            echo "<div>";
465
            echo "  <div>
466 467 468 469 470 471 472 473
                    GENI Users; be sure to add ssh keys at <b>your</b> portal if
                    you want to log in from your desktop, else you
	            will be limited to using a shell window in your browser. 
                    </div>
                  </div>\n";
        }
        else {
            echo "<div>";
474
            echo "  <div>
475 476 477 478 479 480
                     <a href='ssh-keys.php'>Manage your SSH keys</a> if
                     you want to log in from your desktop, else you
		     will be limited to using a shell window in your browser. 
                    </div>
                  </div>\n";
        }
481 482
        echo "  </div>\n";
        echo "</div>\n";
483
    }
484

485 486 487 488
    #
    # Spit out an experient name box, which is optional and prefilled
    # with a default.
    #
489
    if ($this_user && ISADMIN()) {
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
        $thisclass = "form-group";
        if ($errors && array_key_exists("name", $errors)) {
            $thisclass .= " has-error";
        }
        echo "<div class='form-horizontal'>
                <div class='$thisclass'>
                  <label class='col-sm-4 control-label'
                         style='text-align: right;'>Name:</label>
                  <div class='col-sm-6'
                       data-toggle='popover'
		       data-delay='{hide:1500, show:500}'
		       data-html='true'
		       data-content='Provide a unique name to identity your
                           new experiment. If you are in
                           the habit of starting more then one experiment at
                           a time this is really handy when trying to tell
                           one experiment from another, or when referring to
                           an experiment when asking for help.'>
                    <input id='experiment_name'
                           placeholder='Optional'
                           class='form-control'
                           name='formfields[name]'
                           value='" . $formfields["name"] . "'>\n";
        if ($errors && array_key_exists("name", $errors)) {
            echo "  <label class='control-label'>". $errors["name"] . "</label>";
        }
        echo "    </div>
                </div>
              </div>\n";
    }
     
521 522 523
    #
    # Spit out a project selection list if more then one project membership
    #
524
    if ($this_user && !$this_user->webonly()) {
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
        if (count($projlist) == 1) {
            echo "<input id='profile_pid' type='hidden'
                     name='formfields[pid]'
                     value='" . $formfields["pid"] . "'>\n";
        }
        else {
            $project_options = "";
            while (list($pid) = each($projlist)) {
                $selected = "";
                if ($formfields["pid"] == $pid) {
                    $selected = "selected";
                }
                $project_options .= 
                    "<option $selected value='$pid'>$pid</option>\n";
            }
            $html =
                "<div class='form-horizontal'><div class='form-group'>
                   <label class='col-sm-4 control-label'
                      style='text-align: right;'>Project:</label>
                   <div class='col-sm-6'>
                       <select name=\"formfields[pid]\"
		              id='profile_pid' class='form-control'>
                       $project_options</select></div></div></div>\n";
            echo $html;
        }
    }

552 553
    if (isset($this_user) && !$this_user->webonly() && 
        ($ISCLOUD || ISADMIN() || STUDLY())) {
554 555
	$am_options = "";
	while (list($am, $urn) = each($am_array)) {
556
	    $amlist[] = $am;
557 558 559 560 561 562 563
	    $selected = "";
	    if ($formfields["where"] == $am) {
		$selected = "selected";
	    }
	    $am_options .= 
		"<option $selected value='$am'>$am</option>\n";
	}
564
        $html =
Leigh B Stoller's avatar
Leigh B Stoller committed
565 566
                "<div class='form-horizontal' id='aggregate_selector'>
                   <div class='form-group'>
567
                   <label class='col-sm-4 control-label'
568 569
                      style='text-align: right;'>
       <a href=cluster-status.php target=_blank>Cluster:</a></label>
570 571 572
                   <div class='col-sm-6'>
                       <select name=\"formfields[where]\"
		              id='profile_where' class='form-control'>
573 574 575
                       $am_options</select><br>
		       <div class='alert alert-warning' id='where-warning' style='display: none'>This profile only works on some clusters. Incompatible clusters are unselectable.</div>
</div></div><div>\n";
576
            echo $html;
577
    }
578
    echo "</fieldset>
579
           <div class='form-group row'>
580 581 582 583 584
           <div class='col-sm-6 col-sm-offset-3'>
           <button class='btn btn-primary btn-block hidden'
              id='configurator_button'
              type='button'>Configure
           </button>
585
           <button class='btn btn-success btn-block' id='instantiate_submit'
Keith Downie's avatar
Keith Downie committed
586
              type='submit' name='create'>Create!
587
           </button>
588
           </div>
589
           </div>
Leigh B Stoller's avatar
Leigh B Stoller committed
590
        </div>\n";
591
    if (!isset($this_user)) {
592 593
        echo "</div>
              </div>\n";
594
	SpitVerifyModal("verify_modal", "Create");
595
    
596 597 598 599 600 601 602 603
	if ($newuser) {
	    if (is_string($newuser)) {
		$stuffing = $newuser;
	    }
	    else {
		$stuffing = substr(GENHASH(), 0, 16);
	    }
	    mail($formfields["email"],
604
		 "aptlab.net: Verification code for creating your experiment",
605 606 607
		 "Here is your user verification code. Please copy and\n".
		 "paste this code into the box on the experiment page.\n\n".
		 "      $stuffing\n",
Leigh B Stoller's avatar
Leigh B Stoller committed
608
		 "From: $APTMAIL");
609
	    echo "<input type='hidden' name='stuffing' value='$stuffing' />";
Leigh B Stoller's avatar
Leigh B Stoller committed
610 611
	}
    }
612
    echo "</div>\n";
613 614 615 616 617
    # This is for a PP rspec.
    echo "<textarea name='formfields[pp_rspec]'
		    id='pp_rspec_textarea'
                    class='form-control hidden'
                    type='textarea'></textarea>\n";
Leigh B Stoller's avatar
Leigh B Stoller committed
618 619
    echo "</form>\n";

620
    SpitTopologyViewModal("quickvm_topomodal", $profile_array);
621 622 623 624 625
    echo "<div id='waitwait_div'></div>\n";
    echo "<div id='ppmodal_div'></div>\n";
    echo "<div id='instantiate_div'></div>\n";
    echo "<div id='editmodal_div'></div>\n";
    SpitOopsModal("oops");
Keith Downie's avatar
Keith Downie committed
626

627 628
    if (isset($this_user) && !$this_user->webonly() &&
        ($ISCLOUD || ISADMIN() || STUDLY())) {
629 630 631 632
	echo "<script type='text/plain' id='amlist-json'>\n";
	echo htmlentities(json_encode($amlist));
	echo "</script>\n";
    }
633
    echo "<script type='text/javascript'>\n";
634 635 636
    echo "    window.PROFILE    = '" . $formfields["profile"] . "';\n";
    echo "    window.AJAXURL    = 'server-ajax.php';\n";
    echo "    window.SHOWABOUT  = $showabout;\n";
637
    echo "    window.NOPPRSPEC  = $nopprspec;\n";
638
    echo "    window.REGISTERED = $registered;\n";
639 640
    echo "    window.WEBONLY    = $webonly;\n";
    echo "    window.PORTAL     = '$portal';\n";
641 642 643 644
    if ($newuser) {
	echo "window.APT_OPTIONS.isNewUser = true;\n";
    }
    echo "</script>\n";
645
    echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
646
    echo "<script src='js/lib/bootstrap.js'></script>\n";
647
    echo "<script src='js/lib/require.js' data-main='js/instantiate'></script>";
Leigh B Stoller's avatar
Leigh B Stoller committed
648 649 650
}

if (!isset($create)) {
651 652 653 654
    $defaults = array();
    $defaults["username"] = "";
    $defaults["email"]    = "";
    $defaults["sshkey"]   = "";
655 656
    $defaults["profile"]  = (isset($profile) ?
                             $profile->uuid() : $profile_default);
657
    $defaults["where"]    = $DEFAULT_AGGREGATE;
658 659 660 661 662 663 664 665
    if ($this_user && count($projlist)) {
	list($project, $grouplist) = each($projlist);
        $defaults["pid"] = $project;
        reset($projlist);
    }
    else {
        $defaults["pid"] = "";
    }
666

667
    # 
668
    # Look for current user or cookie that tells us who the user is. 
669
    #
670
    if ($this_user) {
671 672
	$defaults["username"] = $this_user->uid();
	$defaults["email"]    = $this_user->email();
673 674 675 676 677 678 679
	#
	# Look for an key marked as an APT uploaded key and use that.
	# If no APT key, use any uploaded key; if the user leaves this
	# key in the form, it will become the official APT key.
	#
	$sshkey = $this_user->GetAPTSSHKey();
	if (!$sshkey) {
680 681
	    $sshkeys = $this_user->GetSSHKeys();
	    if (count($sshkeys)) {
682
		$sshkey = $sshkeys[0];
683 684
	    }
	}
685 686 687
	if ($sshkey) {
	    $defaults["sshkey"] = $sshkey;
	}
688 689
    }
    elseif (isset($_COOKIE['quickvm_user'])) {
Leigh B Stoller's avatar
Leigh B Stoller committed
690 691 692 693 694 695
	$geniuser = GeniUser::Lookup("sa", $_COOKIE['quickvm_user']);
	if ($geniuser) {
	    #
	    # Look for existing quickvm. User not allowed to create
	    # another one.
	    #
Leigh B Stoller's avatar
Leigh B Stoller committed
696 697
	    $instance = Instance::LookupByCreator($geniuser->uuid());
	    if ($instance && $instance->status() != "terminating") {
698 699
		header("Location: status.php?oneonly=1&uuid=" .
		       $instance->uuid());
Leigh B Stoller's avatar
Leigh B Stoller committed
700 701
		return;
	    }
702 703 704 705 706 707 708 709
            #
            # Watch for too many instances by guest user and redirect
            # to the signup page.
            #
            if (Instance::GuestInstanceCount($geniuser) > $MAXGUESTINSTANCES) {
		header("Location: signup.php?toomany=1");
		return;
            }
710 711 712
	    $defaults["username"] = $geniuser->name();
	    $defaults["email"]    = $geniuser->email();
	    $defaults["sshkey"]   = $geniuser->SSHKey();
Leigh B Stoller's avatar
Leigh B Stoller committed
713 714
	}
    }
715
    SPITFORM($defaults, false, array());
716
    echo "<div style='display: none'><div id='jacks-dummy'></div></div>\n";
Leigh B Stoller's avatar
Leigh B Stoller committed
717 718 719 720 721 722 723 724 725
    SPITFOOTER();
    return;
}
#
# Otherwise, must validate and redisplay if errors
#
$errors = array();
$args   = array();

726 727 728 729
if (!$this_user) {
    #
    # These check do not matter for a logged in user; we ignore the values.
    #
730
    if (!isset($formfields["email"]) || $formfields["email"] == "") {
731 732
	$errors["email"] = "Missing Field";
    }
733
    elseif (! TBvalid_email($formfields["email"])) {
734 735
	$errors["email"] = TBFieldErrorString();
    }
736
    if (!isset($formfields["username"]) || $formfields["username"] == "") {
737 738
	$errors["username"] = "Missing Field";
    }
739
    elseif (! TBvalid_uid($formfields["username"])) {
740 741
	$errors["username"] = TBFieldErrorString();
    }
742
    elseif (User::LookupByUid($formfields["username"])) {
743
        # Do not allow uid overlap with real users.
744
	$errors["username"] = "Already in use - if you have an Emulab account, log in first";
745
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
746
}
747
if (!isset($formfields["profile"]) || $formfields["profile"] == "") {
Leigh B Stoller's avatar
Leigh B Stoller committed
748
    $errors["profile"] = "No selection made";
Leigh B Stoller's avatar
Leigh B Stoller committed
749
}
750 751
elseif (! array_key_exists($formfields["profile"], $profile_array)) {
    $errors["profile"] = "Invalid Profile: " . $formfields["profile"];
Leigh B Stoller's avatar
Leigh B Stoller committed
752
}
753 754 755 756 757 758
else {
    $profile = Profile::Lookup($formfields["profile"]);
    if (!$profile) {
	$errors["profile"] = "No such profile in the database";
    }
}
Leigh B Stoller's avatar
Leigh B Stoller committed
759

760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
if ($this_user) {
    #
    # Project has to exist.  
    #
    $project = Project::LookupByPid($formfields["pid"]);
    if (!$project) {
        $errors["pid"] = "No such project";
    }
    # User better be a member.
    elseif (!ISADMIN() &&
        (!$project->IsMember($this_user, $isapproved) || !$isapproved)) {
        $errors["pid"] = "Illegal project";
    }
    else {
        $args["pid"] = $project->pid_idx();
    }
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792

    # Experiment name is optional, we generate one later.
    if (isset($formfields["name"]) && $formfields["name"] != "") {
        if (strlen($formfields["name"]) > 16) {
            $errors["name"] = "Too long; must be <= 16 characters";
        }
        elseif (!TBvalid_eid($formfields["name"])) {
            $errors["name"] = TBFieldErrorString();
        }
        elseif ($project &&
                Instance::LookupByName($project, $formfields["name"])) {
            $errors["name"] = "Already in use by another experiment";
        }
        else {
            $args["instance_name"] = $formfields["name"];
        }
    }
793 794
}

Leigh B Stoller's avatar
Leigh B Stoller committed
795 796 797
#
# More sanity checks. 
#
798
if (!$this_user) {
799
    $geniuser = GeniUser::LookupByEmail("sa", $formfields["email"]);
800
    if ($geniuser) {
801
	if ($geniuser->name() != $formfields["username"]) {    
Leigh B Stoller's avatar
Leigh B Stoller committed
802
            $errors["email"] = "Already in use by another guest user";
803 804
	    unset($geniuser);
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
805 806
    }
}
807

808 809 810 811 812 813 814 815 816 817
#
# Real users are allowed to use Paramterized Profiles, which means
# we could get an rspec.
#
if ($profile && $profile->isParameterized() && 
    $this_user && !$this_user->IsNonLocal() &&
    isset($formfields["pp_rspec"]) && $formfields["pp_rspec"] != "") {
    $args["rspec"] = $formfields["pp_rspec"];
}

818 819 820 821 822
#
# Allow admin users to select the Aggregate. Experimental.
#
$aggregate_urn = "";

823
if ($this_user && ($ISCLOUD || ISADMIN() || STUDLY())) {
824 825 826 827 828 829 830 831 832 833
    if (isset($formfields["where"]) && $formfields["where"] != "") {
	if (array_key_exists($formfields["where"], $am_array)) {
	    $aggregate_urn = $am_array[$formfields["where"]];
	}
	else {
	    $errors["where"] = "Invalid Aggregate";
	}
    }
}

834 835 836 837 838 839
if (count($errors)) {
    SPITFORM($formfields, false, $errors);
    SPITFOOTER();
    return;
}

840 841
#
# SSH keys are now optional for guest users; they just have to
842 843 844 845
# use the web based ssh window.
#
# Backend verifies pubkey and returns error. We first look for a 
# file and then fall back to an inline field.
846
#
847 848 849 850 851 852
if (isset($_FILES['keyfile']) &&
    $_FILES['keyfile']['name'] != "" &&
    $_FILES['keyfile']['name'] != "none") {

    $localfile = $_FILES['keyfile']['tmp_name'];
    $args["sshkey"] = file_get_contents($localfile);
Leigh B Stoller's avatar
Leigh B Stoller committed
853 854 855 856 857
    #
    # The filename will be lost on another trip through the browser.
    # So stick the key into the box.
    #
    $formfields["sshkey"] = $args["sshkey"];
858 859
}
elseif (isset($formfields["sshkey"]) && $formfields["sshkey"] != "") {
860
    $args["sshkey"] = $formfields["sshkey"];
Leigh B Stoller's avatar
Leigh B Stoller committed
861 862 863
}

if (count($errors)) {
864
    SPITFORM($formfields, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
865 866 867
    SPITFOOTER();
    return;
}
868
# Silently ignore the form for a logged in user. 
869 870 871
$args["username"] = ($this_user ? $this_user->uid() : $formfields["username"]);
$args["email"]    = ($this_user ? $this_user->email() : $formfields["email"]);
$args["profile"]  = $formfields["profile"];
Leigh B Stoller's avatar
Leigh B Stoller committed
872 873 874 875 876

#
# See if user exists and is verified. We send email with a code, which
# they have to paste back into a box we add to the form. See above.
#
Leigh B Stoller's avatar
Leigh B Stoller committed
877 878 879 880 881
# We also get here if the user exists, but the browser did not have
# the tokens, as will happen if switching to another browser. We
# force the user to repeat the verification with the same code we
# have stored in the DB.
#
882 883 884
if (!$this_user &&
    (!$geniuser || !isset($_COOKIE['quickvm_authkey']) ||
     $_COOKIE['quickvm_authkey'] != $geniuser->auth_token())) {
Leigh B Stoller's avatar
Leigh B Stoller committed
885 886
    if (isset($stuffing) && $stuffing != "") {
	if (! (isset($verify) && $verify == $stuffing)) {
887
	    SPITFORM($formfields, $stuffing, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
888 889 890
	    SPITFOOTER();
	    return;
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
891 892 893 894 895
	#
	# If this is an existing user and they give us the right code,
	# we can check again for an existing VM and redirect to the
	# status page, like we do above.
	#
896
	if ($geniuser) {
Leigh B Stoller's avatar
Leigh B Stoller committed
897 898
	    $instance = Instance::LookupByCreator($geniuser->uuid());
	    if ($instance && $instance->status() != "terminating") {
899 900 901 902 903
		# Reset this cookie so status page is happy and so we
                # will stop asking.
		setcookie("quickvm_user",
			  $geniuser->uuid(), time() + (24 * 3600 * 30),
			  "/", $TBAUTHDOMAIN, 0);
904 905
		header("Location: status.php?oneonly=1&uuid=" .
		       $instance->uuid());
Leigh B Stoller's avatar
Leigh B Stoller committed
906 907
		return;
	    }
908 909 910 911 912 913 914 915
            #
            # Watch for too many instances by guest user and redirect
            # to the signup page.
            #
            if (Instance::GuestInstanceCount($geniuser) > $MAXGUESTINSTANCES) {
		header("Location: signup.php?toomany=1");
		return;
            }
Leigh B Stoller's avatar
Leigh B Stoller committed
916
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
917 918 919 920
	# Pass to backend to save in user object.
	$args["auth_token"] = $stuffing;
    }
    else {
Leigh B Stoller's avatar
Leigh B Stoller committed
921 922
	# Existing user, use existing auth token.
	# New user, we create a new one.
923
	$token = ($geniuser ? $geniuser->auth_token() : true);
Leigh B Stoller's avatar
Leigh B Stoller committed
924

925
	SPITFORM($formfields, $token, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
926 927 928 929 930
	SPITFOOTER();
	return;
    }
}

931 932
# Admins can change aggregate.
$options      = ($aggregate_urn != "" ? " -a '$aggregate_urn'" : "");
Leigh B Stoller's avatar
Leigh B Stoller committed
933 934

#
935
# Invoke the backend.
Leigh B Stoller's avatar
Leigh B Stoller committed
936
#
937 938
list ($instance, $creator) =
    Instance::Instantiate($this_user, $options, $args, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
939

Leigh B Stoller's avatar
Leigh B Stoller committed
940
if (!$instance) {
941
    SPITFORM($formfields, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
942 943 944
    SPITFOOTER();
    return;
}
945
    
946
#
Leigh B Stoller's avatar
Leigh B Stoller committed
947
# Remember the user and auth key so that we can verify.
948 949 950 951 952 953
#
# The cookie handling is a pain since we run this under the aptlab
# virtual host, but the config uses a different domain, and so the
# cookies do not work. So, we have to look at our SERVER_NAME and
# set the cookie appropriately. 
#
954
if (!$this_user) {
Leigh B Stoller's avatar
Leigh B Stoller committed
955 956
    $cookiedomain = $TBAUTHDOMAIN;

957 958 959 960 961 962
    setcookie("quickvm_user",
	      $creator->uuid(), time() + (24 * 3600 * 30),
	      "/", $cookiedomain, 0);
    setcookie("quickvm_authkey",
	      $creator->auth_token(), time() + (24 * 3600 * 30),
	      "/", $cookiedomain, 0);
963
}
Leigh B Stoller's avatar
Leigh B Stoller committed
964
header("Location: status.php?uuid=" . $instance->uuid());
Leigh B Stoller's avatar
Leigh B Stoller committed
965
?>