modgroups.in 8 KB
Newer Older
1 2 3
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
4
# Copyright (c) 2005, 2006, 2007 University of Utah and the Flux Group.
5 6 7 8
# All rights reserved.
#
use English;
use Getopt::Std;
9
use strict;
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

#
# Modify groups (add and subtract in DB) for a user, and then call
# other scripts that need to do something about it.
#
# Note that this script does not create accounts or groups. That should
# already have been done with other scripts.
#
sub usage()
{
    print STDOUT
	"Usage: modgroups [-a pid:gid:trust[,pid:gid:trust]...] ".
	                 "[-m pid:gid:trust[,pid:gid:trust]...] ".
	                 "[-r pid:gid[,pid:gid]...] user\n";
    exit(-1);
}
my $optlist = "dr:a:m:";
my $debug   = 0;
my $user;
my @addlist = ();
my @modlist = ();	# Just changing the trust value ...
my @remlist = ();
32
my $optarg  = "";
33 34 35 36 37 38 39 40 41 42 43 44

#
# Configure variables
#
my $TB		   = "@prefix@";
my $TBOPS	   = "@TBOPSEMAIL@";
my $TBLOGS	   = "@TBLOGSEMAIL@";
my $CONTROL        = "@USERNODE@";
my $BOSSNODE       = "@BOSSNODE@";
my $WIKISUPPORT    = @WIKISUPPORT@;
my $BUGDBSUPPORT   = @BUGDBSUPPORT@;
my $CHATSUPPORT    = @CHATSUPPORT@;
45
my $ELABINELAB	   = @ELABINELAB@;
46
my $GENELISTS      = "$TB/sbin/genelists";
47
my $MODBUDDIES     = "$TB/sbin/modjabberbuddies";
48
my $SETCHATMEMBERS = "$TB/sbin/setchatmembers";
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
my $SETGROUPS	   = "$TB/sbin/setgroups";
my $SSH		   = "$TB/bin/sshtb";
my $SAVEUID	   = $UID;

#
# Untaint the path
#
$ENV{'PATH'} = "/bin:/usr/bin";
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

#
# Turn off line buffering on output
#
$| = 1;

#
# Load the Testbed support stuff.
#
use lib "@prefix@/lib";
use libaudit;
use libdb;
use libtestbed;
71
use User;
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

# Protos
sub fatal($);

#
# Please do not run it as root. Hard to track what has happened.
#
if ($UID == 0) {
    die("*** $0:\n".
	"    Please do not run this as root!\n");
}

#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
88
my %options = ();
89 90 91 92
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"d"})) {
93 94
    $debug  = 1;
    $optarg = "-d";
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
}
if (defined($options{"a"})) {
    my @tokens = split(",", $options{"a"});

    foreach my $token (@tokens) {
	#
	# Untaint,
	#
	if ($token =~ /^([-\w]+):([-\w]+):([-\w]+)$/) {
	    push(@addlist, "$1:$2:$3");
	}
	else {
	    die("Bad data in token: $token.");
	}
    }
}
if (defined($options{"m"})) {
    my @tokens = split(",", $options{"m"});

    foreach my $token (@tokens) {
	#
	# Untaint,
	#
	if ($token =~ /^([-\w]+):([-\w]+):([-\w]+)$/) {
	    push(@modlist, "$1:$2:$3");
	}
	else {
	    die("Bad data in token: $token.");
	}
    }
}
if (defined($options{"r"})) {
    my @tokens = split(",", $options{"r"});

    foreach my $token (@tokens) {
	#
	# Untaint,
	#
	if ($token =~ /^([-\w]+):([-\w]+)$/) {
	    push(@remlist, "$1:$2");
	}
	else {
	    die("Bad data in token: $token.");
	}
    }
}
usage()
    if (@ARGV != 1);
$user = $ARGV[0];

# Untaint the user.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
146
if ($user =~ /^([-\w]+)$/) {
147 148 149 150 151 152
    $user = $1;
}
else {
    die("Bad user name: $user.");
}

153 154 155 156
# Map invoking user to object.
my $this_user = User->ThisUser();
if (! defined($this_user)) {
    fatal("You ($UID) do not exist!");
157 158
}

159 160 161 162
# Map target user to object.
my $target_user = User->Lookup($user);
if (! defined($target_user)) {
    fatal("$user does not exist!");
163 164
}

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
#
# Permission checks. Do this later.
#

#
# This script is always audited. Mail is sent automatically upon exit.
#
if (AuditStart(0)) {
    #
    # Parent exits normally
    #
    exit(0);
}

#
# Add groups. Do any necessary callouts.
#
foreach my $token (@addlist) {
    my ($pid,$gid,$trust) = split(":", $token);
184
    my $sendemail = 1;
185

186 187 188 189 190
    my $group = Group->LookupByPidGid($pid, $gid);
    if (! defined($group)) {
	fatal("Cannot find group object for $pid/$gid");
    }

191 192 193 194 195
    #
    # Look to see if there is already an entry; this happens as the result
    # of either a join request or a new project request. The row is inserted
    # but with a trust value of "none". We probably want a pending table ...
    #
196
    my $membership = $group->LookupUser($target_user);
197

198
    if (defined($membership)) {
199 200 201
	#
	# Sanity check; better be trust=none.
	#
202
	my $curtrust = $membership->trust();
203 204 205

	fatal("Group membership table problem; ".
	      "$user,$pid,$gid,$trust,$curtrust\n")
206
	    if ($curtrust ne "none" && $curtrust ne $trust && !$ELABINELAB);
207

208
	$membership->ModifyTrust($trust) == 0
209
	    or fatal("Could not modify $membership to $trust");
210 211 212 213

	$sendemail = 0
	    if ($ELABINELAB &&
		$group->IsProjectGroup() && $group->IsLeader($target_user));
214 215
    }
    else {
216
	$group->AddMemberShip($target_user, $trust) == 0
217
	    or fatal("Could not add $user to $group with trust $trust");
218
    }
219

220 221 222 223
    #
    # Tell chat subsystem to change groups ...
    #
    if ($CHATSUPPORT) {
224
	system("$MODBUDDIES $optarg -a $pid:$gid $user") == 0 or
225
	    fatal("$MODBUDDIES '-a $pid:$gid $user' failed!");
226 227 228

	# Does not work yet.
	if (0) {
229
	    system("$SETCHATMEMBERS $optarg -g $gid $pid") == 0 or
230 231
		fatal("$SETCHATMEMBERS '-g $gid $pid' failed!");
	}
232
    }
233 234 235 236 237

    if ($sendemail) {
	$group->SendApprovalEmail($this_user, $target_user) == 0 or
	    fatal("Could not send approval email to $target_user in $group");
    }
238 239 240 241 242 243 244 245
}

#
# Update groups. Do any necessary callouts.
#
foreach my $token (@modlist) {
    my ($pid,$gid,$trust) = split(":", $token);

246 247 248 249 250 251 252 253 254 255
    my $group = Group->LookupByPidGid($pid, $gid);
    if (! defined($group)) {
	fatal("Cannot find group object for $pid/$gid");
    }
    my $membership = $group->LookupUser($target_user);
    if (! defined($membership)) {
	fatal("Cannot find membership object for $user in $group");
    }
    $membership->ModifyTrust($trust) == 0
	    or fatal("Could not modify $membership to '$trust'");
256 257 258

    $group->SendTrustChangeEmail($this_user, $target_user) == 0
	or fatal("Could not send trust change email to $target_user");
259 260 261 262 263 264 265 266 267
}

#
# Remove groups. Do any necessary callouts.
#
foreach my $token (@remlist) {
    my ($pid,$gid)  = split(":", $token);
    my @delgroups   = ();

268 269 270 271 272
    my $group = Group->LookupByPidGid($pid, $gid);
    if (! defined($group)) {
	fatal("Cannot find group object for $pid/$gid");
    }

273 274 275 276 277 278
    #
    # Special case; if pid==gid then delete from the project entirely.
    # However, we want to get a list of the groups about to be deleted
    # so we know what to hand off to the callouts.
    #
    if ($pid eq $gid) {
279 280 281 282
	my $project = Project->LookupByPid($pid);
	
	if (! defined($project)) {
	    fatal("Cannot find group object for $pid/$gid");
283
	}
284 285
	$project->DeleteUser($target_user, \@delgroups) == 0
	    or fatal("Could not delete $user from $project");
286 287
    }
    else {
288 289 290 291
	@delgroups = ($group);

	$group->DeleteMemberShip($target_user) == 0
	    or fatal("Could not delete $user from $group");
292 293
    }

294 295 296 297 298 299 300 301 302
    #
    # When deleting a user from a group, need to call genelists on the
    # project, not the user, since that information is obviously gone from
    # the DB, and genelists will not generate the right lists.
    #
    print "Updating email lists for project $pid\n";
    system("$GENELISTS $optarg -p $pid") == 0 or
	fatal("$GENELISTS -p $pid failed!");

303 304 305 306
    #
    # Tell chat subsystem to change groups ...
    #
    if ($CHATSUPPORT) {
307
	my @optlist = ();
308

309 310 311 312
	foreach my $delgroup (@delgroups) {
	    my $groupname = $delgroup->gid();
	    
	    push(@optlist, "$pid:$groupname");
313

314 315
	    # Does not work yet.
	    if (0) {
316 317
		system("$SETCHATMEMBERS $optarg -g $groupname $pid") == 0 or
		    fatal("$SETCHATMEMBERS '-g $groupname $pid' failed!");
318
	    }
319
	}
320 321 322
	if (@optlist) {
	    my $chatargs = join(",", @optlist);
	
323
	    system("$MODBUDDIES $optarg -r $chatargs $user") == 0 or
324 325
		fatal("$MODBUDDIES '-r $chatargs $user' failed!");
	}
326 327 328
    }
}

329 330 331 332 333 334 335 336 337 338 339
#
# Call genelists, but just when adding or modifying. For deletion, it
# is handled above.
#
if (@addlist || @modlist) {
    print "Updating email lists for user $user\n";
    
    system("$GENELISTS $optarg -u $user") == 0 or
	fatal("$GENELISTS -u $user failed!");
}

340 341 342 343 344 345 346 347 348 349 350 351 352 353
#
# Finally, call setgroups to do the rest.
#
system("$SETGROUPS $user") == 0 or
    fatal("$SETGROUPS $user failed!");

exit(0);

sub fatal($) {
    my($mesg) = $_[0];

    die("*** $0:\n".
	"    $mesg\n");
}