creategeniuser.in 6.61 KB
Newer Older
1 2
#!/usr/bin/perl -w
#
3
# Copyright (c) 2008-2017 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 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
# 
# {{{GENIPUBLIC-LICENSE
# 
# GENI Public License
# 
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and/or hardware specification (the "Work") to
# deal in the Work without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Work, and to permit persons to whom the Work
# is furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Work.
# 
# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS
# IN THE WORK.
# 
# }}}
#
use strict;
use English;
use Getopt::Std;
use Data::Dumper;

#
# Create a geni user. This is a nonlocal user, derived from the
# only info we have which is the urn/email. We use this from CloudLab
# web interface (geni-login) to create a stub local account for a
# geni user. 
#
sub usage()
{
43
    print STDERR "Usage: $0 [-n] [-p project] <urn> <email>\n";
44
    print STDERR "Usage: $0 -r <urn>\n";
45 46
    exit(1);
}
47
my $optlist  = "nrp:";
48 49
my $impotent = 0;
my $delete   = 0;
50
my $project;
51 52 53 54 55 56

# Configure ...
my $TB		  = "@prefix@";
my $TBACCT	  = "$TB/sbin/tbacct";
my $MKUSERCERT    = "$TB/sbin/mkusercert";
my $MODGROUPS     = "$TB/sbin/modgroups";
57
my $WAP           = "$TB/sbin/withadminprivs";
58 59 60

use lib '@prefix@/lib';
use emutil;
61
use emdb;
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
use User;
use Project;
use GeniCertificate;
use GeniHRN;
use EmulabConstants;

# Protos.
sub DeleteGeniUser($);

sub fatal($)
{
    my ($msg) = @_;

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

#
# Parse command arguments. Once we return from getopts, all that should be
# left are the required arguments.
#
my %options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"n"})) {
    $impotent = 1;
}
if (defined($options{"r"})) {
    usage()
	if (@ARGV != 1);
    $delete = 1;
}
else {
    usage()
	if (@ARGV != 2);
}
my $urn     = $ARGV[0];
fatal("Invalid urn")
    if (! GeniHRN::IsValid($urn));
if ($delete) {
    exit(DeleteGeniUser($urn));
}

106 107 108 109 110 111 112 113
# Make sure we can get this project.
if (defined($options{"p"})) {
    my $pid  = $options{"p"};
    $project = Project->Lookup($pid);
    if (!defined($project)) {
	fatal("No such project!");
    }
}
114 115 116 117 118
my $email   = $ARGV[1];
my $usr_uid;
fatal("Invalid email")
    if (! User->ValidEmail($email));

119
#
120
# Must not be a user with same nonlocal ID.
121 122 123 124 125 126 127 128 129
# We need to lock for concurrent attempt to create this user.
# Lets use a DB process lock, named by the URN.
#
my $safe_urn = DBQuoteSpecial($urn);
my $lock_result = DBQueryWarn("select GET_LOCK($safe_urn, 90)");
if (!$lock_result ||
    !$lock_result->numrows) {
    fatal("Could not get SQL lock for a long time!");
}
130
if (User->LookupNonLocal($urn)) {
131 132 133 134
    print "Already have a user with that nonlocal ID (urn)\n";
    DBQueryWarn("select RELEASE_LOCK($safe_urn)");
    # Caller looks for non-zero status. 
    exit(1);
135 136 137 138 139 140 141 142
}

#
# Parse urn and email, maybe we can get a unique uid out of one.
#
my (undef,undef,$uid) = GeniHRN::Parse($urn);
fatal("Could not parse urn")
    if (!defined($uid));
143
if (User->ValidUID($uid)) {
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    $usr_uid = $uid;
}
else {
    #
    # Split email and try that.
    #
    my ($token) = split("@", $email);
    if (defined($token) &&
	User->ValidUID($token) && !User->Lookup($token)) {
	$usr_uid = $token;
    }
}
#
# Neither worked, so need to generate something. Ick.
#
if (!defined($usr_uid)) {
160 161 162 163 164 165 166 167 168 169 170 171 172 173
    # Random
    $usr_uid = "g" . substr(lc(emutil::GenHash()), 0, 6);
}

#
# Now generate a unique uid. We append a two digit integer to
# avoid using up local uid space.
#
my $i;
$uid = substr($uid, 0, 6);
for ($i = 0 ; $i <= 99; $i++) {
    if (!User->Lookup("${uid}${i}")) {
	$usr_uid = "${uid}${i}";
	last;
174 175
    }
}
176 177 178
if ($i > 99) {
    $usr_uid = "g" . substr(lc(emutil::GenHash()), 0, 6);
}
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
if ($impotent) {
    print "Would create nolocal user '$usr_uid' ...\n";
    exit(0);
}

#
# Okay, create new account in the DB.
#
my $user = User->Create($usr_uid,
			$User::NEWUSER_FLAGS_NONLOCAL,
			{"usr_name"      => "Geni User $usr_uid",
			 "usr_email"     => $email,
			 "nonlocal_id"   => $urn,
			 "nonlocal_type" => "geni",
			});
fatal("Could not create user!")
    if (!defined($user));

#
198 199 200
# Add them to the holding project. This will need more thought.  Start
# them out with user permissions, which will prevent them doing much
# of anything. Adjusted later.
201
#
202
if (defined($project) &&
203
    $project->AddMemberShip($user, $Group::MemberShip::TRUSTSTRING_LOCALROOT)) {
204 205 206 207 208 209 210
    $user->Delete();
    fatal("Could not add new user to project");
}

# And then instantiate the user.
system("$TBACCT add $usr_uid");
if ($?) {
Leigh Stoller's avatar
Leigh Stoller committed
211
    $project->GetProjectGroup()->DeleteMemberShip($user)
212
	if (defined($project));
213
    # Make sure the entire account is deleted. Leave DB record on failure
214 215 216
    # so that we can try again later. We are invoked as elabman, so we
    # cap use wap here (required for -f option).
    if (system("$WAP $TBACCT -f del $usr_uid") == 0) {
217 218
	$user->Delete();
    }
219 220 221 222 223 224 225 226
    fatal("Could not instantiate user account!")
}

# We need to generate the encrypted ssl certificate to keep
# things happy.
my $certpass = substr(lc(emutil::GenHash()), 0, 10);
system("$MKUSERCERT -p $certpass $usr_uid");
if ($?) {
Leigh Stoller's avatar
Leigh Stoller committed
227
    $project->GetProjectGroup()->DeleteMemberShip($user)
228
	if (defined($project));
229 230 231 232 233
    # Make sure the entire account is deleted. Leave DB record on failure
    # so that we can try again later. 
    if (system("$TBACCT -f del $usr_uid") == 0) {
	$user->Delete();
    }
234 235
    fatal("Could not create local SSL certificate");
}
236 237
# Do not really need to do this, all locks released on exit.
DBQueryWarn("select RELEASE_LOCK($safe_urn)");
238 239 240 241 242 243 244 245 246 247 248 249 250 251
exit(0);

#
# Delete (purge!) geni user. Not to be used generally, please use
# the normal archive path. This is for debugging.
#
sub DeleteGeniUser($)
{
    my ($urn) = @_;
    my $user = User->LookupNonLocal($urn);
    if (!defined($user)) {
	fatal("No such local user!");
    }
    my $uid = $user->uid();
252 253 254 255 256
    my $project = $user->DefaultProject();
    if (defined($project)) {
	my $pid = $project->pid();
	system("$MODGROUPS -r $pid:$pid $uid");
    }
257 258 259 260 261 262
    system("$TBACCT -f del $uid") == 0 or
	fatal("$TBACCT $uid failed!");

    $user->Delete();
    return 0;
}