quickvm.php 15.1 KB
Newer Older
Leigh B Stoller's avatar
Leigh B Stoller committed
1 2
<?php
#
3
# Copyright (c) 2000-2014 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");
28
chdir("apt");
Leigh B Stoller's avatar
Leigh B Stoller committed
29
include("quickvm_sup.php");
30
$page_title = "QuickVM Create";
Leigh B Stoller's avatar
Leigh B Stoller committed
31
$dblink = GetDBLink("sa");
Leigh B Stoller's avatar
Leigh B Stoller committed
32

33 34 35 36 37 38
#
# Get current user but make sure coming in on SSL.
#
RedirectSecure();
$this_user = CheckLogin($check_status);

Leigh B Stoller's avatar
Leigh B Stoller committed
39 40 41 42 43 44
#
# Verify page arguments.
#
$optargs = OptionalPageArguments("create",      PAGEARG_STRING,
				 "username",    PAGEARG_STRING,
				 "email",	PAGEARG_STRING,
Leigh B Stoller's avatar
Leigh B Stoller committed
45
				 "profile",     PAGEARG_STRING,
Leigh B Stoller's avatar
Leigh B Stoller committed
46 47
				 "stuffing",    PAGEARG_STRING,
				 "verify",      PAGEARG_STRING,
Leigh B Stoller's avatar
Leigh B Stoller committed
48
				 "sshkey",	PAGEARG_STRING,
49
				 "profile",     PAGEARG_INTEGER,
Leigh B Stoller's avatar
Leigh B Stoller committed
50 51 52 53 54 55 56 57 58
				 "ajax_request",  PAGEARG_BOOLEAN,
				 "ajax_method",   PAGEARG_STRING,
				 "ajax_argument", PAGEARG_STRING);

#
# Deal with ajax requests.
#
if (isset($ajax_request)) {
    if ($ajax_method == "getprofile") {
59
	$profile_idx = addslashes($ajax_argument);
Leigh B Stoller's avatar
Leigh B Stoller committed
60
	$query_result =
61 62
	    DBQueryWarn("select * from apt_profiles ".
			"where idx='$profile_idx'");
Leigh B Stoller's avatar
Leigh B Stoller committed
63 64

	if (!$query_result || !mysql_num_rows($query_result)) {
65
	    SPITAJAX_ERROR(1, "No such profile $profile_idx!");
Leigh B Stoller's avatar
Leigh B Stoller committed
66 67 68 69 70 71
	    exit();
	}
	$row = mysql_fetch_array($query_result);
	
	SPITAJAX_RESPONSE(array('rspec' => $row['rspec'],
				'name'  => $row['name'],
72
				'idx'   => $row['idx'],
Leigh B Stoller's avatar
Leigh B Stoller committed
73 74 75 76
				'description' => $row['description']));
    }
    exit();
}
Leigh B Stoller's avatar
Leigh B Stoller committed
77 78 79 80 81

# Form defaults.
$username_default = "Pick a user name";
$email_default    = "Your email address";
$sshkey_default   = "Your SSH public key";
82
$profile_default  = "ThreeVMs";
Leigh B Stoller's avatar
Leigh B Stoller committed
83
$profile_array    = array();
Leigh B Stoller's avatar
Leigh B Stoller committed
84

Leigh B Stoller's avatar
Leigh B Stoller committed
85
$query_result =
86 87 88
    DBQueryFatal("select * from apt_profiles ".
		 "where public=1 " .
		 ($this_user ? "or creator_idx=" . $this_user->uid_idx() : ""));
Leigh B Stoller's avatar
Leigh B Stoller committed
89
while ($row = mysql_fetch_array($query_result)) {
90 91 92 93
    $profile_array[$row["idx"]] = $row["name"];
    if ($row["pid"] == $TBOPSPID && $row["name"] == $profile_default) {
	$profile_default = $row["idx"];
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
94
}
95 96 97 98
# URL specified profile to use.
if (isset($profile) && array_key_exists($profile, $profile_array)) {
    $profile_default = $profile;
}
Leigh B Stoller's avatar
Leigh B Stoller committed
99

Leigh B Stoller's avatar
Leigh B Stoller committed
100
function SPITFORM($username, $email, $sshkey, $profile, $newuser, $errors)
Leigh B Stoller's avatar
Leigh B Stoller committed
101 102 103
{
    global $TBBASE, $TBMAIL_OPS;
    global $username_default, $email_default, $sshkey_default;
Leigh B Stoller's avatar
Leigh B Stoller committed
104
    global $profile_default, $profile_array;
Leigh B Stoller's avatar
Leigh B Stoller committed
105 106 107 108

    $username_value   = "";
    $email_value      = "";
    $sshkey_value     = "";
Leigh B Stoller's avatar
Leigh B Stoller committed
109
    $profile_value    = "";
Leigh B Stoller's avatar
Leigh B Stoller committed
110 111 112
    $username_error   = "";
    $email_error      = "";
    $sshkey_error     = "";
Leigh B Stoller's avatar
Leigh B Stoller committed
113
    $profile_error    = "";
Leigh B Stoller's avatar
Leigh B Stoller committed
114 115 116 117 118 119 120 121 122 123 124
    $internal_error   = null;

    if (isset($username) && $username != "") {
	$username_value = CleanString($username);
    }
    if (isset($email) && $email != "") {
	$email_value = CleanString($email);
    }
    if (isset($sshkey) && $sshkey != "") {
	$sshkey_value = CleanString($sshkey);
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
125 126
    if (isset($profile) && $profile != "") {
	$profile_value = CleanString($profile);
Leigh B Stoller's avatar
Leigh B Stoller committed
127 128 129 130 131 132 133 134 135 136 137
    }
    if ($errors) {
	while (list ($name, $message) = each ($errors)) {
	# XSS prevention.
	    $message = CleanString($message);
	    if ($name == "username")
		$username_error = $message;
	    elseif ($name == "email")
		$email_error = $message;
	    elseif ($name == "sshkey")
		$sshkey_error = $message;
Leigh B Stoller's avatar
Leigh B Stoller committed
138 139
	    elseif ($name == "profile")
		$profile_error = $message;
Leigh B Stoller's avatar
Leigh B Stoller committed
140 141 142 143 144
	    elseif ($name == "internal") {
		$internal_error = $message;
	    }
	}
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
145
    SPITHEADER(1);
Leigh B Stoller's avatar
Leigh B Stoller committed
146 147 148 149

    if ($internal_error) {
	echo "<center><h2>$internal_error</h2></center><br>\n";
    }
150
    echo "<div class='row'>
Leigh B Stoller's avatar
Leigh B Stoller committed
151
          <div class='col-lg-6  col-lg-offset-3
152
                      col-md-6  col-md-offset-3
Leigh B Stoller's avatar
Leigh B Stoller committed
153 154
                      col-sm-8  col-sm-offset-2
                      col-xs-12 col-xs-offset-0'>\n";
155
    echo "<form id='quickvm_form' role='form'
Leigh B Stoller's avatar
Leigh B Stoller committed
156
            method='post' action='quickvm.php'>\n";
157 158 159 160 161 162
    echo "<div class='panel panel-default'>
           <div class='panel-heading'>
              <h3 class='panel-title'>
                 Create an Experiment</h3></div>
           <div class='panel-body'>
            <div class='form-group'>
Leigh B Stoller's avatar
Leigh B Stoller committed
163 164
                <input name='username' id='username'
                       value='$username_value'
165
                       class='form-control'
Leigh B Stoller's avatar
Leigh B Stoller committed
166
                       placeholder='$username_default' autofocus type='text'>
167
		<label style='color: red'
Leigh B Stoller's avatar
Leigh B Stoller committed
168 169
                       for='username'>$username_error</label>
            </div>
170 171
            <div class='form-group'>
                <input name='email' id='email' type='text'
Leigh B Stoller's avatar
Leigh B Stoller committed
172
                       value='$email_value'
173
                       class='form-control'
Leigh B Stoller's avatar
Leigh B Stoller committed
174
                       placeholder='$email_default' type='text' />
175
		<label 
Leigh B Stoller's avatar
Leigh B Stoller committed
176 177 178
                       style='color: red'
                       for='email'>$email_error</label>
            </div>
179
            <div class='form-group'>
Leigh B Stoller's avatar
Leigh B Stoller committed
180 181
                <textarea id='sshkey' name='sshkey'
                          placeholder='$sshkey_default'
182
                          class='form-control'
Leigh B Stoller's avatar
Leigh B Stoller committed
183
                          rows=4 cols=45>$sshkey_value</textarea>
184
		<label
Leigh B Stoller's avatar
Leigh B Stoller committed
185 186 187
                       style='color: red'
                       for='sshkey'>$sshkey_error</label>
            </div>
Keith Downie's avatar
Keith Downie committed
188 189 190 191
            <div id='profile_well' class='form-group well well-md'>

            <span id='selected_profile_text' class='pull-left'>
            </span>
192
            <input id='selected_profile' type='hidden' name='profile'/>
Keith Downie's avatar
Keith Downie committed
193 194

              <button id='profile' class='btn btn-primary btn-xs pull-right' 
195
              type='button' name='profile_button'>
Keith Downie's avatar
Keith Downie committed
196 197 198
              Select a Profile
              </button>\n";
    echo " <label
Leigh B Stoller's avatar
Leigh B Stoller committed
199
                       style='color: red'
Leigh B Stoller's avatar
Leigh B Stoller committed
200
                       for='profile'>$profile_error</label>
Leigh B Stoller's avatar
Leigh B Stoller committed
201
            </div>
Keith Downie's avatar
Keith Downie committed
202
            <button class='btn btn-primary btn-sm pull-left'
203
                type='button' name='reset' id='reset-form'>
Keith Downie's avatar
Keith Downie committed
204 205 206 207
                      Reset Form</button>
            <button class='btn btn-success pull-right'
              type='submit' name='create'>Create!
            </button>
208
            <br> 
Keith Downie's avatar
Keith Downie committed
209
            
210 211 212
        </div>
        </div>
        </div>
Leigh B Stoller's avatar
Leigh B Stoller committed
213 214
        </div>\n";

215 216
    SpitVerifyModal("verify_modal", "Create");
    
Leigh B Stoller's avatar
Leigh B Stoller committed
217 218 219 220 221 222 223
    if ($newuser) {
	if (is_string($newuser)) {
	    $stuffing = $newuser;
	}
	else {
	    $stuffing = substr(GENHASH(), 0, 16);
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
224 225 226
	mail($email, "Confirm your email to create your Experiment",
	     "Here is your user verification code. Please copy and\n".
	     "paste this code into the box on the experiment page.\n\n".
Leigh B Stoller's avatar
Leigh B Stoller committed
227 228 229 230 231 232
	     "      $stuffing\n",
	     "From: $TBMAIL_OPS");
	echo "<input type='hidden' name='stuffing' value='$stuffing' />";
    }
    echo "</form>\n";

233
    SpitTopologyViewModal("quickvm_topomodal", $profile_array);
Keith Downie's avatar
Keith Downie committed
234

235 236
    echo "<script type='text/javascript'>\n";
    if (isset($profile) && $profile != "") {
237
        echo "window.PROFILE = '$profile_value';\n";
238 239
    }
    else {
240
        echo "window.PROFILE = '$profile_default';\n";
241 242 243 244 245 246
    }
    if ($newuser) {
	echo "window.APT_OPTIONS.isNewUser = true;\n";
    }
    echo "</script>\n";
    echo "<script src='js/lib/require.js' data-main='js/quickvm'></script>";
Leigh B Stoller's avatar
Leigh B Stoller committed
247 248 249 250 251
}

if (!isset($create)) {
    $username = null;
    $email    = null;
Leigh B Stoller's avatar
Leigh B Stoller committed
252
    $sshkey   = null;
253 254 255 256

    # 
    # Look for cookie that tells us who the user is. 
    #
Leigh B Stoller's avatar
Leigh B Stoller committed
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
    if (isset($_COOKIE['quickvm_user'])) {
	$geniuser = GeniUser::Lookup("sa", $_COOKIE['quickvm_user']);
	if ($geniuser) {
	    #
	    # Look for existing quickvm. User not allowed to create
	    # another one.
	    #
	    $quickvm = QuickVM::LookupByCreator($geniuser->uuid());
	    if ($quickvm && $quickvm->status() != "terminating") {
		header("Location: quickvm_status.php?uuid=" . $quickvm->uuid());
		return;
	    }
	    $username = $geniuser->name();
	    $email    = $geniuser->email();
	    $sshkey   = $geniuser->SSHKey();
	}
    }
274 275 276 277
    elseif ($this_user) {
	$username = $this_user->uid();
	$email    = $this_user->email();
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
278 279 280 281 282 283 284 285 286 287
    SPITFORM($username, $email, $sshkey, null, false, null);
    SPITFOOTER();
    return;
}
#
# Otherwise, must validate and redisplay if errors
#
$errors = array();
$args   = array();

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
if (!$this_user) {
    #
    # These check do not matter for a logged in user; we ignore the values.
    #
    if (!isset($email) || $email == "" || $email == $email_default) {
	$errors["email"] = "Missing Field";
    }
    elseif (! TBvalid_email($email)) {
	$errors["email"] = TBFieldErrorString();
    }
    if (!isset($username) || $username == "" ||
	$username == $username_default) {
	$errors["username"] = "Missing Field";
    }
    elseif (! TBvalid_uid($username)) {
	$errors["username"] = TBFieldErrorString();
    }
    elseif (User::LookupByUid($username)) {
        # Do not allow uid overlap with real users.
	$errors["username"] = "Already in use";
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
309
}
Leigh B Stoller's avatar
Leigh B Stoller committed
310 311
if (!isset($profile) || $profile == "") {
    $errors["profile"] = "No selection made";
Leigh B Stoller's avatar
Leigh B Stoller committed
312
}
Leigh B Stoller's avatar
Leigh B Stoller committed
313 314
elseif (! array_key_exists($profile, $profile_array)) {
    $errors["profile"] = "Invalid Profile: $profile";
Leigh B Stoller's avatar
Leigh B Stoller committed
315 316 317
}

if (count($errors)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
318
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
319 320 321 322 323 324 325
    SPITFOOTER();
    return;
}

#
# More sanity checks. 
#
326 327 328 329 330 331 332
if (!$this_user) {
    $geniuser = GeniUser::LookupByEmail("sa", $email);
    if ($geniuser) {
	if ($geniuser->name() != $username) {    
	    $errors["email"] = "Already in use by another user";
	    unset($geniuser);
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
333 334 335 336 337
    }
}
# Existing users are allowed to resuse their ssh key, but can supply
# a new one if they want.
if (!isset($sshkey) || $sshkey == "" || $sshkey == $sshkey_default) {
338
    if (!($geniuser || $this_user)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
339 340 341 342 343 344 345 346
	$errors["sshkey"] = "Missing Field";
    }
}
else {
    $args["sshkey"] = $sshkey;
}

if (count($errors)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
347
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
348 349 350
    SPITFOOTER();
    return;
}
351 352 353
# Silently ignore the form for a logged in user. 
$args["username"] = ($this_user ? $this_user->uid() : $username);
$args["email"]    = ($this_user ? $this_user->email() : $email);
Leigh B Stoller's avatar
Leigh B Stoller committed
354
$args["profile"]  = $profile;
Leigh B Stoller's avatar
Leigh B Stoller committed
355 356 357 358 359

#
# 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
360 361 362 363 364
# 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.
#
365 366 367
if (!$this_user &&
    (!$geniuser || !isset($_COOKIE['quickvm_authkey']) ||
     $_COOKIE['quickvm_authkey'] != $geniuser->auth_token())) {
Leigh B Stoller's avatar
Leigh B Stoller committed
368 369
    if (isset($stuffing) && $stuffing != "") {
	if (! (isset($verify) && $verify == $stuffing)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
370
	    SPITFORM($username, $email, $sshkey, $profile, $stuffing, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
371 372 373
	    SPITFOOTER();
	    return;
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
374 375 376 377 378
	#
	# 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.
	#
379 380
	if ($geniuser) {
	    $quickvm = QuickVM::LookupByCreator($geniuser->uuid());
Leigh B Stoller's avatar
Leigh B Stoller committed
381 382 383 384 385
	    if ($quickvm && $quickvm->status() != "terminating") {
		header("Location: quickvm_status.php?uuid=" . $quickvm->uuid());
		return;
	    }
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
386 387 388 389
	# Pass to backend to save in user object.
	$args["auth_token"] = $stuffing;
    }
    else {
Leigh B Stoller's avatar
Leigh B Stoller committed
390 391
	# Existing user, use existing auth token.
	# New user, we create a new one.
392
	$token = ($geniuser ? $geniuser->auth_token() : true);
Leigh B Stoller's avatar
Leigh B Stoller committed
393

Leigh B Stoller's avatar
Leigh B Stoller committed
394
	SPITFORM($username, $email, $sshkey, $profile, $token, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
	SPITFOOTER();
	return;
    }
}

# This is so we can look up the slice after the backend creates it.
$args["name"] = substr(GENHASH(), 0, 16);

#
# Generate a temporary file and write in the XML goo. 
#
$xmlname = tempnam("/tmp", "quickvm");
if (! $xmlname) {
    TBERROR("Could not create temporary filename", 0);
    $errors["internal"] = "Transient error(1); please try again later.";
}
elseif (! ($fp = fopen($xmlname, "w"))) {
    TBERROR("Could not open temp file $xmlname", 0);
    $errors["internal"] = "Transient error(2); please try again later.";
}
else {
    fwrite($fp, "<quickvm>\n");
    foreach ($args as $name => $value) {
	fwrite($fp, "<attribute name=\"$name\">");
	fwrite($fp, "  <value>" . htmlspecialchars($value) . "</value>");
	fwrite($fp, "</attribute>\n");
    }
    fwrite($fp, "</quickvm>\n");
    fclose($fp);
    chmod($xmlname, 0666);
}
if (count($errors)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
427
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
428 429 430 431 432 433 434 435 436 437 438
    SPITFOOTER();
    return;
}

#
# Invoke the backend. This will create the user and the slice record
# in the SA database, and then fork off in the background. If the
# first part works, we can return to the user and use some nifty ajax
# and javascript to watch for progress. We use a cookie that holds
# the slice uuid so that the JS code can ask about it.
#
439 440
# This option is used to tell the backend that it is okay to look
# in the emulab users table.
Leigh B Stoller's avatar
Leigh B Stoller committed
441
#
442 443 444
$opt = ($this_user ? "-l" : "");

$retval = SUEXEC("nobody", "nobody", "webquickvm $opt $xmlname",
Leigh B Stoller's avatar
Leigh B Stoller committed
445
		 SUEXEC_ACTION_CONTINUE);
Leigh B Stoller's avatar
Leigh B Stoller committed
446 447 448 449 450 451 452 453 454 455 456 457 458 459

if ($retval != 0) {
    if ($retval < 0) {
	$errors["internal"] = "Transient error(3); please try again later.";
    }
    else {
	if (count($suexec_output_array)) {
	    $line = $suexec_output_array[$i];
	    $errors["internal"] = $line;
	}
	else {
	    $errors["internal"] = "Transient error(4); please try again later.";
	}
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
460
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
461 462 463 464 465 466 467 468
    SPITFOOTER();
    return;
}
unlink($xmlname);

$quickvm = QuickVM::LookupByName($args["name"]);
if (!$quickvm) {
    $errors["internal"] = "Transient error(5); please try again later.";
Leigh B Stoller's avatar
Leigh B Stoller committed
469
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
470 471 472
    SPITFOOTER();
    return;
}
473 474 475 476 477 478
if ($this_user) {
    $creator = $this_user;
}
else {
    $creator = GeniUser::Lookup("sa", $quickvm->creator_uuid());
}
Leigh B Stoller's avatar
Leigh B Stoller committed
479 480
if (! $creator) {
    $errors["internal"] = "Transient error(6); please try again later.";
Leigh B Stoller's avatar
Leigh B Stoller committed
481
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
482 483 484
    SPITFOOTER();
    return;
}
485
#
Leigh B Stoller's avatar
Leigh B Stoller committed
486
# Remember the user and auth key so that we can verify.
487 488 489 490 491 492
#
# 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. 
#
493 494 495 496 497 498 499 500 501 502 503 504 505
if (!$this_user) {
    if (stristr($_SERVER["SERVER_NAME"], $TBAUTHDOMAIN)) {
	$cookiedomain = $TBAUTHDOMAIN;
    }
    else {
	$cookiedomain = $_SERVER["SERVER_NAME"];
    }
    setcookie("quickvm_user",
	      $creator->uuid(), time() + (24 * 3600 * 30),
	      "/", $cookiedomain, 0);
    setcookie("quickvm_authkey",
	      $creator->auth_token(), time() + (24 * 3600 * 30),
	      "/", $cookiedomain, 0);
506
}
Leigh B Stoller's avatar
Leigh B Stoller committed
507 508
header("Location: quickvm_status.php?uuid=" . $quickvm->uuid());
?>