instantiate.php 16.3 KB
Newer Older
Leigh B Stoller's avatar
Leigh B Stoller committed
1
2
<?php
#
3
# Copyright (c) 2000-2016 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");
33
34
# Must be after quickvm_sup.php since it changes the auth domain.
include_once("../session.php");
Robert Ricci's avatar
Robert Ricci committed
35
$page_title = "Instantiate a Profile";
Leigh B Stoller's avatar
Leigh B Stoller committed
36
$dblink = GetDBLink("sa");
Leigh B Stoller's avatar
Leigh B Stoller committed
37

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

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

63
if ($ISAPT && !$this_user) {
64
65
66
67
68
69
70
71
72
73
74
75
76
    #
    # 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
77
78
            header("Location: login.php?from=instantiate&referrer=".
                   urlencode($_SERVER['REQUEST_URI']));
79
80
81
	}
    }
}
82

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#
# Alternate version of the picker, temporary.
#
if (isset($classic)) {
    #
    # This file is the default picker.
    #
    if ($classic == "true") {
        setcookie("picker", "classic", 0, "/", $TBAUTHDOMAIN, 0);
        $classic = 1;
    }
    else {
        setcookie("picker", "new", 0, "/", $TBAUTHDOMAIN, 0);
        $classic = 0;
    }
}
elseif (isset($_COOKIE['picker'])) {
    $classic = ($_COOKIE['picker'] == "classic" ? 1 : 0);
}
else {
103
    $classic = 0;
104
105
}

106
107
if ($this_user) {
    $projlist = $this_user->ProjectAccessList($TB_PROJECT_CREATEEXPT);
108
109
110
111
112
113
    #
    # Cull out the nonlocal projects, we do not want to show those
    # since they are just the holding projects.
    #
    $tmp = array();
    while (list($pid) = each($projlist)) {
114
115
116
        # Watch out for killing page variable called "project"
        $proj = Project::Lookup($pid);
        if ($proj && !$proj->IsNonLocal()) {
117
118
119
120
121
            $tmp[$pid] = $projlist[$pid];
        }
    }
    $projlist = $tmp;
    
122
123
124
125
126
127
    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();
    }
128
}
129
if ($ISCLOUD) {
130
131
    $profile_default     = "OpenStack";
    $profile_default_pid = "emulab-ops";
132
}
133
134
135
136
elseif ($ISPNET) {
    $profile_default     = "OneVM";
    $profile_default_pid = $TBOPSPID;
}
137
138
139
140
else {
    $profile_default     = "OneVM";
    $profile_default_pid = $TBOPSPID;
}
141
142
$profile_array  = array();
$am_array       = Instance::DefaultAggregateList();
Leigh B Stoller's avatar
Leigh B Stoller committed
143

144
145
146
147
#
# 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.
#
148
if (isset($profile)) {
149
150
    #
    # Guest users must use the uuid, but logged in users may use the
151
    # internal index. But, we have to support simple the URL too, which
152
    # is /p/project/profilename, but only for public profiles.
153
    #
154
    if (isset($project) && isset($profile)) {
155
	$obj = Profile::LookupByName($project, $profile, $version);
156
157
158
159
160
    }
    elseif ($this_user || IsValidUUID($profile)) {
	$obj = Profile::Lookup($profile);
    }
    else {
161
162
163
	SPITUSERERROR("Illegal profile for guest user: $profile");
	exit();
    }
164
    if (! $obj || $obj->deleted()) {
165
166
167
168
	SPITUSERERROR("No such profile: $profile");
	exit();
    }
    if (IsValidUUID($profile)) {
169
170
171
172
173
	#
	# 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.
	#
174
	if (0 && $profile == $obj->profile_uuid() && !$obj->published()) {
175
176
177
178
179
180
	    $obj = $obj->LookupMostRecentPublished();
	    if (! $obj) {
		SPITUSERERROR("No published version for profile");
		exit();
	    }
	}
181
182
183
        $profile = $obj;
	$profile_array[$profile->uuid()] = $profile->name();
	$profilename = $profile->name();
184
185
    }
    else {
186
187
188
189
190
	#
	# 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.
	#
191
	if (0 && !isset($version) && !$obj->published()) {
192
193
194
195
196
197
198
	    $obj = $obj->LookupMostRecentPublished();
	    if (! $obj) {
		SPITUSERERROR("No published version for profile");
		exit();
	    }
	}
	 
199
	#
Leigh B Stoller's avatar
Leigh B Stoller committed
200
	# Must be public or pass the permission test for the user.
201
202
	#
	if (! ($obj->ispublic() ||
203
	       (isset($this_user) && $obj->CanInstantiate($this_user)))) {
204
205
206
	    SPITUSERERROR("No permission to use profile: $profile");
	    exit();
	}
207
208
209
	$profile = $obj;
	$profile_array[$profile->uuid()] = $profile->name();
	$profilename = $profile->name();
210
    }
211
212
213
214
    if ($profile->isDisabled()) {
        SPITUSERERROR("This profile is disabled!");
        exit();
    }
215
}
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
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 ";
237
    }
238
239
240
241
242
243
244

    $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 ".
245
246
		     "where locked is null and p.disabled=0 and ".
                     "      v.disabled=0 and ($whereclause) ".
247
		     "order by p.topdog desc");
248
249
    while ($row = mysql_fetch_array($query_result)) {
	$profile_array[$row["uuid"]] = $row["name"];
250
251
        if ($row["pid"] == $profile_default_pid &&
            $row["name"] == $profile_default) {
252
	    $profile_default = $row["uuid"];
253
	}
254
    }
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
    #
    # A specific profile, but we still want to give the user the selection
    # list above, but the profile might not be in the list if it is not
    # the highest numbered version.
    #
    if (isset($default)) {
        if (IsValidUUID($default)) {
            $obj = Profile::Lookup($default);
            if (!$obj) {
                SPITUSERERROR("Unknown default profile: $default");
                exit();
            }
            if (! ($obj->ispublic() ||
                   (isset($this_user) && $obj->CanInstantiate($this_user)))) {
                SPITUSERERROR("No permission to use profile: $default");
                exit();
            }
272
273
274
275
            if ($obj->isDisabled()) {
                SPITUSERERROR("This profile is disabled!");
                exit();
            }
276
277
278
279
280
281
282
283
            $profile_array[$obj->uuid()] = $obj->name();
            $profile_default = $obj->uuid();
        }
        else {
            SPITUSERERROR("Illegal default profile: $default");
            exit();
        }
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
284
}
Leigh B Stoller's avatar
Leigh B Stoller committed
285

286
287
288
289
290
291
292
293
294
#
# Rebuild the array with extra info for the profile picker.
#
$tmp_array = array();

while (list ($uuid, $title) = each ($profile_array)) {
    $tmp = Profile::Lookup($uuid);
    if ($tmp) {
        list ($lastused, $count) = $tmp->UsageInfo($this_user);
295
296
297
        if ($lastused == 0) {
            list ($unused, $count) = $tmp->UsageInfo(null);
        }
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
        
        $tmp_array[$uuid] =
            array("name"     => $tmp->name(),
                  "project"  => $tmp->pid(),
                  "favorite" => $tmp->isFavorite($this_user),
                  "lastused" => $lastused,
                  "usecount" => $count);
    }
}
#
# Now we want to order the list.
#
if ($this_user) {
    uasort($tmp_array, function($a, $b) {
        if ($a["lastused"] == $b["lastused"]) {
            return 0;
        }
        return ($a["lastused"] > $b["lastused"]) ? -1 : 1;
    });
}
else {
    uasort($tmp_array, function($a, $b) {
        if ($a["usecount"] == $b["usecount"]) {
            return 0;
        }
        return ($a["usecount"] > $b["usecount"]) ? -1 : 1;
    });
}
$profile_array = $tmp_array;
#TBERROR(print_r($profile_array, true), 0);

329
function SPITFORM($formfields, $newuser, $errors)
Leigh B Stoller's avatar
Leigh B Stoller committed
330
{
331
    global $TBBASE, $APTMAIL, $ISAPT, $ISCLOUD, $ISPNET, $PORTAL_NAME;
332
    global $profile_array, $this_user, $profilename, $profile, $am_array;
333
    global $projlist, $classic;
334
    $amlist     = array();
335
    $fedlist    = array();
336
    $showabout  = ($ISAPT && !$this_user ? 1 : 0);
337
    $registered = (isset($this_user) ? "true" : "false");
338
339
    # We use webonly to mark users that have no project membership
    # at the Geni portal.
Leigh B Stoller's avatar
Leigh B Stoller committed
340
341
    $webonly    = (isset($this_user) &&
                   $this_user->webonly() ? "true" : "false");
342
    $cancopy    = (isset($this_user) && !$this_user->webonly() ? 1 : 0);
343
    $nopprspec  = (!isset($this_user) ? "true" : "false");
344
    $portal     = "";
345
346
347
    $showpicker = (isset($profile) ? 0 : 1);
    if (isset($profilename)) {
        $profilename = "'$profilename'";
348
        $profilevers = $profile->version();
349
350
351
    }
    else {
        $profilename = "null";
352
        $profilevers = "null";
353
    }
354
    SPITHEADER(1);
355

356
357
358
359
    if (!$classic) {
        echo "<link rel='stylesheet' href='css/picker.css'>\n";
    }

360
361
362
363
364
365
366
    # 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";
367
368
369
    echo "<script type='text/plain' id='profiles-json'>\n";
    echo htmlentities(json_encode($profile_array));
    echo "</script>\n";
370
    
371
372
373
374
375
376
377
378
    # 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
379

380
381
    # Place to hang the toplevel template.
    echo "<div id='main-body'></div>\n";
382

Keith Downie's avatar
Keith Downie committed
383
    #
384
    # Spit out a project selection list if a real user.
Keith Downie's avatar
Keith Downie committed
385
    #
386
387
388
389
    if ($this_user && !$this_user->webonly()) {
        $plist = array();
        while (list($project) = each($projlist)) {
            $plist[] = $project;
390
        }
391
392
393
        echo "<script type='text/plain' id='projects-json'>\n";
        echo htmlentities(json_encode($plist));
        echo "</script>\n";
394
    }
395
    #
396
    # And AM list if that is allowed.
397
    #
398
    if (isset($this_user) && !$this_user->webonly() && !$ISAPT && !$ISPNET) {
399
400
	$am_options = "";
	while (list($am, $urn) = each($am_array)) {
401
	    $amlist[$urn] = $am;
402
403
404
405
406
407
408
            #
            # We need to mark federated sites for the cluster dropdown.
            #
            $aggregate = Aggregate::Lookup($urn);
            if ($aggregate && $aggregate->isfederate()) {
                $fedlist[] = "'" . $aggregate->name() . "'";
            }
409
410
411
412
        }
	echo "<script type='text/plain' id='amlist-json'>\n";
	echo htmlentities(json_encode($amlist));
	echo "</script>\n";
Keith Downie's avatar
Keith Downie committed
413
        echo "<script type='text/javascript'>\n";
414
        echo "    window.FEDERATEDLIST  = [". implode(",", $fedlist) . "];\n";
Keith Downie's avatar
Keith Downie committed
415
416
        echo "</script>\n";
    }
417
    SpitOopsModal("oops");
418
    echo "<script type='text/javascript'>\n";
419
    echo "    window.PROFILE    = '" . $formfields["profile"] . "';\n";
420
    echo "    window.PROFILENAME= $profilename;\n";
421
    echo "    window.PROFILEVERS= $profilevers;\n";
422
423
    echo "    window.AJAXURL    = 'server-ajax.php';\n";
    echo "    window.SHOWABOUT  = $showabout;\n";
424
    echo "    window.NOPPRSPEC  = $nopprspec;\n";
425
    echo "    window.REGISTERED = $registered;\n";
426
427
    echo "    window.WEBONLY    = $webonly;\n";
    echo "    window.PORTAL     = '$portal';\n";
428
429
    echo "    window.SHOWPICKER = $showpicker;\n";
    echo "    window.CANCOPY = $cancopy;\n";
430
431
    $isadmin = (isset($this_user) && ISADMIN() ? 1 : 0);
    echo "    window.ISADMIN    = $isadmin;\n";
432
    $multisite = (isset($this_user) ? 1 : 0);
433
    echo "    window.MULTISITE  = $multisite;\n";
434
435
    $doconstraints = (isset($this_user) &&
                      (ISADMINISTRATOR() || STUDLY()) ? 1 : 0);
436
    echo "    window.DOCONSTRAINTS = 1;\n";
437
    echo "    window.PORTAL_NAME = '$PORTAL_NAME';\n";
438
    echo "    window.CLASSIC = " . ($classic ? "true" : "false") . ";\n";
439
    echo "</script>\n";
440
441
    echo "<script src='js/lib/jquery-2.0.3.min.js?nocache=asdfasdf'></script>\n";
    echo "<script src='js/lib/bootstrap.js?nocache=asdfasdf'></script>\n";
442
443
444
    echo "<script src='js/lib/require.js?nocache=asdfasdf' ".
        "data-main='js/instantiate" . ($classic ? "" : "-new") .
        ".js?nocache=asdfasdf'></script>";
Leigh B Stoller's avatar
Leigh B Stoller committed
445
446
447
}

if (!isset($create)) {
448
449
450
451
    $defaults = array();
    $defaults["username"] = "";
    $defaults["email"]    = "";
    $defaults["sshkey"]   = "";
452
453
    $defaults["profile"]  = (isset($profile) ?
                             $profile->uuid() : $profile_default);
454
    $defaults["where"]    = $DEFAULT_AGGREGATE;
455
456
457
458
459
460
461
462
    if ($this_user && count($projlist)) {
	list($project, $grouplist) = each($projlist);
        $defaults["pid"] = $project;
        reset($projlist);
    }
    else {
        $defaults["pid"] = "";
    }
463

464
    # 
465
    # Look for current user or cookie that tells us who the user is. 
466
    #
467
    if ($this_user) {
468
469
	$defaults["username"] = $this_user->uid();
	$defaults["email"]    = $this_user->email();
470
471
472
473
474
475
476
	#
	# 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) {
477
478
	    $sshkeys = $this_user->GetSSHKeys();
	    if (count($sshkeys)) {
479
		$sshkey = $sshkeys[0];
480
481
	    }
	}
482
483
484
	if ($sshkey) {
	    $defaults["sshkey"] = $sshkey;
	}
485
486
    }
    elseif (isset($_COOKIE['quickvm_user'])) {
Leigh B Stoller's avatar
Leigh B Stoller committed
487
488
489
490
491
492
	$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
493
494
	    $instance = Instance::LookupByCreator($geniuser->uuid());
	    if ($instance && $instance->status() != "terminating") {
495
496
		header("Location: status.php?oneonly=1&uuid=" .
		       $instance->uuid());
Leigh B Stoller's avatar
Leigh B Stoller committed
497
498
		return;
	    }
499
500
501
502
503
504
505
506
            #
            # 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;
            }
507
508
509
	    $defaults["username"] = $geniuser->name();
	    $defaults["email"]    = $geniuser->email();
	    $defaults["sshkey"]   = $geniuser->SSHKey();
Leigh B Stoller's avatar
Leigh B Stoller committed
510
511
	}
    }
512
513
514
515
    # We use a session, in case we need to do verification or other things.
    session_start();
    session_unset();

516
    SPITFORM($defaults, false, array());
517
    echo "<div style='display: none'><div id='jacks-dummy'></div></div>\n";
Leigh B Stoller's avatar
Leigh B Stoller committed
518
519
520
521
    SPITFOOTER();
    return;
}
?>