approveuser.php3 15.2 KB
Newer Older
1
<?php
Leigh Stoller's avatar
Leigh Stoller committed
2
#
Mike Hibler's avatar
Mike Hibler committed
3
# Copyright (c) 2000-2013 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
# 
# {{{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/>.
# 
# }}}
Leigh Stoller's avatar
Leigh Stoller committed
23
#
24 25 26
include("defs.php3");

#
27
# Only known and logged in users.
28
#
29 30
$this_user = CheckLoginOrDie();
$uid       = $this_user->uid();
31

32 33 34 35 36
#
# The reason for this call is to make sure that globals are set properly.
#
$reqargs = RequiredPageArguments();

37 38 39 40 41
#
# Standard Testbed Header
#
PAGEHEADER("New Users Approved");

42
# Local used below.
43 44
$projectchecks = array();

45
# Hmm, is this needed?
46 47
ignore_user_abort(1);

48 49 50 51
#
# Walk the list of post variables, looking for the special post format.
# See approveuser_form.php3:
#
Leigh Stoller's avatar
Leigh Stoller committed
52
#             uid     menu     project/group
53 54
#	name=Uxxxx$$approval-testbed/testbed value=approved,denied,postpone
#	name=Uxxxx$$trust-testbed/testbed value=user,local_root
55 56 57 58 59
#
# We make two passes over the post vars. The first does a sanity check so
# that we can bail out without doing anything. This allows the user to
# back up and make changes without worrying about some stuff being done and
# other stuff not. 
60 61 62
#

#
Mike Hibler's avatar
Mike Hibler committed
63
# copy of _POST we use to do actual work.
64
# this is so I can insert an implicit default-group approval
Mike Hibler's avatar
Mike Hibler committed
65
# while iterating over _POST.
66 67 68 69
# A bit kludgey, indeed.
#
$POST_VARS_COPY = array();
 
Mike Hibler's avatar
Mike Hibler committed
70
while (list ($header, $value) = each ($_POST)) {
71
    #echo "$header: $value<br>\n";
72
    $POST_VARS_COPY[$header] = $value; 
73 74 75 76 77 78

    $approval_string = strstr($header, "\$\$approval-");
    if (! $approval_string) {
	continue;
    }

79
    $user     = substr($header, 1, strpos($header, "\$\$", 0) - 1);
Leigh Stoller's avatar
Leigh Stoller committed
80 81 82
    $projgrp  = substr($approval_string, strlen("\$\$approval-"));
    $project  = substr($projgrp, 0, strpos($projgrp, "/", 0));
    $group    = substr($projgrp, strpos($projgrp, "/", 0) + 1);
83 84 85 86 87 88 89 90
    $approval = $value;

    if (!$user || strcmp($user, "") == 0) {
	TBERROR("Parse error finding user in approveuser.php3", 1);
    }
    if (!$project || strcmp($project, "") == 0) {
	TBERROR("Parse error finding project in approveuser.php3", 1);
    }
Leigh Stoller's avatar
Leigh Stoller committed
91 92 93
    if (!$group || strcmp($group, "") == 0) {
	TBERROR("Parse error finding group in approveuser.php3", 1);
    }
94 95 96 97 98 99 100 101
    if (!$approval || strcmp($approval, "") == 0) {
	TBERROR("Parse error finding approval in approveuser.php3", 1);
    }

    #
    # There should be a corresponding trust variable in the POST vars.
    # Note that we construct the variable name and indirect to it.
    #
102 103 104
    $foo      = "U${user}\$\$trust-$project/$group";
    #echo "$foo<br>\n";
    
Mike Hibler's avatar
Mike Hibler committed
105
    $newtrust = $_POST[$foo];
106 107 108
    if (!$newtrust || strcmp($newtrust, "") == 0) {
	TBERROR("Parse error finding trust in approveuser.php3", 1);
    }
Leigh Stoller's avatar
Leigh Stoller committed
109 110 111 112 113 114
    #echo "User $user, Project $project,
    #      Group $group, Approval $approval, Trust $newtrust<br>\n";
    
    if (strcmp($newtrust, "user") &&
	strcmp($newtrust, "local_root") &&
	strcmp($newtrust, "group_root")) {
115 116 117 118
	TBERROR("Invalid trust $newtrust for user $user approveuser.php3.", 1);
    }

    #
119
    # Verify an actual user that is being approved.
120
    #
121
    if (! ($target_user = User::Lookup($user))) {
122
	TBERROR("Trying to approve unknown user $user.", 1);
123
    }
124
    $target_uid = $target_user->uid();
125 126 127 128 129 130 131 132 133 134

    # Ditto the project.
    if (! ($target_project = Project::Lookup($project))) {
	TBERROR("Trying to approve user into unknown project $project.", 1);
    }

    # Ditto the group.
    if (! ($target_group = Group::LookupByPidGid($project, $group))) {
	TBERROR("Trying to approve user into unknown group $group", 1);
    }
135
    
136
    #
137 138
    # Check that the current uid has the necessary trust level
    # to approver users in the project/group. Also, only project leaders
139
    # can add someone to the default group as group_root.
140
    #
141
    if (! $target_group->AccessCheck($this_user, $TB_PROJECT_ADDUSER)) {
Leigh Stoller's avatar
Leigh Stoller committed
142 143
	USERERROR("You are not allowed to approve users in ".
		  "$project/$group!", 1);
144
    }
145

146 147 148 149 150
    if ($newtrust == "group_root" && $project == $group &&
	!$target_project->AccessCheck($this_user,
				      $TB_PROJECT_BESTOWGROUPROOT)) {
	USERERROR("You do not have permission to add new users with group ".
		  "root trust to the default group!", 1);
151
    }
Leigh Stoller's avatar
Leigh Stoller committed
152
    
153
    #
154 155
    # Check if already approved in the project/group. If already an
    # approved member, something went wrong.
156
    #
157
    $target_group->IsMember($target_user, $isapproved);
Leigh Stoller's avatar
Leigh Stoller committed
158
    if ($isapproved) {
159
	USERERROR("$target_uid is already an approved member of ".
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
		  "$project/$group!", 1);
    }

    #
    # Verify approval value. 
    #
    if (strcmp($approval, "postpone") &&
	strcmp($approval, "deny") &&
	strcmp($approval, "nuke") &&
	strcmp($approval, "approve")) {
	TBERROR("Invalid approval value $approval in approveuser.php3.", 1);
    }

    #
    # If denying project membership, then there must be equivalent denial
    # for all subgroups. We can either do it for the user, or require that the
    # user understand whats happening. I prefer the latter, so look for this
    # and spit back an error. Note that we cannot rely on the post vars for
    # this, but must look in the DB for the group set, and then check to make
    # sure there are post vars for *all* of them.
    #
    if (strcmp($project, $group) == 0 &&
	(strcmp($approval, "deny") == 0 ||
	 strcmp($approval, "nuke") == 0)) {
184 185 186 187 188 189

	# List of subgroup membership in this project.
	$grouplist = $target_project->GroupList($target_user);

	foreach ($grouplist as $subgroup) {
	    $gid = $subgroup->gid();
190 191 192 193 194

            #
            # Create and indirect through post var for subgroup approval value.
            #
	    $foo = "$user\$\$approval-$project/$gid";
195
	    
Mike Hibler's avatar
Mike Hibler committed
196 197
	    if (!isset($_POST[$foo]) ||
		($_POST[$foo] != "deny" && $_POST[$foo] != "nuke")) {
198 199 200 201
		USERERROR("If you wish to deny/nuke user $target_uid in ".
			  "project $project then you must deny/nuke in all ".
			  "of the subgroups $target_uid is attempting to ".
			  "join.", 1);
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
	    }
	}
    }

    if (strcmp($approval, "approve") == 0)
	$projectchecks[$user][] = array($project, $group, $newtrust);

    #
    # When operating on a user for a subgroup, the user must already be in the
    # default group, or there must be an appropriate default group operation
    # in the POST vars. In other words, we do not allow users to be
    # approved/denied/postponed to a subgroup without a default group
    # operation as well. At present, all users must be in the default group
    # in addition to subgroups.
    #
    if (strcmp($project, $group) == 0)
	continue;

220
    $target_project->IsMember($target_user, $isapproved);
221 222 223 224 225 226
    if ($isapproved)
	continue;

    #
    # Create and indirect through post var for project approval value.
    #
227 228
    $foo = "U${user}\$\$approval-$project/$project";
    $bar = "U${user}\$\$trust-$project/$project";
Mike Hibler's avatar
Mike Hibler committed
229
    $default_approval = (isset($_POST[$foo]) ? $_POST[$foo] : "");
230
    
231
    if ($default_approval == "") {
232 233 234 235
	# Implicit group approval as user.
	# Short circuit all the perms-checking, and squeeze it in
	# all the appropriate places.
	
236
	# 1. For the strcmp below.
237 238
	$default_approval = $approval;

239
	# 2. For the sanity check
240 241
	$projectchecks[$user][] = array($project, $project, "user");	

242 243
	# 3. For the while loop which does the actual work
	$POST_VARS_COPY[$foo] = $approval;
Mike Hibler's avatar
Mike Hibler committed
244 245
	$_POST[$foo] = $approval;
	$_POST[$bar] = "user";
246 247 248
    }
    if (strcmp($approval, "approve") == 0 &&
	strcmp($default_approval, "approve")) {
249
	USERERROR("You cannot approve $target_uid in $project/$group without ".
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
		  "approval in the default group ($project/$project)!", 1);
    }
}

#
# Sanity check. I hate this stuff.
# 
while (list ($user, $value) = each ($projectchecks)) {
    $projtrust   = array();
    $grouptrust  = array();
    $pidlist     = array();
    
    while (list ($a, $b) = each ($value)) {
	$pid   = $b[0];
	$gid   = $b[1];
	$trust = $b[2];

Leigh Stoller's avatar
Leigh Stoller committed
267
	#echo "$user $pid $gid $trust<br>\n";
268

269 270 271 272 273 274 275
	if (! ($target_group = Group::LookupByPidGid($pid, $gid))) {
	    TBERROR("Could not find group object for $project/$group", 1);
	}

	if (! ($target_user = User::Lookup($user))) {
	    TBERROR("Could not find user object for $user", 1);
	}
276
	$target_uid = $target_user->uid();
277
	
278 279 280 281 282 283 284 285
	#
	# This looks for different trust levels in different subgroups
	# of the same project. We are only checking the form arguments
	# here; we will do a check against the DB below. 
	# 
	if (strcmp($pid, $gid)) {
	    if (isset($grouptrust[$pid]) &&
		strcmp($grouptrust[$pid], $trust)) {
286 287
		USERERROR("User $target_uid may not have different trust ".
			  "levels in different subgroups of $pid!", 1);
288 289 290 291 292 293 294 295 296 297 298
	    }
	    $grouptrust[$pid] = $trust;
	}
	else {
	    #
	    # Stash the project default group trust so that we can also
	    # do a consistency check against it.
	    #
	    $projtrust[$pid] = $trust;
	}
	$pidlist[$pid] = $pid;
299

300
	$target_group->CheckTrustConsistency($target_user, $trust, 1);
301 302 303 304 305
    }
    
    reset($value);
}

Mike Hibler's avatar
Mike Hibler committed
306
reset($_POST);
307

308 309
STARTBUSY("Approving Users");

310 311 312
#
# Okay, all sanity tests passed for all post vars. Now do the actual work.
# 
313
while (list ($header, $value) = each ($POST_VARS_COPY)) {
314
    #echo "$header: $value<br>\n";
315
    flush();
316 317 318

    $approval_string = strstr($header, "\$\$approval-");
    if (! $approval_string) {
Leigh Stoller's avatar
Leigh Stoller committed
319
	continue;
320 321
    }

322
    $user     = substr($header, 1, strpos($header, "\$\$", 0) - 1);
323 324 325 326 327
    $projgrp  = substr($approval_string, strlen("\$\$approval-"));
    $project  = substr($projgrp, 0, strpos($projgrp, "/", 0));
    $group    = substr($projgrp, strpos($projgrp, "/", 0) + 1);
    $approval = $value;

328
    #
329 330
    # Corresponding trust value.
    #
331
    $foo      = "U${user}\$\$trust-$project/$group";
Mike Hibler's avatar
Mike Hibler committed
332
    $newtrust = $_POST[$foo];
333 334 335 336 337 338 339 340

    #
    # Get the current status for the user, which we might need to change.
    #
    # We change the status only if this person is joining his first project.
    # In this case, the status will be either "newuser" or "unapproved",
    # and we will change it to "unapproved" or "active", respectively.
    # If the status is "active", we leave it alone. 
341
    #
342 343
    if (! ($target_user = User::Lookup($user))) {
	TBERROR("Trying to approve unknown user $user.", 1);
344
    }
345 346 347
    $curstatus  = $target_user->status();
    $user_email = $target_user->email();
    $user_name  = $target_user->name();
348
    $user_uid   = $target_user->uid();
349 350
    #echo "Status = $curstatus, Email = $user_email<br>\n";

351 352 353 354 355 356 357 358
    # Ditto the project and group
    if (! ($target_project = Project::Lookup($project))) {
	TBERROR("Trying to approve user into unknown project $project.", 1);
    }
    if (! ($target_group = Group::LookupByPidGid($project, $group))) {
	TBERROR("Trying to approve user into unknown group $group", 1);
    }

359 360
    #
    # Email info for current user.
361 362 363
    #
    $uid_name  = $this_user->name();
    $uid_email = $this_user->email();
364 365

    #
366
    # Email info for the proj/group leaders too.
367
    #
368
    $leaders = $target_group->LeaderMailList();
369

370 371 372 373 374
    #
    # Well, looks like everything is okay. Change the project membership
    # value appropriately.
    #
    if (strcmp($approval, "postpone") == 0) {
375
	echo "<p>
376
                  Membership status for user $user_uid in $project/$group was
377
                  <b>postponed</b> for later decision.\n";
378 379 380 381
        continue;
    }
    if (strcmp($approval, "deny") == 0) {
        #
Leigh Stoller's avatar
Leigh Stoller committed
382 383
        # Must delete the group_membership record since we require that the 
        # user reapply once denied. Send the luser email to let him know.
384
        #
385
	$target_group->DeleteMember($target_user);
Leigh Stoller's avatar
Leigh Stoller committed
386

387 388
	TBMAIL("$user_name '$user_uid' <$user_email>",
	     "Membership Denied in '$project/$group'",
389
	     "\n".
390
	     "This message is to notify you that you have been denied\n".
391
	     "membership in project/group $project/$group.\n".
392 393 394 395 396 397 398
	     "\n\n".
	     "Thanks,\n".
	     "Testbed Operations\n",
	     "From: $uid_name <$uid_email>\n".
	     "Cc:  $leaders\n".
	     "Bcc: $TBMAIL_AUDIT\n".
	     "Errors-To: $TBMAIL_WWW");
399

400
	echo "<p>
401
                User $user_uid was <b>denied</b> membership in $project/$group.
402 403
                <br>
                The user will need to reapply again if this was in error.\n";
404 405 406

	continue;
    }
407 408
    if (strcmp($approval, "nuke") == 0) {
        #
Leigh Stoller's avatar
Leigh Stoller committed
409 410
        # Must delete the group_membership record since we require that the 
        # user reapply once denied. Send the luser email to let him know.
411
        #
412
	$target_group->DeleteMember($target_user);
413 414 415

	#
	# See if user is in any other projects (even unapproved).
Leigh Stoller's avatar
Leigh Stoller committed
416
	#
417
	$project_list = $target_user->ProjectMembershipList();
418 419 420 421

	#
	# If yes, then we cannot safely delete the user account.
	#
422
	if (count($project_list)) {
423
	    echo "<p>
424 425
                  User $user_uid was <b>denied</b> membership in
                  $project/$group.
426
                  <br>
427
                  Since the user is a member (or requesting membership)
428
		  in other projects, the account cannot be safely removed.\n";
429 430 431 432 433 434 435 436 437 438 439 440
	    
	    continue;
	}

	#
	# No other project membership. If the user is unapproved/newuser, 
	# it means he was never approved in any project, and so will
	# likely not be missed. He will be unapproved if he did his
	# verification.
	#
	if (strcmp($curstatus, "newuser") &&
	    strcmp($curstatus, "unapproved")) {
441
	    echo "<p>
442 443
                  User $user_uid was <b>denied</b> membership in
                  $project/$group.
444
                  <br>
445 446
                  Since the user has been approved by, or was active in other
		  projects in the past, the account cannot be safely removed.
447
                  \n";
448 449
	    continue;
	}
450
	SUEXEC($uid, $TBADMINGROUP, "webrmuser -n -p $project $user_uid", 1); 
451

452
	echo "<p>
453
                User $user_uid was <b>denied</b> membership in $project/$group.
454
                <br>
455
		The account has also been <b>terminated</b>!\n";
456 457 458

	continue;
    }
459 460 461 462 463 464
    if (strcmp($approval, "approve") == 0) {
        #
        # Change the status if necessary. This only happens for new
	# users being added to their first project. After this, the status is
        # going to be "active", and we just leave it that way.
	#
465 466 467
	if ($curstatus != TBDB_USERSTATUS_ACTIVE) {
	    if ($curstatus == TBDB_USERSTATUS_UNAPPROVED) {
		$target_user->SetStatus(TBDB_USERSTATUS_ACTIVE);
468
	    }
469
	    else {
470
	        TBERROR("Invalid $user status $curstatus", 1);
471
	    }
472 473
	    if (!($user_interface =
		  $target_project->default_user_interface())) {
474
		$user_interface = TBDB_USER_INTERFACE_EMULAB;
475
	    }
476
	    $target_user->SetUserInterface($user_interface);
477 478 479 480

            #
            # Create user account on control node.
            #
481
	    SUEXEC($uid, $TBADMINGROUP, "webtbacct add $user_uid", 1);
482
	}
483 484 485 486
        #
	# Only need to add new membership.
	# 
	SUEXEC($uid, $TBADMINGROUP,
487
	       "webmodgroups -a $project:$group:$newtrust $user_uid", 1);
488

489
	echo "<p>
490 491
                  User $user_uid was <b>granted</b> membership in
                  $project/$group with $newtrust permissions.\n";
492
		
493 494 495
	continue;
    }
}
496
CLEARBUSY();
497

498 499 500 501
#
# Standard Testbed Footer
# 
PAGEFOOTER();
502
?>